diff options
Diffstat (limited to 'security')
61 files changed, 23351 insertions, 0 deletions
diff --git a/security/Kconfig b/security/Kconfig new file mode 100644 index 000000000000..dcf04a09185d --- /dev/null +++ b/security/Kconfig | |||
@@ -0,0 +1,91 @@ | |||
1 | # | ||
2 | # Security configuration | ||
3 | # | ||
4 | |||
5 | menu "Security options" | ||
6 | |||
7 | config KEYS | ||
8 | bool "Enable access key retention support" | ||
9 | help | ||
10 | This option provides support for retaining authentication tokens and | ||
11 | access keys in the kernel. | ||
12 | |||
13 | It also includes provision of methods by which such keys might be | ||
14 | associated with a process so that network filesystems, encryption | ||
15 | support and the like can find them. | ||
16 | |||
17 | Furthermore, a special type of key is available that acts as keyring: | ||
18 | a searchable sequence of keys. Each process is equipped with access | ||
19 | to five standard keyrings: UID-specific, GID-specific, session, | ||
20 | process and thread. | ||
21 | |||
22 | If you are unsure as to whether this is required, answer N. | ||
23 | |||
24 | config KEYS_DEBUG_PROC_KEYS | ||
25 | bool "Enable the /proc/keys file by which all keys may be viewed" | ||
26 | depends on KEYS | ||
27 | help | ||
28 | This option turns on support for the /proc/keys file through which | ||
29 | all the keys on the system can be listed. | ||
30 | |||
31 | This option is a slight security risk in that it makes it possible | ||
32 | for anyone to see all the keys on the system. Normally the manager | ||
33 | pretends keys that are inaccessible to a process don't exist as far | ||
34 | as that process is concerned. | ||
35 | |||
36 | config SECURITY | ||
37 | bool "Enable different security models" | ||
38 | help | ||
39 | This allows you to choose different security modules to be | ||
40 | configured into your kernel. | ||
41 | |||
42 | If this option is not selected, the default Linux security | ||
43 | model will be used. | ||
44 | |||
45 | If you are unsure how to answer this question, answer N. | ||
46 | |||
47 | config SECURITY_NETWORK | ||
48 | bool "Socket and Networking Security Hooks" | ||
49 | depends on SECURITY | ||
50 | help | ||
51 | This enables the socket and networking security hooks. | ||
52 | If enabled, a security module can use these hooks to | ||
53 | implement socket and networking access controls. | ||
54 | If you are unsure how to answer this question, answer N. | ||
55 | |||
56 | config SECURITY_CAPABILITIES | ||
57 | tristate "Default Linux Capabilities" | ||
58 | depends on SECURITY | ||
59 | help | ||
60 | This enables the "default" Linux capabilities functionality. | ||
61 | If you are unsure how to answer this question, answer Y. | ||
62 | |||
63 | config SECURITY_ROOTPLUG | ||
64 | tristate "Root Plug Support" | ||
65 | depends on USB && SECURITY | ||
66 | help | ||
67 | This is a sample LSM module that should only be used as such. | ||
68 | It prevents any programs running with egid == 0 if a specific | ||
69 | USB device is not present in the system. | ||
70 | |||
71 | See <http://www.linuxjournal.com/article.php?sid=6279> for | ||
72 | more information about this module. | ||
73 | |||
74 | If you are unsure how to answer this question, answer N. | ||
75 | |||
76 | config SECURITY_SECLVL | ||
77 | tristate "BSD Secure Levels" | ||
78 | depends on SECURITY | ||
79 | select CRYPTO | ||
80 | select CRYPTO_SHA1 | ||
81 | help | ||
82 | Implements BSD Secure Levels as an LSM. See | ||
83 | <file:Documentation/seclvl.txt> for instructions on how to use this | ||
84 | module. | ||
85 | |||
86 | If you are unsure how to answer this question, answer N. | ||
87 | |||
88 | source security/selinux/Kconfig | ||
89 | |||
90 | endmenu | ||
91 | |||
diff --git a/security/Makefile b/security/Makefile new file mode 100644 index 000000000000..197cc2f3f1ec --- /dev/null +++ b/security/Makefile | |||
@@ -0,0 +1,19 @@ | |||
1 | # | ||
2 | # Makefile for the kernel security code | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_KEYS) += keys/ | ||
6 | subdir-$(CONFIG_SECURITY_SELINUX) += selinux | ||
7 | |||
8 | # if we don't select a security model, use the default capabilities | ||
9 | ifneq ($(CONFIG_SECURITY),y) | ||
10 | obj-y += commoncap.o | ||
11 | endif | ||
12 | |||
13 | # Object file lists | ||
14 | obj-$(CONFIG_SECURITY) += security.o dummy.o | ||
15 | # Must precede capability.o in order to stack properly. | ||
16 | obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o | ||
17 | obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o | ||
18 | obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o | ||
19 | obj-$(CONFIG_SECURITY_SECLVL) += seclvl.o | ||
diff --git a/security/capability.c b/security/capability.c new file mode 100644 index 000000000000..ec18d6075625 --- /dev/null +++ b/security/capability.c | |||
@@ -0,0 +1,104 @@ | |||
1 | /* | ||
2 | * Capabilities Linux Security Module | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/security.h> | ||
16 | #include <linux/file.h> | ||
17 | #include <linux/mm.h> | ||
18 | #include <linux/mman.h> | ||
19 | #include <linux/pagemap.h> | ||
20 | #include <linux/swap.h> | ||
21 | #include <linux/smp_lock.h> | ||
22 | #include <linux/skbuff.h> | ||
23 | #include <linux/netlink.h> | ||
24 | #include <linux/ptrace.h> | ||
25 | #include <linux/moduleparam.h> | ||
26 | |||
27 | static struct security_operations capability_ops = { | ||
28 | .ptrace = cap_ptrace, | ||
29 | .capget = cap_capget, | ||
30 | .capset_check = cap_capset_check, | ||
31 | .capset_set = cap_capset_set, | ||
32 | .capable = cap_capable, | ||
33 | .settime = cap_settime, | ||
34 | .netlink_send = cap_netlink_send, | ||
35 | .netlink_recv = cap_netlink_recv, | ||
36 | |||
37 | .bprm_apply_creds = cap_bprm_apply_creds, | ||
38 | .bprm_set_security = cap_bprm_set_security, | ||
39 | .bprm_secureexec = cap_bprm_secureexec, | ||
40 | |||
41 | .inode_setxattr = cap_inode_setxattr, | ||
42 | .inode_removexattr = cap_inode_removexattr, | ||
43 | |||
44 | .task_post_setuid = cap_task_post_setuid, | ||
45 | .task_reparent_to_init = cap_task_reparent_to_init, | ||
46 | |||
47 | .syslog = cap_syslog, | ||
48 | |||
49 | .vm_enough_memory = cap_vm_enough_memory, | ||
50 | }; | ||
51 | |||
52 | #define MY_NAME __stringify(KBUILD_MODNAME) | ||
53 | |||
54 | /* flag to keep track of how we were registered */ | ||
55 | static int secondary; | ||
56 | |||
57 | static int capability_disable; | ||
58 | module_param_named(disable, capability_disable, int, 0); | ||
59 | MODULE_PARM_DESC(disable, "To disable capabilities module set disable = 1"); | ||
60 | |||
61 | static int __init capability_init (void) | ||
62 | { | ||
63 | if (capability_disable) { | ||
64 | printk(KERN_INFO "Capabilities disabled at initialization\n"); | ||
65 | return 0; | ||
66 | } | ||
67 | /* register ourselves with the security framework */ | ||
68 | if (register_security (&capability_ops)) { | ||
69 | /* try registering with primary module */ | ||
70 | if (mod_reg_security (MY_NAME, &capability_ops)) { | ||
71 | printk (KERN_INFO "Failure registering capabilities " | ||
72 | "with primary security module.\n"); | ||
73 | return -EINVAL; | ||
74 | } | ||
75 | secondary = 1; | ||
76 | } | ||
77 | printk (KERN_INFO "Capability LSM initialized%s\n", | ||
78 | secondary ? " as secondary" : ""); | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static void __exit capability_exit (void) | ||
83 | { | ||
84 | if (capability_disable) | ||
85 | return; | ||
86 | /* remove ourselves from the security framework */ | ||
87 | if (secondary) { | ||
88 | if (mod_unreg_security (MY_NAME, &capability_ops)) | ||
89 | printk (KERN_INFO "Failure unregistering capabilities " | ||
90 | "with primary module.\n"); | ||
91 | return; | ||
92 | } | ||
93 | |||
94 | if (unregister_security (&capability_ops)) { | ||
95 | printk (KERN_INFO | ||
96 | "Failure unregistering capabilities with the kernel\n"); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | security_initcall (capability_init); | ||
101 | module_exit (capability_exit); | ||
102 | |||
103 | MODULE_DESCRIPTION("Standard Linux Capabilities Security Module"); | ||
104 | MODULE_LICENSE("GPL"); | ||
diff --git a/security/commoncap.c b/security/commoncap.c new file mode 100644 index 000000000000..849b8c338ee8 --- /dev/null +++ b/security/commoncap.c | |||
@@ -0,0 +1,345 @@ | |||
1 | /* Common capabilities, needed by capability.o and root_plug.o | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License as published by | ||
5 | * the Free Software Foundation; either version 2 of the License, or | ||
6 | * (at your option) any later version. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/config.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/security.h> | ||
15 | #include <linux/file.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/mman.h> | ||
18 | #include <linux/pagemap.h> | ||
19 | #include <linux/swap.h> | ||
20 | #include <linux/smp_lock.h> | ||
21 | #include <linux/skbuff.h> | ||
22 | #include <linux/netlink.h> | ||
23 | #include <linux/ptrace.h> | ||
24 | #include <linux/xattr.h> | ||
25 | #include <linux/hugetlb.h> | ||
26 | |||
27 | int cap_netlink_send(struct sock *sk, struct sk_buff *skb) | ||
28 | { | ||
29 | NETLINK_CB(skb).eff_cap = current->cap_effective; | ||
30 | return 0; | ||
31 | } | ||
32 | |||
33 | EXPORT_SYMBOL(cap_netlink_send); | ||
34 | |||
35 | int cap_netlink_recv(struct sk_buff *skb) | ||
36 | { | ||
37 | if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) | ||
38 | return -EPERM; | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | EXPORT_SYMBOL(cap_netlink_recv); | ||
43 | |||
44 | int cap_capable (struct task_struct *tsk, int cap) | ||
45 | { | ||
46 | /* Derived from include/linux/sched.h:capable. */ | ||
47 | if (cap_raised(tsk->cap_effective, cap)) | ||
48 | return 0; | ||
49 | return -EPERM; | ||
50 | } | ||
51 | |||
52 | int cap_settime(struct timespec *ts, struct timezone *tz) | ||
53 | { | ||
54 | if (!capable(CAP_SYS_TIME)) | ||
55 | return -EPERM; | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | int cap_ptrace (struct task_struct *parent, struct task_struct *child) | ||
60 | { | ||
61 | /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */ | ||
62 | if (!cap_issubset (child->cap_permitted, current->cap_permitted) && | ||
63 | !capable(CAP_SYS_PTRACE)) | ||
64 | return -EPERM; | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | int cap_capget (struct task_struct *target, kernel_cap_t *effective, | ||
69 | kernel_cap_t *inheritable, kernel_cap_t *permitted) | ||
70 | { | ||
71 | /* Derived from kernel/capability.c:sys_capget. */ | ||
72 | *effective = cap_t (target->cap_effective); | ||
73 | *inheritable = cap_t (target->cap_inheritable); | ||
74 | *permitted = cap_t (target->cap_permitted); | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | int cap_capset_check (struct task_struct *target, kernel_cap_t *effective, | ||
79 | kernel_cap_t *inheritable, kernel_cap_t *permitted) | ||
80 | { | ||
81 | /* Derived from kernel/capability.c:sys_capset. */ | ||
82 | /* verify restrictions on target's new Inheritable set */ | ||
83 | if (!cap_issubset (*inheritable, | ||
84 | cap_combine (target->cap_inheritable, | ||
85 | current->cap_permitted))) { | ||
86 | return -EPERM; | ||
87 | } | ||
88 | |||
89 | /* verify restrictions on target's new Permitted set */ | ||
90 | if (!cap_issubset (*permitted, | ||
91 | cap_combine (target->cap_permitted, | ||
92 | current->cap_permitted))) { | ||
93 | return -EPERM; | ||
94 | } | ||
95 | |||
96 | /* verify the _new_Effective_ is a subset of the _new_Permitted_ */ | ||
97 | if (!cap_issubset (*effective, *permitted)) { | ||
98 | return -EPERM; | ||
99 | } | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | void cap_capset_set (struct task_struct *target, kernel_cap_t *effective, | ||
105 | kernel_cap_t *inheritable, kernel_cap_t *permitted) | ||
106 | { | ||
107 | target->cap_effective = *effective; | ||
108 | target->cap_inheritable = *inheritable; | ||
109 | target->cap_permitted = *permitted; | ||
110 | } | ||
111 | |||
112 | int cap_bprm_set_security (struct linux_binprm *bprm) | ||
113 | { | ||
114 | /* Copied from fs/exec.c:prepare_binprm. */ | ||
115 | |||
116 | /* We don't have VFS support for capabilities yet */ | ||
117 | cap_clear (bprm->cap_inheritable); | ||
118 | cap_clear (bprm->cap_permitted); | ||
119 | cap_clear (bprm->cap_effective); | ||
120 | |||
121 | /* To support inheritance of root-permissions and suid-root | ||
122 | * executables under compatibility mode, we raise all three | ||
123 | * capability sets for the file. | ||
124 | * | ||
125 | * If only the real uid is 0, we only raise the inheritable | ||
126 | * and permitted sets of the executable file. | ||
127 | */ | ||
128 | |||
129 | if (!issecure (SECURE_NOROOT)) { | ||
130 | if (bprm->e_uid == 0 || current->uid == 0) { | ||
131 | cap_set_full (bprm->cap_inheritable); | ||
132 | cap_set_full (bprm->cap_permitted); | ||
133 | } | ||
134 | if (bprm->e_uid == 0) | ||
135 | cap_set_full (bprm->cap_effective); | ||
136 | } | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | ||
141 | { | ||
142 | /* Derived from fs/exec.c:compute_creds. */ | ||
143 | kernel_cap_t new_permitted, working; | ||
144 | |||
145 | new_permitted = cap_intersect (bprm->cap_permitted, cap_bset); | ||
146 | working = cap_intersect (bprm->cap_inheritable, | ||
147 | current->cap_inheritable); | ||
148 | new_permitted = cap_combine (new_permitted, working); | ||
149 | |||
150 | if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || | ||
151 | !cap_issubset (new_permitted, current->cap_permitted)) { | ||
152 | current->mm->dumpable = 0; | ||
153 | |||
154 | if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) { | ||
155 | if (!capable(CAP_SETUID)) { | ||
156 | bprm->e_uid = current->uid; | ||
157 | bprm->e_gid = current->gid; | ||
158 | } | ||
159 | if (!capable (CAP_SETPCAP)) { | ||
160 | new_permitted = cap_intersect (new_permitted, | ||
161 | current->cap_permitted); | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | |||
166 | current->suid = current->euid = current->fsuid = bprm->e_uid; | ||
167 | current->sgid = current->egid = current->fsgid = bprm->e_gid; | ||
168 | |||
169 | /* For init, we want to retain the capabilities set | ||
170 | * in the init_task struct. Thus we skip the usual | ||
171 | * capability rules */ | ||
172 | if (current->pid != 1) { | ||
173 | current->cap_permitted = new_permitted; | ||
174 | current->cap_effective = | ||
175 | cap_intersect (new_permitted, bprm->cap_effective); | ||
176 | } | ||
177 | |||
178 | /* AUD: Audit candidate if current->cap_effective is set */ | ||
179 | |||
180 | current->keep_capabilities = 0; | ||
181 | } | ||
182 | |||
183 | int cap_bprm_secureexec (struct linux_binprm *bprm) | ||
184 | { | ||
185 | /* If/when this module is enhanced to incorporate capability | ||
186 | bits on files, the test below should be extended to also perform a | ||
187 | test between the old and new capability sets. For now, | ||
188 | it simply preserves the legacy decision algorithm used by | ||
189 | the old userland. */ | ||
190 | return (current->euid != current->uid || | ||
191 | current->egid != current->gid); | ||
192 | } | ||
193 | |||
194 | int cap_inode_setxattr(struct dentry *dentry, char *name, void *value, | ||
195 | size_t size, int flags) | ||
196 | { | ||
197 | if (!strncmp(name, XATTR_SECURITY_PREFIX, | ||
198 | sizeof(XATTR_SECURITY_PREFIX) - 1) && | ||
199 | !capable(CAP_SYS_ADMIN)) | ||
200 | return -EPERM; | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | int cap_inode_removexattr(struct dentry *dentry, char *name) | ||
205 | { | ||
206 | if (!strncmp(name, XATTR_SECURITY_PREFIX, | ||
207 | sizeof(XATTR_SECURITY_PREFIX) - 1) && | ||
208 | !capable(CAP_SYS_ADMIN)) | ||
209 | return -EPERM; | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | /* moved from kernel/sys.c. */ | ||
214 | /* | ||
215 | * cap_emulate_setxuid() fixes the effective / permitted capabilities of | ||
216 | * a process after a call to setuid, setreuid, or setresuid. | ||
217 | * | ||
218 | * 1) When set*uiding _from_ one of {r,e,s}uid == 0 _to_ all of | ||
219 | * {r,e,s}uid != 0, the permitted and effective capabilities are | ||
220 | * cleared. | ||
221 | * | ||
222 | * 2) When set*uiding _from_ euid == 0 _to_ euid != 0, the effective | ||
223 | * capabilities of the process are cleared. | ||
224 | * | ||
225 | * 3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective | ||
226 | * capabilities are set to the permitted capabilities. | ||
227 | * | ||
228 | * fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should | ||
229 | * never happen. | ||
230 | * | ||
231 | * -astor | ||
232 | * | ||
233 | * cevans - New behaviour, Oct '99 | ||
234 | * A process may, via prctl(), elect to keep its capabilities when it | ||
235 | * calls setuid() and switches away from uid==0. Both permitted and | ||
236 | * effective sets will be retained. | ||
237 | * Without this change, it was impossible for a daemon to drop only some | ||
238 | * of its privilege. The call to setuid(!=0) would drop all privileges! | ||
239 | * Keeping uid 0 is not an option because uid 0 owns too many vital | ||
240 | * files.. | ||
241 | * Thanks to Olaf Kirch and Peter Benie for spotting this. | ||
242 | */ | ||
243 | static inline void cap_emulate_setxuid (int old_ruid, int old_euid, | ||
244 | int old_suid) | ||
245 | { | ||
246 | if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) && | ||
247 | (current->uid != 0 && current->euid != 0 && current->suid != 0) && | ||
248 | !current->keep_capabilities) { | ||
249 | cap_clear (current->cap_permitted); | ||
250 | cap_clear (current->cap_effective); | ||
251 | } | ||
252 | if (old_euid == 0 && current->euid != 0) { | ||
253 | cap_clear (current->cap_effective); | ||
254 | } | ||
255 | if (old_euid != 0 && current->euid == 0) { | ||
256 | current->cap_effective = current->cap_permitted; | ||
257 | } | ||
258 | } | ||
259 | |||
260 | int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid, | ||
261 | int flags) | ||
262 | { | ||
263 | switch (flags) { | ||
264 | case LSM_SETID_RE: | ||
265 | case LSM_SETID_ID: | ||
266 | case LSM_SETID_RES: | ||
267 | /* Copied from kernel/sys.c:setreuid/setuid/setresuid. */ | ||
268 | if (!issecure (SECURE_NO_SETUID_FIXUP)) { | ||
269 | cap_emulate_setxuid (old_ruid, old_euid, old_suid); | ||
270 | } | ||
271 | break; | ||
272 | case LSM_SETID_FS: | ||
273 | { | ||
274 | uid_t old_fsuid = old_ruid; | ||
275 | |||
276 | /* Copied from kernel/sys.c:setfsuid. */ | ||
277 | |||
278 | /* | ||
279 | * FIXME - is fsuser used for all CAP_FS_MASK capabilities? | ||
280 | * if not, we might be a bit too harsh here. | ||
281 | */ | ||
282 | |||
283 | if (!issecure (SECURE_NO_SETUID_FIXUP)) { | ||
284 | if (old_fsuid == 0 && current->fsuid != 0) { | ||
285 | cap_t (current->cap_effective) &= | ||
286 | ~CAP_FS_MASK; | ||
287 | } | ||
288 | if (old_fsuid != 0 && current->fsuid == 0) { | ||
289 | cap_t (current->cap_effective) |= | ||
290 | (cap_t (current->cap_permitted) & | ||
291 | CAP_FS_MASK); | ||
292 | } | ||
293 | } | ||
294 | break; | ||
295 | } | ||
296 | default: | ||
297 | return -EINVAL; | ||
298 | } | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | void cap_task_reparent_to_init (struct task_struct *p) | ||
304 | { | ||
305 | p->cap_effective = CAP_INIT_EFF_SET; | ||
306 | p->cap_inheritable = CAP_INIT_INH_SET; | ||
307 | p->cap_permitted = CAP_FULL_SET; | ||
308 | p->keep_capabilities = 0; | ||
309 | return; | ||
310 | } | ||
311 | |||
312 | int cap_syslog (int type) | ||
313 | { | ||
314 | if ((type != 3 && type != 10) && !capable(CAP_SYS_ADMIN)) | ||
315 | return -EPERM; | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | int cap_vm_enough_memory(long pages) | ||
320 | { | ||
321 | int cap_sys_admin = 0; | ||
322 | |||
323 | if (cap_capable(current, CAP_SYS_ADMIN) == 0) | ||
324 | cap_sys_admin = 1; | ||
325 | return __vm_enough_memory(pages, cap_sys_admin); | ||
326 | } | ||
327 | |||
328 | EXPORT_SYMBOL(cap_capable); | ||
329 | EXPORT_SYMBOL(cap_settime); | ||
330 | EXPORT_SYMBOL(cap_ptrace); | ||
331 | EXPORT_SYMBOL(cap_capget); | ||
332 | EXPORT_SYMBOL(cap_capset_check); | ||
333 | EXPORT_SYMBOL(cap_capset_set); | ||
334 | EXPORT_SYMBOL(cap_bprm_set_security); | ||
335 | EXPORT_SYMBOL(cap_bprm_apply_creds); | ||
336 | EXPORT_SYMBOL(cap_bprm_secureexec); | ||
337 | EXPORT_SYMBOL(cap_inode_setxattr); | ||
338 | EXPORT_SYMBOL(cap_inode_removexattr); | ||
339 | EXPORT_SYMBOL(cap_task_post_setuid); | ||
340 | EXPORT_SYMBOL(cap_task_reparent_to_init); | ||
341 | EXPORT_SYMBOL(cap_syslog); | ||
342 | EXPORT_SYMBOL(cap_vm_enough_memory); | ||
343 | |||
344 | MODULE_DESCRIPTION("Standard Linux Common Capabilities Security Module"); | ||
345 | MODULE_LICENSE("GPL"); | ||
diff --git a/security/dummy.c b/security/dummy.c new file mode 100644 index 000000000000..b32eff146547 --- /dev/null +++ b/security/dummy.c | |||
@@ -0,0 +1,996 @@ | |||
1 | /* | ||
2 | * Stub functions for the default security function pointers in case no | ||
3 | * security model is loaded. | ||
4 | * | ||
5 | * Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com> | ||
6 | * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com> | ||
7 | * Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | */ | ||
14 | |||
15 | #undef DEBUG | ||
16 | |||
17 | #include <linux/config.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/mman.h> | ||
21 | #include <linux/pagemap.h> | ||
22 | #include <linux/swap.h> | ||
23 | #include <linux/security.h> | ||
24 | #include <linux/skbuff.h> | ||
25 | #include <linux/netlink.h> | ||
26 | #include <net/sock.h> | ||
27 | #include <linux/xattr.h> | ||
28 | #include <linux/hugetlb.h> | ||
29 | #include <linux/ptrace.h> | ||
30 | #include <linux/file.h> | ||
31 | |||
32 | static int dummy_ptrace (struct task_struct *parent, struct task_struct *child) | ||
33 | { | ||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | static int dummy_capget (struct task_struct *target, kernel_cap_t * effective, | ||
38 | kernel_cap_t * inheritable, kernel_cap_t * permitted) | ||
39 | { | ||
40 | *effective = *inheritable = *permitted = 0; | ||
41 | if (!issecure(SECURE_NOROOT)) { | ||
42 | if (target->euid == 0) { | ||
43 | *permitted |= (~0 & ~CAP_FS_MASK); | ||
44 | *effective |= (~0 & ~CAP_TO_MASK(CAP_SETPCAP) & ~CAP_FS_MASK); | ||
45 | } | ||
46 | if (target->fsuid == 0) { | ||
47 | *permitted |= CAP_FS_MASK; | ||
48 | *effective |= CAP_FS_MASK; | ||
49 | } | ||
50 | } | ||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | static int dummy_capset_check (struct task_struct *target, | ||
55 | kernel_cap_t * effective, | ||
56 | kernel_cap_t * inheritable, | ||
57 | kernel_cap_t * permitted) | ||
58 | { | ||
59 | return -EPERM; | ||
60 | } | ||
61 | |||
62 | static void dummy_capset_set (struct task_struct *target, | ||
63 | kernel_cap_t * effective, | ||
64 | kernel_cap_t * inheritable, | ||
65 | kernel_cap_t * permitted) | ||
66 | { | ||
67 | return; | ||
68 | } | ||
69 | |||
70 | static int dummy_acct (struct file *file) | ||
71 | { | ||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static int dummy_capable (struct task_struct *tsk, int cap) | ||
76 | { | ||
77 | if (cap_raised (tsk->cap_effective, cap)) | ||
78 | return 0; | ||
79 | return -EPERM; | ||
80 | } | ||
81 | |||
82 | static int dummy_sysctl (ctl_table * table, int op) | ||
83 | { | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static int dummy_quotactl (int cmds, int type, int id, struct super_block *sb) | ||
88 | { | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static int dummy_quota_on (struct dentry *dentry) | ||
93 | { | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static int dummy_syslog (int type) | ||
98 | { | ||
99 | if ((type != 3 && type != 10) && current->euid) | ||
100 | return -EPERM; | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int dummy_settime(struct timespec *ts, struct timezone *tz) | ||
105 | { | ||
106 | if (!capable(CAP_SYS_TIME)) | ||
107 | return -EPERM; | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static int dummy_vm_enough_memory(long pages) | ||
112 | { | ||
113 | int cap_sys_admin = 0; | ||
114 | |||
115 | if (dummy_capable(current, CAP_SYS_ADMIN) == 0) | ||
116 | cap_sys_admin = 1; | ||
117 | return __vm_enough_memory(pages, cap_sys_admin); | ||
118 | } | ||
119 | |||
120 | static int dummy_bprm_alloc_security (struct linux_binprm *bprm) | ||
121 | { | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | static void dummy_bprm_free_security (struct linux_binprm *bprm) | ||
126 | { | ||
127 | return; | ||
128 | } | ||
129 | |||
130 | static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | ||
131 | { | ||
132 | if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) { | ||
133 | current->mm->dumpable = 0; | ||
134 | |||
135 | if ((unsafe & ~LSM_UNSAFE_PTRACE_CAP) && !capable(CAP_SETUID)) { | ||
136 | bprm->e_uid = current->uid; | ||
137 | bprm->e_gid = current->gid; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | current->suid = current->euid = current->fsuid = bprm->e_uid; | ||
142 | current->sgid = current->egid = current->fsgid = bprm->e_gid; | ||
143 | |||
144 | dummy_capget(current, ¤t->cap_effective, ¤t->cap_inheritable, ¤t->cap_permitted); | ||
145 | } | ||
146 | |||
147 | static void dummy_bprm_post_apply_creds (struct linux_binprm *bprm) | ||
148 | { | ||
149 | return; | ||
150 | } | ||
151 | |||
152 | static int dummy_bprm_set_security (struct linux_binprm *bprm) | ||
153 | { | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static int dummy_bprm_check_security (struct linux_binprm *bprm) | ||
158 | { | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int dummy_bprm_secureexec (struct linux_binprm *bprm) | ||
163 | { | ||
164 | /* The new userland will simply use the value provided | ||
165 | in the AT_SECURE field to decide whether secure mode | ||
166 | is required. Hence, this logic is required to preserve | ||
167 | the legacy decision algorithm used by the old userland. */ | ||
168 | return (current->euid != current->uid || | ||
169 | current->egid != current->gid); | ||
170 | } | ||
171 | |||
172 | static int dummy_sb_alloc_security (struct super_block *sb) | ||
173 | { | ||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static void dummy_sb_free_security (struct super_block *sb) | ||
178 | { | ||
179 | return; | ||
180 | } | ||
181 | |||
182 | static int dummy_sb_copy_data (struct file_system_type *type, | ||
183 | void *orig, void *copy) | ||
184 | { | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static int dummy_sb_kern_mount (struct super_block *sb, void *data) | ||
189 | { | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static int dummy_sb_statfs (struct super_block *sb) | ||
194 | { | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static int dummy_sb_mount (char *dev_name, struct nameidata *nd, char *type, | ||
199 | unsigned long flags, void *data) | ||
200 | { | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int dummy_sb_check_sb (struct vfsmount *mnt, struct nameidata *nd) | ||
205 | { | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static int dummy_sb_umount (struct vfsmount *mnt, int flags) | ||
210 | { | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static void dummy_sb_umount_close (struct vfsmount *mnt) | ||
215 | { | ||
216 | return; | ||
217 | } | ||
218 | |||
219 | static void dummy_sb_umount_busy (struct vfsmount *mnt) | ||
220 | { | ||
221 | return; | ||
222 | } | ||
223 | |||
224 | static void dummy_sb_post_remount (struct vfsmount *mnt, unsigned long flags, | ||
225 | void *data) | ||
226 | { | ||
227 | return; | ||
228 | } | ||
229 | |||
230 | |||
231 | static void dummy_sb_post_mountroot (void) | ||
232 | { | ||
233 | return; | ||
234 | } | ||
235 | |||
236 | static void dummy_sb_post_addmount (struct vfsmount *mnt, struct nameidata *nd) | ||
237 | { | ||
238 | return; | ||
239 | } | ||
240 | |||
241 | static int dummy_sb_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd) | ||
242 | { | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | static void dummy_sb_post_pivotroot (struct nameidata *old_nd, struct nameidata *new_nd) | ||
247 | { | ||
248 | return; | ||
249 | } | ||
250 | |||
251 | static int dummy_inode_alloc_security (struct inode *inode) | ||
252 | { | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static void dummy_inode_free_security (struct inode *inode) | ||
257 | { | ||
258 | return; | ||
259 | } | ||
260 | |||
261 | static int dummy_inode_create (struct inode *inode, struct dentry *dentry, | ||
262 | int mask) | ||
263 | { | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static void dummy_inode_post_create (struct inode *inode, struct dentry *dentry, | ||
268 | int mask) | ||
269 | { | ||
270 | return; | ||
271 | } | ||
272 | |||
273 | static int dummy_inode_link (struct dentry *old_dentry, struct inode *inode, | ||
274 | struct dentry *new_dentry) | ||
275 | { | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static void dummy_inode_post_link (struct dentry *old_dentry, | ||
280 | struct inode *inode, | ||
281 | struct dentry *new_dentry) | ||
282 | { | ||
283 | return; | ||
284 | } | ||
285 | |||
286 | static int dummy_inode_unlink (struct inode *inode, struct dentry *dentry) | ||
287 | { | ||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static int dummy_inode_symlink (struct inode *inode, struct dentry *dentry, | ||
292 | const char *name) | ||
293 | { | ||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | static void dummy_inode_post_symlink (struct inode *inode, | ||
298 | struct dentry *dentry, const char *name) | ||
299 | { | ||
300 | return; | ||
301 | } | ||
302 | |||
303 | static int dummy_inode_mkdir (struct inode *inode, struct dentry *dentry, | ||
304 | int mask) | ||
305 | { | ||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | static void dummy_inode_post_mkdir (struct inode *inode, struct dentry *dentry, | ||
310 | int mask) | ||
311 | { | ||
312 | return; | ||
313 | } | ||
314 | |||
315 | static int dummy_inode_rmdir (struct inode *inode, struct dentry *dentry) | ||
316 | { | ||
317 | return 0; | ||
318 | } | ||
319 | |||
320 | static int dummy_inode_mknod (struct inode *inode, struct dentry *dentry, | ||
321 | int mode, dev_t dev) | ||
322 | { | ||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | static void dummy_inode_post_mknod (struct inode *inode, struct dentry *dentry, | ||
327 | int mode, dev_t dev) | ||
328 | { | ||
329 | return; | ||
330 | } | ||
331 | |||
332 | static int dummy_inode_rename (struct inode *old_inode, | ||
333 | struct dentry *old_dentry, | ||
334 | struct inode *new_inode, | ||
335 | struct dentry *new_dentry) | ||
336 | { | ||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | static void dummy_inode_post_rename (struct inode *old_inode, | ||
341 | struct dentry *old_dentry, | ||
342 | struct inode *new_inode, | ||
343 | struct dentry *new_dentry) | ||
344 | { | ||
345 | return; | ||
346 | } | ||
347 | |||
348 | static int dummy_inode_readlink (struct dentry *dentry) | ||
349 | { | ||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static int dummy_inode_follow_link (struct dentry *dentry, | ||
354 | struct nameidata *nameidata) | ||
355 | { | ||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | static int dummy_inode_permission (struct inode *inode, int mask, struct nameidata *nd) | ||
360 | { | ||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | static int dummy_inode_setattr (struct dentry *dentry, struct iattr *iattr) | ||
365 | { | ||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | static int dummy_inode_getattr (struct vfsmount *mnt, struct dentry *dentry) | ||
370 | { | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | static void dummy_inode_delete (struct inode *ino) | ||
375 | { | ||
376 | return; | ||
377 | } | ||
378 | |||
379 | static int dummy_inode_setxattr (struct dentry *dentry, char *name, void *value, | ||
380 | size_t size, int flags) | ||
381 | { | ||
382 | if (!strncmp(name, XATTR_SECURITY_PREFIX, | ||
383 | sizeof(XATTR_SECURITY_PREFIX) - 1) && | ||
384 | !capable(CAP_SYS_ADMIN)) | ||
385 | return -EPERM; | ||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | static void dummy_inode_post_setxattr (struct dentry *dentry, char *name, void *value, | ||
390 | size_t size, int flags) | ||
391 | { | ||
392 | } | ||
393 | |||
394 | static int dummy_inode_getxattr (struct dentry *dentry, char *name) | ||
395 | { | ||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | static int dummy_inode_listxattr (struct dentry *dentry) | ||
400 | { | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int dummy_inode_removexattr (struct dentry *dentry, char *name) | ||
405 | { | ||
406 | if (!strncmp(name, XATTR_SECURITY_PREFIX, | ||
407 | sizeof(XATTR_SECURITY_PREFIX) - 1) && | ||
408 | !capable(CAP_SYS_ADMIN)) | ||
409 | return -EPERM; | ||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | static int dummy_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size) | ||
414 | { | ||
415 | return -EOPNOTSUPP; | ||
416 | } | ||
417 | |||
418 | static int dummy_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) | ||
419 | { | ||
420 | return -EOPNOTSUPP; | ||
421 | } | ||
422 | |||
423 | static int dummy_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size) | ||
424 | { | ||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | static int dummy_file_permission (struct file *file, int mask) | ||
429 | { | ||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | static int dummy_file_alloc_security (struct file *file) | ||
434 | { | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | static void dummy_file_free_security (struct file *file) | ||
439 | { | ||
440 | return; | ||
441 | } | ||
442 | |||
443 | static int dummy_file_ioctl (struct file *file, unsigned int command, | ||
444 | unsigned long arg) | ||
445 | { | ||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static int dummy_file_mmap (struct file *file, unsigned long reqprot, | ||
450 | unsigned long prot, | ||
451 | unsigned long flags) | ||
452 | { | ||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | static int dummy_file_mprotect (struct vm_area_struct *vma, | ||
457 | unsigned long reqprot, | ||
458 | unsigned long prot) | ||
459 | { | ||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | static int dummy_file_lock (struct file *file, unsigned int cmd) | ||
464 | { | ||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | static int dummy_file_fcntl (struct file *file, unsigned int cmd, | ||
469 | unsigned long arg) | ||
470 | { | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static int dummy_file_set_fowner (struct file *file) | ||
475 | { | ||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | static int dummy_file_send_sigiotask (struct task_struct *tsk, | ||
480 | struct fown_struct *fown, int sig) | ||
481 | { | ||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static int dummy_file_receive (struct file *file) | ||
486 | { | ||
487 | return 0; | ||
488 | } | ||
489 | |||
490 | static int dummy_task_create (unsigned long clone_flags) | ||
491 | { | ||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | static int dummy_task_alloc_security (struct task_struct *p) | ||
496 | { | ||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | static void dummy_task_free_security (struct task_struct *p) | ||
501 | { | ||
502 | return; | ||
503 | } | ||
504 | |||
505 | static int dummy_task_setuid (uid_t id0, uid_t id1, uid_t id2, int flags) | ||
506 | { | ||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | static int dummy_task_post_setuid (uid_t id0, uid_t id1, uid_t id2, int flags) | ||
511 | { | ||
512 | dummy_capget(current, ¤t->cap_effective, ¤t->cap_inheritable, ¤t->cap_permitted); | ||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | static int dummy_task_setgid (gid_t id0, gid_t id1, gid_t id2, int flags) | ||
517 | { | ||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | static int dummy_task_setpgid (struct task_struct *p, pid_t pgid) | ||
522 | { | ||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | static int dummy_task_getpgid (struct task_struct *p) | ||
527 | { | ||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | static int dummy_task_getsid (struct task_struct *p) | ||
532 | { | ||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | static int dummy_task_setgroups (struct group_info *group_info) | ||
537 | { | ||
538 | return 0; | ||
539 | } | ||
540 | |||
541 | static int dummy_task_setnice (struct task_struct *p, int nice) | ||
542 | { | ||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | static int dummy_task_setrlimit (unsigned int resource, struct rlimit *new_rlim) | ||
547 | { | ||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | static int dummy_task_setscheduler (struct task_struct *p, int policy, | ||
552 | struct sched_param *lp) | ||
553 | { | ||
554 | return 0; | ||
555 | } | ||
556 | |||
557 | static int dummy_task_getscheduler (struct task_struct *p) | ||
558 | { | ||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | static int dummy_task_wait (struct task_struct *p) | ||
563 | { | ||
564 | return 0; | ||
565 | } | ||
566 | |||
567 | static int dummy_task_kill (struct task_struct *p, struct siginfo *info, | ||
568 | int sig) | ||
569 | { | ||
570 | return 0; | ||
571 | } | ||
572 | |||
573 | static int dummy_task_prctl (int option, unsigned long arg2, unsigned long arg3, | ||
574 | unsigned long arg4, unsigned long arg5) | ||
575 | { | ||
576 | return 0; | ||
577 | } | ||
578 | |||
579 | static void dummy_task_reparent_to_init (struct task_struct *p) | ||
580 | { | ||
581 | p->euid = p->fsuid = 0; | ||
582 | return; | ||
583 | } | ||
584 | |||
585 | static void dummy_task_to_inode(struct task_struct *p, struct inode *inode) | ||
586 | { } | ||
587 | |||
588 | static int dummy_ipc_permission (struct kern_ipc_perm *ipcp, short flag) | ||
589 | { | ||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | static int dummy_msg_msg_alloc_security (struct msg_msg *msg) | ||
594 | { | ||
595 | return 0; | ||
596 | } | ||
597 | |||
598 | static void dummy_msg_msg_free_security (struct msg_msg *msg) | ||
599 | { | ||
600 | return; | ||
601 | } | ||
602 | |||
603 | static int dummy_msg_queue_alloc_security (struct msg_queue *msq) | ||
604 | { | ||
605 | return 0; | ||
606 | } | ||
607 | |||
608 | static void dummy_msg_queue_free_security (struct msg_queue *msq) | ||
609 | { | ||
610 | return; | ||
611 | } | ||
612 | |||
613 | static int dummy_msg_queue_associate (struct msg_queue *msq, | ||
614 | int msqflg) | ||
615 | { | ||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | static int dummy_msg_queue_msgctl (struct msg_queue *msq, int cmd) | ||
620 | { | ||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | static int dummy_msg_queue_msgsnd (struct msg_queue *msq, struct msg_msg *msg, | ||
625 | int msgflg) | ||
626 | { | ||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | static int dummy_msg_queue_msgrcv (struct msg_queue *msq, struct msg_msg *msg, | ||
631 | struct task_struct *target, long type, | ||
632 | int mode) | ||
633 | { | ||
634 | return 0; | ||
635 | } | ||
636 | |||
637 | static int dummy_shm_alloc_security (struct shmid_kernel *shp) | ||
638 | { | ||
639 | return 0; | ||
640 | } | ||
641 | |||
642 | static void dummy_shm_free_security (struct shmid_kernel *shp) | ||
643 | { | ||
644 | return; | ||
645 | } | ||
646 | |||
647 | static int dummy_shm_associate (struct shmid_kernel *shp, int shmflg) | ||
648 | { | ||
649 | return 0; | ||
650 | } | ||
651 | |||
652 | static int dummy_shm_shmctl (struct shmid_kernel *shp, int cmd) | ||
653 | { | ||
654 | return 0; | ||
655 | } | ||
656 | |||
657 | static int dummy_shm_shmat (struct shmid_kernel *shp, char __user *shmaddr, | ||
658 | int shmflg) | ||
659 | { | ||
660 | return 0; | ||
661 | } | ||
662 | |||
663 | static int dummy_sem_alloc_security (struct sem_array *sma) | ||
664 | { | ||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | static void dummy_sem_free_security (struct sem_array *sma) | ||
669 | { | ||
670 | return; | ||
671 | } | ||
672 | |||
673 | static int dummy_sem_associate (struct sem_array *sma, int semflg) | ||
674 | { | ||
675 | return 0; | ||
676 | } | ||
677 | |||
678 | static int dummy_sem_semctl (struct sem_array *sma, int cmd) | ||
679 | { | ||
680 | return 0; | ||
681 | } | ||
682 | |||
683 | static int dummy_sem_semop (struct sem_array *sma, | ||
684 | struct sembuf *sops, unsigned nsops, int alter) | ||
685 | { | ||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | static int dummy_netlink_send (struct sock *sk, struct sk_buff *skb) | ||
690 | { | ||
691 | NETLINK_CB(skb).eff_cap = current->cap_effective; | ||
692 | return 0; | ||
693 | } | ||
694 | |||
695 | static int dummy_netlink_recv (struct sk_buff *skb) | ||
696 | { | ||
697 | if (!cap_raised (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN)) | ||
698 | return -EPERM; | ||
699 | return 0; | ||
700 | } | ||
701 | |||
702 | #ifdef CONFIG_SECURITY_NETWORK | ||
703 | static int dummy_unix_stream_connect (struct socket *sock, | ||
704 | struct socket *other, | ||
705 | struct sock *newsk) | ||
706 | { | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | static int dummy_unix_may_send (struct socket *sock, | ||
711 | struct socket *other) | ||
712 | { | ||
713 | return 0; | ||
714 | } | ||
715 | |||
716 | static int dummy_socket_create (int family, int type, | ||
717 | int protocol, int kern) | ||
718 | { | ||
719 | return 0; | ||
720 | } | ||
721 | |||
722 | static void dummy_socket_post_create (struct socket *sock, int family, int type, | ||
723 | int protocol, int kern) | ||
724 | { | ||
725 | return; | ||
726 | } | ||
727 | |||
728 | static int dummy_socket_bind (struct socket *sock, struct sockaddr *address, | ||
729 | int addrlen) | ||
730 | { | ||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | static int dummy_socket_connect (struct socket *sock, struct sockaddr *address, | ||
735 | int addrlen) | ||
736 | { | ||
737 | return 0; | ||
738 | } | ||
739 | |||
740 | static int dummy_socket_listen (struct socket *sock, int backlog) | ||
741 | { | ||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | static int dummy_socket_accept (struct socket *sock, struct socket *newsock) | ||
746 | { | ||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | static void dummy_socket_post_accept (struct socket *sock, | ||
751 | struct socket *newsock) | ||
752 | { | ||
753 | return; | ||
754 | } | ||
755 | |||
756 | static int dummy_socket_sendmsg (struct socket *sock, struct msghdr *msg, | ||
757 | int size) | ||
758 | { | ||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | static int dummy_socket_recvmsg (struct socket *sock, struct msghdr *msg, | ||
763 | int size, int flags) | ||
764 | { | ||
765 | return 0; | ||
766 | } | ||
767 | |||
768 | static int dummy_socket_getsockname (struct socket *sock) | ||
769 | { | ||
770 | return 0; | ||
771 | } | ||
772 | |||
773 | static int dummy_socket_getpeername (struct socket *sock) | ||
774 | { | ||
775 | return 0; | ||
776 | } | ||
777 | |||
778 | static int dummy_socket_setsockopt (struct socket *sock, int level, int optname) | ||
779 | { | ||
780 | return 0; | ||
781 | } | ||
782 | |||
783 | static int dummy_socket_getsockopt (struct socket *sock, int level, int optname) | ||
784 | { | ||
785 | return 0; | ||
786 | } | ||
787 | |||
788 | static int dummy_socket_shutdown (struct socket *sock, int how) | ||
789 | { | ||
790 | return 0; | ||
791 | } | ||
792 | |||
793 | static int dummy_socket_sock_rcv_skb (struct sock *sk, struct sk_buff *skb) | ||
794 | { | ||
795 | return 0; | ||
796 | } | ||
797 | |||
798 | static int dummy_socket_getpeersec(struct socket *sock, char __user *optval, | ||
799 | int __user *optlen, unsigned len) | ||
800 | { | ||
801 | return -ENOPROTOOPT; | ||
802 | } | ||
803 | |||
804 | static inline int dummy_sk_alloc_security (struct sock *sk, int family, int priority) | ||
805 | { | ||
806 | return 0; | ||
807 | } | ||
808 | |||
809 | static inline void dummy_sk_free_security (struct sock *sk) | ||
810 | { | ||
811 | } | ||
812 | #endif /* CONFIG_SECURITY_NETWORK */ | ||
813 | |||
814 | static int dummy_register_security (const char *name, struct security_operations *ops) | ||
815 | { | ||
816 | return -EINVAL; | ||
817 | } | ||
818 | |||
819 | static int dummy_unregister_security (const char *name, struct security_operations *ops) | ||
820 | { | ||
821 | return -EINVAL; | ||
822 | } | ||
823 | |||
824 | static void dummy_d_instantiate (struct dentry *dentry, struct inode *inode) | ||
825 | { | ||
826 | return; | ||
827 | } | ||
828 | |||
829 | static int dummy_getprocattr(struct task_struct *p, char *name, void *value, size_t size) | ||
830 | { | ||
831 | return -EINVAL; | ||
832 | } | ||
833 | |||
834 | static int dummy_setprocattr(struct task_struct *p, char *name, void *value, size_t size) | ||
835 | { | ||
836 | return -EINVAL; | ||
837 | } | ||
838 | |||
839 | |||
840 | struct security_operations dummy_security_ops; | ||
841 | |||
842 | #define set_to_dummy_if_null(ops, function) \ | ||
843 | do { \ | ||
844 | if (!ops->function) { \ | ||
845 | ops->function = dummy_##function; \ | ||
846 | pr_debug("Had to override the " #function \ | ||
847 | " security operation with the dummy one.\n");\ | ||
848 | } \ | ||
849 | } while (0) | ||
850 | |||
851 | void security_fixup_ops (struct security_operations *ops) | ||
852 | { | ||
853 | set_to_dummy_if_null(ops, ptrace); | ||
854 | set_to_dummy_if_null(ops, capget); | ||
855 | set_to_dummy_if_null(ops, capset_check); | ||
856 | set_to_dummy_if_null(ops, capset_set); | ||
857 | set_to_dummy_if_null(ops, acct); | ||
858 | set_to_dummy_if_null(ops, capable); | ||
859 | set_to_dummy_if_null(ops, quotactl); | ||
860 | set_to_dummy_if_null(ops, quota_on); | ||
861 | set_to_dummy_if_null(ops, sysctl); | ||
862 | set_to_dummy_if_null(ops, syslog); | ||
863 | set_to_dummy_if_null(ops, settime); | ||
864 | set_to_dummy_if_null(ops, vm_enough_memory); | ||
865 | set_to_dummy_if_null(ops, bprm_alloc_security); | ||
866 | set_to_dummy_if_null(ops, bprm_free_security); | ||
867 | set_to_dummy_if_null(ops, bprm_apply_creds); | ||
868 | set_to_dummy_if_null(ops, bprm_post_apply_creds); | ||
869 | set_to_dummy_if_null(ops, bprm_set_security); | ||
870 | set_to_dummy_if_null(ops, bprm_check_security); | ||
871 | set_to_dummy_if_null(ops, bprm_secureexec); | ||
872 | set_to_dummy_if_null(ops, sb_alloc_security); | ||
873 | set_to_dummy_if_null(ops, sb_free_security); | ||
874 | set_to_dummy_if_null(ops, sb_copy_data); | ||
875 | set_to_dummy_if_null(ops, sb_kern_mount); | ||
876 | set_to_dummy_if_null(ops, sb_statfs); | ||
877 | set_to_dummy_if_null(ops, sb_mount); | ||
878 | set_to_dummy_if_null(ops, sb_check_sb); | ||
879 | set_to_dummy_if_null(ops, sb_umount); | ||
880 | set_to_dummy_if_null(ops, sb_umount_close); | ||
881 | set_to_dummy_if_null(ops, sb_umount_busy); | ||
882 | set_to_dummy_if_null(ops, sb_post_remount); | ||
883 | set_to_dummy_if_null(ops, sb_post_mountroot); | ||
884 | set_to_dummy_if_null(ops, sb_post_addmount); | ||
885 | set_to_dummy_if_null(ops, sb_pivotroot); | ||
886 | set_to_dummy_if_null(ops, sb_post_pivotroot); | ||
887 | set_to_dummy_if_null(ops, inode_alloc_security); | ||
888 | set_to_dummy_if_null(ops, inode_free_security); | ||
889 | set_to_dummy_if_null(ops, inode_create); | ||
890 | set_to_dummy_if_null(ops, inode_post_create); | ||
891 | set_to_dummy_if_null(ops, inode_link); | ||
892 | set_to_dummy_if_null(ops, inode_post_link); | ||
893 | set_to_dummy_if_null(ops, inode_unlink); | ||
894 | set_to_dummy_if_null(ops, inode_symlink); | ||
895 | set_to_dummy_if_null(ops, inode_post_symlink); | ||
896 | set_to_dummy_if_null(ops, inode_mkdir); | ||
897 | set_to_dummy_if_null(ops, inode_post_mkdir); | ||
898 | set_to_dummy_if_null(ops, inode_rmdir); | ||
899 | set_to_dummy_if_null(ops, inode_mknod); | ||
900 | set_to_dummy_if_null(ops, inode_post_mknod); | ||
901 | set_to_dummy_if_null(ops, inode_rename); | ||
902 | set_to_dummy_if_null(ops, inode_post_rename); | ||
903 | set_to_dummy_if_null(ops, inode_readlink); | ||
904 | set_to_dummy_if_null(ops, inode_follow_link); | ||
905 | set_to_dummy_if_null(ops, inode_permission); | ||
906 | set_to_dummy_if_null(ops, inode_setattr); | ||
907 | set_to_dummy_if_null(ops, inode_getattr); | ||
908 | set_to_dummy_if_null(ops, inode_delete); | ||
909 | set_to_dummy_if_null(ops, inode_setxattr); | ||
910 | set_to_dummy_if_null(ops, inode_post_setxattr); | ||
911 | set_to_dummy_if_null(ops, inode_getxattr); | ||
912 | set_to_dummy_if_null(ops, inode_listxattr); | ||
913 | set_to_dummy_if_null(ops, inode_removexattr); | ||
914 | set_to_dummy_if_null(ops, inode_getsecurity); | ||
915 | set_to_dummy_if_null(ops, inode_setsecurity); | ||
916 | set_to_dummy_if_null(ops, inode_listsecurity); | ||
917 | set_to_dummy_if_null(ops, file_permission); | ||
918 | set_to_dummy_if_null(ops, file_alloc_security); | ||
919 | set_to_dummy_if_null(ops, file_free_security); | ||
920 | set_to_dummy_if_null(ops, file_ioctl); | ||
921 | set_to_dummy_if_null(ops, file_mmap); | ||
922 | set_to_dummy_if_null(ops, file_mprotect); | ||
923 | set_to_dummy_if_null(ops, file_lock); | ||
924 | set_to_dummy_if_null(ops, file_fcntl); | ||
925 | set_to_dummy_if_null(ops, file_set_fowner); | ||
926 | set_to_dummy_if_null(ops, file_send_sigiotask); | ||
927 | set_to_dummy_if_null(ops, file_receive); | ||
928 | set_to_dummy_if_null(ops, task_create); | ||
929 | set_to_dummy_if_null(ops, task_alloc_security); | ||
930 | set_to_dummy_if_null(ops, task_free_security); | ||
931 | set_to_dummy_if_null(ops, task_setuid); | ||
932 | set_to_dummy_if_null(ops, task_post_setuid); | ||
933 | set_to_dummy_if_null(ops, task_setgid); | ||
934 | set_to_dummy_if_null(ops, task_setpgid); | ||
935 | set_to_dummy_if_null(ops, task_getpgid); | ||
936 | set_to_dummy_if_null(ops, task_getsid); | ||
937 | set_to_dummy_if_null(ops, task_setgroups); | ||
938 | set_to_dummy_if_null(ops, task_setnice); | ||
939 | set_to_dummy_if_null(ops, task_setrlimit); | ||
940 | set_to_dummy_if_null(ops, task_setscheduler); | ||
941 | set_to_dummy_if_null(ops, task_getscheduler); | ||
942 | set_to_dummy_if_null(ops, task_wait); | ||
943 | set_to_dummy_if_null(ops, task_kill); | ||
944 | set_to_dummy_if_null(ops, task_prctl); | ||
945 | set_to_dummy_if_null(ops, task_reparent_to_init); | ||
946 | set_to_dummy_if_null(ops, task_to_inode); | ||
947 | set_to_dummy_if_null(ops, ipc_permission); | ||
948 | set_to_dummy_if_null(ops, msg_msg_alloc_security); | ||
949 | set_to_dummy_if_null(ops, msg_msg_free_security); | ||
950 | set_to_dummy_if_null(ops, msg_queue_alloc_security); | ||
951 | set_to_dummy_if_null(ops, msg_queue_free_security); | ||
952 | set_to_dummy_if_null(ops, msg_queue_associate); | ||
953 | set_to_dummy_if_null(ops, msg_queue_msgctl); | ||
954 | set_to_dummy_if_null(ops, msg_queue_msgsnd); | ||
955 | set_to_dummy_if_null(ops, msg_queue_msgrcv); | ||
956 | set_to_dummy_if_null(ops, shm_alloc_security); | ||
957 | set_to_dummy_if_null(ops, shm_free_security); | ||
958 | set_to_dummy_if_null(ops, shm_associate); | ||
959 | set_to_dummy_if_null(ops, shm_shmctl); | ||
960 | set_to_dummy_if_null(ops, shm_shmat); | ||
961 | set_to_dummy_if_null(ops, sem_alloc_security); | ||
962 | set_to_dummy_if_null(ops, sem_free_security); | ||
963 | set_to_dummy_if_null(ops, sem_associate); | ||
964 | set_to_dummy_if_null(ops, sem_semctl); | ||
965 | set_to_dummy_if_null(ops, sem_semop); | ||
966 | set_to_dummy_if_null(ops, netlink_send); | ||
967 | set_to_dummy_if_null(ops, netlink_recv); | ||
968 | set_to_dummy_if_null(ops, register_security); | ||
969 | set_to_dummy_if_null(ops, unregister_security); | ||
970 | set_to_dummy_if_null(ops, d_instantiate); | ||
971 | set_to_dummy_if_null(ops, getprocattr); | ||
972 | set_to_dummy_if_null(ops, setprocattr); | ||
973 | #ifdef CONFIG_SECURITY_NETWORK | ||
974 | set_to_dummy_if_null(ops, unix_stream_connect); | ||
975 | set_to_dummy_if_null(ops, unix_may_send); | ||
976 | set_to_dummy_if_null(ops, socket_create); | ||
977 | set_to_dummy_if_null(ops, socket_post_create); | ||
978 | set_to_dummy_if_null(ops, socket_bind); | ||
979 | set_to_dummy_if_null(ops, socket_connect); | ||
980 | set_to_dummy_if_null(ops, socket_listen); | ||
981 | set_to_dummy_if_null(ops, socket_accept); | ||
982 | set_to_dummy_if_null(ops, socket_post_accept); | ||
983 | set_to_dummy_if_null(ops, socket_sendmsg); | ||
984 | set_to_dummy_if_null(ops, socket_recvmsg); | ||
985 | set_to_dummy_if_null(ops, socket_getsockname); | ||
986 | set_to_dummy_if_null(ops, socket_getpeername); | ||
987 | set_to_dummy_if_null(ops, socket_setsockopt); | ||
988 | set_to_dummy_if_null(ops, socket_getsockopt); | ||
989 | set_to_dummy_if_null(ops, socket_shutdown); | ||
990 | set_to_dummy_if_null(ops, socket_sock_rcv_skb); | ||
991 | set_to_dummy_if_null(ops, socket_getpeersec); | ||
992 | set_to_dummy_if_null(ops, sk_alloc_security); | ||
993 | set_to_dummy_if_null(ops, sk_free_security); | ||
994 | #endif /* CONFIG_SECURITY_NETWORK */ | ||
995 | } | ||
996 | |||
diff --git a/security/keys/Makefile b/security/keys/Makefile new file mode 100644 index 000000000000..ddb495d65062 --- /dev/null +++ b/security/keys/Makefile | |||
@@ -0,0 +1,14 @@ | |||
1 | # | ||
2 | # Makefile for key management | ||
3 | # | ||
4 | |||
5 | obj-y := \ | ||
6 | key.o \ | ||
7 | keyring.o \ | ||
8 | keyctl.o \ | ||
9 | process_keys.o \ | ||
10 | user_defined.o \ | ||
11 | request_key.o | ||
12 | |||
13 | obj-$(CONFIG_KEYS_COMPAT) += compat.o | ||
14 | obj-$(CONFIG_PROC_FS) += proc.o | ||
diff --git a/security/keys/compat.c b/security/keys/compat.c new file mode 100644 index 000000000000..aff8b22dcb5c --- /dev/null +++ b/security/keys/compat.c | |||
@@ -0,0 +1,78 @@ | |||
1 | /* compat.c: 32-bit compatibility syscall for 64-bit systems | ||
2 | * | ||
3 | * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/sched.h> | ||
13 | #include <linux/syscalls.h> | ||
14 | #include <linux/keyctl.h> | ||
15 | #include <linux/compat.h> | ||
16 | #include "internal.h" | ||
17 | |||
18 | /*****************************************************************************/ | ||
19 | /* | ||
20 | * the key control system call, 32-bit compatibility version for 64-bit archs | ||
21 | * - this should only be called if the 64-bit arch uses weird pointers in | ||
22 | * 32-bit mode or doesn't guarantee that the top 32-bits of the argument | ||
23 | * registers on taking a 32-bit syscall are zero | ||
24 | * - if you can, you should call sys_keyctl directly | ||
25 | */ | ||
26 | asmlinkage long compat_sys_keyctl(u32 option, | ||
27 | u32 arg2, u32 arg3, u32 arg4, u32 arg5) | ||
28 | { | ||
29 | switch (option) { | ||
30 | case KEYCTL_GET_KEYRING_ID: | ||
31 | return keyctl_get_keyring_ID(arg2, arg3); | ||
32 | |||
33 | case KEYCTL_JOIN_SESSION_KEYRING: | ||
34 | return keyctl_join_session_keyring(compat_ptr(arg2)); | ||
35 | |||
36 | case KEYCTL_UPDATE: | ||
37 | return keyctl_update_key(arg2, compat_ptr(arg3), arg4); | ||
38 | |||
39 | case KEYCTL_REVOKE: | ||
40 | return keyctl_revoke_key(arg2); | ||
41 | |||
42 | case KEYCTL_DESCRIBE: | ||
43 | return keyctl_describe_key(arg2, compat_ptr(arg3), arg4); | ||
44 | |||
45 | case KEYCTL_CLEAR: | ||
46 | return keyctl_keyring_clear(arg2); | ||
47 | |||
48 | case KEYCTL_LINK: | ||
49 | return keyctl_keyring_link(arg2, arg3); | ||
50 | |||
51 | case KEYCTL_UNLINK: | ||
52 | return keyctl_keyring_unlink(arg2, arg3); | ||
53 | |||
54 | case KEYCTL_SEARCH: | ||
55 | return keyctl_keyring_search(arg2, compat_ptr(arg3), | ||
56 | compat_ptr(arg4), arg5); | ||
57 | |||
58 | case KEYCTL_READ: | ||
59 | return keyctl_read_key(arg2, compat_ptr(arg3), arg4); | ||
60 | |||
61 | case KEYCTL_CHOWN: | ||
62 | return keyctl_chown_key(arg2, arg3, arg4); | ||
63 | |||
64 | case KEYCTL_SETPERM: | ||
65 | return keyctl_setperm_key(arg2, arg3); | ||
66 | |||
67 | case KEYCTL_INSTANTIATE: | ||
68 | return keyctl_instantiate_key(arg2, compat_ptr(arg3), arg4, | ||
69 | arg5); | ||
70 | |||
71 | case KEYCTL_NEGATE: | ||
72 | return keyctl_negate_key(arg2, arg3, arg4); | ||
73 | |||
74 | default: | ||
75 | return -EOPNOTSUPP; | ||
76 | } | ||
77 | |||
78 | } /* end compat_sys_keyctl() */ | ||
diff --git a/security/keys/internal.h b/security/keys/internal.h new file mode 100644 index 000000000000..67b2b93a7489 --- /dev/null +++ b/security/keys/internal.h | |||
@@ -0,0 +1,123 @@ | |||
1 | /* internal.h: authentication token and access key management internal defs | ||
2 | * | ||
3 | * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #ifndef _INTERNAL_H | ||
13 | #define _INTERNAL_H | ||
14 | |||
15 | #include <linux/key.h> | ||
16 | #include <linux/key-ui.h> | ||
17 | |||
18 | extern struct key_type key_type_dead; | ||
19 | extern struct key_type key_type_user; | ||
20 | |||
21 | /*****************************************************************************/ | ||
22 | /* | ||
23 | * keep track of keys for a user | ||
24 | * - this needs to be separate to user_struct to avoid a refcount-loop | ||
25 | * (user_struct pins some keyrings which pin this struct) | ||
26 | * - this also keeps track of keys under request from userspace for this UID | ||
27 | */ | ||
28 | struct key_user { | ||
29 | struct rb_node node; | ||
30 | struct list_head consq; /* construction queue */ | ||
31 | spinlock_t lock; | ||
32 | atomic_t usage; /* for accessing qnkeys & qnbytes */ | ||
33 | atomic_t nkeys; /* number of keys */ | ||
34 | atomic_t nikeys; /* number of instantiated keys */ | ||
35 | uid_t uid; | ||
36 | int qnkeys; /* number of keys allocated to this user */ | ||
37 | int qnbytes; /* number of bytes allocated to this user */ | ||
38 | }; | ||
39 | |||
40 | #define KEYQUOTA_MAX_KEYS 100 | ||
41 | #define KEYQUOTA_MAX_BYTES 10000 | ||
42 | #define KEYQUOTA_LINK_BYTES 4 /* a link in a keyring is worth 4 bytes */ | ||
43 | |||
44 | extern struct rb_root key_user_tree; | ||
45 | extern spinlock_t key_user_lock; | ||
46 | extern struct key_user root_key_user; | ||
47 | |||
48 | extern struct key_user *key_user_lookup(uid_t uid); | ||
49 | extern void key_user_put(struct key_user *user); | ||
50 | |||
51 | |||
52 | |||
53 | extern struct rb_root key_serial_tree; | ||
54 | extern spinlock_t key_serial_lock; | ||
55 | extern struct semaphore key_alloc_sem; | ||
56 | extern struct rw_semaphore key_construction_sem; | ||
57 | extern wait_queue_head_t request_key_conswq; | ||
58 | |||
59 | |||
60 | extern void keyring_publish_name(struct key *keyring); | ||
61 | |||
62 | extern int __key_link(struct key *keyring, struct key *key); | ||
63 | |||
64 | extern struct key *__keyring_search_one(struct key *keyring, | ||
65 | const struct key_type *type, | ||
66 | const char *description, | ||
67 | key_perm_t perm); | ||
68 | |||
69 | typedef int (*key_match_func_t)(const struct key *, const void *); | ||
70 | |||
71 | extern struct key *keyring_search_aux(struct key *keyring, | ||
72 | struct key_type *type, | ||
73 | const void *description, | ||
74 | key_match_func_t match); | ||
75 | |||
76 | extern struct key *search_process_keyrings_aux(struct key_type *type, | ||
77 | const void *description, | ||
78 | key_match_func_t match); | ||
79 | |||
80 | extern struct key *find_keyring_by_name(const char *name, key_serial_t bound); | ||
81 | |||
82 | extern int install_thread_keyring(struct task_struct *tsk); | ||
83 | |||
84 | /* | ||
85 | * keyctl functions | ||
86 | */ | ||
87 | extern long keyctl_get_keyring_ID(key_serial_t, int); | ||
88 | extern long keyctl_join_session_keyring(const char __user *); | ||
89 | extern long keyctl_update_key(key_serial_t, const void __user *, size_t); | ||
90 | extern long keyctl_revoke_key(key_serial_t); | ||
91 | extern long keyctl_keyring_clear(key_serial_t); | ||
92 | extern long keyctl_keyring_link(key_serial_t, key_serial_t); | ||
93 | extern long keyctl_keyring_unlink(key_serial_t, key_serial_t); | ||
94 | extern long keyctl_describe_key(key_serial_t, char __user *, size_t); | ||
95 | extern long keyctl_keyring_search(key_serial_t, const char __user *, | ||
96 | const char __user *, key_serial_t); | ||
97 | extern long keyctl_read_key(key_serial_t, char __user *, size_t); | ||
98 | extern long keyctl_chown_key(key_serial_t, uid_t, gid_t); | ||
99 | extern long keyctl_setperm_key(key_serial_t, key_perm_t); | ||
100 | extern long keyctl_instantiate_key(key_serial_t, const void __user *, | ||
101 | size_t, key_serial_t); | ||
102 | extern long keyctl_negate_key(key_serial_t, unsigned, key_serial_t); | ||
103 | |||
104 | |||
105 | /* | ||
106 | * debugging key validation | ||
107 | */ | ||
108 | #ifdef KEY_DEBUGGING | ||
109 | extern void __key_check(const struct key *); | ||
110 | |||
111 | static inline void key_check(const struct key *key) | ||
112 | { | ||
113 | if (key && (IS_ERR(key) || key->magic != KEY_DEBUG_MAGIC)) | ||
114 | __key_check(key); | ||
115 | } | ||
116 | |||
117 | #else | ||
118 | |||
119 | #define key_check(key) do {} while(0) | ||
120 | |||
121 | #endif | ||
122 | |||
123 | #endif /* _INTERNAL_H */ | ||
diff --git a/security/keys/key.c b/security/keys/key.c new file mode 100644 index 000000000000..59402c843203 --- /dev/null +++ b/security/keys/key.c | |||
@@ -0,0 +1,1040 @@ | |||
1 | /* key.c: basic authentication token and access key management | ||
2 | * | ||
3 | * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/workqueue.h> | ||
17 | #include <linux/err.h> | ||
18 | #include "internal.h" | ||
19 | |||
20 | static kmem_cache_t *key_jar; | ||
21 | static key_serial_t key_serial_next = 3; | ||
22 | struct rb_root key_serial_tree; /* tree of keys indexed by serial */ | ||
23 | DEFINE_SPINLOCK(key_serial_lock); | ||
24 | |||
25 | struct rb_root key_user_tree; /* tree of quota records indexed by UID */ | ||
26 | DEFINE_SPINLOCK(key_user_lock); | ||
27 | |||
28 | static LIST_HEAD(key_types_list); | ||
29 | static DECLARE_RWSEM(key_types_sem); | ||
30 | |||
31 | static void key_cleanup(void *data); | ||
32 | static DECLARE_WORK(key_cleanup_task, key_cleanup, NULL); | ||
33 | |||
34 | /* we serialise key instantiation and link */ | ||
35 | DECLARE_RWSEM(key_construction_sem); | ||
36 | |||
37 | /* any key who's type gets unegistered will be re-typed to this */ | ||
38 | struct key_type key_type_dead = { | ||
39 | .name = "dead", | ||
40 | }; | ||
41 | |||
42 | #ifdef KEY_DEBUGGING | ||
43 | void __key_check(const struct key *key) | ||
44 | { | ||
45 | printk("__key_check: key %p {%08x} should be {%08x}\n", | ||
46 | key, key->magic, KEY_DEBUG_MAGIC); | ||
47 | BUG(); | ||
48 | } | ||
49 | #endif | ||
50 | |||
51 | /*****************************************************************************/ | ||
52 | /* | ||
53 | * get the key quota record for a user, allocating a new record if one doesn't | ||
54 | * already exist | ||
55 | */ | ||
56 | struct key_user *key_user_lookup(uid_t uid) | ||
57 | { | ||
58 | struct key_user *candidate = NULL, *user; | ||
59 | struct rb_node *parent = NULL; | ||
60 | struct rb_node **p; | ||
61 | |||
62 | try_again: | ||
63 | p = &key_user_tree.rb_node; | ||
64 | spin_lock(&key_user_lock); | ||
65 | |||
66 | /* search the tree for a user record with a matching UID */ | ||
67 | while (*p) { | ||
68 | parent = *p; | ||
69 | user = rb_entry(parent, struct key_user, node); | ||
70 | |||
71 | if (uid < user->uid) | ||
72 | p = &(*p)->rb_left; | ||
73 | else if (uid > user->uid) | ||
74 | p = &(*p)->rb_right; | ||
75 | else | ||
76 | goto found; | ||
77 | } | ||
78 | |||
79 | /* if we get here, we failed to find a match in the tree */ | ||
80 | if (!candidate) { | ||
81 | /* allocate a candidate user record if we don't already have | ||
82 | * one */ | ||
83 | spin_unlock(&key_user_lock); | ||
84 | |||
85 | user = NULL; | ||
86 | candidate = kmalloc(sizeof(struct key_user), GFP_KERNEL); | ||
87 | if (unlikely(!candidate)) | ||
88 | goto out; | ||
89 | |||
90 | /* the allocation may have scheduled, so we need to repeat the | ||
91 | * search lest someone else added the record whilst we were | ||
92 | * asleep */ | ||
93 | goto try_again; | ||
94 | } | ||
95 | |||
96 | /* if we get here, then the user record still hadn't appeared on the | ||
97 | * second pass - so we use the candidate record */ | ||
98 | atomic_set(&candidate->usage, 1); | ||
99 | atomic_set(&candidate->nkeys, 0); | ||
100 | atomic_set(&candidate->nikeys, 0); | ||
101 | candidate->uid = uid; | ||
102 | candidate->qnkeys = 0; | ||
103 | candidate->qnbytes = 0; | ||
104 | spin_lock_init(&candidate->lock); | ||
105 | INIT_LIST_HEAD(&candidate->consq); | ||
106 | |||
107 | rb_link_node(&candidate->node, parent, p); | ||
108 | rb_insert_color(&candidate->node, &key_user_tree); | ||
109 | spin_unlock(&key_user_lock); | ||
110 | user = candidate; | ||
111 | goto out; | ||
112 | |||
113 | /* okay - we found a user record for this UID */ | ||
114 | found: | ||
115 | atomic_inc(&user->usage); | ||
116 | spin_unlock(&key_user_lock); | ||
117 | if (candidate) | ||
118 | kfree(candidate); | ||
119 | out: | ||
120 | return user; | ||
121 | |||
122 | } /* end key_user_lookup() */ | ||
123 | |||
124 | /*****************************************************************************/ | ||
125 | /* | ||
126 | * dispose of a user structure | ||
127 | */ | ||
128 | void key_user_put(struct key_user *user) | ||
129 | { | ||
130 | if (atomic_dec_and_lock(&user->usage, &key_user_lock)) { | ||
131 | rb_erase(&user->node, &key_user_tree); | ||
132 | spin_unlock(&key_user_lock); | ||
133 | |||
134 | kfree(user); | ||
135 | } | ||
136 | |||
137 | } /* end key_user_put() */ | ||
138 | |||
139 | /*****************************************************************************/ | ||
140 | /* | ||
141 | * insert a key with a fixed serial number | ||
142 | */ | ||
143 | static void __init __key_insert_serial(struct key *key) | ||
144 | { | ||
145 | struct rb_node *parent, **p; | ||
146 | struct key *xkey; | ||
147 | |||
148 | parent = NULL; | ||
149 | p = &key_serial_tree.rb_node; | ||
150 | |||
151 | while (*p) { | ||
152 | parent = *p; | ||
153 | xkey = rb_entry(parent, struct key, serial_node); | ||
154 | |||
155 | if (key->serial < xkey->serial) | ||
156 | p = &(*p)->rb_left; | ||
157 | else if (key->serial > xkey->serial) | ||
158 | p = &(*p)->rb_right; | ||
159 | else | ||
160 | BUG(); | ||
161 | } | ||
162 | |||
163 | /* we've found a suitable hole - arrange for this key to occupy it */ | ||
164 | rb_link_node(&key->serial_node, parent, p); | ||
165 | rb_insert_color(&key->serial_node, &key_serial_tree); | ||
166 | |||
167 | } /* end __key_insert_serial() */ | ||
168 | |||
169 | /*****************************************************************************/ | ||
170 | /* | ||
171 | * assign a key the next unique serial number | ||
172 | * - we work through all the serial numbers between 2 and 2^31-1 in turn and | ||
173 | * then wrap | ||
174 | */ | ||
175 | static inline void key_alloc_serial(struct key *key) | ||
176 | { | ||
177 | struct rb_node *parent, **p; | ||
178 | struct key *xkey; | ||
179 | |||
180 | spin_lock(&key_serial_lock); | ||
181 | |||
182 | /* propose a likely serial number and look for a hole for it in the | ||
183 | * serial number tree */ | ||
184 | key->serial = key_serial_next; | ||
185 | if (key->serial < 3) | ||
186 | key->serial = 3; | ||
187 | key_serial_next = key->serial + 1; | ||
188 | |||
189 | parent = NULL; | ||
190 | p = &key_serial_tree.rb_node; | ||
191 | |||
192 | while (*p) { | ||
193 | parent = *p; | ||
194 | xkey = rb_entry(parent, struct key, serial_node); | ||
195 | |||
196 | if (key->serial < xkey->serial) | ||
197 | p = &(*p)->rb_left; | ||
198 | else if (key->serial > xkey->serial) | ||
199 | p = &(*p)->rb_right; | ||
200 | else | ||
201 | goto serial_exists; | ||
202 | } | ||
203 | goto insert_here; | ||
204 | |||
205 | /* we found a key with the proposed serial number - walk the tree from | ||
206 | * that point looking for the next unused serial number */ | ||
207 | serial_exists: | ||
208 | for (;;) { | ||
209 | key->serial = key_serial_next; | ||
210 | if (key->serial < 2) | ||
211 | key->serial = 2; | ||
212 | key_serial_next = key->serial + 1; | ||
213 | |||
214 | if (!parent->rb_parent) | ||
215 | p = &key_serial_tree.rb_node; | ||
216 | else if (parent->rb_parent->rb_left == parent) | ||
217 | p = &parent->rb_parent->rb_left; | ||
218 | else | ||
219 | p = &parent->rb_parent->rb_right; | ||
220 | |||
221 | parent = rb_next(parent); | ||
222 | if (!parent) | ||
223 | break; | ||
224 | |||
225 | xkey = rb_entry(parent, struct key, serial_node); | ||
226 | if (key->serial < xkey->serial) | ||
227 | goto insert_here; | ||
228 | } | ||
229 | |||
230 | /* we've found a suitable hole - arrange for this key to occupy it */ | ||
231 | insert_here: | ||
232 | rb_link_node(&key->serial_node, parent, p); | ||
233 | rb_insert_color(&key->serial_node, &key_serial_tree); | ||
234 | |||
235 | spin_unlock(&key_serial_lock); | ||
236 | |||
237 | } /* end key_alloc_serial() */ | ||
238 | |||
239 | /*****************************************************************************/ | ||
240 | /* | ||
241 | * allocate a key of the specified type | ||
242 | * - update the user's quota to reflect the existence of the key | ||
243 | * - called from a key-type operation with key_types_sem read-locked by either | ||
244 | * key_create_or_update() or by key_duplicate(); this prevents unregistration | ||
245 | * of the key type | ||
246 | * - upon return the key is as yet uninstantiated; the caller needs to either | ||
247 | * instantiate the key or discard it before returning | ||
248 | */ | ||
249 | struct key *key_alloc(struct key_type *type, const char *desc, | ||
250 | uid_t uid, gid_t gid, key_perm_t perm, | ||
251 | int not_in_quota) | ||
252 | { | ||
253 | struct key_user *user = NULL; | ||
254 | struct key *key; | ||
255 | size_t desclen, quotalen; | ||
256 | |||
257 | key = ERR_PTR(-EINVAL); | ||
258 | if (!desc || !*desc) | ||
259 | goto error; | ||
260 | |||
261 | desclen = strlen(desc) + 1; | ||
262 | quotalen = desclen + type->def_datalen; | ||
263 | |||
264 | /* get hold of the key tracking for this user */ | ||
265 | user = key_user_lookup(uid); | ||
266 | if (!user) | ||
267 | goto no_memory_1; | ||
268 | |||
269 | /* check that the user's quota permits allocation of another key and | ||
270 | * its description */ | ||
271 | if (!not_in_quota) { | ||
272 | spin_lock(&user->lock); | ||
273 | if (user->qnkeys + 1 >= KEYQUOTA_MAX_KEYS && | ||
274 | user->qnbytes + quotalen >= KEYQUOTA_MAX_BYTES | ||
275 | ) | ||
276 | goto no_quota; | ||
277 | |||
278 | user->qnkeys++; | ||
279 | user->qnbytes += quotalen; | ||
280 | spin_unlock(&user->lock); | ||
281 | } | ||
282 | |||
283 | /* allocate and initialise the key and its description */ | ||
284 | key = kmem_cache_alloc(key_jar, SLAB_KERNEL); | ||
285 | if (!key) | ||
286 | goto no_memory_2; | ||
287 | |||
288 | if (desc) { | ||
289 | key->description = kmalloc(desclen, GFP_KERNEL); | ||
290 | if (!key->description) | ||
291 | goto no_memory_3; | ||
292 | |||
293 | memcpy(key->description, desc, desclen); | ||
294 | } | ||
295 | |||
296 | atomic_set(&key->usage, 1); | ||
297 | rwlock_init(&key->lock); | ||
298 | init_rwsem(&key->sem); | ||
299 | key->type = type; | ||
300 | key->user = user; | ||
301 | key->quotalen = quotalen; | ||
302 | key->datalen = type->def_datalen; | ||
303 | key->uid = uid; | ||
304 | key->gid = gid; | ||
305 | key->perm = perm; | ||
306 | key->flags = 0; | ||
307 | key->expiry = 0; | ||
308 | key->payload.data = NULL; | ||
309 | |||
310 | if (!not_in_quota) | ||
311 | key->flags |= KEY_FLAG_IN_QUOTA; | ||
312 | |||
313 | memset(&key->type_data, 0, sizeof(key->type_data)); | ||
314 | |||
315 | #ifdef KEY_DEBUGGING | ||
316 | key->magic = KEY_DEBUG_MAGIC; | ||
317 | #endif | ||
318 | |||
319 | /* publish the key by giving it a serial number */ | ||
320 | atomic_inc(&user->nkeys); | ||
321 | key_alloc_serial(key); | ||
322 | |||
323 | error: | ||
324 | return key; | ||
325 | |||
326 | no_memory_3: | ||
327 | kmem_cache_free(key_jar, key); | ||
328 | no_memory_2: | ||
329 | if (!not_in_quota) { | ||
330 | spin_lock(&user->lock); | ||
331 | user->qnkeys--; | ||
332 | user->qnbytes -= quotalen; | ||
333 | spin_unlock(&user->lock); | ||
334 | } | ||
335 | key_user_put(user); | ||
336 | no_memory_1: | ||
337 | key = ERR_PTR(-ENOMEM); | ||
338 | goto error; | ||
339 | |||
340 | no_quota: | ||
341 | spin_unlock(&user->lock); | ||
342 | key_user_put(user); | ||
343 | key = ERR_PTR(-EDQUOT); | ||
344 | goto error; | ||
345 | |||
346 | } /* end key_alloc() */ | ||
347 | |||
348 | EXPORT_SYMBOL(key_alloc); | ||
349 | |||
350 | /*****************************************************************************/ | ||
351 | /* | ||
352 | * reserve an amount of quota for the key's payload | ||
353 | */ | ||
354 | int key_payload_reserve(struct key *key, size_t datalen) | ||
355 | { | ||
356 | int delta = (int) datalen - key->datalen; | ||
357 | int ret = 0; | ||
358 | |||
359 | key_check(key); | ||
360 | |||
361 | /* contemplate the quota adjustment */ | ||
362 | if (delta != 0 && key->flags & KEY_FLAG_IN_QUOTA) { | ||
363 | spin_lock(&key->user->lock); | ||
364 | |||
365 | if (delta > 0 && | ||
366 | key->user->qnbytes + delta > KEYQUOTA_MAX_BYTES | ||
367 | ) { | ||
368 | ret = -EDQUOT; | ||
369 | } | ||
370 | else { | ||
371 | key->user->qnbytes += delta; | ||
372 | key->quotalen += delta; | ||
373 | } | ||
374 | spin_unlock(&key->user->lock); | ||
375 | } | ||
376 | |||
377 | /* change the recorded data length if that didn't generate an error */ | ||
378 | if (ret == 0) | ||
379 | key->datalen = datalen; | ||
380 | |||
381 | return ret; | ||
382 | |||
383 | } /* end key_payload_reserve() */ | ||
384 | |||
385 | EXPORT_SYMBOL(key_payload_reserve); | ||
386 | |||
387 | /*****************************************************************************/ | ||
388 | /* | ||
389 | * instantiate a key and link it into the target keyring atomically | ||
390 | * - called with the target keyring's semaphore writelocked | ||
391 | */ | ||
392 | static int __key_instantiate_and_link(struct key *key, | ||
393 | const void *data, | ||
394 | size_t datalen, | ||
395 | struct key *keyring) | ||
396 | { | ||
397 | int ret, awaken; | ||
398 | |||
399 | key_check(key); | ||
400 | key_check(keyring); | ||
401 | |||
402 | awaken = 0; | ||
403 | ret = -EBUSY; | ||
404 | |||
405 | down_write(&key_construction_sem); | ||
406 | |||
407 | /* can't instantiate twice */ | ||
408 | if (!(key->flags & KEY_FLAG_INSTANTIATED)) { | ||
409 | /* instantiate the key */ | ||
410 | ret = key->type->instantiate(key, data, datalen); | ||
411 | |||
412 | if (ret == 0) { | ||
413 | /* mark the key as being instantiated */ | ||
414 | write_lock(&key->lock); | ||
415 | |||
416 | atomic_inc(&key->user->nikeys); | ||
417 | key->flags |= KEY_FLAG_INSTANTIATED; | ||
418 | |||
419 | if (key->flags & KEY_FLAG_USER_CONSTRUCT) { | ||
420 | key->flags &= ~KEY_FLAG_USER_CONSTRUCT; | ||
421 | awaken = 1; | ||
422 | } | ||
423 | |||
424 | write_unlock(&key->lock); | ||
425 | |||
426 | /* and link it into the destination keyring */ | ||
427 | if (keyring) | ||
428 | ret = __key_link(keyring, key); | ||
429 | } | ||
430 | } | ||
431 | |||
432 | up_write(&key_construction_sem); | ||
433 | |||
434 | /* wake up anyone waiting for a key to be constructed */ | ||
435 | if (awaken) | ||
436 | wake_up_all(&request_key_conswq); | ||
437 | |||
438 | return ret; | ||
439 | |||
440 | } /* end __key_instantiate_and_link() */ | ||
441 | |||
442 | /*****************************************************************************/ | ||
443 | /* | ||
444 | * instantiate a key and link it into the target keyring atomically | ||
445 | */ | ||
446 | int key_instantiate_and_link(struct key *key, | ||
447 | const void *data, | ||
448 | size_t datalen, | ||
449 | struct key *keyring) | ||
450 | { | ||
451 | int ret; | ||
452 | |||
453 | if (keyring) | ||
454 | down_write(&keyring->sem); | ||
455 | |||
456 | ret = __key_instantiate_and_link(key, data, datalen, keyring); | ||
457 | |||
458 | if (keyring) | ||
459 | up_write(&keyring->sem); | ||
460 | |||
461 | return ret; | ||
462 | } /* end key_instantiate_and_link() */ | ||
463 | |||
464 | EXPORT_SYMBOL(key_instantiate_and_link); | ||
465 | |||
466 | /*****************************************************************************/ | ||
467 | /* | ||
468 | * negatively instantiate a key and link it into the target keyring atomically | ||
469 | */ | ||
470 | int key_negate_and_link(struct key *key, | ||
471 | unsigned timeout, | ||
472 | struct key *keyring) | ||
473 | { | ||
474 | struct timespec now; | ||
475 | int ret, awaken; | ||
476 | |||
477 | key_check(key); | ||
478 | key_check(keyring); | ||
479 | |||
480 | awaken = 0; | ||
481 | ret = -EBUSY; | ||
482 | |||
483 | if (keyring) | ||
484 | down_write(&keyring->sem); | ||
485 | |||
486 | down_write(&key_construction_sem); | ||
487 | |||
488 | /* can't instantiate twice */ | ||
489 | if (!(key->flags & KEY_FLAG_INSTANTIATED)) { | ||
490 | /* mark the key as being negatively instantiated */ | ||
491 | write_lock(&key->lock); | ||
492 | |||
493 | atomic_inc(&key->user->nikeys); | ||
494 | key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE; | ||
495 | now = current_kernel_time(); | ||
496 | key->expiry = now.tv_sec + timeout; | ||
497 | |||
498 | if (key->flags & KEY_FLAG_USER_CONSTRUCT) { | ||
499 | key->flags &= ~KEY_FLAG_USER_CONSTRUCT; | ||
500 | awaken = 1; | ||
501 | } | ||
502 | |||
503 | write_unlock(&key->lock); | ||
504 | ret = 0; | ||
505 | |||
506 | /* and link it into the destination keyring */ | ||
507 | if (keyring) | ||
508 | ret = __key_link(keyring, key); | ||
509 | } | ||
510 | |||
511 | up_write(&key_construction_sem); | ||
512 | |||
513 | if (keyring) | ||
514 | up_write(&keyring->sem); | ||
515 | |||
516 | /* wake up anyone waiting for a key to be constructed */ | ||
517 | if (awaken) | ||
518 | wake_up_all(&request_key_conswq); | ||
519 | |||
520 | return ret; | ||
521 | |||
522 | } /* end key_negate_and_link() */ | ||
523 | |||
524 | EXPORT_SYMBOL(key_negate_and_link); | ||
525 | |||
526 | /*****************************************************************************/ | ||
527 | /* | ||
528 | * do cleaning up in process context so that we don't have to disable | ||
529 | * interrupts all over the place | ||
530 | */ | ||
531 | static void key_cleanup(void *data) | ||
532 | { | ||
533 | struct rb_node *_n; | ||
534 | struct key *key; | ||
535 | |||
536 | go_again: | ||
537 | /* look for a dead key in the tree */ | ||
538 | spin_lock(&key_serial_lock); | ||
539 | |||
540 | for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { | ||
541 | key = rb_entry(_n, struct key, serial_node); | ||
542 | |||
543 | if (atomic_read(&key->usage) == 0) | ||
544 | goto found_dead_key; | ||
545 | } | ||
546 | |||
547 | spin_unlock(&key_serial_lock); | ||
548 | return; | ||
549 | |||
550 | found_dead_key: | ||
551 | /* we found a dead key - once we've removed it from the tree, we can | ||
552 | * drop the lock */ | ||
553 | rb_erase(&key->serial_node, &key_serial_tree); | ||
554 | spin_unlock(&key_serial_lock); | ||
555 | |||
556 | /* deal with the user's key tracking and quota */ | ||
557 | if (key->flags & KEY_FLAG_IN_QUOTA) { | ||
558 | spin_lock(&key->user->lock); | ||
559 | key->user->qnkeys--; | ||
560 | key->user->qnbytes -= key->quotalen; | ||
561 | spin_unlock(&key->user->lock); | ||
562 | } | ||
563 | |||
564 | atomic_dec(&key->user->nkeys); | ||
565 | if (key->flags & KEY_FLAG_INSTANTIATED) | ||
566 | atomic_dec(&key->user->nikeys); | ||
567 | |||
568 | key_user_put(key->user); | ||
569 | |||
570 | /* now throw away the key memory */ | ||
571 | if (key->type->destroy) | ||
572 | key->type->destroy(key); | ||
573 | |||
574 | kfree(key->description); | ||
575 | |||
576 | #ifdef KEY_DEBUGGING | ||
577 | key->magic = KEY_DEBUG_MAGIC_X; | ||
578 | #endif | ||
579 | kmem_cache_free(key_jar, key); | ||
580 | |||
581 | /* there may, of course, be more than one key to destroy */ | ||
582 | goto go_again; | ||
583 | |||
584 | } /* end key_cleanup() */ | ||
585 | |||
586 | /*****************************************************************************/ | ||
587 | /* | ||
588 | * dispose of a reference to a key | ||
589 | * - when all the references are gone, we schedule the cleanup task to come and | ||
590 | * pull it out of the tree in definite process context | ||
591 | */ | ||
592 | void key_put(struct key *key) | ||
593 | { | ||
594 | if (key) { | ||
595 | key_check(key); | ||
596 | |||
597 | if (atomic_dec_and_test(&key->usage)) | ||
598 | schedule_work(&key_cleanup_task); | ||
599 | } | ||
600 | |||
601 | } /* end key_put() */ | ||
602 | |||
603 | EXPORT_SYMBOL(key_put); | ||
604 | |||
605 | /*****************************************************************************/ | ||
606 | /* | ||
607 | * find a key by its serial number | ||
608 | */ | ||
609 | struct key *key_lookup(key_serial_t id) | ||
610 | { | ||
611 | struct rb_node *n; | ||
612 | struct key *key; | ||
613 | |||
614 | spin_lock(&key_serial_lock); | ||
615 | |||
616 | /* search the tree for the specified key */ | ||
617 | n = key_serial_tree.rb_node; | ||
618 | while (n) { | ||
619 | key = rb_entry(n, struct key, serial_node); | ||
620 | |||
621 | if (id < key->serial) | ||
622 | n = n->rb_left; | ||
623 | else if (id > key->serial) | ||
624 | n = n->rb_right; | ||
625 | else | ||
626 | goto found; | ||
627 | } | ||
628 | |||
629 | not_found: | ||
630 | key = ERR_PTR(-ENOKEY); | ||
631 | goto error; | ||
632 | |||
633 | found: | ||
634 | /* pretent doesn't exist if it's dead */ | ||
635 | if (atomic_read(&key->usage) == 0 || | ||
636 | (key->flags & KEY_FLAG_DEAD) || | ||
637 | key->type == &key_type_dead) | ||
638 | goto not_found; | ||
639 | |||
640 | /* this races with key_put(), but that doesn't matter since key_put() | ||
641 | * doesn't actually change the key | ||
642 | */ | ||
643 | atomic_inc(&key->usage); | ||
644 | |||
645 | error: | ||
646 | spin_unlock(&key_serial_lock); | ||
647 | return key; | ||
648 | |||
649 | } /* end key_lookup() */ | ||
650 | |||
651 | /*****************************************************************************/ | ||
652 | /* | ||
653 | * find and lock the specified key type against removal | ||
654 | * - we return with the sem readlocked | ||
655 | */ | ||
656 | struct key_type *key_type_lookup(const char *type) | ||
657 | { | ||
658 | struct key_type *ktype; | ||
659 | |||
660 | down_read(&key_types_sem); | ||
661 | |||
662 | /* look up the key type to see if it's one of the registered kernel | ||
663 | * types */ | ||
664 | list_for_each_entry(ktype, &key_types_list, link) { | ||
665 | if (strcmp(ktype->name, type) == 0) | ||
666 | goto found_kernel_type; | ||
667 | } | ||
668 | |||
669 | up_read(&key_types_sem); | ||
670 | ktype = ERR_PTR(-ENOKEY); | ||
671 | |||
672 | found_kernel_type: | ||
673 | return ktype; | ||
674 | |||
675 | } /* end key_type_lookup() */ | ||
676 | |||
677 | /*****************************************************************************/ | ||
678 | /* | ||
679 | * unlock a key type | ||
680 | */ | ||
681 | void key_type_put(struct key_type *ktype) | ||
682 | { | ||
683 | up_read(&key_types_sem); | ||
684 | |||
685 | } /* end key_type_put() */ | ||
686 | |||
687 | /*****************************************************************************/ | ||
688 | /* | ||
689 | * attempt to update an existing key | ||
690 | * - the key has an incremented refcount | ||
691 | * - we need to put the key if we get an error | ||
692 | */ | ||
693 | static inline struct key *__key_update(struct key *key, const void *payload, | ||
694 | size_t plen) | ||
695 | { | ||
696 | int ret; | ||
697 | |||
698 | /* need write permission on the key to update it */ | ||
699 | ret = -EACCES; | ||
700 | if (!key_permission(key, KEY_WRITE)) | ||
701 | goto error; | ||
702 | |||
703 | ret = -EEXIST; | ||
704 | if (!key->type->update) | ||
705 | goto error; | ||
706 | |||
707 | down_write(&key->sem); | ||
708 | |||
709 | ret = key->type->update(key, payload, plen); | ||
710 | |||
711 | if (ret == 0) { | ||
712 | /* updating a negative key instantiates it */ | ||
713 | write_lock(&key->lock); | ||
714 | key->flags &= ~KEY_FLAG_NEGATIVE; | ||
715 | write_unlock(&key->lock); | ||
716 | } | ||
717 | |||
718 | up_write(&key->sem); | ||
719 | |||
720 | if (ret < 0) | ||
721 | goto error; | ||
722 | out: | ||
723 | return key; | ||
724 | |||
725 | error: | ||
726 | key_put(key); | ||
727 | key = ERR_PTR(ret); | ||
728 | goto out; | ||
729 | |||
730 | } /* end __key_update() */ | ||
731 | |||
732 | /*****************************************************************************/ | ||
733 | /* | ||
734 | * search the specified keyring for a key of the same description; if one is | ||
735 | * found, update it, otherwise add a new one | ||
736 | */ | ||
737 | struct key *key_create_or_update(struct key *keyring, | ||
738 | const char *type, | ||
739 | const char *description, | ||
740 | const void *payload, | ||
741 | size_t plen, | ||
742 | int not_in_quota) | ||
743 | { | ||
744 | struct key_type *ktype; | ||
745 | struct key *key = NULL; | ||
746 | key_perm_t perm; | ||
747 | int ret; | ||
748 | |||
749 | key_check(keyring); | ||
750 | |||
751 | /* look up the key type to see if it's one of the registered kernel | ||
752 | * types */ | ||
753 | ktype = key_type_lookup(type); | ||
754 | if (IS_ERR(ktype)) { | ||
755 | key = ERR_PTR(-ENODEV); | ||
756 | goto error; | ||
757 | } | ||
758 | |||
759 | ret = -EINVAL; | ||
760 | if (!ktype->match || !ktype->instantiate) | ||
761 | goto error_2; | ||
762 | |||
763 | /* search for an existing key of the same type and description in the | ||
764 | * destination keyring | ||
765 | */ | ||
766 | down_write(&keyring->sem); | ||
767 | |||
768 | key = __keyring_search_one(keyring, ktype, description, 0); | ||
769 | if (!IS_ERR(key)) | ||
770 | goto found_matching_key; | ||
771 | |||
772 | /* if we're going to allocate a new key, we're going to have to modify | ||
773 | * the keyring */ | ||
774 | ret = -EACCES; | ||
775 | if (!key_permission(keyring, KEY_WRITE)) | ||
776 | goto error_3; | ||
777 | |||
778 | /* decide on the permissions we want */ | ||
779 | perm = KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK; | ||
780 | |||
781 | if (ktype->read) | ||
782 | perm |= KEY_USR_READ; | ||
783 | |||
784 | if (ktype == &key_type_keyring || ktype->update) | ||
785 | perm |= KEY_USR_WRITE; | ||
786 | |||
787 | /* allocate a new key */ | ||
788 | key = key_alloc(ktype, description, current->fsuid, current->fsgid, | ||
789 | perm, not_in_quota); | ||
790 | if (IS_ERR(key)) { | ||
791 | ret = PTR_ERR(key); | ||
792 | goto error_3; | ||
793 | } | ||
794 | |||
795 | /* instantiate it and link it into the target keyring */ | ||
796 | ret = __key_instantiate_and_link(key, payload, plen, keyring); | ||
797 | if (ret < 0) { | ||
798 | key_put(key); | ||
799 | key = ERR_PTR(ret); | ||
800 | } | ||
801 | |||
802 | error_3: | ||
803 | up_write(&keyring->sem); | ||
804 | error_2: | ||
805 | key_type_put(ktype); | ||
806 | error: | ||
807 | return key; | ||
808 | |||
809 | found_matching_key: | ||
810 | /* we found a matching key, so we're going to try to update it | ||
811 | * - we can drop the locks first as we have the key pinned | ||
812 | */ | ||
813 | up_write(&keyring->sem); | ||
814 | key_type_put(ktype); | ||
815 | |||
816 | key = __key_update(key, payload, plen); | ||
817 | goto error; | ||
818 | |||
819 | } /* end key_create_or_update() */ | ||
820 | |||
821 | EXPORT_SYMBOL(key_create_or_update); | ||
822 | |||
823 | /*****************************************************************************/ | ||
824 | /* | ||
825 | * update a key | ||
826 | */ | ||
827 | int key_update(struct key *key, const void *payload, size_t plen) | ||
828 | { | ||
829 | int ret; | ||
830 | |||
831 | key_check(key); | ||
832 | |||
833 | /* the key must be writable */ | ||
834 | ret = -EACCES; | ||
835 | if (!key_permission(key, KEY_WRITE)) | ||
836 | goto error; | ||
837 | |||
838 | /* attempt to update it if supported */ | ||
839 | ret = -EOPNOTSUPP; | ||
840 | if (key->type->update) { | ||
841 | down_write(&key->sem); | ||
842 | ret = key->type->update(key, payload, plen); | ||
843 | |||
844 | if (ret == 0) { | ||
845 | /* updating a negative key instantiates it */ | ||
846 | write_lock(&key->lock); | ||
847 | key->flags &= ~KEY_FLAG_NEGATIVE; | ||
848 | write_unlock(&key->lock); | ||
849 | } | ||
850 | |||
851 | up_write(&key->sem); | ||
852 | } | ||
853 | |||
854 | error: | ||
855 | return ret; | ||
856 | |||
857 | } /* end key_update() */ | ||
858 | |||
859 | EXPORT_SYMBOL(key_update); | ||
860 | |||
861 | /*****************************************************************************/ | ||
862 | /* | ||
863 | * duplicate a key, potentially with a revised description | ||
864 | * - must be supported by the keytype (keyrings for instance can be duplicated) | ||
865 | */ | ||
866 | struct key *key_duplicate(struct key *source, const char *desc) | ||
867 | { | ||
868 | struct key *key; | ||
869 | int ret; | ||
870 | |||
871 | key_check(source); | ||
872 | |||
873 | if (!desc) | ||
874 | desc = source->description; | ||
875 | |||
876 | down_read(&key_types_sem); | ||
877 | |||
878 | ret = -EINVAL; | ||
879 | if (!source->type->duplicate) | ||
880 | goto error; | ||
881 | |||
882 | /* allocate and instantiate a key */ | ||
883 | key = key_alloc(source->type, desc, current->fsuid, current->fsgid, | ||
884 | source->perm, 0); | ||
885 | if (IS_ERR(key)) | ||
886 | goto error_k; | ||
887 | |||
888 | down_read(&source->sem); | ||
889 | ret = key->type->duplicate(key, source); | ||
890 | up_read(&source->sem); | ||
891 | if (ret < 0) | ||
892 | goto error2; | ||
893 | |||
894 | atomic_inc(&key->user->nikeys); | ||
895 | |||
896 | write_lock(&key->lock); | ||
897 | key->flags |= KEY_FLAG_INSTANTIATED; | ||
898 | write_unlock(&key->lock); | ||
899 | |||
900 | error_k: | ||
901 | up_read(&key_types_sem); | ||
902 | out: | ||
903 | return key; | ||
904 | |||
905 | error2: | ||
906 | key_put(key); | ||
907 | error: | ||
908 | up_read(&key_types_sem); | ||
909 | key = ERR_PTR(ret); | ||
910 | goto out; | ||
911 | |||
912 | } /* end key_duplicate() */ | ||
913 | |||
914 | /*****************************************************************************/ | ||
915 | /* | ||
916 | * revoke a key | ||
917 | */ | ||
918 | void key_revoke(struct key *key) | ||
919 | { | ||
920 | key_check(key); | ||
921 | |||
922 | /* make sure no one's trying to change or use the key when we mark | ||
923 | * it */ | ||
924 | down_write(&key->sem); | ||
925 | write_lock(&key->lock); | ||
926 | key->flags |= KEY_FLAG_REVOKED; | ||
927 | write_unlock(&key->lock); | ||
928 | up_write(&key->sem); | ||
929 | |||
930 | } /* end key_revoke() */ | ||
931 | |||
932 | EXPORT_SYMBOL(key_revoke); | ||
933 | |||
934 | /*****************************************************************************/ | ||
935 | /* | ||
936 | * register a type of key | ||
937 | */ | ||
938 | int register_key_type(struct key_type *ktype) | ||
939 | { | ||
940 | struct key_type *p; | ||
941 | int ret; | ||
942 | |||
943 | ret = -EEXIST; | ||
944 | down_write(&key_types_sem); | ||
945 | |||
946 | /* disallow key types with the same name */ | ||
947 | list_for_each_entry(p, &key_types_list, link) { | ||
948 | if (strcmp(p->name, ktype->name) == 0) | ||
949 | goto out; | ||
950 | } | ||
951 | |||
952 | /* store the type */ | ||
953 | list_add(&ktype->link, &key_types_list); | ||
954 | ret = 0; | ||
955 | |||
956 | out: | ||
957 | up_write(&key_types_sem); | ||
958 | return ret; | ||
959 | |||
960 | } /* end register_key_type() */ | ||
961 | |||
962 | EXPORT_SYMBOL(register_key_type); | ||
963 | |||
964 | /*****************************************************************************/ | ||
965 | /* | ||
966 | * unregister a type of key | ||
967 | */ | ||
968 | void unregister_key_type(struct key_type *ktype) | ||
969 | { | ||
970 | struct rb_node *_n; | ||
971 | struct key *key; | ||
972 | |||
973 | down_write(&key_types_sem); | ||
974 | |||
975 | /* withdraw the key type */ | ||
976 | list_del_init(&ktype->link); | ||
977 | |||
978 | /* need to withdraw all keys of this type */ | ||
979 | spin_lock(&key_serial_lock); | ||
980 | |||
981 | for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { | ||
982 | key = rb_entry(_n, struct key, serial_node); | ||
983 | |||
984 | if (key->type != ktype) | ||
985 | continue; | ||
986 | |||
987 | write_lock(&key->lock); | ||
988 | key->type = &key_type_dead; | ||
989 | write_unlock(&key->lock); | ||
990 | |||
991 | /* there shouldn't be anyone looking at the description or | ||
992 | * payload now */ | ||
993 | if (ktype->destroy) | ||
994 | ktype->destroy(key); | ||
995 | memset(&key->payload, 0xbd, sizeof(key->payload)); | ||
996 | } | ||
997 | |||
998 | spin_unlock(&key_serial_lock); | ||
999 | up_write(&key_types_sem); | ||
1000 | |||
1001 | } /* end unregister_key_type() */ | ||
1002 | |||
1003 | EXPORT_SYMBOL(unregister_key_type); | ||
1004 | |||
1005 | /*****************************************************************************/ | ||
1006 | /* | ||
1007 | * initialise the key management stuff | ||
1008 | */ | ||
1009 | void __init key_init(void) | ||
1010 | { | ||
1011 | /* allocate a slab in which we can store keys */ | ||
1012 | key_jar = kmem_cache_create("key_jar", sizeof(struct key), | ||
1013 | 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL); | ||
1014 | |||
1015 | /* add the special key types */ | ||
1016 | list_add_tail(&key_type_keyring.link, &key_types_list); | ||
1017 | list_add_tail(&key_type_dead.link, &key_types_list); | ||
1018 | list_add_tail(&key_type_user.link, &key_types_list); | ||
1019 | |||
1020 | /* record the root user tracking */ | ||
1021 | rb_link_node(&root_key_user.node, | ||
1022 | NULL, | ||
1023 | &key_user_tree.rb_node); | ||
1024 | |||
1025 | rb_insert_color(&root_key_user.node, | ||
1026 | &key_user_tree); | ||
1027 | |||
1028 | /* record root's user standard keyrings */ | ||
1029 | key_check(&root_user_keyring); | ||
1030 | key_check(&root_session_keyring); | ||
1031 | |||
1032 | __key_insert_serial(&root_user_keyring); | ||
1033 | __key_insert_serial(&root_session_keyring); | ||
1034 | |||
1035 | keyring_publish_name(&root_user_keyring); | ||
1036 | keyring_publish_name(&root_session_keyring); | ||
1037 | |||
1038 | /* link the two root keyrings together */ | ||
1039 | key_link(&root_session_keyring, &root_user_keyring); | ||
1040 | } /* end key_init() */ | ||
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c new file mode 100644 index 000000000000..dc0011b3fac9 --- /dev/null +++ b/security/keys/keyctl.c | |||
@@ -0,0 +1,987 @@ | |||
1 | /* keyctl.c: userspace keyctl operations | ||
2 | * | ||
3 | * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/syscalls.h> | ||
17 | #include <linux/keyctl.h> | ||
18 | #include <linux/fs.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <asm/uaccess.h> | ||
21 | #include "internal.h" | ||
22 | |||
23 | /*****************************************************************************/ | ||
24 | /* | ||
25 | * extract the description of a new key from userspace and either add it as a | ||
26 | * new key to the specified keyring or update a matching key in that keyring | ||
27 | * - the keyring must be writable | ||
28 | * - returns the new key's serial number | ||
29 | * - implements add_key() | ||
30 | */ | ||
31 | asmlinkage long sys_add_key(const char __user *_type, | ||
32 | const char __user *_description, | ||
33 | const void __user *_payload, | ||
34 | size_t plen, | ||
35 | key_serial_t ringid) | ||
36 | { | ||
37 | struct key *keyring, *key; | ||
38 | char type[32], *description; | ||
39 | void *payload; | ||
40 | long dlen, ret; | ||
41 | |||
42 | ret = -EINVAL; | ||
43 | if (plen > 32767) | ||
44 | goto error; | ||
45 | |||
46 | /* draw all the data into kernel space */ | ||
47 | ret = strncpy_from_user(type, _type, sizeof(type) - 1); | ||
48 | if (ret < 0) | ||
49 | goto error; | ||
50 | type[31] = '\0'; | ||
51 | |||
52 | ret = -EFAULT; | ||
53 | dlen = strnlen_user(_description, PAGE_SIZE - 1); | ||
54 | if (dlen <= 0) | ||
55 | goto error; | ||
56 | |||
57 | ret = -EINVAL; | ||
58 | if (dlen > PAGE_SIZE - 1) | ||
59 | goto error; | ||
60 | |||
61 | ret = -ENOMEM; | ||
62 | description = kmalloc(dlen + 1, GFP_KERNEL); | ||
63 | if (!description) | ||
64 | goto error; | ||
65 | |||
66 | ret = -EFAULT; | ||
67 | if (copy_from_user(description, _description, dlen + 1) != 0) | ||
68 | goto error2; | ||
69 | |||
70 | /* pull the payload in if one was supplied */ | ||
71 | payload = NULL; | ||
72 | |||
73 | if (_payload) { | ||
74 | ret = -ENOMEM; | ||
75 | payload = kmalloc(plen, GFP_KERNEL); | ||
76 | if (!payload) | ||
77 | goto error2; | ||
78 | |||
79 | ret = -EFAULT; | ||
80 | if (copy_from_user(payload, _payload, plen) != 0) | ||
81 | goto error3; | ||
82 | } | ||
83 | |||
84 | /* find the target keyring (which must be writable) */ | ||
85 | keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); | ||
86 | if (IS_ERR(keyring)) { | ||
87 | ret = PTR_ERR(keyring); | ||
88 | goto error3; | ||
89 | } | ||
90 | |||
91 | /* create or update the requested key and add it to the target | ||
92 | * keyring */ | ||
93 | key = key_create_or_update(keyring, type, description, | ||
94 | payload, plen, 0); | ||
95 | if (!IS_ERR(key)) { | ||
96 | ret = key->serial; | ||
97 | key_put(key); | ||
98 | } | ||
99 | else { | ||
100 | ret = PTR_ERR(key); | ||
101 | } | ||
102 | |||
103 | key_put(keyring); | ||
104 | error3: | ||
105 | kfree(payload); | ||
106 | error2: | ||
107 | kfree(description); | ||
108 | error: | ||
109 | return ret; | ||
110 | |||
111 | } /* end sys_add_key() */ | ||
112 | |||
113 | /*****************************************************************************/ | ||
114 | /* | ||
115 | * search the process keyrings for a matching key | ||
116 | * - nested keyrings may also be searched if they have Search permission | ||
117 | * - if a key is found, it will be attached to the destination keyring if | ||
118 | * there's one specified | ||
119 | * - /sbin/request-key will be invoked if _callout_info is non-NULL | ||
120 | * - the _callout_info string will be passed to /sbin/request-key | ||
121 | * - if the _callout_info string is empty, it will be rendered as "-" | ||
122 | * - implements request_key() | ||
123 | */ | ||
124 | asmlinkage long sys_request_key(const char __user *_type, | ||
125 | const char __user *_description, | ||
126 | const char __user *_callout_info, | ||
127 | key_serial_t destringid) | ||
128 | { | ||
129 | struct key_type *ktype; | ||
130 | struct key *key, *dest; | ||
131 | char type[32], *description, *callout_info; | ||
132 | long dlen, ret; | ||
133 | |||
134 | /* pull the type into kernel space */ | ||
135 | ret = strncpy_from_user(type, _type, sizeof(type) - 1); | ||
136 | if (ret < 0) | ||
137 | goto error; | ||
138 | type[31] = '\0'; | ||
139 | |||
140 | /* pull the description into kernel space */ | ||
141 | ret = -EFAULT; | ||
142 | dlen = strnlen_user(_description, PAGE_SIZE - 1); | ||
143 | if (dlen <= 0) | ||
144 | goto error; | ||
145 | |||
146 | ret = -EINVAL; | ||
147 | if (dlen > PAGE_SIZE - 1) | ||
148 | goto error; | ||
149 | |||
150 | ret = -ENOMEM; | ||
151 | description = kmalloc(dlen + 1, GFP_KERNEL); | ||
152 | if (!description) | ||
153 | goto error; | ||
154 | |||
155 | ret = -EFAULT; | ||
156 | if (copy_from_user(description, _description, dlen + 1) != 0) | ||
157 | goto error2; | ||
158 | |||
159 | /* pull the callout info into kernel space */ | ||
160 | callout_info = NULL; | ||
161 | if (_callout_info) { | ||
162 | ret = -EFAULT; | ||
163 | dlen = strnlen_user(_callout_info, PAGE_SIZE - 1); | ||
164 | if (dlen <= 0) | ||
165 | goto error2; | ||
166 | |||
167 | ret = -EINVAL; | ||
168 | if (dlen > PAGE_SIZE - 1) | ||
169 | goto error2; | ||
170 | |||
171 | ret = -ENOMEM; | ||
172 | callout_info = kmalloc(dlen + 1, GFP_KERNEL); | ||
173 | if (!callout_info) | ||
174 | goto error2; | ||
175 | |||
176 | ret = -EFAULT; | ||
177 | if (copy_from_user(callout_info, _callout_info, dlen + 1) != 0) | ||
178 | goto error3; | ||
179 | } | ||
180 | |||
181 | /* get the destination keyring if specified */ | ||
182 | dest = NULL; | ||
183 | if (destringid) { | ||
184 | dest = lookup_user_key(destringid, 1, 0, KEY_WRITE); | ||
185 | if (IS_ERR(dest)) { | ||
186 | ret = PTR_ERR(dest); | ||
187 | goto error3; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | /* find the key type */ | ||
192 | ktype = key_type_lookup(type); | ||
193 | if (IS_ERR(ktype)) { | ||
194 | ret = PTR_ERR(ktype); | ||
195 | goto error4; | ||
196 | } | ||
197 | |||
198 | /* do the search */ | ||
199 | key = request_key(ktype, description, callout_info); | ||
200 | if (IS_ERR(key)) { | ||
201 | ret = PTR_ERR(key); | ||
202 | goto error5; | ||
203 | } | ||
204 | |||
205 | /* link the resulting key to the destination keyring */ | ||
206 | if (dest) { | ||
207 | ret = key_link(dest, key); | ||
208 | if (ret < 0) | ||
209 | goto error6; | ||
210 | } | ||
211 | |||
212 | ret = key->serial; | ||
213 | |||
214 | error6: | ||
215 | key_put(key); | ||
216 | error5: | ||
217 | key_type_put(ktype); | ||
218 | error4: | ||
219 | key_put(dest); | ||
220 | error3: | ||
221 | kfree(callout_info); | ||
222 | error2: | ||
223 | kfree(description); | ||
224 | error: | ||
225 | return ret; | ||
226 | |||
227 | } /* end sys_request_key() */ | ||
228 | |||
229 | /*****************************************************************************/ | ||
230 | /* | ||
231 | * get the ID of the specified process keyring | ||
232 | * - the keyring must have search permission to be found | ||
233 | * - implements keyctl(KEYCTL_GET_KEYRING_ID) | ||
234 | */ | ||
235 | long keyctl_get_keyring_ID(key_serial_t id, int create) | ||
236 | { | ||
237 | struct key *key; | ||
238 | long ret; | ||
239 | |||
240 | key = lookup_user_key(id, create, 0, KEY_SEARCH); | ||
241 | if (IS_ERR(key)) { | ||
242 | ret = PTR_ERR(key); | ||
243 | goto error; | ||
244 | } | ||
245 | |||
246 | ret = key->serial; | ||
247 | key_put(key); | ||
248 | error: | ||
249 | return ret; | ||
250 | |||
251 | } /* end keyctl_get_keyring_ID() */ | ||
252 | |||
253 | /*****************************************************************************/ | ||
254 | /* | ||
255 | * join the session keyring | ||
256 | * - implements keyctl(KEYCTL_JOIN_SESSION_KEYRING) | ||
257 | */ | ||
258 | long keyctl_join_session_keyring(const char __user *_name) | ||
259 | { | ||
260 | char *name; | ||
261 | long nlen, ret; | ||
262 | |||
263 | /* fetch the name from userspace */ | ||
264 | name = NULL; | ||
265 | if (_name) { | ||
266 | ret = -EFAULT; | ||
267 | nlen = strnlen_user(_name, PAGE_SIZE - 1); | ||
268 | if (nlen <= 0) | ||
269 | goto error; | ||
270 | |||
271 | ret = -EINVAL; | ||
272 | if (nlen > PAGE_SIZE - 1) | ||
273 | goto error; | ||
274 | |||
275 | ret = -ENOMEM; | ||
276 | name = kmalloc(nlen + 1, GFP_KERNEL); | ||
277 | if (!name) | ||
278 | goto error; | ||
279 | |||
280 | ret = -EFAULT; | ||
281 | if (copy_from_user(name, _name, nlen + 1) != 0) | ||
282 | goto error2; | ||
283 | } | ||
284 | |||
285 | /* join the session */ | ||
286 | ret = join_session_keyring(name); | ||
287 | |||
288 | error2: | ||
289 | kfree(name); | ||
290 | error: | ||
291 | return ret; | ||
292 | |||
293 | } /* end keyctl_join_session_keyring() */ | ||
294 | |||
295 | /*****************************************************************************/ | ||
296 | /* | ||
297 | * update a key's data payload | ||
298 | * - the key must be writable | ||
299 | * - implements keyctl(KEYCTL_UPDATE) | ||
300 | */ | ||
301 | long keyctl_update_key(key_serial_t id, | ||
302 | const void __user *_payload, | ||
303 | size_t plen) | ||
304 | { | ||
305 | struct key *key; | ||
306 | void *payload; | ||
307 | long ret; | ||
308 | |||
309 | ret = -EINVAL; | ||
310 | if (plen > PAGE_SIZE) | ||
311 | goto error; | ||
312 | |||
313 | /* pull the payload in if one was supplied */ | ||
314 | payload = NULL; | ||
315 | if (_payload) { | ||
316 | ret = -ENOMEM; | ||
317 | payload = kmalloc(plen, GFP_KERNEL); | ||
318 | if (!payload) | ||
319 | goto error; | ||
320 | |||
321 | ret = -EFAULT; | ||
322 | if (copy_from_user(payload, _payload, plen) != 0) | ||
323 | goto error2; | ||
324 | } | ||
325 | |||
326 | /* find the target key (which must be writable) */ | ||
327 | key = lookup_user_key(id, 0, 0, KEY_WRITE); | ||
328 | if (IS_ERR(key)) { | ||
329 | ret = PTR_ERR(key); | ||
330 | goto error2; | ||
331 | } | ||
332 | |||
333 | /* update the key */ | ||
334 | ret = key_update(key, payload, plen); | ||
335 | |||
336 | key_put(key); | ||
337 | error2: | ||
338 | kfree(payload); | ||
339 | error: | ||
340 | return ret; | ||
341 | |||
342 | } /* end keyctl_update_key() */ | ||
343 | |||
344 | /*****************************************************************************/ | ||
345 | /* | ||
346 | * revoke a key | ||
347 | * - the key must be writable | ||
348 | * - implements keyctl(KEYCTL_REVOKE) | ||
349 | */ | ||
350 | long keyctl_revoke_key(key_serial_t id) | ||
351 | { | ||
352 | struct key *key; | ||
353 | long ret; | ||
354 | |||
355 | key = lookup_user_key(id, 0, 0, KEY_WRITE); | ||
356 | if (IS_ERR(key)) { | ||
357 | ret = PTR_ERR(key); | ||
358 | goto error; | ||
359 | } | ||
360 | |||
361 | key_revoke(key); | ||
362 | ret = 0; | ||
363 | |||
364 | key_put(key); | ||
365 | error: | ||
366 | return 0; | ||
367 | |||
368 | } /* end keyctl_revoke_key() */ | ||
369 | |||
370 | /*****************************************************************************/ | ||
371 | /* | ||
372 | * clear the specified process keyring | ||
373 | * - the keyring must be writable | ||
374 | * - implements keyctl(KEYCTL_CLEAR) | ||
375 | */ | ||
376 | long keyctl_keyring_clear(key_serial_t ringid) | ||
377 | { | ||
378 | struct key *keyring; | ||
379 | long ret; | ||
380 | |||
381 | keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); | ||
382 | if (IS_ERR(keyring)) { | ||
383 | ret = PTR_ERR(keyring); | ||
384 | goto error; | ||
385 | } | ||
386 | |||
387 | ret = keyring_clear(keyring); | ||
388 | |||
389 | key_put(keyring); | ||
390 | error: | ||
391 | return ret; | ||
392 | |||
393 | } /* end keyctl_keyring_clear() */ | ||
394 | |||
395 | /*****************************************************************************/ | ||
396 | /* | ||
397 | * link a key into a keyring | ||
398 | * - the keyring must be writable | ||
399 | * - the key must be linkable | ||
400 | * - implements keyctl(KEYCTL_LINK) | ||
401 | */ | ||
402 | long keyctl_keyring_link(key_serial_t id, key_serial_t ringid) | ||
403 | { | ||
404 | struct key *keyring, *key; | ||
405 | long ret; | ||
406 | |||
407 | keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); | ||
408 | if (IS_ERR(keyring)) { | ||
409 | ret = PTR_ERR(keyring); | ||
410 | goto error; | ||
411 | } | ||
412 | |||
413 | key = lookup_user_key(id, 1, 0, KEY_LINK); | ||
414 | if (IS_ERR(key)) { | ||
415 | ret = PTR_ERR(key); | ||
416 | goto error2; | ||
417 | } | ||
418 | |||
419 | ret = key_link(keyring, key); | ||
420 | |||
421 | key_put(key); | ||
422 | error2: | ||
423 | key_put(keyring); | ||
424 | error: | ||
425 | return ret; | ||
426 | |||
427 | } /* end keyctl_keyring_link() */ | ||
428 | |||
429 | /*****************************************************************************/ | ||
430 | /* | ||
431 | * unlink the first attachment of a key from a keyring | ||
432 | * - the keyring must be writable | ||
433 | * - we don't need any permissions on the key | ||
434 | * - implements keyctl(KEYCTL_UNLINK) | ||
435 | */ | ||
436 | long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) | ||
437 | { | ||
438 | struct key *keyring, *key; | ||
439 | long ret; | ||
440 | |||
441 | keyring = lookup_user_key(ringid, 0, 0, KEY_WRITE); | ||
442 | if (IS_ERR(keyring)) { | ||
443 | ret = PTR_ERR(keyring); | ||
444 | goto error; | ||
445 | } | ||
446 | |||
447 | key = lookup_user_key(id, 0, 0, 0); | ||
448 | if (IS_ERR(key)) { | ||
449 | ret = PTR_ERR(key); | ||
450 | goto error2; | ||
451 | } | ||
452 | |||
453 | ret = key_unlink(keyring, key); | ||
454 | |||
455 | key_put(key); | ||
456 | error2: | ||
457 | key_put(keyring); | ||
458 | error: | ||
459 | return ret; | ||
460 | |||
461 | } /* end keyctl_keyring_unlink() */ | ||
462 | |||
463 | /*****************************************************************************/ | ||
464 | /* | ||
465 | * describe a user key | ||
466 | * - the key must have view permission | ||
467 | * - if there's a buffer, we place up to buflen bytes of data into it | ||
468 | * - unless there's an error, we return the amount of description available, | ||
469 | * irrespective of how much we may have copied | ||
470 | * - the description is formatted thus: | ||
471 | * type;uid;gid;perm;description<NUL> | ||
472 | * - implements keyctl(KEYCTL_DESCRIBE) | ||
473 | */ | ||
474 | long keyctl_describe_key(key_serial_t keyid, | ||
475 | char __user *buffer, | ||
476 | size_t buflen) | ||
477 | { | ||
478 | struct key *key; | ||
479 | char *tmpbuf; | ||
480 | long ret; | ||
481 | |||
482 | key = lookup_user_key(keyid, 0, 1, KEY_VIEW); | ||
483 | if (IS_ERR(key)) { | ||
484 | ret = PTR_ERR(key); | ||
485 | goto error; | ||
486 | } | ||
487 | |||
488 | /* calculate how much description we're going to return */ | ||
489 | ret = -ENOMEM; | ||
490 | tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
491 | if (!tmpbuf) | ||
492 | goto error2; | ||
493 | |||
494 | ret = snprintf(tmpbuf, PAGE_SIZE - 1, | ||
495 | "%s;%d;%d;%06x;%s", | ||
496 | key->type->name, | ||
497 | key->uid, | ||
498 | key->gid, | ||
499 | key->perm, | ||
500 | key->description ? key->description :"" | ||
501 | ); | ||
502 | |||
503 | /* include a NUL char at the end of the data */ | ||
504 | if (ret > PAGE_SIZE - 1) | ||
505 | ret = PAGE_SIZE - 1; | ||
506 | tmpbuf[ret] = 0; | ||
507 | ret++; | ||
508 | |||
509 | /* consider returning the data */ | ||
510 | if (buffer && buflen > 0) { | ||
511 | if (buflen > ret) | ||
512 | buflen = ret; | ||
513 | |||
514 | if (copy_to_user(buffer, tmpbuf, buflen) != 0) | ||
515 | ret = -EFAULT; | ||
516 | } | ||
517 | |||
518 | kfree(tmpbuf); | ||
519 | error2: | ||
520 | key_put(key); | ||
521 | error: | ||
522 | return ret; | ||
523 | |||
524 | } /* end keyctl_describe_key() */ | ||
525 | |||
526 | /*****************************************************************************/ | ||
527 | /* | ||
528 | * search the specified keyring for a matching key | ||
529 | * - the start keyring must be searchable | ||
530 | * - nested keyrings may also be searched if they are searchable | ||
531 | * - only keys with search permission may be found | ||
532 | * - if a key is found, it will be attached to the destination keyring if | ||
533 | * there's one specified | ||
534 | * - implements keyctl(KEYCTL_SEARCH) | ||
535 | */ | ||
536 | long keyctl_keyring_search(key_serial_t ringid, | ||
537 | const char __user *_type, | ||
538 | const char __user *_description, | ||
539 | key_serial_t destringid) | ||
540 | { | ||
541 | struct key_type *ktype; | ||
542 | struct key *keyring, *key, *dest; | ||
543 | char type[32], *description; | ||
544 | long dlen, ret; | ||
545 | |||
546 | /* pull the type and description into kernel space */ | ||
547 | ret = strncpy_from_user(type, _type, sizeof(type) - 1); | ||
548 | if (ret < 0) | ||
549 | goto error; | ||
550 | type[31] = '\0'; | ||
551 | |||
552 | ret = -EFAULT; | ||
553 | dlen = strnlen_user(_description, PAGE_SIZE - 1); | ||
554 | if (dlen <= 0) | ||
555 | goto error; | ||
556 | |||
557 | ret = -EINVAL; | ||
558 | if (dlen > PAGE_SIZE - 1) | ||
559 | goto error; | ||
560 | |||
561 | ret = -ENOMEM; | ||
562 | description = kmalloc(dlen + 1, GFP_KERNEL); | ||
563 | if (!description) | ||
564 | goto error; | ||
565 | |||
566 | ret = -EFAULT; | ||
567 | if (copy_from_user(description, _description, dlen + 1) != 0) | ||
568 | goto error2; | ||
569 | |||
570 | /* get the keyring at which to begin the search */ | ||
571 | keyring = lookup_user_key(ringid, 0, 0, KEY_SEARCH); | ||
572 | if (IS_ERR(keyring)) { | ||
573 | ret = PTR_ERR(keyring); | ||
574 | goto error2; | ||
575 | } | ||
576 | |||
577 | /* get the destination keyring if specified */ | ||
578 | dest = NULL; | ||
579 | if (destringid) { | ||
580 | dest = lookup_user_key(destringid, 1, 0, KEY_WRITE); | ||
581 | if (IS_ERR(dest)) { | ||
582 | ret = PTR_ERR(dest); | ||
583 | goto error3; | ||
584 | } | ||
585 | } | ||
586 | |||
587 | /* find the key type */ | ||
588 | ktype = key_type_lookup(type); | ||
589 | if (IS_ERR(ktype)) { | ||
590 | ret = PTR_ERR(ktype); | ||
591 | goto error4; | ||
592 | } | ||
593 | |||
594 | /* do the search */ | ||
595 | key = keyring_search(keyring, ktype, description); | ||
596 | if (IS_ERR(key)) { | ||
597 | ret = PTR_ERR(key); | ||
598 | |||
599 | /* treat lack or presence of a negative key the same */ | ||
600 | if (ret == -EAGAIN) | ||
601 | ret = -ENOKEY; | ||
602 | goto error5; | ||
603 | } | ||
604 | |||
605 | /* link the resulting key to the destination keyring if we can */ | ||
606 | if (dest) { | ||
607 | ret = -EACCES; | ||
608 | if (!key_permission(key, KEY_LINK)) | ||
609 | goto error6; | ||
610 | |||
611 | ret = key_link(dest, key); | ||
612 | if (ret < 0) | ||
613 | goto error6; | ||
614 | } | ||
615 | |||
616 | ret = key->serial; | ||
617 | |||
618 | error6: | ||
619 | key_put(key); | ||
620 | error5: | ||
621 | key_type_put(ktype); | ||
622 | error4: | ||
623 | key_put(dest); | ||
624 | error3: | ||
625 | key_put(keyring); | ||
626 | error2: | ||
627 | kfree(description); | ||
628 | error: | ||
629 | return ret; | ||
630 | |||
631 | } /* end keyctl_keyring_search() */ | ||
632 | |||
633 | /*****************************************************************************/ | ||
634 | /* | ||
635 | * see if the key we're looking at is the target key | ||
636 | */ | ||
637 | static int keyctl_read_key_same(const struct key *key, const void *target) | ||
638 | { | ||
639 | return key == target; | ||
640 | |||
641 | } /* end keyctl_read_key_same() */ | ||
642 | |||
643 | /*****************************************************************************/ | ||
644 | /* | ||
645 | * read a user key's payload | ||
646 | * - the keyring must be readable or the key must be searchable from the | ||
647 | * process's keyrings | ||
648 | * - if there's a buffer, we place up to buflen bytes of data into it | ||
649 | * - unless there's an error, we return the amount of data in the key, | ||
650 | * irrespective of how much we may have copied | ||
651 | * - implements keyctl(KEYCTL_READ) | ||
652 | */ | ||
653 | long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) | ||
654 | { | ||
655 | struct key *key, *skey; | ||
656 | long ret; | ||
657 | |||
658 | /* find the key first */ | ||
659 | key = lookup_user_key(keyid, 0, 0, 0); | ||
660 | if (!IS_ERR(key)) { | ||
661 | /* see if we can read it directly */ | ||
662 | if (key_permission(key, KEY_READ)) | ||
663 | goto can_read_key; | ||
664 | |||
665 | /* can't; see if it's searchable from this process's | ||
666 | * keyrings */ | ||
667 | ret = -ENOKEY; | ||
668 | if (key_permission(key, KEY_SEARCH)) { | ||
669 | /* okay - we do have search permission on the key | ||
670 | * itself, but do we have the key? */ | ||
671 | skey = search_process_keyrings_aux(key->type, key, | ||
672 | keyctl_read_key_same); | ||
673 | if (!IS_ERR(skey)) | ||
674 | goto can_read_key2; | ||
675 | } | ||
676 | |||
677 | goto error2; | ||
678 | } | ||
679 | |||
680 | ret = -ENOKEY; | ||
681 | goto error; | ||
682 | |||
683 | /* the key is probably readable - now try to read it */ | ||
684 | can_read_key2: | ||
685 | key_put(skey); | ||
686 | can_read_key: | ||
687 | ret = key_validate(key); | ||
688 | if (ret == 0) { | ||
689 | ret = -EOPNOTSUPP; | ||
690 | if (key->type->read) { | ||
691 | /* read the data with the semaphore held (since we | ||
692 | * might sleep) */ | ||
693 | down_read(&key->sem); | ||
694 | ret = key->type->read(key, buffer, buflen); | ||
695 | up_read(&key->sem); | ||
696 | } | ||
697 | } | ||
698 | |||
699 | error2: | ||
700 | key_put(key); | ||
701 | error: | ||
702 | return ret; | ||
703 | |||
704 | } /* end keyctl_read_key() */ | ||
705 | |||
706 | /*****************************************************************************/ | ||
707 | /* | ||
708 | * change the ownership of a key | ||
709 | * - the keyring owned by the changer | ||
710 | * - if the uid or gid is -1, then that parameter is not changed | ||
711 | * - implements keyctl(KEYCTL_CHOWN) | ||
712 | */ | ||
713 | long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) | ||
714 | { | ||
715 | struct key *key; | ||
716 | long ret; | ||
717 | |||
718 | ret = 0; | ||
719 | if (uid == (uid_t) -1 && gid == (gid_t) -1) | ||
720 | goto error; | ||
721 | |||
722 | key = lookup_user_key(id, 1, 1, 0); | ||
723 | if (IS_ERR(key)) { | ||
724 | ret = PTR_ERR(key); | ||
725 | goto error; | ||
726 | } | ||
727 | |||
728 | /* make the changes with the locks held to prevent chown/chown races */ | ||
729 | ret = -EACCES; | ||
730 | down_write(&key->sem); | ||
731 | write_lock(&key->lock); | ||
732 | |||
733 | if (!capable(CAP_SYS_ADMIN)) { | ||
734 | /* only the sysadmin can chown a key to some other UID */ | ||
735 | if (uid != (uid_t) -1 && key->uid != uid) | ||
736 | goto no_access; | ||
737 | |||
738 | /* only the sysadmin can set the key's GID to a group other | ||
739 | * than one of those that the current process subscribes to */ | ||
740 | if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid)) | ||
741 | goto no_access; | ||
742 | } | ||
743 | |||
744 | /* change the UID (have to update the quotas) */ | ||
745 | if (uid != (uid_t) -1 && uid != key->uid) { | ||
746 | /* don't support UID changing yet */ | ||
747 | ret = -EOPNOTSUPP; | ||
748 | goto no_access; | ||
749 | } | ||
750 | |||
751 | /* change the GID */ | ||
752 | if (gid != (gid_t) -1) | ||
753 | key->gid = gid; | ||
754 | |||
755 | ret = 0; | ||
756 | |||
757 | no_access: | ||
758 | write_unlock(&key->lock); | ||
759 | up_write(&key->sem); | ||
760 | key_put(key); | ||
761 | error: | ||
762 | return ret; | ||
763 | |||
764 | } /* end keyctl_chown_key() */ | ||
765 | |||
766 | /*****************************************************************************/ | ||
767 | /* | ||
768 | * change the permission mask on a key | ||
769 | * - the keyring owned by the changer | ||
770 | * - implements keyctl(KEYCTL_SETPERM) | ||
771 | */ | ||
772 | long keyctl_setperm_key(key_serial_t id, key_perm_t perm) | ||
773 | { | ||
774 | struct key *key; | ||
775 | long ret; | ||
776 | |||
777 | ret = -EINVAL; | ||
778 | if (perm & ~(KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL)) | ||
779 | goto error; | ||
780 | |||
781 | key = lookup_user_key(id, 1, 1, 0); | ||
782 | if (IS_ERR(key)) { | ||
783 | ret = PTR_ERR(key); | ||
784 | goto error; | ||
785 | } | ||
786 | |||
787 | /* make the changes with the locks held to prevent chown/chmod | ||
788 | * races */ | ||
789 | ret = -EACCES; | ||
790 | down_write(&key->sem); | ||
791 | write_lock(&key->lock); | ||
792 | |||
793 | /* if we're not the sysadmin, we can only chmod a key that we | ||
794 | * own */ | ||
795 | if (!capable(CAP_SYS_ADMIN) && key->uid != current->fsuid) | ||
796 | goto no_access; | ||
797 | |||
798 | /* changing the permissions mask */ | ||
799 | key->perm = perm; | ||
800 | ret = 0; | ||
801 | |||
802 | no_access: | ||
803 | write_unlock(&key->lock); | ||
804 | up_write(&key->sem); | ||
805 | key_put(key); | ||
806 | error: | ||
807 | return ret; | ||
808 | |||
809 | } /* end keyctl_setperm_key() */ | ||
810 | |||
811 | /*****************************************************************************/ | ||
812 | /* | ||
813 | * instantiate the key with the specified payload, and, if one is given, link | ||
814 | * the key into the keyring | ||
815 | */ | ||
816 | long keyctl_instantiate_key(key_serial_t id, | ||
817 | const void __user *_payload, | ||
818 | size_t plen, | ||
819 | key_serial_t ringid) | ||
820 | { | ||
821 | struct key *key, *keyring; | ||
822 | void *payload; | ||
823 | long ret; | ||
824 | |||
825 | ret = -EINVAL; | ||
826 | if (plen > 32767) | ||
827 | goto error; | ||
828 | |||
829 | /* pull the payload in if one was supplied */ | ||
830 | payload = NULL; | ||
831 | |||
832 | if (_payload) { | ||
833 | ret = -ENOMEM; | ||
834 | payload = kmalloc(plen, GFP_KERNEL); | ||
835 | if (!payload) | ||
836 | goto error; | ||
837 | |||
838 | ret = -EFAULT; | ||
839 | if (copy_from_user(payload, _payload, plen) != 0) | ||
840 | goto error2; | ||
841 | } | ||
842 | |||
843 | /* find the target key (which must be writable) */ | ||
844 | key = lookup_user_key(id, 0, 1, KEY_WRITE); | ||
845 | if (IS_ERR(key)) { | ||
846 | ret = PTR_ERR(key); | ||
847 | goto error2; | ||
848 | } | ||
849 | |||
850 | /* find the destination keyring if present (which must also be | ||
851 | * writable) */ | ||
852 | keyring = NULL; | ||
853 | if (ringid) { | ||
854 | keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); | ||
855 | if (IS_ERR(keyring)) { | ||
856 | ret = PTR_ERR(keyring); | ||
857 | goto error3; | ||
858 | } | ||
859 | } | ||
860 | |||
861 | /* instantiate the key and link it into a keyring */ | ||
862 | ret = key_instantiate_and_link(key, payload, plen, keyring); | ||
863 | |||
864 | key_put(keyring); | ||
865 | error3: | ||
866 | key_put(key); | ||
867 | error2: | ||
868 | kfree(payload); | ||
869 | error: | ||
870 | return ret; | ||
871 | |||
872 | } /* end keyctl_instantiate_key() */ | ||
873 | |||
874 | /*****************************************************************************/ | ||
875 | /* | ||
876 | * negatively instantiate the key with the given timeout (in seconds), and, if | ||
877 | * one is given, link the key into the keyring | ||
878 | */ | ||
879 | long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid) | ||
880 | { | ||
881 | struct key *key, *keyring; | ||
882 | long ret; | ||
883 | |||
884 | /* find the target key (which must be writable) */ | ||
885 | key = lookup_user_key(id, 0, 1, KEY_WRITE); | ||
886 | if (IS_ERR(key)) { | ||
887 | ret = PTR_ERR(key); | ||
888 | goto error; | ||
889 | } | ||
890 | |||
891 | /* find the destination keyring if present (which must also be | ||
892 | * writable) */ | ||
893 | keyring = NULL; | ||
894 | if (ringid) { | ||
895 | keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); | ||
896 | if (IS_ERR(keyring)) { | ||
897 | ret = PTR_ERR(keyring); | ||
898 | goto error2; | ||
899 | } | ||
900 | } | ||
901 | |||
902 | /* instantiate the key and link it into a keyring */ | ||
903 | ret = key_negate_and_link(key, timeout, keyring); | ||
904 | |||
905 | key_put(keyring); | ||
906 | error2: | ||
907 | key_put(key); | ||
908 | error: | ||
909 | return ret; | ||
910 | |||
911 | } /* end keyctl_negate_key() */ | ||
912 | |||
913 | /*****************************************************************************/ | ||
914 | /* | ||
915 | * the key control system call | ||
916 | */ | ||
917 | asmlinkage long sys_keyctl(int option, unsigned long arg2, unsigned long arg3, | ||
918 | unsigned long arg4, unsigned long arg5) | ||
919 | { | ||
920 | switch (option) { | ||
921 | case KEYCTL_GET_KEYRING_ID: | ||
922 | return keyctl_get_keyring_ID((key_serial_t) arg2, | ||
923 | (int) arg3); | ||
924 | |||
925 | case KEYCTL_JOIN_SESSION_KEYRING: | ||
926 | return keyctl_join_session_keyring((const char __user *) arg2); | ||
927 | |||
928 | case KEYCTL_UPDATE: | ||
929 | return keyctl_update_key((key_serial_t) arg2, | ||
930 | (const void __user *) arg3, | ||
931 | (size_t) arg4); | ||
932 | |||
933 | case KEYCTL_REVOKE: | ||
934 | return keyctl_revoke_key((key_serial_t) arg2); | ||
935 | |||
936 | case KEYCTL_DESCRIBE: | ||
937 | return keyctl_describe_key((key_serial_t) arg2, | ||
938 | (char __user *) arg3, | ||
939 | (unsigned) arg4); | ||
940 | |||
941 | case KEYCTL_CLEAR: | ||
942 | return keyctl_keyring_clear((key_serial_t) arg2); | ||
943 | |||
944 | case KEYCTL_LINK: | ||
945 | return keyctl_keyring_link((key_serial_t) arg2, | ||
946 | (key_serial_t) arg3); | ||
947 | |||
948 | case KEYCTL_UNLINK: | ||
949 | return keyctl_keyring_unlink((key_serial_t) arg2, | ||
950 | (key_serial_t) arg3); | ||
951 | |||
952 | case KEYCTL_SEARCH: | ||
953 | return keyctl_keyring_search((key_serial_t) arg2, | ||
954 | (const char __user *) arg3, | ||
955 | (const char __user *) arg4, | ||
956 | (key_serial_t) arg5); | ||
957 | |||
958 | case KEYCTL_READ: | ||
959 | return keyctl_read_key((key_serial_t) arg2, | ||
960 | (char __user *) arg3, | ||
961 | (size_t) arg4); | ||
962 | |||
963 | case KEYCTL_CHOWN: | ||
964 | return keyctl_chown_key((key_serial_t) arg2, | ||
965 | (uid_t) arg3, | ||
966 | (gid_t) arg4); | ||
967 | |||
968 | case KEYCTL_SETPERM: | ||
969 | return keyctl_setperm_key((key_serial_t) arg2, | ||
970 | (key_perm_t) arg3); | ||
971 | |||
972 | case KEYCTL_INSTANTIATE: | ||
973 | return keyctl_instantiate_key((key_serial_t) arg2, | ||
974 | (const void __user *) arg3, | ||
975 | (size_t) arg4, | ||
976 | (key_serial_t) arg5); | ||
977 | |||
978 | case KEYCTL_NEGATE: | ||
979 | return keyctl_negate_key((key_serial_t) arg2, | ||
980 | (unsigned) arg3, | ||
981 | (key_serial_t) arg4); | ||
982 | |||
983 | default: | ||
984 | return -EOPNOTSUPP; | ||
985 | } | ||
986 | |||
987 | } /* end sys_keyctl() */ | ||
diff --git a/security/keys/keyring.c b/security/keys/keyring.c new file mode 100644 index 000000000000..e2ab4f8e7481 --- /dev/null +++ b/security/keys/keyring.c | |||
@@ -0,0 +1,895 @@ | |||
1 | /* keyring.c: keyring handling | ||
2 | * | ||
3 | * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/seq_file.h> | ||
17 | #include <linux/err.h> | ||
18 | #include <asm/uaccess.h> | ||
19 | #include "internal.h" | ||
20 | |||
21 | /* | ||
22 | * when plumbing the depths of the key tree, this sets a hard limit set on how | ||
23 | * deep we're willing to go | ||
24 | */ | ||
25 | #define KEYRING_SEARCH_MAX_DEPTH 6 | ||
26 | |||
27 | /* | ||
28 | * we keep all named keyrings in a hash to speed looking them up | ||
29 | */ | ||
30 | #define KEYRING_NAME_HASH_SIZE (1 << 5) | ||
31 | |||
32 | static struct list_head keyring_name_hash[KEYRING_NAME_HASH_SIZE]; | ||
33 | static DEFINE_RWLOCK(keyring_name_lock); | ||
34 | |||
35 | static inline unsigned keyring_hash(const char *desc) | ||
36 | { | ||
37 | unsigned bucket = 0; | ||
38 | |||
39 | for (; *desc; desc++) | ||
40 | bucket += (unsigned char) *desc; | ||
41 | |||
42 | return bucket & (KEYRING_NAME_HASH_SIZE - 1); | ||
43 | } | ||
44 | |||
45 | /* | ||
46 | * the keyring type definition | ||
47 | */ | ||
48 | static int keyring_instantiate(struct key *keyring, | ||
49 | const void *data, size_t datalen); | ||
50 | static int keyring_duplicate(struct key *keyring, const struct key *source); | ||
51 | static int keyring_match(const struct key *keyring, const void *criterion); | ||
52 | static void keyring_destroy(struct key *keyring); | ||
53 | static void keyring_describe(const struct key *keyring, struct seq_file *m); | ||
54 | static long keyring_read(const struct key *keyring, | ||
55 | char __user *buffer, size_t buflen); | ||
56 | |||
57 | struct key_type key_type_keyring = { | ||
58 | .name = "keyring", | ||
59 | .def_datalen = sizeof(struct keyring_list), | ||
60 | .instantiate = keyring_instantiate, | ||
61 | .duplicate = keyring_duplicate, | ||
62 | .match = keyring_match, | ||
63 | .destroy = keyring_destroy, | ||
64 | .describe = keyring_describe, | ||
65 | .read = keyring_read, | ||
66 | }; | ||
67 | |||
68 | /* | ||
69 | * semaphore to serialise link/link calls to prevent two link calls in parallel | ||
70 | * introducing a cycle | ||
71 | */ | ||
72 | DECLARE_RWSEM(keyring_serialise_link_sem); | ||
73 | |||
74 | /*****************************************************************************/ | ||
75 | /* | ||
76 | * publish the name of a keyring so that it can be found by name (if it has | ||
77 | * one) | ||
78 | */ | ||
79 | void keyring_publish_name(struct key *keyring) | ||
80 | { | ||
81 | int bucket; | ||
82 | |||
83 | if (keyring->description) { | ||
84 | bucket = keyring_hash(keyring->description); | ||
85 | |||
86 | write_lock(&keyring_name_lock); | ||
87 | |||
88 | if (!keyring_name_hash[bucket].next) | ||
89 | INIT_LIST_HEAD(&keyring_name_hash[bucket]); | ||
90 | |||
91 | list_add_tail(&keyring->type_data.link, | ||
92 | &keyring_name_hash[bucket]); | ||
93 | |||
94 | write_unlock(&keyring_name_lock); | ||
95 | } | ||
96 | |||
97 | } /* end keyring_publish_name() */ | ||
98 | |||
99 | /*****************************************************************************/ | ||
100 | /* | ||
101 | * initialise a keyring | ||
102 | * - we object if we were given any data | ||
103 | */ | ||
104 | static int keyring_instantiate(struct key *keyring, | ||
105 | const void *data, size_t datalen) | ||
106 | { | ||
107 | int ret; | ||
108 | |||
109 | ret = -EINVAL; | ||
110 | if (datalen == 0) { | ||
111 | /* make the keyring available by name if it has one */ | ||
112 | keyring_publish_name(keyring); | ||
113 | ret = 0; | ||
114 | } | ||
115 | |||
116 | return ret; | ||
117 | |||
118 | } /* end keyring_instantiate() */ | ||
119 | |||
120 | /*****************************************************************************/ | ||
121 | /* | ||
122 | * duplicate the list of subscribed keys from a source keyring into this one | ||
123 | */ | ||
124 | static int keyring_duplicate(struct key *keyring, const struct key *source) | ||
125 | { | ||
126 | struct keyring_list *sklist, *klist; | ||
127 | unsigned max; | ||
128 | size_t size; | ||
129 | int loop, ret; | ||
130 | |||
131 | const unsigned limit = | ||
132 | (PAGE_SIZE - sizeof(*klist)) / sizeof(struct key); | ||
133 | |||
134 | ret = 0; | ||
135 | sklist = source->payload.subscriptions; | ||
136 | |||
137 | if (sklist && sklist->nkeys > 0) { | ||
138 | max = sklist->nkeys; | ||
139 | BUG_ON(max > limit); | ||
140 | |||
141 | max = (max + 3) & ~3; | ||
142 | if (max > limit) | ||
143 | max = limit; | ||
144 | |||
145 | ret = -ENOMEM; | ||
146 | size = sizeof(*klist) + sizeof(struct key) * max; | ||
147 | klist = kmalloc(size, GFP_KERNEL); | ||
148 | if (!klist) | ||
149 | goto error; | ||
150 | |||
151 | klist->maxkeys = max; | ||
152 | klist->nkeys = sklist->nkeys; | ||
153 | memcpy(klist->keys, | ||
154 | sklist->keys, | ||
155 | sklist->nkeys * sizeof(struct key)); | ||
156 | |||
157 | for (loop = klist->nkeys - 1; loop >= 0; loop--) | ||
158 | atomic_inc(&klist->keys[loop]->usage); | ||
159 | |||
160 | keyring->payload.subscriptions = klist; | ||
161 | ret = 0; | ||
162 | } | ||
163 | |||
164 | error: | ||
165 | return ret; | ||
166 | |||
167 | } /* end keyring_duplicate() */ | ||
168 | |||
169 | /*****************************************************************************/ | ||
170 | /* | ||
171 | * match keyrings on their name | ||
172 | */ | ||
173 | static int keyring_match(const struct key *keyring, const void *description) | ||
174 | { | ||
175 | return keyring->description && | ||
176 | strcmp(keyring->description, description) == 0; | ||
177 | |||
178 | } /* end keyring_match() */ | ||
179 | |||
180 | /*****************************************************************************/ | ||
181 | /* | ||
182 | * dispose of the data dangling from the corpse of a keyring | ||
183 | */ | ||
184 | static void keyring_destroy(struct key *keyring) | ||
185 | { | ||
186 | struct keyring_list *klist; | ||
187 | int loop; | ||
188 | |||
189 | if (keyring->description) { | ||
190 | write_lock(&keyring_name_lock); | ||
191 | list_del(&keyring->type_data.link); | ||
192 | write_unlock(&keyring_name_lock); | ||
193 | } | ||
194 | |||
195 | klist = keyring->payload.subscriptions; | ||
196 | if (klist) { | ||
197 | for (loop = klist->nkeys - 1; loop >= 0; loop--) | ||
198 | key_put(klist->keys[loop]); | ||
199 | kfree(klist); | ||
200 | } | ||
201 | |||
202 | } /* end keyring_destroy() */ | ||
203 | |||
204 | /*****************************************************************************/ | ||
205 | /* | ||
206 | * describe the keyring | ||
207 | */ | ||
208 | static void keyring_describe(const struct key *keyring, struct seq_file *m) | ||
209 | { | ||
210 | struct keyring_list *klist; | ||
211 | |||
212 | if (keyring->description) { | ||
213 | seq_puts(m, keyring->description); | ||
214 | } | ||
215 | else { | ||
216 | seq_puts(m, "[anon]"); | ||
217 | } | ||
218 | |||
219 | klist = keyring->payload.subscriptions; | ||
220 | if (klist) | ||
221 | seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys); | ||
222 | else | ||
223 | seq_puts(m, ": empty"); | ||
224 | |||
225 | } /* end keyring_describe() */ | ||
226 | |||
227 | /*****************************************************************************/ | ||
228 | /* | ||
229 | * read a list of key IDs from the keyring's contents | ||
230 | */ | ||
231 | static long keyring_read(const struct key *keyring, | ||
232 | char __user *buffer, size_t buflen) | ||
233 | { | ||
234 | struct keyring_list *klist; | ||
235 | struct key *key; | ||
236 | size_t qty, tmp; | ||
237 | int loop, ret; | ||
238 | |||
239 | ret = 0; | ||
240 | klist = keyring->payload.subscriptions; | ||
241 | |||
242 | if (klist) { | ||
243 | /* calculate how much data we could return */ | ||
244 | qty = klist->nkeys * sizeof(key_serial_t); | ||
245 | |||
246 | if (buffer && buflen > 0) { | ||
247 | if (buflen > qty) | ||
248 | buflen = qty; | ||
249 | |||
250 | /* copy the IDs of the subscribed keys into the | ||
251 | * buffer */ | ||
252 | ret = -EFAULT; | ||
253 | |||
254 | for (loop = 0; loop < klist->nkeys; loop++) { | ||
255 | key = klist->keys[loop]; | ||
256 | |||
257 | tmp = sizeof(key_serial_t); | ||
258 | if (tmp > buflen) | ||
259 | tmp = buflen; | ||
260 | |||
261 | if (copy_to_user(buffer, | ||
262 | &key->serial, | ||
263 | tmp) != 0) | ||
264 | goto error; | ||
265 | |||
266 | buflen -= tmp; | ||
267 | if (buflen == 0) | ||
268 | break; | ||
269 | buffer += tmp; | ||
270 | } | ||
271 | } | ||
272 | |||
273 | ret = qty; | ||
274 | } | ||
275 | |||
276 | error: | ||
277 | return ret; | ||
278 | |||
279 | } /* end keyring_read() */ | ||
280 | |||
281 | /*****************************************************************************/ | ||
282 | /* | ||
283 | * allocate a keyring and link into the destination keyring | ||
284 | */ | ||
285 | struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, | ||
286 | int not_in_quota, struct key *dest) | ||
287 | { | ||
288 | struct key *keyring; | ||
289 | int ret; | ||
290 | |||
291 | keyring = key_alloc(&key_type_keyring, description, | ||
292 | uid, gid, KEY_USR_ALL, not_in_quota); | ||
293 | |||
294 | if (!IS_ERR(keyring)) { | ||
295 | ret = key_instantiate_and_link(keyring, NULL, 0, dest); | ||
296 | if (ret < 0) { | ||
297 | key_put(keyring); | ||
298 | keyring = ERR_PTR(ret); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | return keyring; | ||
303 | |||
304 | } /* end keyring_alloc() */ | ||
305 | |||
306 | /*****************************************************************************/ | ||
307 | /* | ||
308 | * search the supplied keyring tree for a key that matches the criterion | ||
309 | * - perform a breadth-then-depth search up to the prescribed limit | ||
310 | * - we only find keys on which we have search permission | ||
311 | * - we use the supplied match function to see if the description (or other | ||
312 | * feature of interest) matches | ||
313 | * - we readlock the keyrings as we search down the tree | ||
314 | * - we return -EAGAIN if we didn't find any matching key | ||
315 | * - we return -ENOKEY if we only found negative matching keys | ||
316 | */ | ||
317 | struct key *keyring_search_aux(struct key *keyring, | ||
318 | struct key_type *type, | ||
319 | const void *description, | ||
320 | key_match_func_t match) | ||
321 | { | ||
322 | struct { | ||
323 | struct key *keyring; | ||
324 | int kix; | ||
325 | } stack[KEYRING_SEARCH_MAX_DEPTH]; | ||
326 | |||
327 | struct keyring_list *keylist; | ||
328 | struct timespec now; | ||
329 | struct key *key; | ||
330 | long err; | ||
331 | int sp, psp, kix; | ||
332 | |||
333 | key_check(keyring); | ||
334 | |||
335 | /* top keyring must have search permission to begin the search */ | ||
336 | key = ERR_PTR(-EACCES); | ||
337 | if (!key_permission(keyring, KEY_SEARCH)) | ||
338 | goto error; | ||
339 | |||
340 | key = ERR_PTR(-ENOTDIR); | ||
341 | if (keyring->type != &key_type_keyring) | ||
342 | goto error; | ||
343 | |||
344 | now = current_kernel_time(); | ||
345 | err = -EAGAIN; | ||
346 | sp = 0; | ||
347 | |||
348 | /* start processing a new keyring */ | ||
349 | descend: | ||
350 | read_lock(&keyring->lock); | ||
351 | if (keyring->flags & KEY_FLAG_REVOKED) | ||
352 | goto not_this_keyring; | ||
353 | |||
354 | keylist = keyring->payload.subscriptions; | ||
355 | if (!keylist) | ||
356 | goto not_this_keyring; | ||
357 | |||
358 | /* iterate through the keys in this keyring first */ | ||
359 | for (kix = 0; kix < keylist->nkeys; kix++) { | ||
360 | key = keylist->keys[kix]; | ||
361 | |||
362 | /* ignore keys not of this type */ | ||
363 | if (key->type != type) | ||
364 | continue; | ||
365 | |||
366 | /* skip revoked keys and expired keys */ | ||
367 | if (key->flags & KEY_FLAG_REVOKED) | ||
368 | continue; | ||
369 | |||
370 | if (key->expiry && now.tv_sec >= key->expiry) | ||
371 | continue; | ||
372 | |||
373 | /* keys that don't match */ | ||
374 | if (!match(key, description)) | ||
375 | continue; | ||
376 | |||
377 | /* key must have search permissions */ | ||
378 | if (!key_permission(key, KEY_SEARCH)) | ||
379 | continue; | ||
380 | |||
381 | /* we set a different error code if we find a negative key */ | ||
382 | if (key->flags & KEY_FLAG_NEGATIVE) { | ||
383 | err = -ENOKEY; | ||
384 | continue; | ||
385 | } | ||
386 | |||
387 | goto found; | ||
388 | } | ||
389 | |||
390 | /* search through the keyrings nested in this one */ | ||
391 | kix = 0; | ||
392 | ascend: | ||
393 | while (kix < keylist->nkeys) { | ||
394 | key = keylist->keys[kix]; | ||
395 | if (key->type != &key_type_keyring) | ||
396 | goto next; | ||
397 | |||
398 | /* recursively search nested keyrings | ||
399 | * - only search keyrings for which we have search permission | ||
400 | */ | ||
401 | if (sp >= KEYRING_SEARCH_MAX_DEPTH) | ||
402 | goto next; | ||
403 | |||
404 | if (!key_permission(key, KEY_SEARCH)) | ||
405 | goto next; | ||
406 | |||
407 | /* evade loops in the keyring tree */ | ||
408 | for (psp = 0; psp < sp; psp++) | ||
409 | if (stack[psp].keyring == keyring) | ||
410 | goto next; | ||
411 | |||
412 | /* stack the current position */ | ||
413 | stack[sp].keyring = keyring; | ||
414 | stack[sp].kix = kix; | ||
415 | sp++; | ||
416 | |||
417 | /* begin again with the new keyring */ | ||
418 | keyring = key; | ||
419 | goto descend; | ||
420 | |||
421 | next: | ||
422 | kix++; | ||
423 | } | ||
424 | |||
425 | /* the keyring we're looking at was disqualified or didn't contain a | ||
426 | * matching key */ | ||
427 | not_this_keyring: | ||
428 | read_unlock(&keyring->lock); | ||
429 | |||
430 | if (sp > 0) { | ||
431 | /* resume the processing of a keyring higher up in the tree */ | ||
432 | sp--; | ||
433 | keyring = stack[sp].keyring; | ||
434 | keylist = keyring->payload.subscriptions; | ||
435 | kix = stack[sp].kix + 1; | ||
436 | goto ascend; | ||
437 | } | ||
438 | |||
439 | key = ERR_PTR(err); | ||
440 | goto error; | ||
441 | |||
442 | /* we found a viable match */ | ||
443 | found: | ||
444 | atomic_inc(&key->usage); | ||
445 | read_unlock(&keyring->lock); | ||
446 | |||
447 | /* unwind the keyring stack */ | ||
448 | while (sp > 0) { | ||
449 | sp--; | ||
450 | read_unlock(&stack[sp].keyring->lock); | ||
451 | } | ||
452 | |||
453 | key_check(key); | ||
454 | error: | ||
455 | return key; | ||
456 | |||
457 | } /* end keyring_search_aux() */ | ||
458 | |||
459 | /*****************************************************************************/ | ||
460 | /* | ||
461 | * search the supplied keyring tree for a key that matches the criterion | ||
462 | * - perform a breadth-then-depth search up to the prescribed limit | ||
463 | * - we only find keys on which we have search permission | ||
464 | * - we readlock the keyrings as we search down the tree | ||
465 | * - we return -EAGAIN if we didn't find any matching key | ||
466 | * - we return -ENOKEY if we only found negative matching keys | ||
467 | */ | ||
468 | struct key *keyring_search(struct key *keyring, | ||
469 | struct key_type *type, | ||
470 | const char *description) | ||
471 | { | ||
472 | return keyring_search_aux(keyring, type, description, type->match); | ||
473 | |||
474 | } /* end keyring_search() */ | ||
475 | |||
476 | EXPORT_SYMBOL(keyring_search); | ||
477 | |||
478 | /*****************************************************************************/ | ||
479 | /* | ||
480 | * search the given keyring only (no recursion) | ||
481 | * - keyring must be locked by caller | ||
482 | */ | ||
483 | struct key *__keyring_search_one(struct key *keyring, | ||
484 | const struct key_type *ktype, | ||
485 | const char *description, | ||
486 | key_perm_t perm) | ||
487 | { | ||
488 | struct keyring_list *klist; | ||
489 | struct key *key; | ||
490 | int loop; | ||
491 | |||
492 | klist = keyring->payload.subscriptions; | ||
493 | if (klist) { | ||
494 | for (loop = 0; loop < klist->nkeys; loop++) { | ||
495 | key = klist->keys[loop]; | ||
496 | |||
497 | if (key->type == ktype && | ||
498 | key->type->match(key, description) && | ||
499 | key_permission(key, perm) && | ||
500 | !(key->flags & KEY_FLAG_REVOKED) | ||
501 | ) | ||
502 | goto found; | ||
503 | } | ||
504 | } | ||
505 | |||
506 | key = ERR_PTR(-ENOKEY); | ||
507 | goto error; | ||
508 | |||
509 | found: | ||
510 | atomic_inc(&key->usage); | ||
511 | error: | ||
512 | return key; | ||
513 | |||
514 | } /* end __keyring_search_one() */ | ||
515 | |||
516 | /*****************************************************************************/ | ||
517 | /* | ||
518 | * find a keyring with the specified name | ||
519 | * - all named keyrings are searched | ||
520 | * - only find keyrings with search permission for the process | ||
521 | * - only find keyrings with a serial number greater than the one specified | ||
522 | */ | ||
523 | struct key *find_keyring_by_name(const char *name, key_serial_t bound) | ||
524 | { | ||
525 | struct key *keyring; | ||
526 | int bucket; | ||
527 | |||
528 | keyring = ERR_PTR(-EINVAL); | ||
529 | if (!name) | ||
530 | goto error; | ||
531 | |||
532 | bucket = keyring_hash(name); | ||
533 | |||
534 | read_lock(&keyring_name_lock); | ||
535 | |||
536 | if (keyring_name_hash[bucket].next) { | ||
537 | /* search this hash bucket for a keyring with a matching name | ||
538 | * that's readable and that hasn't been revoked */ | ||
539 | list_for_each_entry(keyring, | ||
540 | &keyring_name_hash[bucket], | ||
541 | type_data.link | ||
542 | ) { | ||
543 | if (keyring->flags & KEY_FLAG_REVOKED) | ||
544 | continue; | ||
545 | |||
546 | if (strcmp(keyring->description, name) != 0) | ||
547 | continue; | ||
548 | |||
549 | if (!key_permission(keyring, KEY_SEARCH)) | ||
550 | continue; | ||
551 | |||
552 | /* found a potential candidate, but we still need to | ||
553 | * check the serial number */ | ||
554 | if (keyring->serial <= bound) | ||
555 | continue; | ||
556 | |||
557 | /* we've got a match */ | ||
558 | atomic_inc(&keyring->usage); | ||
559 | read_unlock(&keyring_name_lock); | ||
560 | goto error; | ||
561 | } | ||
562 | } | ||
563 | |||
564 | read_unlock(&keyring_name_lock); | ||
565 | keyring = ERR_PTR(-ENOKEY); | ||
566 | |||
567 | error: | ||
568 | return keyring; | ||
569 | |||
570 | } /* end find_keyring_by_name() */ | ||
571 | |||
572 | /*****************************************************************************/ | ||
573 | /* | ||
574 | * see if a cycle will will be created by inserting acyclic tree B in acyclic | ||
575 | * tree A at the topmost level (ie: as a direct child of A) | ||
576 | * - since we are adding B to A at the top level, checking for cycles should | ||
577 | * just be a matter of seeing if node A is somewhere in tree B | ||
578 | */ | ||
579 | static int keyring_detect_cycle(struct key *A, struct key *B) | ||
580 | { | ||
581 | struct { | ||
582 | struct key *subtree; | ||
583 | int kix; | ||
584 | } stack[KEYRING_SEARCH_MAX_DEPTH]; | ||
585 | |||
586 | struct keyring_list *keylist; | ||
587 | struct key *subtree, *key; | ||
588 | int sp, kix, ret; | ||
589 | |||
590 | ret = -EDEADLK; | ||
591 | if (A == B) | ||
592 | goto error; | ||
593 | |||
594 | subtree = B; | ||
595 | sp = 0; | ||
596 | |||
597 | /* start processing a new keyring */ | ||
598 | descend: | ||
599 | read_lock(&subtree->lock); | ||
600 | if (subtree->flags & KEY_FLAG_REVOKED) | ||
601 | goto not_this_keyring; | ||
602 | |||
603 | keylist = subtree->payload.subscriptions; | ||
604 | if (!keylist) | ||
605 | goto not_this_keyring; | ||
606 | kix = 0; | ||
607 | |||
608 | ascend: | ||
609 | /* iterate through the remaining keys in this keyring */ | ||
610 | for (; kix < keylist->nkeys; kix++) { | ||
611 | key = keylist->keys[kix]; | ||
612 | |||
613 | if (key == A) | ||
614 | goto cycle_detected; | ||
615 | |||
616 | /* recursively check nested keyrings */ | ||
617 | if (key->type == &key_type_keyring) { | ||
618 | if (sp >= KEYRING_SEARCH_MAX_DEPTH) | ||
619 | goto too_deep; | ||
620 | |||
621 | /* stack the current position */ | ||
622 | stack[sp].subtree = subtree; | ||
623 | stack[sp].kix = kix; | ||
624 | sp++; | ||
625 | |||
626 | /* begin again with the new keyring */ | ||
627 | subtree = key; | ||
628 | goto descend; | ||
629 | } | ||
630 | } | ||
631 | |||
632 | /* the keyring we're looking at was disqualified or didn't contain a | ||
633 | * matching key */ | ||
634 | not_this_keyring: | ||
635 | read_unlock(&subtree->lock); | ||
636 | |||
637 | if (sp > 0) { | ||
638 | /* resume the checking of a keyring higher up in the tree */ | ||
639 | sp--; | ||
640 | subtree = stack[sp].subtree; | ||
641 | keylist = subtree->payload.subscriptions; | ||
642 | kix = stack[sp].kix + 1; | ||
643 | goto ascend; | ||
644 | } | ||
645 | |||
646 | ret = 0; /* no cycles detected */ | ||
647 | |||
648 | error: | ||
649 | return ret; | ||
650 | |||
651 | too_deep: | ||
652 | ret = -ELOOP; | ||
653 | goto error_unwind; | ||
654 | cycle_detected: | ||
655 | ret = -EDEADLK; | ||
656 | error_unwind: | ||
657 | read_unlock(&subtree->lock); | ||
658 | |||
659 | /* unwind the keyring stack */ | ||
660 | while (sp > 0) { | ||
661 | sp--; | ||
662 | read_unlock(&stack[sp].subtree->lock); | ||
663 | } | ||
664 | |||
665 | goto error; | ||
666 | |||
667 | } /* end keyring_detect_cycle() */ | ||
668 | |||
669 | /*****************************************************************************/ | ||
670 | /* | ||
671 | * link a key into to a keyring | ||
672 | * - must be called with the keyring's semaphore held | ||
673 | */ | ||
674 | int __key_link(struct key *keyring, struct key *key) | ||
675 | { | ||
676 | struct keyring_list *klist, *nklist; | ||
677 | unsigned max; | ||
678 | size_t size; | ||
679 | int ret; | ||
680 | |||
681 | ret = -EKEYREVOKED; | ||
682 | if (keyring->flags & KEY_FLAG_REVOKED) | ||
683 | goto error; | ||
684 | |||
685 | ret = -ENOTDIR; | ||
686 | if (keyring->type != &key_type_keyring) | ||
687 | goto error; | ||
688 | |||
689 | /* serialise link/link calls to prevent parallel calls causing a | ||
690 | * cycle when applied to two keyring in opposite orders */ | ||
691 | down_write(&keyring_serialise_link_sem); | ||
692 | |||
693 | /* check that we aren't going to create a cycle adding one keyring to | ||
694 | * another */ | ||
695 | if (key->type == &key_type_keyring) { | ||
696 | ret = keyring_detect_cycle(keyring, key); | ||
697 | if (ret < 0) | ||
698 | goto error2; | ||
699 | } | ||
700 | |||
701 | /* check that we aren't going to overrun the user's quota */ | ||
702 | ret = key_payload_reserve(keyring, | ||
703 | keyring->datalen + KEYQUOTA_LINK_BYTES); | ||
704 | if (ret < 0) | ||
705 | goto error2; | ||
706 | |||
707 | klist = keyring->payload.subscriptions; | ||
708 | |||
709 | if (klist && klist->nkeys < klist->maxkeys) { | ||
710 | /* there's sufficient slack space to add directly */ | ||
711 | atomic_inc(&key->usage); | ||
712 | |||
713 | write_lock(&keyring->lock); | ||
714 | klist->keys[klist->nkeys++] = key; | ||
715 | write_unlock(&keyring->lock); | ||
716 | |||
717 | ret = 0; | ||
718 | } | ||
719 | else { | ||
720 | /* grow the key list */ | ||
721 | max = 4; | ||
722 | if (klist) | ||
723 | max += klist->maxkeys; | ||
724 | |||
725 | ret = -ENFILE; | ||
726 | size = sizeof(*klist) + sizeof(*key) * max; | ||
727 | if (size > PAGE_SIZE) | ||
728 | goto error3; | ||
729 | |||
730 | ret = -ENOMEM; | ||
731 | nklist = kmalloc(size, GFP_KERNEL); | ||
732 | if (!nklist) | ||
733 | goto error3; | ||
734 | nklist->maxkeys = max; | ||
735 | nklist->nkeys = 0; | ||
736 | |||
737 | if (klist) { | ||
738 | nklist->nkeys = klist->nkeys; | ||
739 | memcpy(nklist->keys, | ||
740 | klist->keys, | ||
741 | sizeof(struct key *) * klist->nkeys); | ||
742 | } | ||
743 | |||
744 | /* add the key into the new space */ | ||
745 | atomic_inc(&key->usage); | ||
746 | |||
747 | write_lock(&keyring->lock); | ||
748 | keyring->payload.subscriptions = nklist; | ||
749 | nklist->keys[nklist->nkeys++] = key; | ||
750 | write_unlock(&keyring->lock); | ||
751 | |||
752 | /* dispose of the old keyring list */ | ||
753 | kfree(klist); | ||
754 | |||
755 | ret = 0; | ||
756 | } | ||
757 | |||
758 | error2: | ||
759 | up_write(&keyring_serialise_link_sem); | ||
760 | error: | ||
761 | return ret; | ||
762 | |||
763 | error3: | ||
764 | /* undo the quota changes */ | ||
765 | key_payload_reserve(keyring, | ||
766 | keyring->datalen - KEYQUOTA_LINK_BYTES); | ||
767 | goto error2; | ||
768 | |||
769 | } /* end __key_link() */ | ||
770 | |||
771 | /*****************************************************************************/ | ||
772 | /* | ||
773 | * link a key to a keyring | ||
774 | */ | ||
775 | int key_link(struct key *keyring, struct key *key) | ||
776 | { | ||
777 | int ret; | ||
778 | |||
779 | key_check(keyring); | ||
780 | key_check(key); | ||
781 | |||
782 | down_write(&keyring->sem); | ||
783 | ret = __key_link(keyring, key); | ||
784 | up_write(&keyring->sem); | ||
785 | |||
786 | return ret; | ||
787 | |||
788 | } /* end key_link() */ | ||
789 | |||
790 | EXPORT_SYMBOL(key_link); | ||
791 | |||
792 | /*****************************************************************************/ | ||
793 | /* | ||
794 | * unlink the first link to a key from a keyring | ||
795 | */ | ||
796 | int key_unlink(struct key *keyring, struct key *key) | ||
797 | { | ||
798 | struct keyring_list *klist; | ||
799 | int loop, ret; | ||
800 | |||
801 | key_check(keyring); | ||
802 | key_check(key); | ||
803 | |||
804 | ret = -ENOTDIR; | ||
805 | if (keyring->type != &key_type_keyring) | ||
806 | goto error; | ||
807 | |||
808 | down_write(&keyring->sem); | ||
809 | |||
810 | klist = keyring->payload.subscriptions; | ||
811 | if (klist) { | ||
812 | /* search the keyring for the key */ | ||
813 | for (loop = 0; loop < klist->nkeys; loop++) | ||
814 | if (klist->keys[loop] == key) | ||
815 | goto key_is_present; | ||
816 | } | ||
817 | |||
818 | up_write(&keyring->sem); | ||
819 | ret = -ENOENT; | ||
820 | goto error; | ||
821 | |||
822 | key_is_present: | ||
823 | /* adjust the user's quota */ | ||
824 | key_payload_reserve(keyring, | ||
825 | keyring->datalen - KEYQUOTA_LINK_BYTES); | ||
826 | |||
827 | /* shuffle down the key pointers | ||
828 | * - it might be worth shrinking the allocated memory, but that runs | ||
829 | * the risk of ENOMEM as we would have to copy | ||
830 | */ | ||
831 | write_lock(&keyring->lock); | ||
832 | |||
833 | klist->nkeys--; | ||
834 | if (loop < klist->nkeys) | ||
835 | memcpy(&klist->keys[loop], | ||
836 | &klist->keys[loop + 1], | ||
837 | (klist->nkeys - loop) * sizeof(struct key *)); | ||
838 | |||
839 | write_unlock(&keyring->lock); | ||
840 | |||
841 | up_write(&keyring->sem); | ||
842 | key_put(key); | ||
843 | ret = 0; | ||
844 | |||
845 | error: | ||
846 | return ret; | ||
847 | |||
848 | } /* end key_unlink() */ | ||
849 | |||
850 | EXPORT_SYMBOL(key_unlink); | ||
851 | |||
852 | /*****************************************************************************/ | ||
853 | /* | ||
854 | * clear the specified process keyring | ||
855 | * - implements keyctl(KEYCTL_CLEAR) | ||
856 | */ | ||
857 | int keyring_clear(struct key *keyring) | ||
858 | { | ||
859 | struct keyring_list *klist; | ||
860 | int loop, ret; | ||
861 | |||
862 | ret = -ENOTDIR; | ||
863 | if (keyring->type == &key_type_keyring) { | ||
864 | /* detach the pointer block with the locks held */ | ||
865 | down_write(&keyring->sem); | ||
866 | |||
867 | klist = keyring->payload.subscriptions; | ||
868 | if (klist) { | ||
869 | /* adjust the quota */ | ||
870 | key_payload_reserve(keyring, | ||
871 | sizeof(struct keyring_list)); | ||
872 | |||
873 | write_lock(&keyring->lock); | ||
874 | keyring->payload.subscriptions = NULL; | ||
875 | write_unlock(&keyring->lock); | ||
876 | } | ||
877 | |||
878 | up_write(&keyring->sem); | ||
879 | |||
880 | /* free the keys after the locks have been dropped */ | ||
881 | if (klist) { | ||
882 | for (loop = klist->nkeys - 1; loop >= 0; loop--) | ||
883 | key_put(klist->keys[loop]); | ||
884 | |||
885 | kfree(klist); | ||
886 | } | ||
887 | |||
888 | ret = 0; | ||
889 | } | ||
890 | |||
891 | return ret; | ||
892 | |||
893 | } /* end keyring_clear() */ | ||
894 | |||
895 | EXPORT_SYMBOL(keyring_clear); | ||
diff --git a/security/keys/proc.c b/security/keys/proc.c new file mode 100644 index 000000000000..91343b85c39c --- /dev/null +++ b/security/keys/proc.c | |||
@@ -0,0 +1,251 @@ | |||
1 | /* proc.c: proc files for key database enumeration | ||
2 | * | ||
3 | * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/fs.h> | ||
17 | #include <linux/proc_fs.h> | ||
18 | #include <linux/seq_file.h> | ||
19 | #include <asm/errno.h> | ||
20 | #include "internal.h" | ||
21 | |||
22 | #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS | ||
23 | static int proc_keys_open(struct inode *inode, struct file *file); | ||
24 | static void *proc_keys_start(struct seq_file *p, loff_t *_pos); | ||
25 | static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos); | ||
26 | static void proc_keys_stop(struct seq_file *p, void *v); | ||
27 | static int proc_keys_show(struct seq_file *m, void *v); | ||
28 | |||
29 | static struct seq_operations proc_keys_ops = { | ||
30 | .start = proc_keys_start, | ||
31 | .next = proc_keys_next, | ||
32 | .stop = proc_keys_stop, | ||
33 | .show = proc_keys_show, | ||
34 | }; | ||
35 | |||
36 | static struct file_operations proc_keys_fops = { | ||
37 | .open = proc_keys_open, | ||
38 | .read = seq_read, | ||
39 | .llseek = seq_lseek, | ||
40 | .release = seq_release, | ||
41 | }; | ||
42 | #endif | ||
43 | |||
44 | static int proc_key_users_open(struct inode *inode, struct file *file); | ||
45 | static void *proc_key_users_start(struct seq_file *p, loff_t *_pos); | ||
46 | static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos); | ||
47 | static void proc_key_users_stop(struct seq_file *p, void *v); | ||
48 | static int proc_key_users_show(struct seq_file *m, void *v); | ||
49 | |||
50 | static struct seq_operations proc_key_users_ops = { | ||
51 | .start = proc_key_users_start, | ||
52 | .next = proc_key_users_next, | ||
53 | .stop = proc_key_users_stop, | ||
54 | .show = proc_key_users_show, | ||
55 | }; | ||
56 | |||
57 | static struct file_operations proc_key_users_fops = { | ||
58 | .open = proc_key_users_open, | ||
59 | .read = seq_read, | ||
60 | .llseek = seq_lseek, | ||
61 | .release = seq_release, | ||
62 | }; | ||
63 | |||
64 | /*****************************************************************************/ | ||
65 | /* | ||
66 | * declare the /proc files | ||
67 | */ | ||
68 | static int __init key_proc_init(void) | ||
69 | { | ||
70 | struct proc_dir_entry *p; | ||
71 | |||
72 | #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS | ||
73 | p = create_proc_entry("keys", 0, NULL); | ||
74 | if (!p) | ||
75 | panic("Cannot create /proc/keys\n"); | ||
76 | |||
77 | p->proc_fops = &proc_keys_fops; | ||
78 | #endif | ||
79 | |||
80 | p = create_proc_entry("key-users", 0, NULL); | ||
81 | if (!p) | ||
82 | panic("Cannot create /proc/key-users\n"); | ||
83 | |||
84 | p->proc_fops = &proc_key_users_fops; | ||
85 | |||
86 | return 0; | ||
87 | |||
88 | } /* end key_proc_init() */ | ||
89 | |||
90 | __initcall(key_proc_init); | ||
91 | |||
92 | /*****************************************************************************/ | ||
93 | /* | ||
94 | * implement "/proc/keys" to provides a list of the keys on the system | ||
95 | */ | ||
96 | #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS | ||
97 | |||
98 | static int proc_keys_open(struct inode *inode, struct file *file) | ||
99 | { | ||
100 | return seq_open(file, &proc_keys_ops); | ||
101 | |||
102 | } | ||
103 | |||
104 | static void *proc_keys_start(struct seq_file *p, loff_t *_pos) | ||
105 | { | ||
106 | struct rb_node *_p; | ||
107 | loff_t pos = *_pos; | ||
108 | |||
109 | spin_lock(&key_serial_lock); | ||
110 | |||
111 | _p = rb_first(&key_serial_tree); | ||
112 | while (pos > 0 && _p) { | ||
113 | pos--; | ||
114 | _p = rb_next(_p); | ||
115 | } | ||
116 | |||
117 | return _p; | ||
118 | |||
119 | } | ||
120 | |||
121 | static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos) | ||
122 | { | ||
123 | (*_pos)++; | ||
124 | return rb_next((struct rb_node *) v); | ||
125 | |||
126 | } | ||
127 | |||
128 | static void proc_keys_stop(struct seq_file *p, void *v) | ||
129 | { | ||
130 | spin_unlock(&key_serial_lock); | ||
131 | } | ||
132 | |||
133 | static int proc_keys_show(struct seq_file *m, void *v) | ||
134 | { | ||
135 | struct rb_node *_p = v; | ||
136 | struct key *key = rb_entry(_p, struct key, serial_node); | ||
137 | struct timespec now; | ||
138 | unsigned long timo; | ||
139 | char xbuf[12]; | ||
140 | |||
141 | now = current_kernel_time(); | ||
142 | |||
143 | read_lock(&key->lock); | ||
144 | |||
145 | /* come up with a suitable timeout value */ | ||
146 | if (key->expiry == 0) { | ||
147 | memcpy(xbuf, "perm", 5); | ||
148 | } | ||
149 | else if (now.tv_sec >= key->expiry) { | ||
150 | memcpy(xbuf, "expd", 5); | ||
151 | } | ||
152 | else { | ||
153 | timo = key->expiry - now.tv_sec; | ||
154 | |||
155 | if (timo < 60) | ||
156 | sprintf(xbuf, "%lus", timo); | ||
157 | else if (timo < 60*60) | ||
158 | sprintf(xbuf, "%lum", timo / 60); | ||
159 | else if (timo < 60*60*24) | ||
160 | sprintf(xbuf, "%luh", timo / (60*60)); | ||
161 | else if (timo < 60*60*24*7) | ||
162 | sprintf(xbuf, "%lud", timo / (60*60*24)); | ||
163 | else | ||
164 | sprintf(xbuf, "%luw", timo / (60*60*24*7)); | ||
165 | } | ||
166 | |||
167 | seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %06x %5d %5d %-9.9s ", | ||
168 | key->serial, | ||
169 | key->flags & KEY_FLAG_INSTANTIATED ? 'I' : '-', | ||
170 | key->flags & KEY_FLAG_REVOKED ? 'R' : '-', | ||
171 | key->flags & KEY_FLAG_DEAD ? 'D' : '-', | ||
172 | key->flags & KEY_FLAG_IN_QUOTA ? 'Q' : '-', | ||
173 | key->flags & KEY_FLAG_USER_CONSTRUCT ? 'U' : '-', | ||
174 | key->flags & KEY_FLAG_NEGATIVE ? 'N' : '-', | ||
175 | atomic_read(&key->usage), | ||
176 | xbuf, | ||
177 | key->perm, | ||
178 | key->uid, | ||
179 | key->gid, | ||
180 | key->type->name); | ||
181 | |||
182 | if (key->type->describe) | ||
183 | key->type->describe(key, m); | ||
184 | seq_putc(m, '\n'); | ||
185 | |||
186 | read_unlock(&key->lock); | ||
187 | |||
188 | return 0; | ||
189 | |||
190 | } | ||
191 | |||
192 | #endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */ | ||
193 | |||
194 | /*****************************************************************************/ | ||
195 | /* | ||
196 | * implement "/proc/key-users" to provides a list of the key users | ||
197 | */ | ||
198 | static int proc_key_users_open(struct inode *inode, struct file *file) | ||
199 | { | ||
200 | return seq_open(file, &proc_key_users_ops); | ||
201 | |||
202 | } | ||
203 | |||
204 | static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) | ||
205 | { | ||
206 | struct rb_node *_p; | ||
207 | loff_t pos = *_pos; | ||
208 | |||
209 | spin_lock(&key_user_lock); | ||
210 | |||
211 | _p = rb_first(&key_user_tree); | ||
212 | while (pos > 0 && _p) { | ||
213 | pos--; | ||
214 | _p = rb_next(_p); | ||
215 | } | ||
216 | |||
217 | return _p; | ||
218 | |||
219 | } | ||
220 | |||
221 | static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) | ||
222 | { | ||
223 | (*_pos)++; | ||
224 | return rb_next((struct rb_node *) v); | ||
225 | |||
226 | } | ||
227 | |||
228 | static void proc_key_users_stop(struct seq_file *p, void *v) | ||
229 | { | ||
230 | spin_unlock(&key_user_lock); | ||
231 | } | ||
232 | |||
233 | static int proc_key_users_show(struct seq_file *m, void *v) | ||
234 | { | ||
235 | struct rb_node *_p = v; | ||
236 | struct key_user *user = rb_entry(_p, struct key_user, node); | ||
237 | |||
238 | seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", | ||
239 | user->uid, | ||
240 | atomic_read(&user->usage), | ||
241 | atomic_read(&user->nkeys), | ||
242 | atomic_read(&user->nikeys), | ||
243 | user->qnkeys, | ||
244 | KEYQUOTA_MAX_KEYS, | ||
245 | user->qnbytes, | ||
246 | KEYQUOTA_MAX_BYTES | ||
247 | ); | ||
248 | |||
249 | return 0; | ||
250 | |||
251 | } | ||
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c new file mode 100644 index 000000000000..2eb0e471cd40 --- /dev/null +++ b/security/keys/process_keys.c | |||
@@ -0,0 +1,665 @@ | |||
1 | /* process_keys.c: management of a process's keyrings | ||
2 | * | ||
3 | * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/keyctl.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/err.h> | ||
19 | #include <asm/uaccess.h> | ||
20 | #include "internal.h" | ||
21 | |||
22 | /* session keyring create vs join semaphore */ | ||
23 | static DECLARE_MUTEX(key_session_sem); | ||
24 | |||
25 | /* the root user's tracking struct */ | ||
26 | struct key_user root_key_user = { | ||
27 | .usage = ATOMIC_INIT(3), | ||
28 | .consq = LIST_HEAD_INIT(root_key_user.consq), | ||
29 | .lock = SPIN_LOCK_UNLOCKED, | ||
30 | .nkeys = ATOMIC_INIT(2), | ||
31 | .nikeys = ATOMIC_INIT(2), | ||
32 | .uid = 0, | ||
33 | }; | ||
34 | |||
35 | /* the root user's UID keyring */ | ||
36 | struct key root_user_keyring = { | ||
37 | .usage = ATOMIC_INIT(1), | ||
38 | .serial = 2, | ||
39 | .type = &key_type_keyring, | ||
40 | .user = &root_key_user, | ||
41 | .lock = RW_LOCK_UNLOCKED, | ||
42 | .sem = __RWSEM_INITIALIZER(root_user_keyring.sem), | ||
43 | .perm = KEY_USR_ALL, | ||
44 | .flags = KEY_FLAG_INSTANTIATED, | ||
45 | .description = "_uid.0", | ||
46 | #ifdef KEY_DEBUGGING | ||
47 | .magic = KEY_DEBUG_MAGIC, | ||
48 | #endif | ||
49 | }; | ||
50 | |||
51 | /* the root user's default session keyring */ | ||
52 | struct key root_session_keyring = { | ||
53 | .usage = ATOMIC_INIT(1), | ||
54 | .serial = 1, | ||
55 | .type = &key_type_keyring, | ||
56 | .user = &root_key_user, | ||
57 | .lock = RW_LOCK_UNLOCKED, | ||
58 | .sem = __RWSEM_INITIALIZER(root_session_keyring.sem), | ||
59 | .perm = KEY_USR_ALL, | ||
60 | .flags = KEY_FLAG_INSTANTIATED, | ||
61 | .description = "_uid_ses.0", | ||
62 | #ifdef KEY_DEBUGGING | ||
63 | .magic = KEY_DEBUG_MAGIC, | ||
64 | #endif | ||
65 | }; | ||
66 | |||
67 | /*****************************************************************************/ | ||
68 | /* | ||
69 | * allocate the keyrings to be associated with a UID | ||
70 | */ | ||
71 | int alloc_uid_keyring(struct user_struct *user) | ||
72 | { | ||
73 | struct key *uid_keyring, *session_keyring; | ||
74 | char buf[20]; | ||
75 | int ret; | ||
76 | |||
77 | /* concoct a default session keyring */ | ||
78 | sprintf(buf, "_uid_ses.%u", user->uid); | ||
79 | |||
80 | session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0, NULL); | ||
81 | if (IS_ERR(session_keyring)) { | ||
82 | ret = PTR_ERR(session_keyring); | ||
83 | goto error; | ||
84 | } | ||
85 | |||
86 | /* and a UID specific keyring, pointed to by the default session | ||
87 | * keyring */ | ||
88 | sprintf(buf, "_uid.%u", user->uid); | ||
89 | |||
90 | uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0, | ||
91 | session_keyring); | ||
92 | if (IS_ERR(uid_keyring)) { | ||
93 | key_put(session_keyring); | ||
94 | ret = PTR_ERR(uid_keyring); | ||
95 | goto error; | ||
96 | } | ||
97 | |||
98 | /* install the keyrings */ | ||
99 | user->uid_keyring = uid_keyring; | ||
100 | user->session_keyring = session_keyring; | ||
101 | ret = 0; | ||
102 | |||
103 | error: | ||
104 | return ret; | ||
105 | |||
106 | } /* end alloc_uid_keyring() */ | ||
107 | |||
108 | /*****************************************************************************/ | ||
109 | /* | ||
110 | * deal with the UID changing | ||
111 | */ | ||
112 | void switch_uid_keyring(struct user_struct *new_user) | ||
113 | { | ||
114 | #if 0 /* do nothing for now */ | ||
115 | struct key *old; | ||
116 | |||
117 | /* switch to the new user's session keyring if we were running under | ||
118 | * root's default session keyring */ | ||
119 | if (new_user->uid != 0 && | ||
120 | current->session_keyring == &root_session_keyring | ||
121 | ) { | ||
122 | atomic_inc(&new_user->session_keyring->usage); | ||
123 | |||
124 | task_lock(current); | ||
125 | old = current->session_keyring; | ||
126 | current->session_keyring = new_user->session_keyring; | ||
127 | task_unlock(current); | ||
128 | |||
129 | key_put(old); | ||
130 | } | ||
131 | #endif | ||
132 | |||
133 | } /* end switch_uid_keyring() */ | ||
134 | |||
135 | /*****************************************************************************/ | ||
136 | /* | ||
137 | * install a fresh thread keyring, discarding the old one | ||
138 | */ | ||
139 | int install_thread_keyring(struct task_struct *tsk) | ||
140 | { | ||
141 | struct key *keyring, *old; | ||
142 | char buf[20]; | ||
143 | int ret; | ||
144 | |||
145 | sprintf(buf, "_tid.%u", tsk->pid); | ||
146 | |||
147 | keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL); | ||
148 | if (IS_ERR(keyring)) { | ||
149 | ret = PTR_ERR(keyring); | ||
150 | goto error; | ||
151 | } | ||
152 | |||
153 | task_lock(tsk); | ||
154 | old = tsk->thread_keyring; | ||
155 | tsk->thread_keyring = keyring; | ||
156 | task_unlock(tsk); | ||
157 | |||
158 | ret = 0; | ||
159 | |||
160 | key_put(old); | ||
161 | error: | ||
162 | return ret; | ||
163 | |||
164 | } /* end install_thread_keyring() */ | ||
165 | |||
166 | /*****************************************************************************/ | ||
167 | /* | ||
168 | * make sure a process keyring is installed | ||
169 | */ | ||
170 | static int install_process_keyring(struct task_struct *tsk) | ||
171 | { | ||
172 | unsigned long flags; | ||
173 | struct key *keyring; | ||
174 | char buf[20]; | ||
175 | int ret; | ||
176 | |||
177 | if (!tsk->signal->process_keyring) { | ||
178 | sprintf(buf, "_pid.%u", tsk->tgid); | ||
179 | |||
180 | keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL); | ||
181 | if (IS_ERR(keyring)) { | ||
182 | ret = PTR_ERR(keyring); | ||
183 | goto error; | ||
184 | } | ||
185 | |||
186 | /* attach or swap keyrings */ | ||
187 | spin_lock_irqsave(&tsk->sighand->siglock, flags); | ||
188 | if (!tsk->signal->process_keyring) { | ||
189 | tsk->signal->process_keyring = keyring; | ||
190 | keyring = NULL; | ||
191 | } | ||
192 | spin_unlock_irqrestore(&tsk->sighand->siglock, flags); | ||
193 | |||
194 | key_put(keyring); | ||
195 | } | ||
196 | |||
197 | ret = 0; | ||
198 | error: | ||
199 | return ret; | ||
200 | |||
201 | } /* end install_process_keyring() */ | ||
202 | |||
203 | /*****************************************************************************/ | ||
204 | /* | ||
205 | * install a session keyring, discarding the old one | ||
206 | * - if a keyring is not supplied, an empty one is invented | ||
207 | */ | ||
208 | static int install_session_keyring(struct task_struct *tsk, | ||
209 | struct key *keyring) | ||
210 | { | ||
211 | unsigned long flags; | ||
212 | struct key *old; | ||
213 | char buf[20]; | ||
214 | int ret; | ||
215 | |||
216 | /* create an empty session keyring */ | ||
217 | if (!keyring) { | ||
218 | sprintf(buf, "_ses.%u", tsk->tgid); | ||
219 | |||
220 | keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL); | ||
221 | if (IS_ERR(keyring)) { | ||
222 | ret = PTR_ERR(keyring); | ||
223 | goto error; | ||
224 | } | ||
225 | } | ||
226 | else { | ||
227 | atomic_inc(&keyring->usage); | ||
228 | } | ||
229 | |||
230 | /* install the keyring */ | ||
231 | spin_lock_irqsave(&tsk->sighand->siglock, flags); | ||
232 | old = tsk->signal->session_keyring; | ||
233 | tsk->signal->session_keyring = keyring; | ||
234 | spin_unlock_irqrestore(&tsk->sighand->siglock, flags); | ||
235 | |||
236 | ret = 0; | ||
237 | |||
238 | key_put(old); | ||
239 | error: | ||
240 | return ret; | ||
241 | |||
242 | } /* end install_session_keyring() */ | ||
243 | |||
244 | /*****************************************************************************/ | ||
245 | /* | ||
246 | * copy the keys in a thread group for fork without CLONE_THREAD | ||
247 | */ | ||
248 | int copy_thread_group_keys(struct task_struct *tsk) | ||
249 | { | ||
250 | unsigned long flags; | ||
251 | |||
252 | key_check(current->thread_group->session_keyring); | ||
253 | key_check(current->thread_group->process_keyring); | ||
254 | |||
255 | /* no process keyring yet */ | ||
256 | tsk->signal->process_keyring = NULL; | ||
257 | |||
258 | /* same session keyring */ | ||
259 | spin_lock_irqsave(¤t->sighand->siglock, flags); | ||
260 | tsk->signal->session_keyring = | ||
261 | key_get(current->signal->session_keyring); | ||
262 | spin_unlock_irqrestore(¤t->sighand->siglock, flags); | ||
263 | |||
264 | return 0; | ||
265 | |||
266 | } /* end copy_thread_group_keys() */ | ||
267 | |||
268 | /*****************************************************************************/ | ||
269 | /* | ||
270 | * copy the keys for fork | ||
271 | */ | ||
272 | int copy_keys(unsigned long clone_flags, struct task_struct *tsk) | ||
273 | { | ||
274 | key_check(tsk->thread_keyring); | ||
275 | |||
276 | /* no thread keyring yet */ | ||
277 | tsk->thread_keyring = NULL; | ||
278 | return 0; | ||
279 | |||
280 | } /* end copy_keys() */ | ||
281 | |||
282 | /*****************************************************************************/ | ||
283 | /* | ||
284 | * dispose of thread group keys upon thread group destruction | ||
285 | */ | ||
286 | void exit_thread_group_keys(struct signal_struct *tg) | ||
287 | { | ||
288 | key_put(tg->session_keyring); | ||
289 | key_put(tg->process_keyring); | ||
290 | |||
291 | } /* end exit_thread_group_keys() */ | ||
292 | |||
293 | /*****************************************************************************/ | ||
294 | /* | ||
295 | * dispose of keys upon thread exit | ||
296 | */ | ||
297 | void exit_keys(struct task_struct *tsk) | ||
298 | { | ||
299 | key_put(tsk->thread_keyring); | ||
300 | |||
301 | } /* end exit_keys() */ | ||
302 | |||
303 | /*****************************************************************************/ | ||
304 | /* | ||
305 | * deal with execve() | ||
306 | */ | ||
307 | int exec_keys(struct task_struct *tsk) | ||
308 | { | ||
309 | unsigned long flags; | ||
310 | struct key *old; | ||
311 | |||
312 | /* newly exec'd tasks don't get a thread keyring */ | ||
313 | task_lock(tsk); | ||
314 | old = tsk->thread_keyring; | ||
315 | tsk->thread_keyring = NULL; | ||
316 | task_unlock(tsk); | ||
317 | |||
318 | key_put(old); | ||
319 | |||
320 | /* discard the process keyring from a newly exec'd task */ | ||
321 | spin_lock_irqsave(&tsk->sighand->siglock, flags); | ||
322 | old = tsk->signal->process_keyring; | ||
323 | tsk->signal->process_keyring = NULL; | ||
324 | spin_unlock_irqrestore(&tsk->sighand->siglock, flags); | ||
325 | |||
326 | key_put(old); | ||
327 | |||
328 | return 0; | ||
329 | |||
330 | } /* end exec_keys() */ | ||
331 | |||
332 | /*****************************************************************************/ | ||
333 | /* | ||
334 | * deal with SUID programs | ||
335 | * - we might want to make this invent a new session keyring | ||
336 | */ | ||
337 | int suid_keys(struct task_struct *tsk) | ||
338 | { | ||
339 | return 0; | ||
340 | |||
341 | } /* end suid_keys() */ | ||
342 | |||
343 | /*****************************************************************************/ | ||
344 | /* | ||
345 | * the filesystem user ID changed | ||
346 | */ | ||
347 | void key_fsuid_changed(struct task_struct *tsk) | ||
348 | { | ||
349 | /* update the ownership of the thread keyring */ | ||
350 | if (tsk->thread_keyring) { | ||
351 | down_write(&tsk->thread_keyring->sem); | ||
352 | write_lock(&tsk->thread_keyring->lock); | ||
353 | tsk->thread_keyring->uid = tsk->fsuid; | ||
354 | write_unlock(&tsk->thread_keyring->lock); | ||
355 | up_write(&tsk->thread_keyring->sem); | ||
356 | } | ||
357 | |||
358 | } /* end key_fsuid_changed() */ | ||
359 | |||
360 | /*****************************************************************************/ | ||
361 | /* | ||
362 | * the filesystem group ID changed | ||
363 | */ | ||
364 | void key_fsgid_changed(struct task_struct *tsk) | ||
365 | { | ||
366 | /* update the ownership of the thread keyring */ | ||
367 | if (tsk->thread_keyring) { | ||
368 | down_write(&tsk->thread_keyring->sem); | ||
369 | write_lock(&tsk->thread_keyring->lock); | ||
370 | tsk->thread_keyring->gid = tsk->fsgid; | ||
371 | write_unlock(&tsk->thread_keyring->lock); | ||
372 | up_write(&tsk->thread_keyring->sem); | ||
373 | } | ||
374 | |||
375 | } /* end key_fsgid_changed() */ | ||
376 | |||
377 | /*****************************************************************************/ | ||
378 | /* | ||
379 | * search the process keyrings for the first matching key | ||
380 | * - we use the supplied match function to see if the description (or other | ||
381 | * feature of interest) matches | ||
382 | * - we return -EAGAIN if we didn't find any matching key | ||
383 | * - we return -ENOKEY if we found only negative matching keys | ||
384 | */ | ||
385 | struct key *search_process_keyrings_aux(struct key_type *type, | ||
386 | const void *description, | ||
387 | key_match_func_t match) | ||
388 | { | ||
389 | struct task_struct *tsk = current; | ||
390 | unsigned long flags; | ||
391 | struct key *key, *ret, *err, *tmp; | ||
392 | |||
393 | /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were | ||
394 | * searchable, but we failed to find a key or we found a negative key; | ||
395 | * otherwise we want to return a sample error (probably -EACCES) if | ||
396 | * none of the keyrings were searchable | ||
397 | * | ||
398 | * in terms of priority: success > -ENOKEY > -EAGAIN > other error | ||
399 | */ | ||
400 | key = NULL; | ||
401 | ret = NULL; | ||
402 | err = ERR_PTR(-EAGAIN); | ||
403 | |||
404 | /* search the thread keyring first */ | ||
405 | if (tsk->thread_keyring) { | ||
406 | key = keyring_search_aux(tsk->thread_keyring, type, | ||
407 | description, match); | ||
408 | if (!IS_ERR(key)) | ||
409 | goto found; | ||
410 | |||
411 | switch (PTR_ERR(key)) { | ||
412 | case -EAGAIN: /* no key */ | ||
413 | if (ret) | ||
414 | break; | ||
415 | case -ENOKEY: /* negative key */ | ||
416 | ret = key; | ||
417 | break; | ||
418 | default: | ||
419 | err = key; | ||
420 | break; | ||
421 | } | ||
422 | } | ||
423 | |||
424 | /* search the process keyring second */ | ||
425 | if (tsk->signal->process_keyring) { | ||
426 | key = keyring_search_aux(tsk->signal->process_keyring, | ||
427 | type, description, match); | ||
428 | if (!IS_ERR(key)) | ||
429 | goto found; | ||
430 | |||
431 | switch (PTR_ERR(key)) { | ||
432 | case -EAGAIN: /* no key */ | ||
433 | if (ret) | ||
434 | break; | ||
435 | case -ENOKEY: /* negative key */ | ||
436 | ret = key; | ||
437 | break; | ||
438 | default: | ||
439 | err = key; | ||
440 | break; | ||
441 | } | ||
442 | } | ||
443 | |||
444 | /* search the session keyring last */ | ||
445 | spin_lock_irqsave(&tsk->sighand->siglock, flags); | ||
446 | |||
447 | tmp = tsk->signal->session_keyring; | ||
448 | if (!tmp) | ||
449 | tmp = tsk->user->session_keyring; | ||
450 | atomic_inc(&tmp->usage); | ||
451 | |||
452 | spin_unlock_irqrestore(&tsk->sighand->siglock, flags); | ||
453 | |||
454 | key = keyring_search_aux(tmp, type, description, match); | ||
455 | key_put(tmp); | ||
456 | if (!IS_ERR(key)) | ||
457 | goto found; | ||
458 | |||
459 | switch (PTR_ERR(key)) { | ||
460 | case -EAGAIN: /* no key */ | ||
461 | if (ret) | ||
462 | break; | ||
463 | case -ENOKEY: /* negative key */ | ||
464 | ret = key; | ||
465 | break; | ||
466 | default: | ||
467 | err = key; | ||
468 | break; | ||
469 | } | ||
470 | |||
471 | /* no key - decide on the error we're going to go for */ | ||
472 | key = ret ? ret : err; | ||
473 | |||
474 | found: | ||
475 | return key; | ||
476 | |||
477 | } /* end search_process_keyrings_aux() */ | ||
478 | |||
479 | /*****************************************************************************/ | ||
480 | /* | ||
481 | * search the process keyrings for the first matching key | ||
482 | * - we return -EAGAIN if we didn't find any matching key | ||
483 | * - we return -ENOKEY if we found only negative matching keys | ||
484 | */ | ||
485 | struct key *search_process_keyrings(struct key_type *type, | ||
486 | const char *description) | ||
487 | { | ||
488 | return search_process_keyrings_aux(type, description, type->match); | ||
489 | |||
490 | } /* end search_process_keyrings() */ | ||
491 | |||
492 | /*****************************************************************************/ | ||
493 | /* | ||
494 | * lookup a key given a key ID from userspace with a given permissions mask | ||
495 | * - don't create special keyrings unless so requested | ||
496 | * - partially constructed keys aren't found unless requested | ||
497 | */ | ||
498 | struct key *lookup_user_key(key_serial_t id, int create, int partial, | ||
499 | key_perm_t perm) | ||
500 | { | ||
501 | struct task_struct *tsk = current; | ||
502 | unsigned long flags; | ||
503 | struct key *key; | ||
504 | int ret; | ||
505 | |||
506 | key = ERR_PTR(-ENOKEY); | ||
507 | |||
508 | switch (id) { | ||
509 | case KEY_SPEC_THREAD_KEYRING: | ||
510 | if (!tsk->thread_keyring) { | ||
511 | if (!create) | ||
512 | goto error; | ||
513 | |||
514 | ret = install_thread_keyring(tsk); | ||
515 | if (ret < 0) { | ||
516 | key = ERR_PTR(ret); | ||
517 | goto error; | ||
518 | } | ||
519 | } | ||
520 | |||
521 | key = tsk->thread_keyring; | ||
522 | atomic_inc(&key->usage); | ||
523 | break; | ||
524 | |||
525 | case KEY_SPEC_PROCESS_KEYRING: | ||
526 | if (!tsk->signal->process_keyring) { | ||
527 | if (!create) | ||
528 | goto error; | ||
529 | |||
530 | ret = install_process_keyring(tsk); | ||
531 | if (ret < 0) { | ||
532 | key = ERR_PTR(ret); | ||
533 | goto error; | ||
534 | } | ||
535 | } | ||
536 | |||
537 | key = tsk->signal->process_keyring; | ||
538 | atomic_inc(&key->usage); | ||
539 | break; | ||
540 | |||
541 | case KEY_SPEC_SESSION_KEYRING: | ||
542 | if (!tsk->signal->session_keyring) { | ||
543 | /* always install a session keyring upon access if one | ||
544 | * doesn't exist yet */ | ||
545 | ret = install_session_keyring( | ||
546 | tsk, tsk->user->session_keyring); | ||
547 | if (ret < 0) | ||
548 | goto error; | ||
549 | } | ||
550 | |||
551 | spin_lock_irqsave(&tsk->sighand->siglock, flags); | ||
552 | key = tsk->signal->session_keyring; | ||
553 | atomic_inc(&key->usage); | ||
554 | spin_unlock_irqrestore(&tsk->sighand->siglock, flags); | ||
555 | break; | ||
556 | |||
557 | case KEY_SPEC_USER_KEYRING: | ||
558 | key = tsk->user->uid_keyring; | ||
559 | atomic_inc(&key->usage); | ||
560 | break; | ||
561 | |||
562 | case KEY_SPEC_USER_SESSION_KEYRING: | ||
563 | key = tsk->user->session_keyring; | ||
564 | atomic_inc(&key->usage); | ||
565 | break; | ||
566 | |||
567 | case KEY_SPEC_GROUP_KEYRING: | ||
568 | /* group keyrings are not yet supported */ | ||
569 | key = ERR_PTR(-EINVAL); | ||
570 | goto error; | ||
571 | |||
572 | default: | ||
573 | key = ERR_PTR(-EINVAL); | ||
574 | if (id < 1) | ||
575 | goto error; | ||
576 | |||
577 | key = key_lookup(id); | ||
578 | if (IS_ERR(key)) | ||
579 | goto error; | ||
580 | break; | ||
581 | } | ||
582 | |||
583 | /* check the status and permissions */ | ||
584 | if (perm) { | ||
585 | ret = key_validate(key); | ||
586 | if (ret < 0) | ||
587 | goto invalid_key; | ||
588 | } | ||
589 | |||
590 | ret = -EIO; | ||
591 | if (!partial && !(key->flags & KEY_FLAG_INSTANTIATED)) | ||
592 | goto invalid_key; | ||
593 | |||
594 | ret = -EACCES; | ||
595 | if (!key_permission(key, perm)) | ||
596 | goto invalid_key; | ||
597 | |||
598 | error: | ||
599 | return key; | ||
600 | |||
601 | invalid_key: | ||
602 | key_put(key); | ||
603 | key = ERR_PTR(ret); | ||
604 | goto error; | ||
605 | |||
606 | } /* end lookup_user_key() */ | ||
607 | |||
608 | /*****************************************************************************/ | ||
609 | /* | ||
610 | * join the named keyring as the session keyring if possible, or attempt to | ||
611 | * create a new one of that name if not | ||
612 | * - if the name is NULL, an empty anonymous keyring is installed instead | ||
613 | * - named session keyring joining is done with a semaphore held | ||
614 | */ | ||
615 | long join_session_keyring(const char *name) | ||
616 | { | ||
617 | struct task_struct *tsk = current; | ||
618 | unsigned long flags; | ||
619 | struct key *keyring; | ||
620 | long ret; | ||
621 | |||
622 | /* if no name is provided, install an anonymous keyring */ | ||
623 | if (!name) { | ||
624 | ret = install_session_keyring(tsk, NULL); | ||
625 | if (ret < 0) | ||
626 | goto error; | ||
627 | |||
628 | spin_lock_irqsave(&tsk->sighand->siglock, flags); | ||
629 | ret = tsk->signal->session_keyring->serial; | ||
630 | spin_unlock_irqrestore(&tsk->sighand->siglock, flags); | ||
631 | goto error; | ||
632 | } | ||
633 | |||
634 | /* allow the user to join or create a named keyring */ | ||
635 | down(&key_session_sem); | ||
636 | |||
637 | /* look for an existing keyring of this name */ | ||
638 | keyring = find_keyring_by_name(name, 0); | ||
639 | if (PTR_ERR(keyring) == -ENOKEY) { | ||
640 | /* not found - try and create a new one */ | ||
641 | keyring = keyring_alloc(name, tsk->uid, tsk->gid, 0, NULL); | ||
642 | if (IS_ERR(keyring)) { | ||
643 | ret = PTR_ERR(keyring); | ||
644 | goto error; | ||
645 | } | ||
646 | } | ||
647 | else if (IS_ERR(keyring)) { | ||
648 | ret = PTR_ERR(keyring); | ||
649 | goto error2; | ||
650 | } | ||
651 | |||
652 | /* we've got a keyring - now to install it */ | ||
653 | ret = install_session_keyring(tsk, keyring); | ||
654 | if (ret < 0) | ||
655 | goto error2; | ||
656 | |||
657 | ret = keyring->serial; | ||
658 | key_put(keyring); | ||
659 | |||
660 | error2: | ||
661 | up(&key_session_sem); | ||
662 | error: | ||
663 | return ret; | ||
664 | |||
665 | } /* end join_session_keyring() */ | ||
diff --git a/security/keys/request_key.c b/security/keys/request_key.c new file mode 100644 index 000000000000..9705b1aeba5d --- /dev/null +++ b/security/keys/request_key.c | |||
@@ -0,0 +1,359 @@ | |||
1 | /* request_key.c: request a key from userspace | ||
2 | * | ||
3 | * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/kmod.h> | ||
15 | #include <linux/err.h> | ||
16 | #include "internal.h" | ||
17 | |||
18 | struct key_construction { | ||
19 | struct list_head link; /* link in construction queue */ | ||
20 | struct key *key; /* key being constructed */ | ||
21 | }; | ||
22 | |||
23 | /* when waiting for someone else's keys, you get added to this */ | ||
24 | DECLARE_WAIT_QUEUE_HEAD(request_key_conswq); | ||
25 | |||
26 | /*****************************************************************************/ | ||
27 | /* | ||
28 | * request userspace finish the construction of a key | ||
29 | * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring> <info>" | ||
30 | * - if callout_info is an empty string, it'll be rendered as a "-" instead | ||
31 | */ | ||
32 | static int call_request_key(struct key *key, | ||
33 | const char *op, | ||
34 | const char *callout_info) | ||
35 | { | ||
36 | struct task_struct *tsk = current; | ||
37 | unsigned long flags; | ||
38 | key_serial_t prkey, sskey; | ||
39 | char *argv[10], *envp[3], uid_str[12], gid_str[12]; | ||
40 | char key_str[12], keyring_str[3][12]; | ||
41 | int i; | ||
42 | |||
43 | /* record the UID and GID */ | ||
44 | sprintf(uid_str, "%d", current->fsuid); | ||
45 | sprintf(gid_str, "%d", current->fsgid); | ||
46 | |||
47 | /* we say which key is under construction */ | ||
48 | sprintf(key_str, "%d", key->serial); | ||
49 | |||
50 | /* we specify the process's default keyrings */ | ||
51 | sprintf(keyring_str[0], "%d", | ||
52 | tsk->thread_keyring ? tsk->thread_keyring->serial : 0); | ||
53 | |||
54 | prkey = 0; | ||
55 | if (tsk->signal->process_keyring) | ||
56 | prkey = tsk->signal->process_keyring->serial; | ||
57 | |||
58 | sskey = 0; | ||
59 | spin_lock_irqsave(&tsk->sighand->siglock, flags); | ||
60 | if (tsk->signal->session_keyring) | ||
61 | sskey = tsk->signal->session_keyring->serial; | ||
62 | spin_unlock_irqrestore(&tsk->sighand->siglock, flags); | ||
63 | |||
64 | |||
65 | if (!sskey) | ||
66 | sskey = tsk->user->session_keyring->serial; | ||
67 | |||
68 | sprintf(keyring_str[1], "%d", prkey); | ||
69 | sprintf(keyring_str[2], "%d", sskey); | ||
70 | |||
71 | /* set up a minimal environment */ | ||
72 | i = 0; | ||
73 | envp[i++] = "HOME=/"; | ||
74 | envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | ||
75 | envp[i] = NULL; | ||
76 | |||
77 | /* set up the argument list */ | ||
78 | i = 0; | ||
79 | argv[i++] = "/sbin/request-key"; | ||
80 | argv[i++] = (char *) op; | ||
81 | argv[i++] = key_str; | ||
82 | argv[i++] = uid_str; | ||
83 | argv[i++] = gid_str; | ||
84 | argv[i++] = keyring_str[0]; | ||
85 | argv[i++] = keyring_str[1]; | ||
86 | argv[i++] = keyring_str[2]; | ||
87 | argv[i++] = callout_info[0] ? (char *) callout_info : "-"; | ||
88 | argv[i] = NULL; | ||
89 | |||
90 | /* do it */ | ||
91 | return call_usermodehelper(argv[0], argv, envp, 1); | ||
92 | |||
93 | } /* end call_request_key() */ | ||
94 | |||
95 | /*****************************************************************************/ | ||
96 | /* | ||
97 | * call out to userspace for the key | ||
98 | * - called with the construction sem held, but the sem is dropped here | ||
99 | * - we ignore program failure and go on key status instead | ||
100 | */ | ||
101 | static struct key *__request_key_construction(struct key_type *type, | ||
102 | const char *description, | ||
103 | const char *callout_info) | ||
104 | { | ||
105 | struct key_construction cons; | ||
106 | struct timespec now; | ||
107 | struct key *key; | ||
108 | int ret, negative; | ||
109 | |||
110 | /* create a key and add it to the queue */ | ||
111 | key = key_alloc(type, description, | ||
112 | current->fsuid, current->fsgid, KEY_USR_ALL, 0); | ||
113 | if (IS_ERR(key)) | ||
114 | goto alloc_failed; | ||
115 | |||
116 | write_lock(&key->lock); | ||
117 | key->flags |= KEY_FLAG_USER_CONSTRUCT; | ||
118 | write_unlock(&key->lock); | ||
119 | |||
120 | cons.key = key; | ||
121 | list_add_tail(&cons.link, &key->user->consq); | ||
122 | |||
123 | /* we drop the construction sem here on behalf of the caller */ | ||
124 | up_write(&key_construction_sem); | ||
125 | |||
126 | /* make the call */ | ||
127 | ret = call_request_key(key, "create", callout_info); | ||
128 | if (ret < 0) | ||
129 | goto request_failed; | ||
130 | |||
131 | /* if the key wasn't instantiated, then we want to give an error */ | ||
132 | ret = -ENOKEY; | ||
133 | if (!(key->flags & KEY_FLAG_INSTANTIATED)) | ||
134 | goto request_failed; | ||
135 | |||
136 | down_write(&key_construction_sem); | ||
137 | list_del(&cons.link); | ||
138 | up_write(&key_construction_sem); | ||
139 | |||
140 | /* also give an error if the key was negatively instantiated */ | ||
141 | check_not_negative: | ||
142 | if (key->flags & KEY_FLAG_NEGATIVE) { | ||
143 | key_put(key); | ||
144 | key = ERR_PTR(-ENOKEY); | ||
145 | } | ||
146 | |||
147 | out: | ||
148 | return key; | ||
149 | |||
150 | request_failed: | ||
151 | /* it wasn't instantiated | ||
152 | * - remove from construction queue | ||
153 | * - mark the key as dead | ||
154 | */ | ||
155 | negative = 0; | ||
156 | down_write(&key_construction_sem); | ||
157 | |||
158 | list_del(&cons.link); | ||
159 | |||
160 | write_lock(&key->lock); | ||
161 | key->flags &= ~KEY_FLAG_USER_CONSTRUCT; | ||
162 | |||
163 | /* check it didn't get instantiated between the check and the down */ | ||
164 | if (!(key->flags & KEY_FLAG_INSTANTIATED)) { | ||
165 | key->flags |= KEY_FLAG_INSTANTIATED | KEY_FLAG_NEGATIVE; | ||
166 | negative = 1; | ||
167 | } | ||
168 | |||
169 | write_unlock(&key->lock); | ||
170 | up_write(&key_construction_sem); | ||
171 | |||
172 | if (!negative) | ||
173 | goto check_not_negative; /* surprisingly, the key got | ||
174 | * instantiated */ | ||
175 | |||
176 | /* set the timeout and store in the session keyring if we can */ | ||
177 | now = current_kernel_time(); | ||
178 | key->expiry = now.tv_sec + key_negative_timeout; | ||
179 | |||
180 | if (current->signal->session_keyring) { | ||
181 | unsigned long flags; | ||
182 | struct key *keyring; | ||
183 | |||
184 | spin_lock_irqsave(¤t->sighand->siglock, flags); | ||
185 | keyring = current->signal->session_keyring; | ||
186 | atomic_inc(&keyring->usage); | ||
187 | spin_unlock_irqrestore(¤t->sighand->siglock, flags); | ||
188 | |||
189 | key_link(keyring, key); | ||
190 | key_put(keyring); | ||
191 | } | ||
192 | |||
193 | key_put(key); | ||
194 | |||
195 | /* notify anyone who was waiting */ | ||
196 | wake_up_all(&request_key_conswq); | ||
197 | |||
198 | key = ERR_PTR(ret); | ||
199 | goto out; | ||
200 | |||
201 | alloc_failed: | ||
202 | up_write(&key_construction_sem); | ||
203 | goto out; | ||
204 | |||
205 | } /* end __request_key_construction() */ | ||
206 | |||
207 | /*****************************************************************************/ | ||
208 | /* | ||
209 | * call out to userspace to request the key | ||
210 | * - we check the construction queue first to see if an appropriate key is | ||
211 | * already being constructed by userspace | ||
212 | */ | ||
213 | static struct key *request_key_construction(struct key_type *type, | ||
214 | const char *description, | ||
215 | struct key_user *user, | ||
216 | const char *callout_info) | ||
217 | { | ||
218 | struct key_construction *pcons; | ||
219 | struct key *key, *ckey; | ||
220 | |||
221 | DECLARE_WAITQUEUE(myself, current); | ||
222 | |||
223 | /* see if there's such a key under construction already */ | ||
224 | down_write(&key_construction_sem); | ||
225 | |||
226 | list_for_each_entry(pcons, &user->consq, link) { | ||
227 | ckey = pcons->key; | ||
228 | |||
229 | if (ckey->type != type) | ||
230 | continue; | ||
231 | |||
232 | if (type->match(ckey, description)) | ||
233 | goto found_key_under_construction; | ||
234 | } | ||
235 | |||
236 | /* see about getting userspace to construct the key */ | ||
237 | key = __request_key_construction(type, description, callout_info); | ||
238 | error: | ||
239 | return key; | ||
240 | |||
241 | /* someone else has the same key under construction | ||
242 | * - we want to keep an eye on their key | ||
243 | */ | ||
244 | found_key_under_construction: | ||
245 | atomic_inc(&ckey->usage); | ||
246 | up_write(&key_construction_sem); | ||
247 | |||
248 | /* wait for the key to be completed one way or another */ | ||
249 | add_wait_queue(&request_key_conswq, &myself); | ||
250 | |||
251 | for (;;) { | ||
252 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
253 | if (!(ckey->flags & KEY_FLAG_USER_CONSTRUCT)) | ||
254 | break; | ||
255 | schedule(); | ||
256 | } | ||
257 | |||
258 | set_current_state(TASK_RUNNING); | ||
259 | remove_wait_queue(&request_key_conswq, &myself); | ||
260 | |||
261 | /* we'll need to search this process's keyrings to see if the key is | ||
262 | * now there since we can't automatically assume it's also available | ||
263 | * there */ | ||
264 | key_put(ckey); | ||
265 | ckey = NULL; | ||
266 | |||
267 | key = NULL; /* request a retry */ | ||
268 | goto error; | ||
269 | |||
270 | } /* end request_key_construction() */ | ||
271 | |||
272 | /*****************************************************************************/ | ||
273 | /* | ||
274 | * request a key | ||
275 | * - search the process's keyrings | ||
276 | * - check the list of keys being created or updated | ||
277 | * - call out to userspace for a key if requested (supplementary info can be | ||
278 | * passed) | ||
279 | */ | ||
280 | struct key *request_key(struct key_type *type, | ||
281 | const char *description, | ||
282 | const char *callout_info) | ||
283 | { | ||
284 | struct key_user *user; | ||
285 | struct key *key; | ||
286 | |||
287 | /* search all the process keyrings for a key */ | ||
288 | key = search_process_keyrings_aux(type, description, type->match); | ||
289 | |||
290 | if (PTR_ERR(key) == -EAGAIN) { | ||
291 | /* the search failed, but the keyrings were searchable, so we | ||
292 | * should consult userspace if we can */ | ||
293 | key = ERR_PTR(-ENOKEY); | ||
294 | if (!callout_info) | ||
295 | goto error; | ||
296 | |||
297 | /* - get hold of the user's construction queue */ | ||
298 | user = key_user_lookup(current->fsuid); | ||
299 | if (!user) { | ||
300 | key = ERR_PTR(-ENOMEM); | ||
301 | goto error; | ||
302 | } | ||
303 | |||
304 | for (;;) { | ||
305 | /* ask userspace (returns NULL if it waited on a key | ||
306 | * being constructed) */ | ||
307 | key = request_key_construction(type, description, | ||
308 | user, callout_info); | ||
309 | if (key) | ||
310 | break; | ||
311 | |||
312 | /* someone else made the key we want, so we need to | ||
313 | * search again as it might now be available to us */ | ||
314 | key = search_process_keyrings_aux(type, description, | ||
315 | type->match); | ||
316 | if (PTR_ERR(key) != -EAGAIN) | ||
317 | break; | ||
318 | } | ||
319 | |||
320 | key_user_put(user); | ||
321 | } | ||
322 | |||
323 | error: | ||
324 | return key; | ||
325 | |||
326 | } /* end request_key() */ | ||
327 | |||
328 | EXPORT_SYMBOL(request_key); | ||
329 | |||
330 | /*****************************************************************************/ | ||
331 | /* | ||
332 | * validate a key | ||
333 | */ | ||
334 | int key_validate(struct key *key) | ||
335 | { | ||
336 | struct timespec now; | ||
337 | int ret = 0; | ||
338 | |||
339 | if (key) { | ||
340 | /* check it's still accessible */ | ||
341 | ret = -EKEYREVOKED; | ||
342 | if (key->flags & (KEY_FLAG_REVOKED | KEY_FLAG_DEAD)) | ||
343 | goto error; | ||
344 | |||
345 | /* check it hasn't expired */ | ||
346 | ret = 0; | ||
347 | if (key->expiry) { | ||
348 | now = current_kernel_time(); | ||
349 | if (now.tv_sec >= key->expiry) | ||
350 | ret = -EKEYEXPIRED; | ||
351 | } | ||
352 | } | ||
353 | |||
354 | error: | ||
355 | return ret; | ||
356 | |||
357 | } /* end key_validate() */ | ||
358 | |||
359 | EXPORT_SYMBOL(key_validate); | ||
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c new file mode 100644 index 000000000000..8d65b3a28129 --- /dev/null +++ b/security/keys/user_defined.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* user_defined.c: user defined key type | ||
2 | * | ||
3 | * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/seq_file.h> | ||
17 | #include <linux/err.h> | ||
18 | #include <asm/uaccess.h> | ||
19 | #include "internal.h" | ||
20 | |||
21 | static int user_instantiate(struct key *key, const void *data, size_t datalen); | ||
22 | static int user_duplicate(struct key *key, const struct key *source); | ||
23 | static int user_update(struct key *key, const void *data, size_t datalen); | ||
24 | static int user_match(const struct key *key, const void *criterion); | ||
25 | static void user_destroy(struct key *key); | ||
26 | static void user_describe(const struct key *user, struct seq_file *m); | ||
27 | static long user_read(const struct key *key, | ||
28 | char __user *buffer, size_t buflen); | ||
29 | |||
30 | /* | ||
31 | * user defined keys take an arbitrary string as the description and an | ||
32 | * arbitrary blob of data as the payload | ||
33 | */ | ||
34 | struct key_type key_type_user = { | ||
35 | .name = "user", | ||
36 | .instantiate = user_instantiate, | ||
37 | .duplicate = user_duplicate, | ||
38 | .update = user_update, | ||
39 | .match = user_match, | ||
40 | .destroy = user_destroy, | ||
41 | .describe = user_describe, | ||
42 | .read = user_read, | ||
43 | }; | ||
44 | |||
45 | /*****************************************************************************/ | ||
46 | /* | ||
47 | * instantiate a user defined key | ||
48 | */ | ||
49 | static int user_instantiate(struct key *key, const void *data, size_t datalen) | ||
50 | { | ||
51 | int ret; | ||
52 | |||
53 | ret = -EINVAL; | ||
54 | if (datalen <= 0 || datalen > 32767 || !data) | ||
55 | goto error; | ||
56 | |||
57 | ret = key_payload_reserve(key, datalen); | ||
58 | if (ret < 0) | ||
59 | goto error; | ||
60 | |||
61 | /* attach the data */ | ||
62 | ret = -ENOMEM; | ||
63 | key->payload.data = kmalloc(datalen, GFP_KERNEL); | ||
64 | if (!key->payload.data) | ||
65 | goto error; | ||
66 | |||
67 | memcpy(key->payload.data, data, datalen); | ||
68 | ret = 0; | ||
69 | |||
70 | error: | ||
71 | return ret; | ||
72 | |||
73 | } /* end user_instantiate() */ | ||
74 | |||
75 | /*****************************************************************************/ | ||
76 | /* | ||
77 | * duplicate a user defined key | ||
78 | */ | ||
79 | static int user_duplicate(struct key *key, const struct key *source) | ||
80 | { | ||
81 | int ret; | ||
82 | |||
83 | /* just copy the payload */ | ||
84 | ret = -ENOMEM; | ||
85 | key->payload.data = kmalloc(source->datalen, GFP_KERNEL); | ||
86 | |||
87 | if (key->payload.data) { | ||
88 | key->datalen = source->datalen; | ||
89 | memcpy(key->payload.data, source->payload.data, source->datalen); | ||
90 | ret = 0; | ||
91 | } | ||
92 | |||
93 | return ret; | ||
94 | |||
95 | } /* end user_duplicate() */ | ||
96 | |||
97 | /*****************************************************************************/ | ||
98 | /* | ||
99 | * update a user defined key | ||
100 | */ | ||
101 | static int user_update(struct key *key, const void *data, size_t datalen) | ||
102 | { | ||
103 | void *new, *zap; | ||
104 | int ret; | ||
105 | |||
106 | ret = -EINVAL; | ||
107 | if (datalen <= 0 || datalen > 32767 || !data) | ||
108 | goto error; | ||
109 | |||
110 | /* copy the data */ | ||
111 | ret = -ENOMEM; | ||
112 | new = kmalloc(datalen, GFP_KERNEL); | ||
113 | if (!new) | ||
114 | goto error; | ||
115 | |||
116 | memcpy(new, data, datalen); | ||
117 | |||
118 | /* check the quota and attach the new data */ | ||
119 | zap = new; | ||
120 | write_lock(&key->lock); | ||
121 | |||
122 | ret = key_payload_reserve(key, datalen); | ||
123 | |||
124 | if (ret == 0) { | ||
125 | /* attach the new data, displacing the old */ | ||
126 | zap = key->payload.data; | ||
127 | key->payload.data = new; | ||
128 | key->expiry = 0; | ||
129 | } | ||
130 | |||
131 | write_unlock(&key->lock); | ||
132 | kfree(zap); | ||
133 | |||
134 | error: | ||
135 | return ret; | ||
136 | |||
137 | } /* end user_update() */ | ||
138 | |||
139 | /*****************************************************************************/ | ||
140 | /* | ||
141 | * match users on their name | ||
142 | */ | ||
143 | static int user_match(const struct key *key, const void *description) | ||
144 | { | ||
145 | return strcmp(key->description, description) == 0; | ||
146 | |||
147 | } /* end user_match() */ | ||
148 | |||
149 | /*****************************************************************************/ | ||
150 | /* | ||
151 | * dispose of the data dangling from the corpse of a user | ||
152 | */ | ||
153 | static void user_destroy(struct key *key) | ||
154 | { | ||
155 | kfree(key->payload.data); | ||
156 | |||
157 | } /* end user_destroy() */ | ||
158 | |||
159 | /*****************************************************************************/ | ||
160 | /* | ||
161 | * describe the user | ||
162 | */ | ||
163 | static void user_describe(const struct key *key, struct seq_file *m) | ||
164 | { | ||
165 | seq_puts(m, key->description); | ||
166 | |||
167 | seq_printf(m, ": %u", key->datalen); | ||
168 | |||
169 | } /* end user_describe() */ | ||
170 | |||
171 | /*****************************************************************************/ | ||
172 | /* | ||
173 | * read the key data | ||
174 | */ | ||
175 | static long user_read(const struct key *key, | ||
176 | char __user *buffer, size_t buflen) | ||
177 | { | ||
178 | long ret = key->datalen; | ||
179 | |||
180 | /* we can return the data as is */ | ||
181 | if (buffer && buflen > 0) { | ||
182 | if (buflen > key->datalen) | ||
183 | buflen = key->datalen; | ||
184 | |||
185 | if (copy_to_user(buffer, key->payload.data, buflen) != 0) | ||
186 | ret = -EFAULT; | ||
187 | } | ||
188 | |||
189 | return ret; | ||
190 | |||
191 | } /* end user_read() */ | ||
diff --git a/security/root_plug.c b/security/root_plug.c new file mode 100644 index 000000000000..07651def2f78 --- /dev/null +++ b/security/root_plug.c | |||
@@ -0,0 +1,142 @@ | |||
1 | /* | ||
2 | * Root Plug sample LSM module | ||
3 | * | ||
4 | * Originally written for a Linux Journal. | ||
5 | * | ||
6 | * Copyright (C) 2002 Greg Kroah-Hartman <greg@kroah.com> | ||
7 | * | ||
8 | * Prevents any programs running with egid == 0 if a specific USB device | ||
9 | * is not present in the system. Yes, it can be gotten around, but is a | ||
10 | * nice starting point for people to play with, and learn the LSM | ||
11 | * interface. | ||
12 | * | ||
13 | * If you want to turn this into something with a semblance of security, | ||
14 | * you need to hook the task_* functions also. | ||
15 | * | ||
16 | * See http://www.linuxjournal.com/article.php?sid=6279 for more information | ||
17 | * about this code. | ||
18 | * | ||
19 | * This program is free software; you can redistribute it and/or | ||
20 | * modify it under the terms of the GNU General Public License as | ||
21 | * published by the Free Software Foundation, version 2 of the | ||
22 | * License. | ||
23 | */ | ||
24 | |||
25 | #include <linux/config.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/security.h> | ||
30 | #include <linux/usb.h> | ||
31 | |||
32 | /* flag to keep track of how we were registered */ | ||
33 | static int secondary; | ||
34 | |||
35 | /* default is a generic type of usb to serial converter */ | ||
36 | static int vendor_id = 0x0557; | ||
37 | static int product_id = 0x2008; | ||
38 | |||
39 | module_param(vendor_id, uint, 0400); | ||
40 | MODULE_PARM_DESC(vendor_id, "USB Vendor ID of device to look for"); | ||
41 | |||
42 | module_param(product_id, uint, 0400); | ||
43 | MODULE_PARM_DESC(product_id, "USB Product ID of device to look for"); | ||
44 | |||
45 | /* should we print out debug messages */ | ||
46 | static int debug = 0; | ||
47 | |||
48 | module_param(debug, bool, 0600); | ||
49 | MODULE_PARM_DESC(debug, "Debug enabled or not"); | ||
50 | |||
51 | #if defined(CONFIG_SECURITY_ROOTPLUG_MODULE) | ||
52 | #define MY_NAME THIS_MODULE->name | ||
53 | #else | ||
54 | #define MY_NAME "root_plug" | ||
55 | #endif | ||
56 | |||
57 | #define root_dbg(fmt, arg...) \ | ||
58 | do { \ | ||
59 | if (debug) \ | ||
60 | printk(KERN_DEBUG "%s: %s: " fmt , \ | ||
61 | MY_NAME , __FUNCTION__ , \ | ||
62 | ## arg); \ | ||
63 | } while (0) | ||
64 | |||
65 | static int rootplug_bprm_check_security (struct linux_binprm *bprm) | ||
66 | { | ||
67 | struct usb_device *dev; | ||
68 | |||
69 | root_dbg("file %s, e_uid = %d, e_gid = %d\n", | ||
70 | bprm->filename, bprm->e_uid, bprm->e_gid); | ||
71 | |||
72 | if (bprm->e_gid == 0) { | ||
73 | dev = usb_find_device(vendor_id, product_id); | ||
74 | if (!dev) { | ||
75 | root_dbg("e_gid = 0, and device not found, " | ||
76 | "task not allowed to run...\n"); | ||
77 | return -EPERM; | ||
78 | } | ||
79 | usb_put_dev(dev); | ||
80 | } | ||
81 | |||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | static struct security_operations rootplug_security_ops = { | ||
86 | /* Use the capability functions for some of the hooks */ | ||
87 | .ptrace = cap_ptrace, | ||
88 | .capget = cap_capget, | ||
89 | .capset_check = cap_capset_check, | ||
90 | .capset_set = cap_capset_set, | ||
91 | .capable = cap_capable, | ||
92 | |||
93 | .bprm_apply_creds = cap_bprm_apply_creds, | ||
94 | .bprm_set_security = cap_bprm_set_security, | ||
95 | |||
96 | .task_post_setuid = cap_task_post_setuid, | ||
97 | .task_reparent_to_init = cap_task_reparent_to_init, | ||
98 | |||
99 | .bprm_check_security = rootplug_bprm_check_security, | ||
100 | }; | ||
101 | |||
102 | static int __init rootplug_init (void) | ||
103 | { | ||
104 | /* register ourselves with the security framework */ | ||
105 | if (register_security (&rootplug_security_ops)) { | ||
106 | printk (KERN_INFO | ||
107 | "Failure registering Root Plug module with the kernel\n"); | ||
108 | /* try registering with primary module */ | ||
109 | if (mod_reg_security (MY_NAME, &rootplug_security_ops)) { | ||
110 | printk (KERN_INFO "Failure registering Root Plug " | ||
111 | " module with primary security module.\n"); | ||
112 | return -EINVAL; | ||
113 | } | ||
114 | secondary = 1; | ||
115 | } | ||
116 | printk (KERN_INFO "Root Plug module initialized, " | ||
117 | "vendor_id = %4.4x, product id = %4.4x\n", vendor_id, product_id); | ||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | static void __exit rootplug_exit (void) | ||
122 | { | ||
123 | /* remove ourselves from the security framework */ | ||
124 | if (secondary) { | ||
125 | if (mod_unreg_security (MY_NAME, &rootplug_security_ops)) | ||
126 | printk (KERN_INFO "Failure unregistering Root Plug " | ||
127 | " module with primary module.\n"); | ||
128 | } else { | ||
129 | if (unregister_security (&rootplug_security_ops)) { | ||
130 | printk (KERN_INFO "Failure unregistering Root Plug " | ||
131 | "module with the kernel\n"); | ||
132 | } | ||
133 | } | ||
134 | printk (KERN_INFO "Root Plug module removed\n"); | ||
135 | } | ||
136 | |||
137 | security_initcall (rootplug_init); | ||
138 | module_exit (rootplug_exit); | ||
139 | |||
140 | MODULE_DESCRIPTION("Root Plug sample LSM module, written for Linux Journal article"); | ||
141 | MODULE_LICENSE("GPL"); | ||
142 | |||
diff --git a/security/seclvl.c b/security/seclvl.c new file mode 100644 index 000000000000..8a0ab0d7949e --- /dev/null +++ b/security/seclvl.c | |||
@@ -0,0 +1,747 @@ | |||
1 | /** | ||
2 | * BSD Secure Levels LSM | ||
3 | * | ||
4 | * Maintainers: | ||
5 | * Michael A. Halcrow <mike@halcrow.us> | ||
6 | * Serge Hallyn <hallyn@cs.wm.edu> | ||
7 | * | ||
8 | * Copyright (c) 2001 WireX Communications, Inc <chris@wirex.com> | ||
9 | * Copyright (c) 2001 Greg Kroah-Hartman <greg@kroah.com> | ||
10 | * Copyright (c) 2002 International Business Machines <robb@austin.ibm.com> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | */ | ||
17 | |||
18 | #include <linux/config.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/moduleparam.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/security.h> | ||
24 | #include <linux/netlink.h> | ||
25 | #include <linux/fs.h> | ||
26 | #include <linux/namei.h> | ||
27 | #include <linux/mount.h> | ||
28 | #include <linux/capability.h> | ||
29 | #include <linux/time.h> | ||
30 | #include <linux/proc_fs.h> | ||
31 | #include <linux/kobject.h> | ||
32 | #include <linux/crypto.h> | ||
33 | #include <asm/scatterlist.h> | ||
34 | #include <linux/gfp.h> | ||
35 | #include <linux/sysfs.h> | ||
36 | |||
37 | #define SHA1_DIGEST_SIZE 20 | ||
38 | |||
39 | /** | ||
40 | * Module parameter that defines the initial secure level. | ||
41 | * | ||
42 | * When built as a module, it defaults to seclvl 1, which is the | ||
43 | * behavior of BSD secure levels. Note that this default behavior | ||
44 | * wrecks havoc on a machine when the seclvl module is compiled into | ||
45 | * the kernel. In that case, we default to seclvl 0. | ||
46 | */ | ||
47 | #ifdef CONFIG_SECURITY_SECLVL_MODULE | ||
48 | static int initlvl = 1; | ||
49 | #else | ||
50 | static int initlvl; | ||
51 | #endif | ||
52 | module_param(initlvl, int, 0); | ||
53 | MODULE_PARM_DESC(initlvl, "Initial secure level (defaults to 1)"); | ||
54 | |||
55 | /* Module parameter that defines the verbosity level */ | ||
56 | static int verbosity; | ||
57 | module_param(verbosity, int, 0); | ||
58 | MODULE_PARM_DESC(verbosity, "Initial verbosity level (0 or 1; defaults to " | ||
59 | "0, which is Quiet)"); | ||
60 | |||
61 | /** | ||
62 | * Optional password which can be passed in to bring seclvl to 0 | ||
63 | * (i.e., for halt/reboot). Defaults to NULL (the passwd attribute | ||
64 | * file will not be registered in sysfs). | ||
65 | * | ||
66 | * This gets converted to its SHA1 hash when stored. It's probably | ||
67 | * not a good idea to use this parameter when loading seclvl from a | ||
68 | * script; use sha1_passwd instead. | ||
69 | */ | ||
70 | |||
71 | #define MAX_PASSWD_SIZE 32 | ||
72 | static char passwd[MAX_PASSWD_SIZE]; | ||
73 | module_param_string(passwd, passwd, sizeof(passwd), 0); | ||
74 | MODULE_PARM_DESC(passwd, | ||
75 | "Plaintext of password that sets seclvl=0 when written to " | ||
76 | "(sysfs mount point)/seclvl/passwd\n"); | ||
77 | |||
78 | /** | ||
79 | * SHA1 hashed version of the optional password which can be passed in | ||
80 | * to bring seclvl to 0 (i.e., for halt/reboot). Must be in | ||
81 | * hexadecimal format (40 characters). Defaults to NULL (the passwd | ||
82 | * attribute file will not be registered in sysfs). | ||
83 | * | ||
84 | * Use the sha1sum utility to generate the SHA1 hash of a password: | ||
85 | * | ||
86 | * echo -n "secret" | sha1sum | ||
87 | */ | ||
88 | #define MAX_SHA1_PASSWD 41 | ||
89 | static char sha1_passwd[MAX_SHA1_PASSWD]; | ||
90 | module_param_string(sha1_passwd, sha1_passwd, sizeof(sha1_passwd), 0); | ||
91 | MODULE_PARM_DESC(sha1_passwd, | ||
92 | "SHA1 hash (40 hexadecimal characters) of password that " | ||
93 | "sets seclvl=0 when plaintext password is written to " | ||
94 | "(sysfs mount point)/seclvl/passwd\n"); | ||
95 | |||
96 | static int hideHash = 1; | ||
97 | module_param(hideHash, int, 0); | ||
98 | MODULE_PARM_DESC(hideHash, "When set to 0, reading seclvl/passwd from sysfs " | ||
99 | "will return the SHA1-hashed value of the password that " | ||
100 | "lowers the secure level to 0.\n"); | ||
101 | |||
102 | #define MY_NAME "seclvl" | ||
103 | |||
104 | /** | ||
105 | * This time-limits log writes to one per second. | ||
106 | */ | ||
107 | #define seclvl_printk(verb, type, fmt, arg...) \ | ||
108 | do { \ | ||
109 | if (verbosity >= verb) { \ | ||
110 | static unsigned long _prior; \ | ||
111 | unsigned long _now = jiffies; \ | ||
112 | if ((_now - _prior) > HZ) { \ | ||
113 | printk(type "%s: %s: " fmt, \ | ||
114 | MY_NAME, __FUNCTION__ , \ | ||
115 | ## arg); \ | ||
116 | _prior = _now; \ | ||
117 | } \ | ||
118 | } \ | ||
119 | } while (0) | ||
120 | |||
121 | /** | ||
122 | * kobject stuff | ||
123 | */ | ||
124 | |||
125 | struct subsystem seclvl_subsys; | ||
126 | |||
127 | struct seclvl_obj { | ||
128 | char *name; | ||
129 | struct list_head slot_list; | ||
130 | struct kobject kobj; | ||
131 | }; | ||
132 | |||
133 | /** | ||
134 | * There is a seclvl_attribute struct for each file in sysfs. | ||
135 | * | ||
136 | * In our case, we have one of these structs for "passwd" and another | ||
137 | * for "seclvl". | ||
138 | */ | ||
139 | struct seclvl_attribute { | ||
140 | struct attribute attr; | ||
141 | ssize_t(*show) (struct seclvl_obj *, char *); | ||
142 | ssize_t(*store) (struct seclvl_obj *, const char *, size_t); | ||
143 | }; | ||
144 | |||
145 | /** | ||
146 | * When this function is called, one of the files in sysfs is being | ||
147 | * written to. attribute->store is a function pointer to whatever the | ||
148 | * struct seclvl_attribute store function pointer points to. It is | ||
149 | * unique for "passwd" and "seclvl". | ||
150 | */ | ||
151 | static ssize_t | ||
152 | seclvl_attr_store(struct kobject *kobj, | ||
153 | struct attribute *attr, const char *buf, size_t len) | ||
154 | { | ||
155 | struct seclvl_obj *obj = container_of(kobj, struct seclvl_obj, kobj); | ||
156 | struct seclvl_attribute *attribute = | ||
157 | container_of(attr, struct seclvl_attribute, attr); | ||
158 | return (attribute->store ? attribute->store(obj, buf, len) : 0); | ||
159 | } | ||
160 | |||
161 | static ssize_t | ||
162 | seclvl_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) | ||
163 | { | ||
164 | struct seclvl_obj *obj = container_of(kobj, struct seclvl_obj, kobj); | ||
165 | struct seclvl_attribute *attribute = | ||
166 | container_of(attr, struct seclvl_attribute, attr); | ||
167 | return (attribute->show ? attribute->show(obj, buf) : 0); | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * Callback function pointers for show and store | ||
172 | */ | ||
173 | static struct sysfs_ops seclvlfs_sysfs_ops = { | ||
174 | .show = seclvl_attr_show, | ||
175 | .store = seclvl_attr_store, | ||
176 | }; | ||
177 | |||
178 | static struct kobj_type seclvl_ktype = { | ||
179 | .sysfs_ops = &seclvlfs_sysfs_ops | ||
180 | }; | ||
181 | |||
182 | decl_subsys(seclvl, &seclvl_ktype, NULL); | ||
183 | |||
184 | /** | ||
185 | * The actual security level. Ranges between -1 and 2 inclusive. | ||
186 | */ | ||
187 | static int seclvl; | ||
188 | |||
189 | /** | ||
190 | * flag to keep track of how we were registered | ||
191 | */ | ||
192 | static int secondary; | ||
193 | |||
194 | /** | ||
195 | * Verifies that the requested secure level is valid, given the current | ||
196 | * secure level. | ||
197 | */ | ||
198 | static int seclvl_sanity(int reqlvl) | ||
199 | { | ||
200 | if ((reqlvl < -1) || (reqlvl > 2)) { | ||
201 | seclvl_printk(1, KERN_WARNING, "Attempt to set seclvl out of " | ||
202 | "range: [%d]\n", reqlvl); | ||
203 | return -EINVAL; | ||
204 | } | ||
205 | if ((seclvl == 0) && (reqlvl == -1)) | ||
206 | return 0; | ||
207 | if (reqlvl < seclvl) { | ||
208 | seclvl_printk(1, KERN_WARNING, "Attempt to lower seclvl to " | ||
209 | "[%d]\n", reqlvl); | ||
210 | return -EPERM; | ||
211 | } | ||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * Called whenever the user reads the sysfs handle to this kernel | ||
217 | * object | ||
218 | */ | ||
219 | static ssize_t seclvl_read_file(struct seclvl_obj *obj, char *buff) | ||
220 | { | ||
221 | return snprintf(buff, PAGE_SIZE, "%d\n", seclvl); | ||
222 | } | ||
223 | |||
224 | /** | ||
225 | * security level advancement rules: | ||
226 | * Valid levels are -1 through 2, inclusive. | ||
227 | * From -1, stuck. [ in case compiled into kernel ] | ||
228 | * From 0 or above, can only increment. | ||
229 | */ | ||
230 | static int do_seclvl_advance(int newlvl) | ||
231 | { | ||
232 | if (newlvl <= seclvl) { | ||
233 | seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl " | ||
234 | "[%d]\n", newlvl); | ||
235 | return -EINVAL; | ||
236 | } | ||
237 | if (newlvl > 2) { | ||
238 | seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl " | ||
239 | "[%d]\n", newlvl); | ||
240 | return -EINVAL; | ||
241 | } | ||
242 | if (seclvl == -1) { | ||
243 | seclvl_printk(1, KERN_WARNING, "Not allowed to advance to " | ||
244 | "seclvl [%d]\n", seclvl); | ||
245 | return -EPERM; | ||
246 | } | ||
247 | seclvl = newlvl; | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | /** | ||
252 | * Called whenever the user writes to the sysfs handle to this kernel | ||
253 | * object (seclvl/seclvl). It expects a single-digit number. | ||
254 | */ | ||
255 | static ssize_t | ||
256 | seclvl_write_file(struct seclvl_obj *obj, const char *buff, size_t count) | ||
257 | { | ||
258 | unsigned long val; | ||
259 | if (count > 2 || (count == 2 && buff[1] != '\n')) { | ||
260 | seclvl_printk(1, KERN_WARNING, "Invalid value passed to " | ||
261 | "seclvl: [%s]\n", buff); | ||
262 | return -EINVAL; | ||
263 | } | ||
264 | val = buff[0] - 48; | ||
265 | if (seclvl_sanity(val)) { | ||
266 | seclvl_printk(1, KERN_WARNING, "Illegal secure level " | ||
267 | "requested: [%d]\n", (int)val); | ||
268 | return -EPERM; | ||
269 | } | ||
270 | if (do_seclvl_advance(val)) { | ||
271 | seclvl_printk(0, KERN_ERR, "Failure advancing security level " | ||
272 | "to %lu\n", val); | ||
273 | } | ||
274 | return count; | ||
275 | } | ||
276 | |||
277 | /* Generate sysfs_attr_seclvl */ | ||
278 | static struct seclvl_attribute sysfs_attr_seclvl = | ||
279 | __ATTR(seclvl, (S_IFREG | S_IRUGO | S_IWUSR), seclvl_read_file, | ||
280 | seclvl_write_file); | ||
281 | |||
282 | static unsigned char hashedPassword[SHA1_DIGEST_SIZE]; | ||
283 | |||
284 | /** | ||
285 | * Called whenever the user reads the sysfs passwd handle. | ||
286 | */ | ||
287 | static ssize_t seclvl_read_passwd(struct seclvl_obj *obj, char *buff) | ||
288 | { | ||
289 | /* So just how good *is* your password? :-) */ | ||
290 | char tmp[3]; | ||
291 | int i = 0; | ||
292 | buff[0] = '\0'; | ||
293 | if (hideHash) { | ||
294 | /* Security through obscurity */ | ||
295 | return 0; | ||
296 | } | ||
297 | while (i < SHA1_DIGEST_SIZE) { | ||
298 | snprintf(tmp, 3, "%02x", hashedPassword[i]); | ||
299 | strncat(buff, tmp, 2); | ||
300 | i++; | ||
301 | } | ||
302 | strcat(buff, "\n"); | ||
303 | return ((SHA1_DIGEST_SIZE * 2) + 1); | ||
304 | } | ||
305 | |||
306 | /** | ||
307 | * Converts a block of plaintext of into its SHA1 hashed value. | ||
308 | * | ||
309 | * It would be nice if crypto had a wrapper to do this for us linear | ||
310 | * people... | ||
311 | */ | ||
312 | static int | ||
313 | plaintext_to_sha1(unsigned char *hash, const char *plaintext, int len) | ||
314 | { | ||
315 | char *pgVirtAddr; | ||
316 | struct crypto_tfm *tfm; | ||
317 | struct scatterlist sg[1]; | ||
318 | if (len > PAGE_SIZE) { | ||
319 | seclvl_printk(0, KERN_ERR, "Plaintext password too large (%d " | ||
320 | "characters). Largest possible is %lu " | ||
321 | "bytes.\n", len, PAGE_SIZE); | ||
322 | return -ENOMEM; | ||
323 | } | ||
324 | tfm = crypto_alloc_tfm("sha1", 0); | ||
325 | if (tfm == NULL) { | ||
326 | seclvl_printk(0, KERN_ERR, | ||
327 | "Failed to load transform for SHA1\n"); | ||
328 | return -ENOSYS; | ||
329 | } | ||
330 | // Just get a new page; don't play around with page boundaries | ||
331 | // and scatterlists. | ||
332 | pgVirtAddr = (char *)__get_free_page(GFP_KERNEL); | ||
333 | sg[0].page = virt_to_page(pgVirtAddr); | ||
334 | sg[0].offset = 0; | ||
335 | sg[0].length = len; | ||
336 | strncpy(pgVirtAddr, plaintext, len); | ||
337 | crypto_digest_init(tfm); | ||
338 | crypto_digest_update(tfm, sg, 1); | ||
339 | crypto_digest_final(tfm, hash); | ||
340 | crypto_free_tfm(tfm); | ||
341 | free_page((unsigned long)pgVirtAddr); | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | /** | ||
346 | * Called whenever the user writes to the sysfs passwd handle to this kernel | ||
347 | * object. It hashes the password and compares the hashed results. | ||
348 | */ | ||
349 | static ssize_t | ||
350 | seclvl_write_passwd(struct seclvl_obj *obj, const char *buff, size_t count) | ||
351 | { | ||
352 | int i; | ||
353 | unsigned char tmp[SHA1_DIGEST_SIZE]; | ||
354 | int rc; | ||
355 | int len; | ||
356 | if (!*passwd && !*sha1_passwd) { | ||
357 | seclvl_printk(0, KERN_ERR, "Attempt to password-unlock the " | ||
358 | "seclvl module, but neither a plain text " | ||
359 | "password nor a SHA1 hashed password was " | ||
360 | "passed in as a module parameter! This is a " | ||
361 | "bug, since it should not be possible to be in " | ||
362 | "this part of the module; please tell a " | ||
363 | "maintainer about this event.\n"); | ||
364 | return -EINVAL; | ||
365 | } | ||
366 | len = strlen(buff); | ||
367 | /* ``echo "secret" > seclvl/passwd'' includes a newline */ | ||
368 | if (buff[len - 1] == '\n') { | ||
369 | len--; | ||
370 | } | ||
371 | /* Hash the password, then compare the hashed values */ | ||
372 | if ((rc = plaintext_to_sha1(tmp, buff, len))) { | ||
373 | seclvl_printk(0, KERN_ERR, "Error hashing password: rc = " | ||
374 | "[%d]\n", rc); | ||
375 | return rc; | ||
376 | } | ||
377 | for (i = 0; i < SHA1_DIGEST_SIZE; i++) { | ||
378 | if (hashedPassword[i] != tmp[i]) { | ||
379 | return -EPERM; | ||
380 | } | ||
381 | } | ||
382 | seclvl_printk(0, KERN_INFO, | ||
383 | "Password accepted; seclvl reduced to 0.\n"); | ||
384 | seclvl = 0; | ||
385 | return count; | ||
386 | } | ||
387 | |||
388 | /* Generate sysfs_attr_passwd */ | ||
389 | static struct seclvl_attribute sysfs_attr_passwd = | ||
390 | __ATTR(passwd, (S_IFREG | S_IRUGO | S_IWUSR), seclvl_read_passwd, | ||
391 | seclvl_write_passwd); | ||
392 | |||
393 | /** | ||
394 | * Explicitely disallow ptrace'ing the init process. | ||
395 | */ | ||
396 | static int seclvl_ptrace(struct task_struct *parent, struct task_struct *child) | ||
397 | { | ||
398 | if (seclvl >= 0) { | ||
399 | if (child->pid == 1) { | ||
400 | seclvl_printk(1, KERN_WARNING, "Attempt to ptrace " | ||
401 | "the init process dissallowed in " | ||
402 | "secure level %d\n", seclvl); | ||
403 | return -EPERM; | ||
404 | } | ||
405 | } | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | /** | ||
410 | * Capability checks for seclvl. The majority of the policy | ||
411 | * enforcement for seclvl takes place here. | ||
412 | */ | ||
413 | static int seclvl_capable(struct task_struct *tsk, int cap) | ||
414 | { | ||
415 | /* init can do anything it wants */ | ||
416 | if (tsk->pid == 1) | ||
417 | return 0; | ||
418 | |||
419 | switch (seclvl) { | ||
420 | case 2: | ||
421 | /* fall through */ | ||
422 | case 1: | ||
423 | if (cap == CAP_LINUX_IMMUTABLE) { | ||
424 | seclvl_printk(1, KERN_WARNING, "Attempt to modify " | ||
425 | "the IMMUTABLE and/or APPEND extended " | ||
426 | "attribute on a file with the IMMUTABLE " | ||
427 | "and/or APPEND extended attribute set " | ||
428 | "denied in seclvl [%d]\n", seclvl); | ||
429 | return -EPERM; | ||
430 | } else if (cap == CAP_SYS_RAWIO) { // Somewhat broad... | ||
431 | seclvl_printk(1, KERN_WARNING, "Attempt to perform " | ||
432 | "raw I/O while in secure level [%d] " | ||
433 | "denied\n", seclvl); | ||
434 | return -EPERM; | ||
435 | } else if (cap == CAP_NET_ADMIN) { | ||
436 | seclvl_printk(1, KERN_WARNING, "Attempt to perform " | ||
437 | "network administrative task while " | ||
438 | "in secure level [%d] denied\n", seclvl); | ||
439 | return -EPERM; | ||
440 | } else if (cap == CAP_SETUID) { | ||
441 | seclvl_printk(1, KERN_WARNING, "Attempt to setuid " | ||
442 | "while in secure level [%d] denied\n", | ||
443 | seclvl); | ||
444 | return -EPERM; | ||
445 | } else if (cap == CAP_SETGID) { | ||
446 | seclvl_printk(1, KERN_WARNING, "Attempt to setgid " | ||
447 | "while in secure level [%d] denied\n", | ||
448 | seclvl); | ||
449 | } else if (cap == CAP_SYS_MODULE) { | ||
450 | seclvl_printk(1, KERN_WARNING, "Attempt to perform " | ||
451 | "a module operation while in secure " | ||
452 | "level [%d] denied\n", seclvl); | ||
453 | return -EPERM; | ||
454 | } | ||
455 | break; | ||
456 | default: | ||
457 | break; | ||
458 | } | ||
459 | /* from dummy.c */ | ||
460 | if (cap_is_fs_cap(cap) ? tsk->fsuid == 0 : tsk->euid == 0) | ||
461 | return 0; /* capability granted */ | ||
462 | seclvl_printk(1, KERN_WARNING, "Capability denied\n"); | ||
463 | return -EPERM; /* capability denied */ | ||
464 | } | ||
465 | |||
466 | /** | ||
467 | * Disallow reversing the clock in seclvl > 1 | ||
468 | */ | ||
469 | static int seclvl_settime(struct timespec *tv, struct timezone *tz) | ||
470 | { | ||
471 | struct timespec now; | ||
472 | if (seclvl > 1) { | ||
473 | now = current_kernel_time(); | ||
474 | if (tv->tv_sec < now.tv_sec || | ||
475 | (tv->tv_sec == now.tv_sec && tv->tv_nsec < now.tv_nsec)) { | ||
476 | seclvl_printk(1, KERN_WARNING, "Attempt to decrement " | ||
477 | "time in secure level %d denied: " | ||
478 | "current->pid = [%d], " | ||
479 | "current->group_leader->pid = [%d]\n", | ||
480 | seclvl, current->pid, | ||
481 | current->group_leader->pid); | ||
482 | return -EPERM; | ||
483 | } /* if attempt to decrement time */ | ||
484 | } /* if seclvl > 1 */ | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | /* claim the blockdev to exclude mounters, release on file close */ | ||
489 | static int seclvl_bd_claim(struct inode *inode) | ||
490 | { | ||
491 | int holder; | ||
492 | struct block_device *bdev = NULL; | ||
493 | dev_t dev = inode->i_rdev; | ||
494 | bdev = open_by_devnum(dev, FMODE_WRITE); | ||
495 | if (bdev) { | ||
496 | if (bd_claim(bdev, &holder)) { | ||
497 | blkdev_put(bdev); | ||
498 | return -EPERM; | ||
499 | } | ||
500 | /* claimed, mark it to release on close */ | ||
501 | inode->i_security = current; | ||
502 | } | ||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | /* release the blockdev if you claimed it */ | ||
507 | static void seclvl_bd_release(struct inode *inode) | ||
508 | { | ||
509 | if (inode && S_ISBLK(inode->i_mode) && inode->i_security == current) { | ||
510 | struct block_device *bdev = inode->i_bdev; | ||
511 | if (bdev) { | ||
512 | bd_release(bdev); | ||
513 | blkdev_put(bdev); | ||
514 | inode->i_security = NULL; | ||
515 | } | ||
516 | } | ||
517 | } | ||
518 | |||
519 | /** | ||
520 | * Security for writes to block devices is regulated by this seclvl | ||
521 | * function. Deny all writes to block devices in seclvl 2. In | ||
522 | * seclvl 1, we only deny writes to *mounted* block devices. | ||
523 | */ | ||
524 | static int | ||
525 | seclvl_inode_permission(struct inode *inode, int mask, struct nameidata *nd) | ||
526 | { | ||
527 | if (current->pid != 1 && S_ISBLK(inode->i_mode) && (mask & MAY_WRITE)) { | ||
528 | switch (seclvl) { | ||
529 | case 2: | ||
530 | seclvl_printk(1, KERN_WARNING, "Write to block device " | ||
531 | "denied in secure level [%d]\n", seclvl); | ||
532 | return -EPERM; | ||
533 | case 1: | ||
534 | if (seclvl_bd_claim(inode)) { | ||
535 | seclvl_printk(1, KERN_WARNING, | ||
536 | "Write to mounted block device " | ||
537 | "denied in secure level [%d]\n", | ||
538 | seclvl); | ||
539 | return -EPERM; | ||
540 | } | ||
541 | } | ||
542 | } | ||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | /** | ||
547 | * The SUID and SGID bits cannot be set in seclvl >= 1 | ||
548 | */ | ||
549 | static int seclvl_inode_setattr(struct dentry *dentry, struct iattr *iattr) | ||
550 | { | ||
551 | if (seclvl > 0) { | ||
552 | if (iattr->ia_valid & ATTR_MODE) | ||
553 | if (iattr->ia_mode & S_ISUID || | ||
554 | iattr->ia_mode & S_ISGID) { | ||
555 | seclvl_printk(1, KERN_WARNING, "Attempt to " | ||
556 | "modify SUID or SGID bit " | ||
557 | "denied in seclvl [%d]\n", | ||
558 | seclvl); | ||
559 | return -EPERM; | ||
560 | } | ||
561 | } | ||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | /* release busied block devices */ | ||
566 | static void seclvl_file_free_security(struct file *filp) | ||
567 | { | ||
568 | struct dentry *dentry = filp->f_dentry; | ||
569 | struct inode *inode = NULL; | ||
570 | |||
571 | if (dentry) { | ||
572 | inode = dentry->d_inode; | ||
573 | seclvl_bd_release(inode); | ||
574 | } | ||
575 | } | ||
576 | |||
577 | /** | ||
578 | * Cannot unmount in secure level 2 | ||
579 | */ | ||
580 | static int seclvl_umount(struct vfsmount *mnt, int flags) | ||
581 | { | ||
582 | if (current->pid == 1) { | ||
583 | return 0; | ||
584 | } | ||
585 | if (seclvl == 2) { | ||
586 | seclvl_printk(1, KERN_WARNING, "Attempt to unmount in secure " | ||
587 | "level %d\n", seclvl); | ||
588 | return -EPERM; | ||
589 | } | ||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | static struct security_operations seclvl_ops = { | ||
594 | .ptrace = seclvl_ptrace, | ||
595 | .capable = seclvl_capable, | ||
596 | .inode_permission = seclvl_inode_permission, | ||
597 | .inode_setattr = seclvl_inode_setattr, | ||
598 | .file_free_security = seclvl_file_free_security, | ||
599 | .settime = seclvl_settime, | ||
600 | .sb_umount = seclvl_umount, | ||
601 | }; | ||
602 | |||
603 | /** | ||
604 | * Process the password-related module parameters | ||
605 | */ | ||
606 | static int processPassword(void) | ||
607 | { | ||
608 | int rc = 0; | ||
609 | hashedPassword[0] = '\0'; | ||
610 | if (*passwd) { | ||
611 | if (*sha1_passwd) { | ||
612 | seclvl_printk(0, KERN_ERR, "Error: Both " | ||
613 | "passwd and sha1_passwd " | ||
614 | "were set, but they are mutually " | ||
615 | "exclusive.\n"); | ||
616 | return -EINVAL; | ||
617 | } | ||
618 | if ((rc = plaintext_to_sha1(hashedPassword, passwd, | ||
619 | strlen(passwd)))) { | ||
620 | seclvl_printk(0, KERN_ERR, "Error: SHA1 support not " | ||
621 | "in kernel\n"); | ||
622 | return rc; | ||
623 | } | ||
624 | /* All static data goes to the BSS, which zero's the | ||
625 | * plaintext password out for us. */ | ||
626 | } else if (*sha1_passwd) { // Base 16 | ||
627 | int i; | ||
628 | i = strlen(sha1_passwd); | ||
629 | if (i != (SHA1_DIGEST_SIZE * 2)) { | ||
630 | seclvl_printk(0, KERN_ERR, "Received [%d] bytes; " | ||
631 | "expected [%d] for the hexadecimal " | ||
632 | "representation of the SHA1 hash of " | ||
633 | "the password.\n", | ||
634 | i, (SHA1_DIGEST_SIZE * 2)); | ||
635 | return -EINVAL; | ||
636 | } | ||
637 | while ((i -= 2) + 2) { | ||
638 | unsigned char tmp; | ||
639 | tmp = sha1_passwd[i + 2]; | ||
640 | sha1_passwd[i + 2] = '\0'; | ||
641 | hashedPassword[i / 2] = (unsigned char) | ||
642 | simple_strtol(&sha1_passwd[i], NULL, 16); | ||
643 | sha1_passwd[i + 2] = tmp; | ||
644 | } | ||
645 | } | ||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | /** | ||
650 | * Sysfs registrations | ||
651 | */ | ||
652 | static int doSysfsRegistrations(void) | ||
653 | { | ||
654 | int rc = 0; | ||
655 | if ((rc = subsystem_register(&seclvl_subsys))) { | ||
656 | seclvl_printk(0, KERN_WARNING, | ||
657 | "Error [%d] registering seclvl subsystem\n", rc); | ||
658 | return rc; | ||
659 | } | ||
660 | sysfs_create_file(&seclvl_subsys.kset.kobj, &sysfs_attr_seclvl.attr); | ||
661 | if (*passwd || *sha1_passwd) { | ||
662 | sysfs_create_file(&seclvl_subsys.kset.kobj, | ||
663 | &sysfs_attr_passwd.attr); | ||
664 | } | ||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | /** | ||
669 | * Initialize the seclvl module. | ||
670 | */ | ||
671 | static int __init seclvl_init(void) | ||
672 | { | ||
673 | int rc = 0; | ||
674 | if (verbosity < 0 || verbosity > 1) { | ||
675 | printk(KERN_ERR "Error: bad verbosity [%d]; only 0 or 1 " | ||
676 | "are valid values\n", verbosity); | ||
677 | rc = -EINVAL; | ||
678 | goto exit; | ||
679 | } | ||
680 | sysfs_attr_seclvl.attr.owner = THIS_MODULE; | ||
681 | sysfs_attr_passwd.attr.owner = THIS_MODULE; | ||
682 | if (initlvl < -1 || initlvl > 2) { | ||
683 | seclvl_printk(0, KERN_ERR, "Error: bad initial securelevel " | ||
684 | "[%d].\n", initlvl); | ||
685 | rc = -EINVAL; | ||
686 | goto exit; | ||
687 | } | ||
688 | seclvl = initlvl; | ||
689 | if ((rc = processPassword())) { | ||
690 | seclvl_printk(0, KERN_ERR, "Error processing the password " | ||
691 | "module parameter(s): rc = [%d]\n", rc); | ||
692 | goto exit; | ||
693 | } | ||
694 | /* register ourselves with the security framework */ | ||
695 | if (register_security(&seclvl_ops)) { | ||
696 | seclvl_printk(0, KERN_ERR, | ||
697 | "seclvl: Failure registering with the " | ||
698 | "kernel.\n"); | ||
699 | /* try registering with primary module */ | ||
700 | rc = mod_reg_security(MY_NAME, &seclvl_ops); | ||
701 | if (rc) { | ||
702 | seclvl_printk(0, KERN_ERR, "seclvl: Failure " | ||
703 | "registering with primary security " | ||
704 | "module.\n"); | ||
705 | goto exit; | ||
706 | } /* if primary module registered */ | ||
707 | secondary = 1; | ||
708 | } /* if we registered ourselves with the security framework */ | ||
709 | if ((rc = doSysfsRegistrations())) { | ||
710 | seclvl_printk(0, KERN_ERR, "Error registering with sysfs\n"); | ||
711 | goto exit; | ||
712 | } | ||
713 | seclvl_printk(0, KERN_INFO, "seclvl: Successfully initialized.\n"); | ||
714 | exit: | ||
715 | if (rc) { | ||
716 | printk(KERN_ERR "seclvl: Error during initialization: rc = " | ||
717 | "[%d]\n", rc); | ||
718 | } | ||
719 | return rc; | ||
720 | } | ||
721 | |||
722 | /** | ||
723 | * Remove the seclvl module. | ||
724 | */ | ||
725 | static void __exit seclvl_exit(void) | ||
726 | { | ||
727 | sysfs_remove_file(&seclvl_subsys.kset.kobj, &sysfs_attr_seclvl.attr); | ||
728 | if (*passwd || *sha1_passwd) { | ||
729 | sysfs_remove_file(&seclvl_subsys.kset.kobj, | ||
730 | &sysfs_attr_passwd.attr); | ||
731 | } | ||
732 | subsystem_unregister(&seclvl_subsys); | ||
733 | if (secondary == 1) { | ||
734 | mod_unreg_security(MY_NAME, &seclvl_ops); | ||
735 | } else if (unregister_security(&seclvl_ops)) { | ||
736 | seclvl_printk(0, KERN_INFO, | ||
737 | "seclvl: Failure unregistering with the " | ||
738 | "kernel\n"); | ||
739 | } | ||
740 | } | ||
741 | |||
742 | module_init(seclvl_init); | ||
743 | module_exit(seclvl_exit); | ||
744 | |||
745 | MODULE_AUTHOR("Michael A. Halcrow <mike@halcrow.us>"); | ||
746 | MODULE_DESCRIPTION("LSM implementation of the BSD Secure Levels"); | ||
747 | MODULE_LICENSE("GPL"); | ||
diff --git a/security/security.c b/security/security.c new file mode 100644 index 000000000000..ed5fb80769c3 --- /dev/null +++ b/security/security.c | |||
@@ -0,0 +1,203 @@ | |||
1 | /* | ||
2 | * Security plug functions | ||
3 | * | ||
4 | * Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com> | ||
5 | * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com> | ||
6 | * Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/config.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/security.h> | ||
20 | |||
21 | #define SECURITY_FRAMEWORK_VERSION "1.0.0" | ||
22 | |||
23 | /* things that live in dummy.c */ | ||
24 | extern struct security_operations dummy_security_ops; | ||
25 | extern void security_fixup_ops(struct security_operations *ops); | ||
26 | |||
27 | struct security_operations *security_ops; /* Initialized to NULL */ | ||
28 | |||
29 | static inline int verify(struct security_operations *ops) | ||
30 | { | ||
31 | /* verify the security_operations structure exists */ | ||
32 | if (!ops) | ||
33 | return -EINVAL; | ||
34 | security_fixup_ops(ops); | ||
35 | return 0; | ||
36 | } | ||
37 | |||
38 | static void __init do_security_initcalls(void) | ||
39 | { | ||
40 | initcall_t *call; | ||
41 | call = __security_initcall_start; | ||
42 | while (call < __security_initcall_end) { | ||
43 | (*call) (); | ||
44 | call++; | ||
45 | } | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * security_init - initializes the security framework | ||
50 | * | ||
51 | * This should be called early in the kernel initialization sequence. | ||
52 | */ | ||
53 | int __init security_init(void) | ||
54 | { | ||
55 | printk(KERN_INFO "Security Framework v" SECURITY_FRAMEWORK_VERSION | ||
56 | " initialized\n"); | ||
57 | |||
58 | if (verify(&dummy_security_ops)) { | ||
59 | printk(KERN_ERR "%s could not verify " | ||
60 | "dummy_security_ops structure.\n", __FUNCTION__); | ||
61 | return -EIO; | ||
62 | } | ||
63 | |||
64 | security_ops = &dummy_security_ops; | ||
65 | do_security_initcalls(); | ||
66 | |||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * register_security - registers a security framework with the kernel | ||
72 | * @ops: a pointer to the struct security_options that is to be registered | ||
73 | * | ||
74 | * This function is to allow a security module to register itself with the | ||
75 | * kernel security subsystem. Some rudimentary checking is done on the @ops | ||
76 | * value passed to this function. A call to unregister_security() should be | ||
77 | * done to remove this security_options structure from the kernel. | ||
78 | * | ||
79 | * If there is already a security module registered with the kernel, | ||
80 | * an error will be returned. Otherwise 0 is returned on success. | ||
81 | */ | ||
82 | int register_security(struct security_operations *ops) | ||
83 | { | ||
84 | if (verify(ops)) { | ||
85 | printk(KERN_DEBUG "%s could not verify " | ||
86 | "security_operations structure.\n", __FUNCTION__); | ||
87 | return -EINVAL; | ||
88 | } | ||
89 | |||
90 | if (security_ops != &dummy_security_ops) | ||
91 | return -EAGAIN; | ||
92 | |||
93 | security_ops = ops; | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | /** | ||
99 | * unregister_security - unregisters a security framework with the kernel | ||
100 | * @ops: a pointer to the struct security_options that is to be registered | ||
101 | * | ||
102 | * This function removes a struct security_operations variable that had | ||
103 | * previously been registered with a successful call to register_security(). | ||
104 | * | ||
105 | * If @ops does not match the valued previously passed to register_security() | ||
106 | * an error is returned. Otherwise the default security options is set to the | ||
107 | * the dummy_security_ops structure, and 0 is returned. | ||
108 | */ | ||
109 | int unregister_security(struct security_operations *ops) | ||
110 | { | ||
111 | if (ops != security_ops) { | ||
112 | printk(KERN_INFO "%s: trying to unregister " | ||
113 | "a security_opts structure that is not " | ||
114 | "registered, failing.\n", __FUNCTION__); | ||
115 | return -EINVAL; | ||
116 | } | ||
117 | |||
118 | security_ops = &dummy_security_ops; | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * mod_reg_security - allows security modules to be "stacked" | ||
125 | * @name: a pointer to a string with the name of the security_options to be registered | ||
126 | * @ops: a pointer to the struct security_options that is to be registered | ||
127 | * | ||
128 | * This function allows security modules to be stacked if the currently loaded | ||
129 | * security module allows this to happen. It passes the @name and @ops to the | ||
130 | * register_security function of the currently loaded security module. | ||
131 | * | ||
132 | * The return value depends on the currently loaded security module, with 0 as | ||
133 | * success. | ||
134 | */ | ||
135 | int mod_reg_security(const char *name, struct security_operations *ops) | ||
136 | { | ||
137 | if (verify(ops)) { | ||
138 | printk(KERN_INFO "%s could not verify " | ||
139 | "security operations.\n", __FUNCTION__); | ||
140 | return -EINVAL; | ||
141 | } | ||
142 | |||
143 | if (ops == security_ops) { | ||
144 | printk(KERN_INFO "%s security operations " | ||
145 | "already registered.\n", __FUNCTION__); | ||
146 | return -EINVAL; | ||
147 | } | ||
148 | |||
149 | return security_ops->register_security(name, ops); | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * mod_unreg_security - allows a security module registered with mod_reg_security() to be unloaded | ||
154 | * @name: a pointer to a string with the name of the security_options to be removed | ||
155 | * @ops: a pointer to the struct security_options that is to be removed | ||
156 | * | ||
157 | * This function allows security modules that have been successfully registered | ||
158 | * with a call to mod_reg_security() to be unloaded from the system. | ||
159 | * This calls the currently loaded security module's unregister_security() call | ||
160 | * with the @name and @ops variables. | ||
161 | * | ||
162 | * The return value depends on the currently loaded security module, with 0 as | ||
163 | * success. | ||
164 | */ | ||
165 | int mod_unreg_security(const char *name, struct security_operations *ops) | ||
166 | { | ||
167 | if (ops == security_ops) { | ||
168 | printk(KERN_INFO "%s invalid attempt to unregister " | ||
169 | " primary security ops.\n", __FUNCTION__); | ||
170 | return -EINVAL; | ||
171 | } | ||
172 | |||
173 | return security_ops->unregister_security(name, ops); | ||
174 | } | ||
175 | |||
176 | /** | ||
177 | * capable - calls the currently loaded security module's capable() function with the specified capability | ||
178 | * @cap: the requested capability level. | ||
179 | * | ||
180 | * This function calls the currently loaded security module's capable() | ||
181 | * function with a pointer to the current task and the specified @cap value. | ||
182 | * | ||
183 | * This allows the security module to implement the capable function call | ||
184 | * however it chooses to. | ||
185 | */ | ||
186 | int capable(int cap) | ||
187 | { | ||
188 | if (security_ops->capable(current, cap)) { | ||
189 | /* capability denied */ | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | /* capability granted */ | ||
194 | current->flags |= PF_SUPERPRIV; | ||
195 | return 1; | ||
196 | } | ||
197 | |||
198 | EXPORT_SYMBOL_GPL(register_security); | ||
199 | EXPORT_SYMBOL_GPL(unregister_security); | ||
200 | EXPORT_SYMBOL_GPL(mod_reg_security); | ||
201 | EXPORT_SYMBOL_GPL(mod_unreg_security); | ||
202 | EXPORT_SYMBOL(capable); | ||
203 | EXPORT_SYMBOL(security_ops); | ||
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig new file mode 100644 index 000000000000..b59582b92283 --- /dev/null +++ b/security/selinux/Kconfig | |||
@@ -0,0 +1,97 @@ | |||
1 | config SECURITY_SELINUX | ||
2 | bool "NSA SELinux Support" | ||
3 | depends on SECURITY && NET && INET | ||
4 | default n | ||
5 | help | ||
6 | This selects NSA Security-Enhanced Linux (SELinux). | ||
7 | You will also need a policy configuration and a labeled filesystem. | ||
8 | You can obtain the policy compiler (checkpolicy), the utility for | ||
9 | labeling filesystems (setfiles), and an example policy configuration | ||
10 | from <http://www.nsa.gov/selinux/>. | ||
11 | If you are unsure how to answer this question, answer N. | ||
12 | |||
13 | config SECURITY_SELINUX_BOOTPARAM | ||
14 | bool "NSA SELinux boot parameter" | ||
15 | depends on SECURITY_SELINUX | ||
16 | default n | ||
17 | help | ||
18 | This option adds a kernel parameter 'selinux', which allows SELinux | ||
19 | to be disabled at boot. If this option is selected, SELinux | ||
20 | functionality can be disabled with selinux=0 on the kernel | ||
21 | command line. The purpose of this option is to allow a single | ||
22 | kernel image to be distributed with SELinux built in, but not | ||
23 | necessarily enabled. | ||
24 | |||
25 | If you are unsure how to answer this question, answer N. | ||
26 | |||
27 | config SECURITY_SELINUX_BOOTPARAM_VALUE | ||
28 | int "NSA SELinux boot parameter default value" | ||
29 | depends on SECURITY_SELINUX_BOOTPARAM | ||
30 | range 0 1 | ||
31 | default 1 | ||
32 | help | ||
33 | This option sets the default value for the kernel parameter | ||
34 | 'selinux', which allows SELinux to be disabled at boot. If this | ||
35 | option is set to 0 (zero), the SELinux kernel parameter will | ||
36 | default to 0, disabling SELinux at bootup. If this option is | ||
37 | set to 1 (one), the SELinux kernel parameter will default to 1, | ||
38 | enabling SELinux at bootup. | ||
39 | |||
40 | If you are unsure how to answer this question, answer 1. | ||
41 | |||
42 | config SECURITY_SELINUX_DISABLE | ||
43 | bool "NSA SELinux runtime disable" | ||
44 | depends on SECURITY_SELINUX | ||
45 | default n | ||
46 | help | ||
47 | This option enables writing to a selinuxfs node 'disable', which | ||
48 | allows SELinux to be disabled at runtime prior to the policy load. | ||
49 | SELinux will then remain disabled until the next boot. | ||
50 | This option is similar to the selinux=0 boot parameter, but is to | ||
51 | support runtime disabling of SELinux, e.g. from /sbin/init, for | ||
52 | portability across platforms where boot parameters are difficult | ||
53 | to employ. | ||
54 | |||
55 | If you are unsure how to answer this question, answer N. | ||
56 | |||
57 | config SECURITY_SELINUX_DEVELOP | ||
58 | bool "NSA SELinux Development Support" | ||
59 | depends on SECURITY_SELINUX | ||
60 | default y | ||
61 | help | ||
62 | This enables the development support option of NSA SELinux, | ||
63 | which is useful for experimenting with SELinux and developing | ||
64 | policies. If unsure, say Y. With this option enabled, the | ||
65 | kernel will start in permissive mode (log everything, deny nothing) | ||
66 | unless you specify enforcing=1 on the kernel command line. You | ||
67 | can interactively toggle the kernel between enforcing mode and | ||
68 | permissive mode (if permitted by the policy) via /selinux/enforce. | ||
69 | |||
70 | config SECURITY_SELINUX_AVC_STATS | ||
71 | bool "NSA SELinux AVC Statistics" | ||
72 | depends on SECURITY_SELINUX | ||
73 | default y | ||
74 | help | ||
75 | This option collects access vector cache statistics to | ||
76 | /selinux/avc/cache_stats, which may be monitored via | ||
77 | tools such as avcstat. | ||
78 | |||
79 | config SECURITY_SELINUX_CHECKREQPROT_VALUE | ||
80 | int "NSA SELinux checkreqprot default value" | ||
81 | depends on SECURITY_SELINUX | ||
82 | range 0 1 | ||
83 | default 1 | ||
84 | help | ||
85 | This option sets the default value for the 'checkreqprot' flag | ||
86 | that determines whether SELinux checks the protection requested | ||
87 | by the application or the protection that will be applied by the | ||
88 | kernel (including any implied execute for read-implies-exec) for | ||
89 | mmap and mprotect calls. If this option is set to 0 (zero), | ||
90 | SELinux will default to checking the protection that will be applied | ||
91 | by the kernel. If this option is set to 1 (one), SELinux will | ||
92 | default to checking the protection requested by the application. | ||
93 | The checkreqprot flag may be changed from the default via the | ||
94 | 'checkreqprot=' boot parameter. It may also be changed at runtime | ||
95 | via /selinux/checkreqprot if authorized by policy. | ||
96 | |||
97 | If you are unsure how to answer this question, answer 1. | ||
diff --git a/security/selinux/Makefile b/security/selinux/Makefile new file mode 100644 index 000000000000..b038cd0fae2e --- /dev/null +++ b/security/selinux/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | # | ||
2 | # Makefile for building the SELinux module as part of the kernel tree. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/ | ||
6 | |||
7 | selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o | ||
8 | |||
9 | selinux-$(CONFIG_SECURITY_NETWORK) += netif.o | ||
10 | |||
11 | EXTRA_CFLAGS += -Isecurity/selinux/include | ||
12 | |||
diff --git a/security/selinux/avc.c b/security/selinux/avc.c new file mode 100644 index 000000000000..fe6285e5c68f --- /dev/null +++ b/security/selinux/avc.c | |||
@@ -0,0 +1,949 @@ | |||
1 | /* | ||
2 | * Implementation of the kernel access vector cache (AVC). | ||
3 | * | ||
4 | * Authors: Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | * James Morris <jmorris@redhat.com> | ||
6 | * | ||
7 | * Update: KaiGai, Kohei <kaigai@ak.jp.nec.com> | ||
8 | * Replaced the avc_lock spinlock by RCU. | ||
9 | * | ||
10 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2, | ||
14 | * as published by the Free Software Foundation. | ||
15 | */ | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/stddef.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/fs.h> | ||
21 | #include <linux/dcache.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/skbuff.h> | ||
24 | #include <linux/percpu.h> | ||
25 | #include <net/sock.h> | ||
26 | #include <linux/un.h> | ||
27 | #include <net/af_unix.h> | ||
28 | #include <linux/ip.h> | ||
29 | #include <linux/audit.h> | ||
30 | #include <linux/ipv6.h> | ||
31 | #include <net/ipv6.h> | ||
32 | #include "avc.h" | ||
33 | #include "avc_ss.h" | ||
34 | |||
35 | static const struct av_perm_to_string | ||
36 | { | ||
37 | u16 tclass; | ||
38 | u32 value; | ||
39 | const char *name; | ||
40 | } av_perm_to_string[] = { | ||
41 | #define S_(c, v, s) { c, v, s }, | ||
42 | #include "av_perm_to_string.h" | ||
43 | #undef S_ | ||
44 | }; | ||
45 | |||
46 | #ifdef CONFIG_AUDIT | ||
47 | static const char *class_to_string[] = { | ||
48 | #define S_(s) s, | ||
49 | #include "class_to_string.h" | ||
50 | #undef S_ | ||
51 | }; | ||
52 | #endif | ||
53 | |||
54 | #define TB_(s) static const char * s [] = { | ||
55 | #define TE_(s) }; | ||
56 | #define S_(s) s, | ||
57 | #include "common_perm_to_string.h" | ||
58 | #undef TB_ | ||
59 | #undef TE_ | ||
60 | #undef S_ | ||
61 | |||
62 | static const struct av_inherit | ||
63 | { | ||
64 | u16 tclass; | ||
65 | const char **common_pts; | ||
66 | u32 common_base; | ||
67 | } av_inherit[] = { | ||
68 | #define S_(c, i, b) { c, common_##i##_perm_to_string, b }, | ||
69 | #include "av_inherit.h" | ||
70 | #undef S_ | ||
71 | }; | ||
72 | |||
73 | #define AVC_CACHE_SLOTS 512 | ||
74 | #define AVC_DEF_CACHE_THRESHOLD 512 | ||
75 | #define AVC_CACHE_RECLAIM 16 | ||
76 | |||
77 | #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS | ||
78 | #define avc_cache_stats_incr(field) \ | ||
79 | do { \ | ||
80 | per_cpu(avc_cache_stats, get_cpu()).field++; \ | ||
81 | put_cpu(); \ | ||
82 | } while (0) | ||
83 | #else | ||
84 | #define avc_cache_stats_incr(field) do {} while (0) | ||
85 | #endif | ||
86 | |||
87 | struct avc_entry { | ||
88 | u32 ssid; | ||
89 | u32 tsid; | ||
90 | u16 tclass; | ||
91 | struct av_decision avd; | ||
92 | atomic_t used; /* used recently */ | ||
93 | }; | ||
94 | |||
95 | struct avc_node { | ||
96 | struct avc_entry ae; | ||
97 | struct list_head list; | ||
98 | struct rcu_head rhead; | ||
99 | }; | ||
100 | |||
101 | struct avc_cache { | ||
102 | struct list_head slots[AVC_CACHE_SLOTS]; | ||
103 | spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */ | ||
104 | atomic_t lru_hint; /* LRU hint for reclaim scan */ | ||
105 | atomic_t active_nodes; | ||
106 | u32 latest_notif; /* latest revocation notification */ | ||
107 | }; | ||
108 | |||
109 | struct avc_callback_node { | ||
110 | int (*callback) (u32 event, u32 ssid, u32 tsid, | ||
111 | u16 tclass, u32 perms, | ||
112 | u32 *out_retained); | ||
113 | u32 events; | ||
114 | u32 ssid; | ||
115 | u32 tsid; | ||
116 | u16 tclass; | ||
117 | u32 perms; | ||
118 | struct avc_callback_node *next; | ||
119 | }; | ||
120 | |||
121 | /* Exported via selinufs */ | ||
122 | unsigned int avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD; | ||
123 | |||
124 | #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS | ||
125 | DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 }; | ||
126 | #endif | ||
127 | |||
128 | static struct avc_cache avc_cache; | ||
129 | static struct avc_callback_node *avc_callbacks; | ||
130 | static kmem_cache_t *avc_node_cachep; | ||
131 | |||
132 | static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) | ||
133 | { | ||
134 | return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1); | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * avc_dump_av - Display an access vector in human-readable form. | ||
139 | * @tclass: target security class | ||
140 | * @av: access vector | ||
141 | */ | ||
142 | static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av) | ||
143 | { | ||
144 | const char **common_pts = NULL; | ||
145 | u32 common_base = 0; | ||
146 | int i, i2, perm; | ||
147 | |||
148 | if (av == 0) { | ||
149 | audit_log_format(ab, " null"); | ||
150 | return; | ||
151 | } | ||
152 | |||
153 | for (i = 0; i < ARRAY_SIZE(av_inherit); i++) { | ||
154 | if (av_inherit[i].tclass == tclass) { | ||
155 | common_pts = av_inherit[i].common_pts; | ||
156 | common_base = av_inherit[i].common_base; | ||
157 | break; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | audit_log_format(ab, " {"); | ||
162 | i = 0; | ||
163 | perm = 1; | ||
164 | while (perm < common_base) { | ||
165 | if (perm & av) { | ||
166 | audit_log_format(ab, " %s", common_pts[i]); | ||
167 | av &= ~perm; | ||
168 | } | ||
169 | i++; | ||
170 | perm <<= 1; | ||
171 | } | ||
172 | |||
173 | while (i < sizeof(av) * 8) { | ||
174 | if (perm & av) { | ||
175 | for (i2 = 0; i2 < ARRAY_SIZE(av_perm_to_string); i2++) { | ||
176 | if ((av_perm_to_string[i2].tclass == tclass) && | ||
177 | (av_perm_to_string[i2].value == perm)) | ||
178 | break; | ||
179 | } | ||
180 | if (i2 < ARRAY_SIZE(av_perm_to_string)) { | ||
181 | audit_log_format(ab, " %s", | ||
182 | av_perm_to_string[i2].name); | ||
183 | av &= ~perm; | ||
184 | } | ||
185 | } | ||
186 | i++; | ||
187 | perm <<= 1; | ||
188 | } | ||
189 | |||
190 | if (av) | ||
191 | audit_log_format(ab, " 0x%x", av); | ||
192 | |||
193 | audit_log_format(ab, " }"); | ||
194 | } | ||
195 | |||
196 | /** | ||
197 | * avc_dump_query - Display a SID pair and a class in human-readable form. | ||
198 | * @ssid: source security identifier | ||
199 | * @tsid: target security identifier | ||
200 | * @tclass: target security class | ||
201 | */ | ||
202 | static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tclass) | ||
203 | { | ||
204 | int rc; | ||
205 | char *scontext; | ||
206 | u32 scontext_len; | ||
207 | |||
208 | rc = security_sid_to_context(ssid, &scontext, &scontext_len); | ||
209 | if (rc) | ||
210 | audit_log_format(ab, "ssid=%d", ssid); | ||
211 | else { | ||
212 | audit_log_format(ab, "scontext=%s", scontext); | ||
213 | kfree(scontext); | ||
214 | } | ||
215 | |||
216 | rc = security_sid_to_context(tsid, &scontext, &scontext_len); | ||
217 | if (rc) | ||
218 | audit_log_format(ab, " tsid=%d", tsid); | ||
219 | else { | ||
220 | audit_log_format(ab, " tcontext=%s", scontext); | ||
221 | kfree(scontext); | ||
222 | } | ||
223 | audit_log_format(ab, " tclass=%s", class_to_string[tclass]); | ||
224 | } | ||
225 | |||
226 | /** | ||
227 | * avc_init - Initialize the AVC. | ||
228 | * | ||
229 | * Initialize the access vector cache. | ||
230 | */ | ||
231 | void __init avc_init(void) | ||
232 | { | ||
233 | int i; | ||
234 | |||
235 | for (i = 0; i < AVC_CACHE_SLOTS; i++) { | ||
236 | INIT_LIST_HEAD(&avc_cache.slots[i]); | ||
237 | spin_lock_init(&avc_cache.slots_lock[i]); | ||
238 | } | ||
239 | atomic_set(&avc_cache.active_nodes, 0); | ||
240 | atomic_set(&avc_cache.lru_hint, 0); | ||
241 | |||
242 | avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), | ||
243 | 0, SLAB_PANIC, NULL, NULL); | ||
244 | |||
245 | audit_log(current->audit_context, "AVC INITIALIZED\n"); | ||
246 | } | ||
247 | |||
248 | int avc_get_hash_stats(char *page) | ||
249 | { | ||
250 | int i, chain_len, max_chain_len, slots_used; | ||
251 | struct avc_node *node; | ||
252 | |||
253 | rcu_read_lock(); | ||
254 | |||
255 | slots_used = 0; | ||
256 | max_chain_len = 0; | ||
257 | for (i = 0; i < AVC_CACHE_SLOTS; i++) { | ||
258 | if (!list_empty(&avc_cache.slots[i])) { | ||
259 | slots_used++; | ||
260 | chain_len = 0; | ||
261 | list_for_each_entry_rcu(node, &avc_cache.slots[i], list) | ||
262 | chain_len++; | ||
263 | if (chain_len > max_chain_len) | ||
264 | max_chain_len = chain_len; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | rcu_read_unlock(); | ||
269 | |||
270 | return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n" | ||
271 | "longest chain: %d\n", | ||
272 | atomic_read(&avc_cache.active_nodes), | ||
273 | slots_used, AVC_CACHE_SLOTS, max_chain_len); | ||
274 | } | ||
275 | |||
276 | static void avc_node_free(struct rcu_head *rhead) | ||
277 | { | ||
278 | struct avc_node *node = container_of(rhead, struct avc_node, rhead); | ||
279 | kmem_cache_free(avc_node_cachep, node); | ||
280 | avc_cache_stats_incr(frees); | ||
281 | } | ||
282 | |||
283 | static void avc_node_delete(struct avc_node *node) | ||
284 | { | ||
285 | list_del_rcu(&node->list); | ||
286 | call_rcu(&node->rhead, avc_node_free); | ||
287 | atomic_dec(&avc_cache.active_nodes); | ||
288 | } | ||
289 | |||
290 | static void avc_node_kill(struct avc_node *node) | ||
291 | { | ||
292 | kmem_cache_free(avc_node_cachep, node); | ||
293 | avc_cache_stats_incr(frees); | ||
294 | atomic_dec(&avc_cache.active_nodes); | ||
295 | } | ||
296 | |||
297 | static void avc_node_replace(struct avc_node *new, struct avc_node *old) | ||
298 | { | ||
299 | list_replace_rcu(&old->list, &new->list); | ||
300 | call_rcu(&old->rhead, avc_node_free); | ||
301 | atomic_dec(&avc_cache.active_nodes); | ||
302 | } | ||
303 | |||
304 | static inline int avc_reclaim_node(void) | ||
305 | { | ||
306 | struct avc_node *node; | ||
307 | int hvalue, try, ecx; | ||
308 | unsigned long flags; | ||
309 | |||
310 | for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++ ) { | ||
311 | hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1); | ||
312 | |||
313 | if (!spin_trylock_irqsave(&avc_cache.slots_lock[hvalue], flags)) | ||
314 | continue; | ||
315 | |||
316 | list_for_each_entry(node, &avc_cache.slots[hvalue], list) { | ||
317 | if (atomic_dec_and_test(&node->ae.used)) { | ||
318 | /* Recently Unused */ | ||
319 | avc_node_delete(node); | ||
320 | avc_cache_stats_incr(reclaims); | ||
321 | ecx++; | ||
322 | if (ecx >= AVC_CACHE_RECLAIM) { | ||
323 | spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags); | ||
324 | goto out; | ||
325 | } | ||
326 | } | ||
327 | } | ||
328 | spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags); | ||
329 | } | ||
330 | out: | ||
331 | return ecx; | ||
332 | } | ||
333 | |||
334 | static struct avc_node *avc_alloc_node(void) | ||
335 | { | ||
336 | struct avc_node *node; | ||
337 | |||
338 | node = kmem_cache_alloc(avc_node_cachep, SLAB_ATOMIC); | ||
339 | if (!node) | ||
340 | goto out; | ||
341 | |||
342 | memset(node, 0, sizeof(*node)); | ||
343 | INIT_RCU_HEAD(&node->rhead); | ||
344 | INIT_LIST_HEAD(&node->list); | ||
345 | atomic_set(&node->ae.used, 1); | ||
346 | avc_cache_stats_incr(allocations); | ||
347 | |||
348 | if (atomic_inc_return(&avc_cache.active_nodes) > avc_cache_threshold) | ||
349 | avc_reclaim_node(); | ||
350 | |||
351 | out: | ||
352 | return node; | ||
353 | } | ||
354 | |||
355 | static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae) | ||
356 | { | ||
357 | node->ae.ssid = ssid; | ||
358 | node->ae.tsid = tsid; | ||
359 | node->ae.tclass = tclass; | ||
360 | memcpy(&node->ae.avd, &ae->avd, sizeof(node->ae.avd)); | ||
361 | } | ||
362 | |||
363 | static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass) | ||
364 | { | ||
365 | struct avc_node *node, *ret = NULL; | ||
366 | int hvalue; | ||
367 | |||
368 | hvalue = avc_hash(ssid, tsid, tclass); | ||
369 | list_for_each_entry_rcu(node, &avc_cache.slots[hvalue], list) { | ||
370 | if (ssid == node->ae.ssid && | ||
371 | tclass == node->ae.tclass && | ||
372 | tsid == node->ae.tsid) { | ||
373 | ret = node; | ||
374 | break; | ||
375 | } | ||
376 | } | ||
377 | |||
378 | if (ret == NULL) { | ||
379 | /* cache miss */ | ||
380 | goto out; | ||
381 | } | ||
382 | |||
383 | /* cache hit */ | ||
384 | if (atomic_read(&ret->ae.used) != 1) | ||
385 | atomic_set(&ret->ae.used, 1); | ||
386 | out: | ||
387 | return ret; | ||
388 | } | ||
389 | |||
390 | /** | ||
391 | * avc_lookup - Look up an AVC entry. | ||
392 | * @ssid: source security identifier | ||
393 | * @tsid: target security identifier | ||
394 | * @tclass: target security class | ||
395 | * @requested: requested permissions, interpreted based on @tclass | ||
396 | * | ||
397 | * Look up an AVC entry that is valid for the | ||
398 | * @requested permissions between the SID pair | ||
399 | * (@ssid, @tsid), interpreting the permissions | ||
400 | * based on @tclass. If a valid AVC entry exists, | ||
401 | * then this function return the avc_node. | ||
402 | * Otherwise, this function returns NULL. | ||
403 | */ | ||
404 | static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass, u32 requested) | ||
405 | { | ||
406 | struct avc_node *node; | ||
407 | |||
408 | avc_cache_stats_incr(lookups); | ||
409 | node = avc_search_node(ssid, tsid, tclass); | ||
410 | |||
411 | if (node && ((node->ae.avd.decided & requested) == requested)) { | ||
412 | avc_cache_stats_incr(hits); | ||
413 | goto out; | ||
414 | } | ||
415 | |||
416 | node = NULL; | ||
417 | avc_cache_stats_incr(misses); | ||
418 | out: | ||
419 | return node; | ||
420 | } | ||
421 | |||
422 | static int avc_latest_notif_update(int seqno, int is_insert) | ||
423 | { | ||
424 | int ret = 0; | ||
425 | static DEFINE_SPINLOCK(notif_lock); | ||
426 | unsigned long flag; | ||
427 | |||
428 | spin_lock_irqsave(¬if_lock, flag); | ||
429 | if (is_insert) { | ||
430 | if (seqno < avc_cache.latest_notif) { | ||
431 | printk(KERN_WARNING "avc: seqno %d < latest_notif %d\n", | ||
432 | seqno, avc_cache.latest_notif); | ||
433 | ret = -EAGAIN; | ||
434 | } | ||
435 | } else { | ||
436 | if (seqno > avc_cache.latest_notif) | ||
437 | avc_cache.latest_notif = seqno; | ||
438 | } | ||
439 | spin_unlock_irqrestore(¬if_lock, flag); | ||
440 | |||
441 | return ret; | ||
442 | } | ||
443 | |||
444 | /** | ||
445 | * avc_insert - Insert an AVC entry. | ||
446 | * @ssid: source security identifier | ||
447 | * @tsid: target security identifier | ||
448 | * @tclass: target security class | ||
449 | * @ae: AVC entry | ||
450 | * | ||
451 | * Insert an AVC entry for the SID pair | ||
452 | * (@ssid, @tsid) and class @tclass. | ||
453 | * The access vectors and the sequence number are | ||
454 | * normally provided by the security server in | ||
455 | * response to a security_compute_av() call. If the | ||
456 | * sequence number @ae->avd.seqno is not less than the latest | ||
457 | * revocation notification, then the function copies | ||
458 | * the access vectors into a cache entry, returns | ||
459 | * avc_node inserted. Otherwise, this function returns NULL. | ||
460 | */ | ||
461 | static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae) | ||
462 | { | ||
463 | struct avc_node *pos, *node = NULL; | ||
464 | int hvalue; | ||
465 | unsigned long flag; | ||
466 | |||
467 | if (avc_latest_notif_update(ae->avd.seqno, 1)) | ||
468 | goto out; | ||
469 | |||
470 | node = avc_alloc_node(); | ||
471 | if (node) { | ||
472 | hvalue = avc_hash(ssid, tsid, tclass); | ||
473 | avc_node_populate(node, ssid, tsid, tclass, ae); | ||
474 | |||
475 | spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag); | ||
476 | list_for_each_entry(pos, &avc_cache.slots[hvalue], list) { | ||
477 | if (pos->ae.ssid == ssid && | ||
478 | pos->ae.tsid == tsid && | ||
479 | pos->ae.tclass == tclass) { | ||
480 | avc_node_replace(node, pos); | ||
481 | goto found; | ||
482 | } | ||
483 | } | ||
484 | list_add_rcu(&node->list, &avc_cache.slots[hvalue]); | ||
485 | found: | ||
486 | spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag); | ||
487 | } | ||
488 | out: | ||
489 | return node; | ||
490 | } | ||
491 | |||
492 | static inline void avc_print_ipv6_addr(struct audit_buffer *ab, | ||
493 | struct in6_addr *addr, u16 port, | ||
494 | char *name1, char *name2) | ||
495 | { | ||
496 | if (!ipv6_addr_any(addr)) | ||
497 | audit_log_format(ab, " %s=%04x:%04x:%04x:%04x:%04x:" | ||
498 | "%04x:%04x:%04x", name1, NIP6(*addr)); | ||
499 | if (port) | ||
500 | audit_log_format(ab, " %s=%d", name2, ntohs(port)); | ||
501 | } | ||
502 | |||
503 | static inline void avc_print_ipv4_addr(struct audit_buffer *ab, u32 addr, | ||
504 | u16 port, char *name1, char *name2) | ||
505 | { | ||
506 | if (addr) | ||
507 | audit_log_format(ab, " %s=%d.%d.%d.%d", name1, NIPQUAD(addr)); | ||
508 | if (port) | ||
509 | audit_log_format(ab, " %s=%d", name2, ntohs(port)); | ||
510 | } | ||
511 | |||
512 | /** | ||
513 | * avc_audit - Audit the granting or denial of permissions. | ||
514 | * @ssid: source security identifier | ||
515 | * @tsid: target security identifier | ||
516 | * @tclass: target security class | ||
517 | * @requested: requested permissions | ||
518 | * @avd: access vector decisions | ||
519 | * @result: result from avc_has_perm_noaudit | ||
520 | * @a: auxiliary audit data | ||
521 | * | ||
522 | * Audit the granting or denial of permissions in accordance | ||
523 | * with the policy. This function is typically called by | ||
524 | * avc_has_perm() after a permission check, but can also be | ||
525 | * called directly by callers who use avc_has_perm_noaudit() | ||
526 | * in order to separate the permission check from the auditing. | ||
527 | * For example, this separation is useful when the permission check must | ||
528 | * be performed under a lock, to allow the lock to be released | ||
529 | * before calling the auditing code. | ||
530 | */ | ||
531 | void avc_audit(u32 ssid, u32 tsid, | ||
532 | u16 tclass, u32 requested, | ||
533 | struct av_decision *avd, int result, struct avc_audit_data *a) | ||
534 | { | ||
535 | struct task_struct *tsk = current; | ||
536 | struct inode *inode = NULL; | ||
537 | u32 denied, audited; | ||
538 | struct audit_buffer *ab; | ||
539 | |||
540 | denied = requested & ~avd->allowed; | ||
541 | if (denied) { | ||
542 | audited = denied; | ||
543 | if (!(audited & avd->auditdeny)) | ||
544 | return; | ||
545 | } else if (result) { | ||
546 | audited = denied = requested; | ||
547 | } else { | ||
548 | audited = requested; | ||
549 | if (!(audited & avd->auditallow)) | ||
550 | return; | ||
551 | } | ||
552 | |||
553 | ab = audit_log_start(current->audit_context); | ||
554 | if (!ab) | ||
555 | return; /* audit_panic has been called */ | ||
556 | audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted"); | ||
557 | avc_dump_av(ab, tclass,audited); | ||
558 | audit_log_format(ab, " for "); | ||
559 | if (a && a->tsk) | ||
560 | tsk = a->tsk; | ||
561 | if (tsk && tsk->pid) { | ||
562 | struct mm_struct *mm; | ||
563 | struct vm_area_struct *vma; | ||
564 | audit_log_format(ab, " pid=%d", tsk->pid); | ||
565 | if (tsk == current) | ||
566 | mm = current->mm; | ||
567 | else | ||
568 | mm = get_task_mm(tsk); | ||
569 | if (mm) { | ||
570 | if (down_read_trylock(&mm->mmap_sem)) { | ||
571 | vma = mm->mmap; | ||
572 | while (vma) { | ||
573 | if ((vma->vm_flags & VM_EXECUTABLE) && | ||
574 | vma->vm_file) { | ||
575 | audit_log_d_path(ab, "exe=", | ||
576 | vma->vm_file->f_dentry, | ||
577 | vma->vm_file->f_vfsmnt); | ||
578 | break; | ||
579 | } | ||
580 | vma = vma->vm_next; | ||
581 | } | ||
582 | up_read(&mm->mmap_sem); | ||
583 | } else { | ||
584 | audit_log_format(ab, " comm=%s", tsk->comm); | ||
585 | } | ||
586 | if (tsk != current) | ||
587 | mmput(mm); | ||
588 | } else { | ||
589 | audit_log_format(ab, " comm=%s", tsk->comm); | ||
590 | } | ||
591 | } | ||
592 | if (a) { | ||
593 | switch (a->type) { | ||
594 | case AVC_AUDIT_DATA_IPC: | ||
595 | audit_log_format(ab, " key=%d", a->u.ipc_id); | ||
596 | break; | ||
597 | case AVC_AUDIT_DATA_CAP: | ||
598 | audit_log_format(ab, " capability=%d", a->u.cap); | ||
599 | break; | ||
600 | case AVC_AUDIT_DATA_FS: | ||
601 | if (a->u.fs.dentry) { | ||
602 | struct dentry *dentry = a->u.fs.dentry; | ||
603 | if (a->u.fs.mnt) { | ||
604 | audit_log_d_path(ab, "path=", dentry, | ||
605 | a->u.fs.mnt); | ||
606 | } else { | ||
607 | audit_log_format(ab, " name=%s", | ||
608 | dentry->d_name.name); | ||
609 | } | ||
610 | inode = dentry->d_inode; | ||
611 | } else if (a->u.fs.inode) { | ||
612 | struct dentry *dentry; | ||
613 | inode = a->u.fs.inode; | ||
614 | dentry = d_find_alias(inode); | ||
615 | if (dentry) { | ||
616 | audit_log_format(ab, " name=%s", | ||
617 | dentry->d_name.name); | ||
618 | dput(dentry); | ||
619 | } | ||
620 | } | ||
621 | if (inode) | ||
622 | audit_log_format(ab, " dev=%s ino=%ld", | ||
623 | inode->i_sb->s_id, | ||
624 | inode->i_ino); | ||
625 | break; | ||
626 | case AVC_AUDIT_DATA_NET: | ||
627 | if (a->u.net.sk) { | ||
628 | struct sock *sk = a->u.net.sk; | ||
629 | struct unix_sock *u; | ||
630 | int len = 0; | ||
631 | char *p = NULL; | ||
632 | |||
633 | switch (sk->sk_family) { | ||
634 | case AF_INET: { | ||
635 | struct inet_sock *inet = inet_sk(sk); | ||
636 | |||
637 | avc_print_ipv4_addr(ab, inet->rcv_saddr, | ||
638 | inet->sport, | ||
639 | "laddr", "lport"); | ||
640 | avc_print_ipv4_addr(ab, inet->daddr, | ||
641 | inet->dport, | ||
642 | "faddr", "fport"); | ||
643 | break; | ||
644 | } | ||
645 | case AF_INET6: { | ||
646 | struct inet_sock *inet = inet_sk(sk); | ||
647 | struct ipv6_pinfo *inet6 = inet6_sk(sk); | ||
648 | |||
649 | avc_print_ipv6_addr(ab, &inet6->rcv_saddr, | ||
650 | inet->sport, | ||
651 | "laddr", "lport"); | ||
652 | avc_print_ipv6_addr(ab, &inet6->daddr, | ||
653 | inet->dport, | ||
654 | "faddr", "fport"); | ||
655 | break; | ||
656 | } | ||
657 | case AF_UNIX: | ||
658 | u = unix_sk(sk); | ||
659 | if (u->dentry) { | ||
660 | audit_log_d_path(ab, "path=", | ||
661 | u->dentry, u->mnt); | ||
662 | break; | ||
663 | } | ||
664 | if (!u->addr) | ||
665 | break; | ||
666 | len = u->addr->len-sizeof(short); | ||
667 | p = &u->addr->name->sun_path[0]; | ||
668 | if (*p) | ||
669 | audit_log_format(ab, | ||
670 | "path=%*.*s", len, | ||
671 | len, p); | ||
672 | else | ||
673 | audit_log_format(ab, | ||
674 | "path=@%*.*s", len-1, | ||
675 | len-1, p+1); | ||
676 | break; | ||
677 | } | ||
678 | } | ||
679 | |||
680 | switch (a->u.net.family) { | ||
681 | case AF_INET: | ||
682 | avc_print_ipv4_addr(ab, a->u.net.v4info.saddr, | ||
683 | a->u.net.sport, | ||
684 | "saddr", "src"); | ||
685 | avc_print_ipv4_addr(ab, a->u.net.v4info.daddr, | ||
686 | a->u.net.dport, | ||
687 | "daddr", "dest"); | ||
688 | break; | ||
689 | case AF_INET6: | ||
690 | avc_print_ipv6_addr(ab, &a->u.net.v6info.saddr, | ||
691 | a->u.net.sport, | ||
692 | "saddr", "src"); | ||
693 | avc_print_ipv6_addr(ab, &a->u.net.v6info.daddr, | ||
694 | a->u.net.dport, | ||
695 | "daddr", "dest"); | ||
696 | break; | ||
697 | } | ||
698 | if (a->u.net.netif) | ||
699 | audit_log_format(ab, " netif=%s", | ||
700 | a->u.net.netif); | ||
701 | break; | ||
702 | } | ||
703 | } | ||
704 | audit_log_format(ab, " "); | ||
705 | avc_dump_query(ab, ssid, tsid, tclass); | ||
706 | audit_log_end(ab); | ||
707 | } | ||
708 | |||
709 | /** | ||
710 | * avc_add_callback - Register a callback for security events. | ||
711 | * @callback: callback function | ||
712 | * @events: security events | ||
713 | * @ssid: source security identifier or %SECSID_WILD | ||
714 | * @tsid: target security identifier or %SECSID_WILD | ||
715 | * @tclass: target security class | ||
716 | * @perms: permissions | ||
717 | * | ||
718 | * Register a callback function for events in the set @events | ||
719 | * related to the SID pair (@ssid, @tsid) and | ||
720 | * and the permissions @perms, interpreting | ||
721 | * @perms based on @tclass. Returns %0 on success or | ||
722 | * -%ENOMEM if insufficient memory exists to add the callback. | ||
723 | */ | ||
724 | int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, | ||
725 | u16 tclass, u32 perms, | ||
726 | u32 *out_retained), | ||
727 | u32 events, u32 ssid, u32 tsid, | ||
728 | u16 tclass, u32 perms) | ||
729 | { | ||
730 | struct avc_callback_node *c; | ||
731 | int rc = 0; | ||
732 | |||
733 | c = kmalloc(sizeof(*c), GFP_ATOMIC); | ||
734 | if (!c) { | ||
735 | rc = -ENOMEM; | ||
736 | goto out; | ||
737 | } | ||
738 | |||
739 | c->callback = callback; | ||
740 | c->events = events; | ||
741 | c->ssid = ssid; | ||
742 | c->tsid = tsid; | ||
743 | c->perms = perms; | ||
744 | c->next = avc_callbacks; | ||
745 | avc_callbacks = c; | ||
746 | out: | ||
747 | return rc; | ||
748 | } | ||
749 | |||
750 | static inline int avc_sidcmp(u32 x, u32 y) | ||
751 | { | ||
752 | return (x == y || x == SECSID_WILD || y == SECSID_WILD); | ||
753 | } | ||
754 | |||
755 | /** | ||
756 | * avc_update_node Update an AVC entry | ||
757 | * @event : Updating event | ||
758 | * @perms : Permission mask bits | ||
759 | * @ssid,@tsid,@tclass : identifier of an AVC entry | ||
760 | * | ||
761 | * if a valid AVC entry doesn't exist,this function returns -ENOENT. | ||
762 | * if kmalloc() called internal returns NULL, this function returns -ENOMEM. | ||
763 | * otherwise, this function update the AVC entry. The original AVC-entry object | ||
764 | * will release later by RCU. | ||
765 | */ | ||
766 | static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass) | ||
767 | { | ||
768 | int hvalue, rc = 0; | ||
769 | unsigned long flag; | ||
770 | struct avc_node *pos, *node, *orig = NULL; | ||
771 | |||
772 | node = avc_alloc_node(); | ||
773 | if (!node) { | ||
774 | rc = -ENOMEM; | ||
775 | goto out; | ||
776 | } | ||
777 | |||
778 | /* Lock the target slot */ | ||
779 | hvalue = avc_hash(ssid, tsid, tclass); | ||
780 | spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag); | ||
781 | |||
782 | list_for_each_entry(pos, &avc_cache.slots[hvalue], list){ | ||
783 | if ( ssid==pos->ae.ssid && | ||
784 | tsid==pos->ae.tsid && | ||
785 | tclass==pos->ae.tclass ){ | ||
786 | orig = pos; | ||
787 | break; | ||
788 | } | ||
789 | } | ||
790 | |||
791 | if (!orig) { | ||
792 | rc = -ENOENT; | ||
793 | avc_node_kill(node); | ||
794 | goto out_unlock; | ||
795 | } | ||
796 | |||
797 | /* | ||
798 | * Copy and replace original node. | ||
799 | */ | ||
800 | |||
801 | avc_node_populate(node, ssid, tsid, tclass, &orig->ae); | ||
802 | |||
803 | switch (event) { | ||
804 | case AVC_CALLBACK_GRANT: | ||
805 | node->ae.avd.allowed |= perms; | ||
806 | break; | ||
807 | case AVC_CALLBACK_TRY_REVOKE: | ||
808 | case AVC_CALLBACK_REVOKE: | ||
809 | node->ae.avd.allowed &= ~perms; | ||
810 | break; | ||
811 | case AVC_CALLBACK_AUDITALLOW_ENABLE: | ||
812 | node->ae.avd.auditallow |= perms; | ||
813 | break; | ||
814 | case AVC_CALLBACK_AUDITALLOW_DISABLE: | ||
815 | node->ae.avd.auditallow &= ~perms; | ||
816 | break; | ||
817 | case AVC_CALLBACK_AUDITDENY_ENABLE: | ||
818 | node->ae.avd.auditdeny |= perms; | ||
819 | break; | ||
820 | case AVC_CALLBACK_AUDITDENY_DISABLE: | ||
821 | node->ae.avd.auditdeny &= ~perms; | ||
822 | break; | ||
823 | } | ||
824 | avc_node_replace(node, orig); | ||
825 | out_unlock: | ||
826 | spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag); | ||
827 | out: | ||
828 | return rc; | ||
829 | } | ||
830 | |||
831 | /** | ||
832 | * avc_ss_reset - Flush the cache and revalidate migrated permissions. | ||
833 | * @seqno: policy sequence number | ||
834 | */ | ||
835 | int avc_ss_reset(u32 seqno) | ||
836 | { | ||
837 | struct avc_callback_node *c; | ||
838 | int i, rc = 0; | ||
839 | unsigned long flag; | ||
840 | struct avc_node *node; | ||
841 | |||
842 | for (i = 0; i < AVC_CACHE_SLOTS; i++) { | ||
843 | spin_lock_irqsave(&avc_cache.slots_lock[i], flag); | ||
844 | list_for_each_entry(node, &avc_cache.slots[i], list) | ||
845 | avc_node_delete(node); | ||
846 | spin_unlock_irqrestore(&avc_cache.slots_lock[i], flag); | ||
847 | } | ||
848 | |||
849 | for (c = avc_callbacks; c; c = c->next) { | ||
850 | if (c->events & AVC_CALLBACK_RESET) { | ||
851 | rc = c->callback(AVC_CALLBACK_RESET, | ||
852 | 0, 0, 0, 0, NULL); | ||
853 | if (rc) | ||
854 | goto out; | ||
855 | } | ||
856 | } | ||
857 | |||
858 | avc_latest_notif_update(seqno, 0); | ||
859 | out: | ||
860 | return rc; | ||
861 | } | ||
862 | |||
863 | /** | ||
864 | * avc_has_perm_noaudit - Check permissions but perform no auditing. | ||
865 | * @ssid: source security identifier | ||
866 | * @tsid: target security identifier | ||
867 | * @tclass: target security class | ||
868 | * @requested: requested permissions, interpreted based on @tclass | ||
869 | * @avd: access vector decisions | ||
870 | * | ||
871 | * Check the AVC to determine whether the @requested permissions are granted | ||
872 | * for the SID pair (@ssid, @tsid), interpreting the permissions | ||
873 | * based on @tclass, and call the security server on a cache miss to obtain | ||
874 | * a new decision and add it to the cache. Return a copy of the decisions | ||
875 | * in @avd. Return %0 if all @requested permissions are granted, | ||
876 | * -%EACCES if any permissions are denied, or another -errno upon | ||
877 | * other errors. This function is typically called by avc_has_perm(), | ||
878 | * but may also be called directly to separate permission checking from | ||
879 | * auditing, e.g. in cases where a lock must be held for the check but | ||
880 | * should be released for the auditing. | ||
881 | */ | ||
882 | int avc_has_perm_noaudit(u32 ssid, u32 tsid, | ||
883 | u16 tclass, u32 requested, | ||
884 | struct av_decision *avd) | ||
885 | { | ||
886 | struct avc_node *node; | ||
887 | struct avc_entry entry, *p_ae; | ||
888 | int rc = 0; | ||
889 | u32 denied; | ||
890 | |||
891 | rcu_read_lock(); | ||
892 | |||
893 | node = avc_lookup(ssid, tsid, tclass, requested); | ||
894 | if (!node) { | ||
895 | rcu_read_unlock(); | ||
896 | rc = security_compute_av(ssid,tsid,tclass,requested,&entry.avd); | ||
897 | if (rc) | ||
898 | goto out; | ||
899 | rcu_read_lock(); | ||
900 | node = avc_insert(ssid,tsid,tclass,&entry); | ||
901 | } | ||
902 | |||
903 | p_ae = node ? &node->ae : &entry; | ||
904 | |||
905 | if (avd) | ||
906 | memcpy(avd, &p_ae->avd, sizeof(*avd)); | ||
907 | |||
908 | denied = requested & ~(p_ae->avd.allowed); | ||
909 | |||
910 | if (!requested || denied) { | ||
911 | if (selinux_enforcing) | ||
912 | rc = -EACCES; | ||
913 | else | ||
914 | if (node) | ||
915 | avc_update_node(AVC_CALLBACK_GRANT,requested, | ||
916 | ssid,tsid,tclass); | ||
917 | } | ||
918 | |||
919 | rcu_read_unlock(); | ||
920 | out: | ||
921 | return rc; | ||
922 | } | ||
923 | |||
924 | /** | ||
925 | * avc_has_perm - Check permissions and perform any appropriate auditing. | ||
926 | * @ssid: source security identifier | ||
927 | * @tsid: target security identifier | ||
928 | * @tclass: target security class | ||
929 | * @requested: requested permissions, interpreted based on @tclass | ||
930 | * @auditdata: auxiliary audit data | ||
931 | * | ||
932 | * Check the AVC to determine whether the @requested permissions are granted | ||
933 | * for the SID pair (@ssid, @tsid), interpreting the permissions | ||
934 | * based on @tclass, and call the security server on a cache miss to obtain | ||
935 | * a new decision and add it to the cache. Audit the granting or denial of | ||
936 | * permissions in accordance with the policy. Return %0 if all @requested | ||
937 | * permissions are granted, -%EACCES if any permissions are denied, or | ||
938 | * another -errno upon other errors. | ||
939 | */ | ||
940 | int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, | ||
941 | u32 requested, struct avc_audit_data *auditdata) | ||
942 | { | ||
943 | struct av_decision avd; | ||
944 | int rc; | ||
945 | |||
946 | rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, &avd); | ||
947 | avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata); | ||
948 | return rc; | ||
949 | } | ||
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c new file mode 100644 index 000000000000..8a2cc75b3948 --- /dev/null +++ b/security/selinux/hooks.c | |||
@@ -0,0 +1,4565 @@ | |||
1 | /* | ||
2 | * NSA Security-Enhanced Linux (SELinux) security module | ||
3 | * | ||
4 | * This file contains the SELinux hook function implementations. | ||
5 | * | ||
6 | * Authors: Stephen Smalley, <sds@epoch.ncsc.mil> | ||
7 | * Chris Vance, <cvance@nai.com> | ||
8 | * Wayne Salamon, <wsalamon@nai.com> | ||
9 | * James Morris <jmorris@redhat.com> | ||
10 | * | ||
11 | * Copyright (C) 2001,2002 Networks Associates Technology, Inc. | ||
12 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> | ||
13 | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. | ||
14 | * <dgoeddel@trustedcs.com> | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or modify | ||
17 | * it under the terms of the GNU General Public License version 2, | ||
18 | * as published by the Free Software Foundation. | ||
19 | */ | ||
20 | |||
21 | #include <linux/config.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/ptrace.h> | ||
26 | #include <linux/errno.h> | ||
27 | #include <linux/sched.h> | ||
28 | #include <linux/security.h> | ||
29 | #include <linux/xattr.h> | ||
30 | #include <linux/capability.h> | ||
31 | #include <linux/unistd.h> | ||
32 | #include <linux/mm.h> | ||
33 | #include <linux/mman.h> | ||
34 | #include <linux/slab.h> | ||
35 | #include <linux/pagemap.h> | ||
36 | #include <linux/swap.h> | ||
37 | #include <linux/smp_lock.h> | ||
38 | #include <linux/spinlock.h> | ||
39 | #include <linux/syscalls.h> | ||
40 | #include <linux/file.h> | ||
41 | #include <linux/namei.h> | ||
42 | #include <linux/mount.h> | ||
43 | #include <linux/ext2_fs.h> | ||
44 | #include <linux/proc_fs.h> | ||
45 | #include <linux/kd.h> | ||
46 | #include <linux/netfilter_ipv4.h> | ||
47 | #include <linux/netfilter_ipv6.h> | ||
48 | #include <linux/tty.h> | ||
49 | #include <net/icmp.h> | ||
50 | #include <net/ip.h> /* for sysctl_local_port_range[] */ | ||
51 | #include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */ | ||
52 | #include <asm/uaccess.h> | ||
53 | #include <asm/semaphore.h> | ||
54 | #include <asm/ioctls.h> | ||
55 | #include <linux/bitops.h> | ||
56 | #include <linux/interrupt.h> | ||
57 | #include <linux/netdevice.h> /* for network interface checks */ | ||
58 | #include <linux/netlink.h> | ||
59 | #include <linux/tcp.h> | ||
60 | #include <linux/udp.h> | ||
61 | #include <linux/quota.h> | ||
62 | #include <linux/un.h> /* for Unix socket types */ | ||
63 | #include <net/af_unix.h> /* for Unix socket types */ | ||
64 | #include <linux/parser.h> | ||
65 | #include <linux/nfs_mount.h> | ||
66 | #include <net/ipv6.h> | ||
67 | #include <linux/hugetlb.h> | ||
68 | #include <linux/personality.h> | ||
69 | #include <linux/sysctl.h> | ||
70 | #include <linux/audit.h> | ||
71 | |||
72 | #include "avc.h" | ||
73 | #include "objsec.h" | ||
74 | #include "netif.h" | ||
75 | |||
76 | #define XATTR_SELINUX_SUFFIX "selinux" | ||
77 | #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX | ||
78 | |||
79 | extern unsigned int policydb_loaded_version; | ||
80 | extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); | ||
81 | |||
82 | #ifdef CONFIG_SECURITY_SELINUX_DEVELOP | ||
83 | int selinux_enforcing = 0; | ||
84 | |||
85 | static int __init enforcing_setup(char *str) | ||
86 | { | ||
87 | selinux_enforcing = simple_strtol(str,NULL,0); | ||
88 | return 1; | ||
89 | } | ||
90 | __setup("enforcing=", enforcing_setup); | ||
91 | #endif | ||
92 | |||
93 | #ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM | ||
94 | int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE; | ||
95 | |||
96 | static int __init selinux_enabled_setup(char *str) | ||
97 | { | ||
98 | selinux_enabled = simple_strtol(str, NULL, 0); | ||
99 | return 1; | ||
100 | } | ||
101 | __setup("selinux=", selinux_enabled_setup); | ||
102 | #endif | ||
103 | |||
104 | /* Original (dummy) security module. */ | ||
105 | static struct security_operations *original_ops = NULL; | ||
106 | |||
107 | /* Minimal support for a secondary security module, | ||
108 | just to allow the use of the dummy or capability modules. | ||
109 | The owlsm module can alternatively be used as a secondary | ||
110 | module as long as CONFIG_OWLSM_FD is not enabled. */ | ||
111 | static struct security_operations *secondary_ops = NULL; | ||
112 | |||
113 | /* Lists of inode and superblock security structures initialized | ||
114 | before the policy was loaded. */ | ||
115 | static LIST_HEAD(superblock_security_head); | ||
116 | static DEFINE_SPINLOCK(sb_security_lock); | ||
117 | |||
118 | /* Allocate and free functions for each kind of security blob. */ | ||
119 | |||
120 | static int task_alloc_security(struct task_struct *task) | ||
121 | { | ||
122 | struct task_security_struct *tsec; | ||
123 | |||
124 | tsec = kmalloc(sizeof(struct task_security_struct), GFP_KERNEL); | ||
125 | if (!tsec) | ||
126 | return -ENOMEM; | ||
127 | |||
128 | memset(tsec, 0, sizeof(struct task_security_struct)); | ||
129 | tsec->magic = SELINUX_MAGIC; | ||
130 | tsec->task = task; | ||
131 | tsec->osid = tsec->sid = tsec->ptrace_sid = SECINITSID_UNLABELED; | ||
132 | task->security = tsec; | ||
133 | |||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static void task_free_security(struct task_struct *task) | ||
138 | { | ||
139 | struct task_security_struct *tsec = task->security; | ||
140 | |||
141 | if (!tsec || tsec->magic != SELINUX_MAGIC) | ||
142 | return; | ||
143 | |||
144 | task->security = NULL; | ||
145 | kfree(tsec); | ||
146 | } | ||
147 | |||
148 | static int inode_alloc_security(struct inode *inode) | ||
149 | { | ||
150 | struct task_security_struct *tsec = current->security; | ||
151 | struct inode_security_struct *isec; | ||
152 | |||
153 | isec = kmalloc(sizeof(struct inode_security_struct), GFP_KERNEL); | ||
154 | if (!isec) | ||
155 | return -ENOMEM; | ||
156 | |||
157 | memset(isec, 0, sizeof(struct inode_security_struct)); | ||
158 | init_MUTEX(&isec->sem); | ||
159 | INIT_LIST_HEAD(&isec->list); | ||
160 | isec->magic = SELINUX_MAGIC; | ||
161 | isec->inode = inode; | ||
162 | isec->sid = SECINITSID_UNLABELED; | ||
163 | isec->sclass = SECCLASS_FILE; | ||
164 | if (tsec && tsec->magic == SELINUX_MAGIC) | ||
165 | isec->task_sid = tsec->sid; | ||
166 | else | ||
167 | isec->task_sid = SECINITSID_UNLABELED; | ||
168 | inode->i_security = isec; | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static void inode_free_security(struct inode *inode) | ||
174 | { | ||
175 | struct inode_security_struct *isec = inode->i_security; | ||
176 | struct superblock_security_struct *sbsec = inode->i_sb->s_security; | ||
177 | |||
178 | if (!isec || isec->magic != SELINUX_MAGIC) | ||
179 | return; | ||
180 | |||
181 | spin_lock(&sbsec->isec_lock); | ||
182 | if (!list_empty(&isec->list)) | ||
183 | list_del_init(&isec->list); | ||
184 | spin_unlock(&sbsec->isec_lock); | ||
185 | |||
186 | inode->i_security = NULL; | ||
187 | kfree(isec); | ||
188 | } | ||
189 | |||
190 | static int file_alloc_security(struct file *file) | ||
191 | { | ||
192 | struct task_security_struct *tsec = current->security; | ||
193 | struct file_security_struct *fsec; | ||
194 | |||
195 | fsec = kmalloc(sizeof(struct file_security_struct), GFP_ATOMIC); | ||
196 | if (!fsec) | ||
197 | return -ENOMEM; | ||
198 | |||
199 | memset(fsec, 0, sizeof(struct file_security_struct)); | ||
200 | fsec->magic = SELINUX_MAGIC; | ||
201 | fsec->file = file; | ||
202 | if (tsec && tsec->magic == SELINUX_MAGIC) { | ||
203 | fsec->sid = tsec->sid; | ||
204 | fsec->fown_sid = tsec->sid; | ||
205 | } else { | ||
206 | fsec->sid = SECINITSID_UNLABELED; | ||
207 | fsec->fown_sid = SECINITSID_UNLABELED; | ||
208 | } | ||
209 | file->f_security = fsec; | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static void file_free_security(struct file *file) | ||
215 | { | ||
216 | struct file_security_struct *fsec = file->f_security; | ||
217 | |||
218 | if (!fsec || fsec->magic != SELINUX_MAGIC) | ||
219 | return; | ||
220 | |||
221 | file->f_security = NULL; | ||
222 | kfree(fsec); | ||
223 | } | ||
224 | |||
225 | static int superblock_alloc_security(struct super_block *sb) | ||
226 | { | ||
227 | struct superblock_security_struct *sbsec; | ||
228 | |||
229 | sbsec = kmalloc(sizeof(struct superblock_security_struct), GFP_KERNEL); | ||
230 | if (!sbsec) | ||
231 | return -ENOMEM; | ||
232 | |||
233 | memset(sbsec, 0, sizeof(struct superblock_security_struct)); | ||
234 | init_MUTEX(&sbsec->sem); | ||
235 | INIT_LIST_HEAD(&sbsec->list); | ||
236 | INIT_LIST_HEAD(&sbsec->isec_head); | ||
237 | spin_lock_init(&sbsec->isec_lock); | ||
238 | sbsec->magic = SELINUX_MAGIC; | ||
239 | sbsec->sb = sb; | ||
240 | sbsec->sid = SECINITSID_UNLABELED; | ||
241 | sbsec->def_sid = SECINITSID_FILE; | ||
242 | sb->s_security = sbsec; | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static void superblock_free_security(struct super_block *sb) | ||
248 | { | ||
249 | struct superblock_security_struct *sbsec = sb->s_security; | ||
250 | |||
251 | if (!sbsec || sbsec->magic != SELINUX_MAGIC) | ||
252 | return; | ||
253 | |||
254 | spin_lock(&sb_security_lock); | ||
255 | if (!list_empty(&sbsec->list)) | ||
256 | list_del_init(&sbsec->list); | ||
257 | spin_unlock(&sb_security_lock); | ||
258 | |||
259 | sb->s_security = NULL; | ||
260 | kfree(sbsec); | ||
261 | } | ||
262 | |||
263 | #ifdef CONFIG_SECURITY_NETWORK | ||
264 | static int sk_alloc_security(struct sock *sk, int family, int priority) | ||
265 | { | ||
266 | struct sk_security_struct *ssec; | ||
267 | |||
268 | if (family != PF_UNIX) | ||
269 | return 0; | ||
270 | |||
271 | ssec = kmalloc(sizeof(*ssec), priority); | ||
272 | if (!ssec) | ||
273 | return -ENOMEM; | ||
274 | |||
275 | memset(ssec, 0, sizeof(*ssec)); | ||
276 | ssec->magic = SELINUX_MAGIC; | ||
277 | ssec->sk = sk; | ||
278 | ssec->peer_sid = SECINITSID_UNLABELED; | ||
279 | sk->sk_security = ssec; | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static void sk_free_security(struct sock *sk) | ||
285 | { | ||
286 | struct sk_security_struct *ssec = sk->sk_security; | ||
287 | |||
288 | if (sk->sk_family != PF_UNIX || ssec->magic != SELINUX_MAGIC) | ||
289 | return; | ||
290 | |||
291 | sk->sk_security = NULL; | ||
292 | kfree(ssec); | ||
293 | } | ||
294 | #endif /* CONFIG_SECURITY_NETWORK */ | ||
295 | |||
296 | /* The security server must be initialized before | ||
297 | any labeling or access decisions can be provided. */ | ||
298 | extern int ss_initialized; | ||
299 | |||
300 | /* The file system's label must be initialized prior to use. */ | ||
301 | |||
302 | static char *labeling_behaviors[6] = { | ||
303 | "uses xattr", | ||
304 | "uses transition SIDs", | ||
305 | "uses task SIDs", | ||
306 | "uses genfs_contexts", | ||
307 | "not configured for labeling", | ||
308 | "uses mountpoint labeling", | ||
309 | }; | ||
310 | |||
311 | static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry); | ||
312 | |||
313 | static inline int inode_doinit(struct inode *inode) | ||
314 | { | ||
315 | return inode_doinit_with_dentry(inode, NULL); | ||
316 | } | ||
317 | |||
318 | enum { | ||
319 | Opt_context = 1, | ||
320 | Opt_fscontext = 2, | ||
321 | Opt_defcontext = 4, | ||
322 | }; | ||
323 | |||
324 | static match_table_t tokens = { | ||
325 | {Opt_context, "context=%s"}, | ||
326 | {Opt_fscontext, "fscontext=%s"}, | ||
327 | {Opt_defcontext, "defcontext=%s"}, | ||
328 | }; | ||
329 | |||
330 | #define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n" | ||
331 | |||
332 | static int try_context_mount(struct super_block *sb, void *data) | ||
333 | { | ||
334 | char *context = NULL, *defcontext = NULL; | ||
335 | const char *name; | ||
336 | u32 sid; | ||
337 | int alloc = 0, rc = 0, seen = 0; | ||
338 | struct task_security_struct *tsec = current->security; | ||
339 | struct superblock_security_struct *sbsec = sb->s_security; | ||
340 | |||
341 | if (!data) | ||
342 | goto out; | ||
343 | |||
344 | name = sb->s_type->name; | ||
345 | |||
346 | if (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) { | ||
347 | |||
348 | /* NFS we understand. */ | ||
349 | if (!strcmp(name, "nfs")) { | ||
350 | struct nfs_mount_data *d = data; | ||
351 | |||
352 | if (d->version < NFS_MOUNT_VERSION) | ||
353 | goto out; | ||
354 | |||
355 | if (d->context[0]) { | ||
356 | context = d->context; | ||
357 | seen |= Opt_context; | ||
358 | } | ||
359 | } else | ||
360 | goto out; | ||
361 | |||
362 | } else { | ||
363 | /* Standard string-based options. */ | ||
364 | char *p, *options = data; | ||
365 | |||
366 | while ((p = strsep(&options, ",")) != NULL) { | ||
367 | int token; | ||
368 | substring_t args[MAX_OPT_ARGS]; | ||
369 | |||
370 | if (!*p) | ||
371 | continue; | ||
372 | |||
373 | token = match_token(p, tokens, args); | ||
374 | |||
375 | switch (token) { | ||
376 | case Opt_context: | ||
377 | if (seen) { | ||
378 | rc = -EINVAL; | ||
379 | printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); | ||
380 | goto out_free; | ||
381 | } | ||
382 | context = match_strdup(&args[0]); | ||
383 | if (!context) { | ||
384 | rc = -ENOMEM; | ||
385 | goto out_free; | ||
386 | } | ||
387 | if (!alloc) | ||
388 | alloc = 1; | ||
389 | seen |= Opt_context; | ||
390 | break; | ||
391 | |||
392 | case Opt_fscontext: | ||
393 | if (seen & (Opt_context|Opt_fscontext)) { | ||
394 | rc = -EINVAL; | ||
395 | printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); | ||
396 | goto out_free; | ||
397 | } | ||
398 | context = match_strdup(&args[0]); | ||
399 | if (!context) { | ||
400 | rc = -ENOMEM; | ||
401 | goto out_free; | ||
402 | } | ||
403 | if (!alloc) | ||
404 | alloc = 1; | ||
405 | seen |= Opt_fscontext; | ||
406 | break; | ||
407 | |||
408 | case Opt_defcontext: | ||
409 | if (sbsec->behavior != SECURITY_FS_USE_XATTR) { | ||
410 | rc = -EINVAL; | ||
411 | printk(KERN_WARNING "SELinux: " | ||
412 | "defcontext option is invalid " | ||
413 | "for this filesystem type\n"); | ||
414 | goto out_free; | ||
415 | } | ||
416 | if (seen & (Opt_context|Opt_defcontext)) { | ||
417 | rc = -EINVAL; | ||
418 | printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); | ||
419 | goto out_free; | ||
420 | } | ||
421 | defcontext = match_strdup(&args[0]); | ||
422 | if (!defcontext) { | ||
423 | rc = -ENOMEM; | ||
424 | goto out_free; | ||
425 | } | ||
426 | if (!alloc) | ||
427 | alloc = 1; | ||
428 | seen |= Opt_defcontext; | ||
429 | break; | ||
430 | |||
431 | default: | ||
432 | rc = -EINVAL; | ||
433 | printk(KERN_WARNING "SELinux: unknown mount " | ||
434 | "option\n"); | ||
435 | goto out_free; | ||
436 | |||
437 | } | ||
438 | } | ||
439 | } | ||
440 | |||
441 | if (!seen) | ||
442 | goto out; | ||
443 | |||
444 | if (context) { | ||
445 | rc = security_context_to_sid(context, strlen(context), &sid); | ||
446 | if (rc) { | ||
447 | printk(KERN_WARNING "SELinux: security_context_to_sid" | ||
448 | "(%s) failed for (dev %s, type %s) errno=%d\n", | ||
449 | context, sb->s_id, name, rc); | ||
450 | goto out_free; | ||
451 | } | ||
452 | |||
453 | rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, | ||
454 | FILESYSTEM__RELABELFROM, NULL); | ||
455 | if (rc) | ||
456 | goto out_free; | ||
457 | |||
458 | rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, | ||
459 | FILESYSTEM__RELABELTO, NULL); | ||
460 | if (rc) | ||
461 | goto out_free; | ||
462 | |||
463 | sbsec->sid = sid; | ||
464 | |||
465 | if (seen & Opt_context) | ||
466 | sbsec->behavior = SECURITY_FS_USE_MNTPOINT; | ||
467 | } | ||
468 | |||
469 | if (defcontext) { | ||
470 | rc = security_context_to_sid(defcontext, strlen(defcontext), &sid); | ||
471 | if (rc) { | ||
472 | printk(KERN_WARNING "SELinux: security_context_to_sid" | ||
473 | "(%s) failed for (dev %s, type %s) errno=%d\n", | ||
474 | defcontext, sb->s_id, name, rc); | ||
475 | goto out_free; | ||
476 | } | ||
477 | |||
478 | if (sid == sbsec->def_sid) | ||
479 | goto out_free; | ||
480 | |||
481 | rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, | ||
482 | FILESYSTEM__RELABELFROM, NULL); | ||
483 | if (rc) | ||
484 | goto out_free; | ||
485 | |||
486 | rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, | ||
487 | FILESYSTEM__ASSOCIATE, NULL); | ||
488 | if (rc) | ||
489 | goto out_free; | ||
490 | |||
491 | sbsec->def_sid = sid; | ||
492 | } | ||
493 | |||
494 | out_free: | ||
495 | if (alloc) { | ||
496 | kfree(context); | ||
497 | kfree(defcontext); | ||
498 | } | ||
499 | out: | ||
500 | return rc; | ||
501 | } | ||
502 | |||
503 | static int superblock_doinit(struct super_block *sb, void *data) | ||
504 | { | ||
505 | struct superblock_security_struct *sbsec = sb->s_security; | ||
506 | struct dentry *root = sb->s_root; | ||
507 | struct inode *inode = root->d_inode; | ||
508 | int rc = 0; | ||
509 | |||
510 | down(&sbsec->sem); | ||
511 | if (sbsec->initialized) | ||
512 | goto out; | ||
513 | |||
514 | if (!ss_initialized) { | ||
515 | /* Defer initialization until selinux_complete_init, | ||
516 | after the initial policy is loaded and the security | ||
517 | server is ready to handle calls. */ | ||
518 | spin_lock(&sb_security_lock); | ||
519 | if (list_empty(&sbsec->list)) | ||
520 | list_add(&sbsec->list, &superblock_security_head); | ||
521 | spin_unlock(&sb_security_lock); | ||
522 | goto out; | ||
523 | } | ||
524 | |||
525 | /* Determine the labeling behavior to use for this filesystem type. */ | ||
526 | rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid); | ||
527 | if (rc) { | ||
528 | printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n", | ||
529 | __FUNCTION__, sb->s_type->name, rc); | ||
530 | goto out; | ||
531 | } | ||
532 | |||
533 | rc = try_context_mount(sb, data); | ||
534 | if (rc) | ||
535 | goto out; | ||
536 | |||
537 | if (sbsec->behavior == SECURITY_FS_USE_XATTR) { | ||
538 | /* Make sure that the xattr handler exists and that no | ||
539 | error other than -ENODATA is returned by getxattr on | ||
540 | the root directory. -ENODATA is ok, as this may be | ||
541 | the first boot of the SELinux kernel before we have | ||
542 | assigned xattr values to the filesystem. */ | ||
543 | if (!inode->i_op->getxattr) { | ||
544 | printk(KERN_WARNING "SELinux: (dev %s, type %s) has no " | ||
545 | "xattr support\n", sb->s_id, sb->s_type->name); | ||
546 | rc = -EOPNOTSUPP; | ||
547 | goto out; | ||
548 | } | ||
549 | rc = inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0); | ||
550 | if (rc < 0 && rc != -ENODATA) { | ||
551 | if (rc == -EOPNOTSUPP) | ||
552 | printk(KERN_WARNING "SELinux: (dev %s, type " | ||
553 | "%s) has no security xattr handler\n", | ||
554 | sb->s_id, sb->s_type->name); | ||
555 | else | ||
556 | printk(KERN_WARNING "SELinux: (dev %s, type " | ||
557 | "%s) getxattr errno %d\n", sb->s_id, | ||
558 | sb->s_type->name, -rc); | ||
559 | goto out; | ||
560 | } | ||
561 | } | ||
562 | |||
563 | if (strcmp(sb->s_type->name, "proc") == 0) | ||
564 | sbsec->proc = 1; | ||
565 | |||
566 | sbsec->initialized = 1; | ||
567 | |||
568 | if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors)) { | ||
569 | printk(KERN_INFO "SELinux: initialized (dev %s, type %s), unknown behavior\n", | ||
570 | sb->s_id, sb->s_type->name); | ||
571 | } | ||
572 | else { | ||
573 | printk(KERN_INFO "SELinux: initialized (dev %s, type %s), %s\n", | ||
574 | sb->s_id, sb->s_type->name, | ||
575 | labeling_behaviors[sbsec->behavior-1]); | ||
576 | } | ||
577 | |||
578 | /* Initialize the root inode. */ | ||
579 | rc = inode_doinit_with_dentry(sb->s_root->d_inode, sb->s_root); | ||
580 | |||
581 | /* Initialize any other inodes associated with the superblock, e.g. | ||
582 | inodes created prior to initial policy load or inodes created | ||
583 | during get_sb by a pseudo filesystem that directly | ||
584 | populates itself. */ | ||
585 | spin_lock(&sbsec->isec_lock); | ||
586 | next_inode: | ||
587 | if (!list_empty(&sbsec->isec_head)) { | ||
588 | struct inode_security_struct *isec = | ||
589 | list_entry(sbsec->isec_head.next, | ||
590 | struct inode_security_struct, list); | ||
591 | struct inode *inode = isec->inode; | ||
592 | spin_unlock(&sbsec->isec_lock); | ||
593 | inode = igrab(inode); | ||
594 | if (inode) { | ||
595 | if (!IS_PRIVATE (inode)) | ||
596 | inode_doinit(inode); | ||
597 | iput(inode); | ||
598 | } | ||
599 | spin_lock(&sbsec->isec_lock); | ||
600 | list_del_init(&isec->list); | ||
601 | goto next_inode; | ||
602 | } | ||
603 | spin_unlock(&sbsec->isec_lock); | ||
604 | out: | ||
605 | up(&sbsec->sem); | ||
606 | return rc; | ||
607 | } | ||
608 | |||
609 | static inline u16 inode_mode_to_security_class(umode_t mode) | ||
610 | { | ||
611 | switch (mode & S_IFMT) { | ||
612 | case S_IFSOCK: | ||
613 | return SECCLASS_SOCK_FILE; | ||
614 | case S_IFLNK: | ||
615 | return SECCLASS_LNK_FILE; | ||
616 | case S_IFREG: | ||
617 | return SECCLASS_FILE; | ||
618 | case S_IFBLK: | ||
619 | return SECCLASS_BLK_FILE; | ||
620 | case S_IFDIR: | ||
621 | return SECCLASS_DIR; | ||
622 | case S_IFCHR: | ||
623 | return SECCLASS_CHR_FILE; | ||
624 | case S_IFIFO: | ||
625 | return SECCLASS_FIFO_FILE; | ||
626 | |||
627 | } | ||
628 | |||
629 | return SECCLASS_FILE; | ||
630 | } | ||
631 | |||
632 | static inline u16 socket_type_to_security_class(int family, int type, int protocol) | ||
633 | { | ||
634 | switch (family) { | ||
635 | case PF_UNIX: | ||
636 | switch (type) { | ||
637 | case SOCK_STREAM: | ||
638 | case SOCK_SEQPACKET: | ||
639 | return SECCLASS_UNIX_STREAM_SOCKET; | ||
640 | case SOCK_DGRAM: | ||
641 | return SECCLASS_UNIX_DGRAM_SOCKET; | ||
642 | } | ||
643 | break; | ||
644 | case PF_INET: | ||
645 | case PF_INET6: | ||
646 | switch (type) { | ||
647 | case SOCK_STREAM: | ||
648 | return SECCLASS_TCP_SOCKET; | ||
649 | case SOCK_DGRAM: | ||
650 | return SECCLASS_UDP_SOCKET; | ||
651 | case SOCK_RAW: | ||
652 | return SECCLASS_RAWIP_SOCKET; | ||
653 | } | ||
654 | break; | ||
655 | case PF_NETLINK: | ||
656 | switch (protocol) { | ||
657 | case NETLINK_ROUTE: | ||
658 | return SECCLASS_NETLINK_ROUTE_SOCKET; | ||
659 | case NETLINK_FIREWALL: | ||
660 | return SECCLASS_NETLINK_FIREWALL_SOCKET; | ||
661 | case NETLINK_TCPDIAG: | ||
662 | return SECCLASS_NETLINK_TCPDIAG_SOCKET; | ||
663 | case NETLINK_NFLOG: | ||
664 | return SECCLASS_NETLINK_NFLOG_SOCKET; | ||
665 | case NETLINK_XFRM: | ||
666 | return SECCLASS_NETLINK_XFRM_SOCKET; | ||
667 | case NETLINK_SELINUX: | ||
668 | return SECCLASS_NETLINK_SELINUX_SOCKET; | ||
669 | case NETLINK_AUDIT: | ||
670 | return SECCLASS_NETLINK_AUDIT_SOCKET; | ||
671 | case NETLINK_IP6_FW: | ||
672 | return SECCLASS_NETLINK_IP6FW_SOCKET; | ||
673 | case NETLINK_DNRTMSG: | ||
674 | return SECCLASS_NETLINK_DNRT_SOCKET; | ||
675 | default: | ||
676 | return SECCLASS_NETLINK_SOCKET; | ||
677 | } | ||
678 | case PF_PACKET: | ||
679 | return SECCLASS_PACKET_SOCKET; | ||
680 | case PF_KEY: | ||
681 | return SECCLASS_KEY_SOCKET; | ||
682 | } | ||
683 | |||
684 | return SECCLASS_SOCKET; | ||
685 | } | ||
686 | |||
687 | #ifdef CONFIG_PROC_FS | ||
688 | static int selinux_proc_get_sid(struct proc_dir_entry *de, | ||
689 | u16 tclass, | ||
690 | u32 *sid) | ||
691 | { | ||
692 | int buflen, rc; | ||
693 | char *buffer, *path, *end; | ||
694 | |||
695 | buffer = (char*)__get_free_page(GFP_KERNEL); | ||
696 | if (!buffer) | ||
697 | return -ENOMEM; | ||
698 | |||
699 | buflen = PAGE_SIZE; | ||
700 | end = buffer+buflen; | ||
701 | *--end = '\0'; | ||
702 | buflen--; | ||
703 | path = end-1; | ||
704 | *path = '/'; | ||
705 | while (de && de != de->parent) { | ||
706 | buflen -= de->namelen + 1; | ||
707 | if (buflen < 0) | ||
708 | break; | ||
709 | end -= de->namelen; | ||
710 | memcpy(end, de->name, de->namelen); | ||
711 | *--end = '/'; | ||
712 | path = end; | ||
713 | de = de->parent; | ||
714 | } | ||
715 | rc = security_genfs_sid("proc", path, tclass, sid); | ||
716 | free_page((unsigned long)buffer); | ||
717 | return rc; | ||
718 | } | ||
719 | #else | ||
720 | static int selinux_proc_get_sid(struct proc_dir_entry *de, | ||
721 | u16 tclass, | ||
722 | u32 *sid) | ||
723 | { | ||
724 | return -EINVAL; | ||
725 | } | ||
726 | #endif | ||
727 | |||
728 | /* The inode's security attributes must be initialized before first use. */ | ||
729 | static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry) | ||
730 | { | ||
731 | struct superblock_security_struct *sbsec = NULL; | ||
732 | struct inode_security_struct *isec = inode->i_security; | ||
733 | u32 sid; | ||
734 | struct dentry *dentry; | ||
735 | #define INITCONTEXTLEN 255 | ||
736 | char *context = NULL; | ||
737 | unsigned len = 0; | ||
738 | int rc = 0; | ||
739 | int hold_sem = 0; | ||
740 | |||
741 | if (isec->initialized) | ||
742 | goto out; | ||
743 | |||
744 | down(&isec->sem); | ||
745 | hold_sem = 1; | ||
746 | if (isec->initialized) | ||
747 | goto out; | ||
748 | |||
749 | sbsec = inode->i_sb->s_security; | ||
750 | if (!sbsec->initialized) { | ||
751 | /* Defer initialization until selinux_complete_init, | ||
752 | after the initial policy is loaded and the security | ||
753 | server is ready to handle calls. */ | ||
754 | spin_lock(&sbsec->isec_lock); | ||
755 | if (list_empty(&isec->list)) | ||
756 | list_add(&isec->list, &sbsec->isec_head); | ||
757 | spin_unlock(&sbsec->isec_lock); | ||
758 | goto out; | ||
759 | } | ||
760 | |||
761 | switch (sbsec->behavior) { | ||
762 | case SECURITY_FS_USE_XATTR: | ||
763 | if (!inode->i_op->getxattr) { | ||
764 | isec->sid = sbsec->def_sid; | ||
765 | break; | ||
766 | } | ||
767 | |||
768 | /* Need a dentry, since the xattr API requires one. | ||
769 | Life would be simpler if we could just pass the inode. */ | ||
770 | if (opt_dentry) { | ||
771 | /* Called from d_instantiate or d_splice_alias. */ | ||
772 | dentry = dget(opt_dentry); | ||
773 | } else { | ||
774 | /* Called from selinux_complete_init, try to find a dentry. */ | ||
775 | dentry = d_find_alias(inode); | ||
776 | } | ||
777 | if (!dentry) { | ||
778 | printk(KERN_WARNING "%s: no dentry for dev=%s " | ||
779 | "ino=%ld\n", __FUNCTION__, inode->i_sb->s_id, | ||
780 | inode->i_ino); | ||
781 | goto out; | ||
782 | } | ||
783 | |||
784 | len = INITCONTEXTLEN; | ||
785 | context = kmalloc(len, GFP_KERNEL); | ||
786 | if (!context) { | ||
787 | rc = -ENOMEM; | ||
788 | dput(dentry); | ||
789 | goto out; | ||
790 | } | ||
791 | rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX, | ||
792 | context, len); | ||
793 | if (rc == -ERANGE) { | ||
794 | /* Need a larger buffer. Query for the right size. */ | ||
795 | rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX, | ||
796 | NULL, 0); | ||
797 | if (rc < 0) { | ||
798 | dput(dentry); | ||
799 | goto out; | ||
800 | } | ||
801 | kfree(context); | ||
802 | len = rc; | ||
803 | context = kmalloc(len, GFP_KERNEL); | ||
804 | if (!context) { | ||
805 | rc = -ENOMEM; | ||
806 | dput(dentry); | ||
807 | goto out; | ||
808 | } | ||
809 | rc = inode->i_op->getxattr(dentry, | ||
810 | XATTR_NAME_SELINUX, | ||
811 | context, len); | ||
812 | } | ||
813 | dput(dentry); | ||
814 | if (rc < 0) { | ||
815 | if (rc != -ENODATA) { | ||
816 | printk(KERN_WARNING "%s: getxattr returned " | ||
817 | "%d for dev=%s ino=%ld\n", __FUNCTION__, | ||
818 | -rc, inode->i_sb->s_id, inode->i_ino); | ||
819 | kfree(context); | ||
820 | goto out; | ||
821 | } | ||
822 | /* Map ENODATA to the default file SID */ | ||
823 | sid = sbsec->def_sid; | ||
824 | rc = 0; | ||
825 | } else { | ||
826 | rc = security_context_to_sid(context, rc, &sid); | ||
827 | if (rc) { | ||
828 | printk(KERN_WARNING "%s: context_to_sid(%s) " | ||
829 | "returned %d for dev=%s ino=%ld\n", | ||
830 | __FUNCTION__, context, -rc, | ||
831 | inode->i_sb->s_id, inode->i_ino); | ||
832 | kfree(context); | ||
833 | /* Leave with the unlabeled SID */ | ||
834 | rc = 0; | ||
835 | break; | ||
836 | } | ||
837 | } | ||
838 | kfree(context); | ||
839 | isec->sid = sid; | ||
840 | break; | ||
841 | case SECURITY_FS_USE_TASK: | ||
842 | isec->sid = isec->task_sid; | ||
843 | break; | ||
844 | case SECURITY_FS_USE_TRANS: | ||
845 | /* Default to the fs SID. */ | ||
846 | isec->sid = sbsec->sid; | ||
847 | |||
848 | /* Try to obtain a transition SID. */ | ||
849 | isec->sclass = inode_mode_to_security_class(inode->i_mode); | ||
850 | rc = security_transition_sid(isec->task_sid, | ||
851 | sbsec->sid, | ||
852 | isec->sclass, | ||
853 | &sid); | ||
854 | if (rc) | ||
855 | goto out; | ||
856 | isec->sid = sid; | ||
857 | break; | ||
858 | default: | ||
859 | /* Default to the fs SID. */ | ||
860 | isec->sid = sbsec->sid; | ||
861 | |||
862 | if (sbsec->proc) { | ||
863 | struct proc_inode *proci = PROC_I(inode); | ||
864 | if (proci->pde) { | ||
865 | isec->sclass = inode_mode_to_security_class(inode->i_mode); | ||
866 | rc = selinux_proc_get_sid(proci->pde, | ||
867 | isec->sclass, | ||
868 | &sid); | ||
869 | if (rc) | ||
870 | goto out; | ||
871 | isec->sid = sid; | ||
872 | } | ||
873 | } | ||
874 | break; | ||
875 | } | ||
876 | |||
877 | isec->initialized = 1; | ||
878 | |||
879 | out: | ||
880 | if (isec->sclass == SECCLASS_FILE) | ||
881 | isec->sclass = inode_mode_to_security_class(inode->i_mode); | ||
882 | |||
883 | if (hold_sem) | ||
884 | up(&isec->sem); | ||
885 | return rc; | ||
886 | } | ||
887 | |||
888 | /* Convert a Linux signal to an access vector. */ | ||
889 | static inline u32 signal_to_av(int sig) | ||
890 | { | ||
891 | u32 perm = 0; | ||
892 | |||
893 | switch (sig) { | ||
894 | case SIGCHLD: | ||
895 | /* Commonly granted from child to parent. */ | ||
896 | perm = PROCESS__SIGCHLD; | ||
897 | break; | ||
898 | case SIGKILL: | ||
899 | /* Cannot be caught or ignored */ | ||
900 | perm = PROCESS__SIGKILL; | ||
901 | break; | ||
902 | case SIGSTOP: | ||
903 | /* Cannot be caught or ignored */ | ||
904 | perm = PROCESS__SIGSTOP; | ||
905 | break; | ||
906 | default: | ||
907 | /* All other signals. */ | ||
908 | perm = PROCESS__SIGNAL; | ||
909 | break; | ||
910 | } | ||
911 | |||
912 | return perm; | ||
913 | } | ||
914 | |||
915 | /* Check permission betweeen a pair of tasks, e.g. signal checks, | ||
916 | fork check, ptrace check, etc. */ | ||
917 | static int task_has_perm(struct task_struct *tsk1, | ||
918 | struct task_struct *tsk2, | ||
919 | u32 perms) | ||
920 | { | ||
921 | struct task_security_struct *tsec1, *tsec2; | ||
922 | |||
923 | tsec1 = tsk1->security; | ||
924 | tsec2 = tsk2->security; | ||
925 | return avc_has_perm(tsec1->sid, tsec2->sid, | ||
926 | SECCLASS_PROCESS, perms, NULL); | ||
927 | } | ||
928 | |||
929 | /* Check whether a task is allowed to use a capability. */ | ||
930 | static int task_has_capability(struct task_struct *tsk, | ||
931 | int cap) | ||
932 | { | ||
933 | struct task_security_struct *tsec; | ||
934 | struct avc_audit_data ad; | ||
935 | |||
936 | tsec = tsk->security; | ||
937 | |||
938 | AVC_AUDIT_DATA_INIT(&ad,CAP); | ||
939 | ad.tsk = tsk; | ||
940 | ad.u.cap = cap; | ||
941 | |||
942 | return avc_has_perm(tsec->sid, tsec->sid, | ||
943 | SECCLASS_CAPABILITY, CAP_TO_MASK(cap), &ad); | ||
944 | } | ||
945 | |||
946 | /* Check whether a task is allowed to use a system operation. */ | ||
947 | static int task_has_system(struct task_struct *tsk, | ||
948 | u32 perms) | ||
949 | { | ||
950 | struct task_security_struct *tsec; | ||
951 | |||
952 | tsec = tsk->security; | ||
953 | |||
954 | return avc_has_perm(tsec->sid, SECINITSID_KERNEL, | ||
955 | SECCLASS_SYSTEM, perms, NULL); | ||
956 | } | ||
957 | |||
958 | /* Check whether a task has a particular permission to an inode. | ||
959 | The 'adp' parameter is optional and allows other audit | ||
960 | data to be passed (e.g. the dentry). */ | ||
961 | static int inode_has_perm(struct task_struct *tsk, | ||
962 | struct inode *inode, | ||
963 | u32 perms, | ||
964 | struct avc_audit_data *adp) | ||
965 | { | ||
966 | struct task_security_struct *tsec; | ||
967 | struct inode_security_struct *isec; | ||
968 | struct avc_audit_data ad; | ||
969 | |||
970 | tsec = tsk->security; | ||
971 | isec = inode->i_security; | ||
972 | |||
973 | if (!adp) { | ||
974 | adp = &ad; | ||
975 | AVC_AUDIT_DATA_INIT(&ad, FS); | ||
976 | ad.u.fs.inode = inode; | ||
977 | } | ||
978 | |||
979 | return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp); | ||
980 | } | ||
981 | |||
982 | /* Same as inode_has_perm, but pass explicit audit data containing | ||
983 | the dentry to help the auditing code to more easily generate the | ||
984 | pathname if needed. */ | ||
985 | static inline int dentry_has_perm(struct task_struct *tsk, | ||
986 | struct vfsmount *mnt, | ||
987 | struct dentry *dentry, | ||
988 | u32 av) | ||
989 | { | ||
990 | struct inode *inode = dentry->d_inode; | ||
991 | struct avc_audit_data ad; | ||
992 | AVC_AUDIT_DATA_INIT(&ad,FS); | ||
993 | ad.u.fs.mnt = mnt; | ||
994 | ad.u.fs.dentry = dentry; | ||
995 | return inode_has_perm(tsk, inode, av, &ad); | ||
996 | } | ||
997 | |||
998 | /* Check whether a task can use an open file descriptor to | ||
999 | access an inode in a given way. Check access to the | ||
1000 | descriptor itself, and then use dentry_has_perm to | ||
1001 | check a particular permission to the file. | ||
1002 | Access to the descriptor is implicitly granted if it | ||
1003 | has the same SID as the process. If av is zero, then | ||
1004 | access to the file is not checked, e.g. for cases | ||
1005 | where only the descriptor is affected like seek. */ | ||
1006 | static inline int file_has_perm(struct task_struct *tsk, | ||
1007 | struct file *file, | ||
1008 | u32 av) | ||
1009 | { | ||
1010 | struct task_security_struct *tsec = tsk->security; | ||
1011 | struct file_security_struct *fsec = file->f_security; | ||
1012 | struct vfsmount *mnt = file->f_vfsmnt; | ||
1013 | struct dentry *dentry = file->f_dentry; | ||
1014 | struct inode *inode = dentry->d_inode; | ||
1015 | struct avc_audit_data ad; | ||
1016 | int rc; | ||
1017 | |||
1018 | AVC_AUDIT_DATA_INIT(&ad, FS); | ||
1019 | ad.u.fs.mnt = mnt; | ||
1020 | ad.u.fs.dentry = dentry; | ||
1021 | |||
1022 | if (tsec->sid != fsec->sid) { | ||
1023 | rc = avc_has_perm(tsec->sid, fsec->sid, | ||
1024 | SECCLASS_FD, | ||
1025 | FD__USE, | ||
1026 | &ad); | ||
1027 | if (rc) | ||
1028 | return rc; | ||
1029 | } | ||
1030 | |||
1031 | /* av is zero if only checking access to the descriptor. */ | ||
1032 | if (av) | ||
1033 | return inode_has_perm(tsk, inode, av, &ad); | ||
1034 | |||
1035 | return 0; | ||
1036 | } | ||
1037 | |||
1038 | /* Check whether a task can create a file. */ | ||
1039 | static int may_create(struct inode *dir, | ||
1040 | struct dentry *dentry, | ||
1041 | u16 tclass) | ||
1042 | { | ||
1043 | struct task_security_struct *tsec; | ||
1044 | struct inode_security_struct *dsec; | ||
1045 | struct superblock_security_struct *sbsec; | ||
1046 | u32 newsid; | ||
1047 | struct avc_audit_data ad; | ||
1048 | int rc; | ||
1049 | |||
1050 | tsec = current->security; | ||
1051 | dsec = dir->i_security; | ||
1052 | sbsec = dir->i_sb->s_security; | ||
1053 | |||
1054 | AVC_AUDIT_DATA_INIT(&ad, FS); | ||
1055 | ad.u.fs.dentry = dentry; | ||
1056 | |||
1057 | rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, | ||
1058 | DIR__ADD_NAME | DIR__SEARCH, | ||
1059 | &ad); | ||
1060 | if (rc) | ||
1061 | return rc; | ||
1062 | |||
1063 | if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { | ||
1064 | newsid = tsec->create_sid; | ||
1065 | } else { | ||
1066 | rc = security_transition_sid(tsec->sid, dsec->sid, tclass, | ||
1067 | &newsid); | ||
1068 | if (rc) | ||
1069 | return rc; | ||
1070 | } | ||
1071 | |||
1072 | rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad); | ||
1073 | if (rc) | ||
1074 | return rc; | ||
1075 | |||
1076 | return avc_has_perm(newsid, sbsec->sid, | ||
1077 | SECCLASS_FILESYSTEM, | ||
1078 | FILESYSTEM__ASSOCIATE, &ad); | ||
1079 | } | ||
1080 | |||
1081 | #define MAY_LINK 0 | ||
1082 | #define MAY_UNLINK 1 | ||
1083 | #define MAY_RMDIR 2 | ||
1084 | |||
1085 | /* Check whether a task can link, unlink, or rmdir a file/directory. */ | ||
1086 | static int may_link(struct inode *dir, | ||
1087 | struct dentry *dentry, | ||
1088 | int kind) | ||
1089 | |||
1090 | { | ||
1091 | struct task_security_struct *tsec; | ||
1092 | struct inode_security_struct *dsec, *isec; | ||
1093 | struct avc_audit_data ad; | ||
1094 | u32 av; | ||
1095 | int rc; | ||
1096 | |||
1097 | tsec = current->security; | ||
1098 | dsec = dir->i_security; | ||
1099 | isec = dentry->d_inode->i_security; | ||
1100 | |||
1101 | AVC_AUDIT_DATA_INIT(&ad, FS); | ||
1102 | ad.u.fs.dentry = dentry; | ||
1103 | |||
1104 | av = DIR__SEARCH; | ||
1105 | av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME); | ||
1106 | rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad); | ||
1107 | if (rc) | ||
1108 | return rc; | ||
1109 | |||
1110 | switch (kind) { | ||
1111 | case MAY_LINK: | ||
1112 | av = FILE__LINK; | ||
1113 | break; | ||
1114 | case MAY_UNLINK: | ||
1115 | av = FILE__UNLINK; | ||
1116 | break; | ||
1117 | case MAY_RMDIR: | ||
1118 | av = DIR__RMDIR; | ||
1119 | break; | ||
1120 | default: | ||
1121 | printk(KERN_WARNING "may_link: unrecognized kind %d\n", kind); | ||
1122 | return 0; | ||
1123 | } | ||
1124 | |||
1125 | rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad); | ||
1126 | return rc; | ||
1127 | } | ||
1128 | |||
1129 | static inline int may_rename(struct inode *old_dir, | ||
1130 | struct dentry *old_dentry, | ||
1131 | struct inode *new_dir, | ||
1132 | struct dentry *new_dentry) | ||
1133 | { | ||
1134 | struct task_security_struct *tsec; | ||
1135 | struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec; | ||
1136 | struct avc_audit_data ad; | ||
1137 | u32 av; | ||
1138 | int old_is_dir, new_is_dir; | ||
1139 | int rc; | ||
1140 | |||
1141 | tsec = current->security; | ||
1142 | old_dsec = old_dir->i_security; | ||
1143 | old_isec = old_dentry->d_inode->i_security; | ||
1144 | old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode); | ||
1145 | new_dsec = new_dir->i_security; | ||
1146 | |||
1147 | AVC_AUDIT_DATA_INIT(&ad, FS); | ||
1148 | |||
1149 | ad.u.fs.dentry = old_dentry; | ||
1150 | rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR, | ||
1151 | DIR__REMOVE_NAME | DIR__SEARCH, &ad); | ||
1152 | if (rc) | ||
1153 | return rc; | ||
1154 | rc = avc_has_perm(tsec->sid, old_isec->sid, | ||
1155 | old_isec->sclass, FILE__RENAME, &ad); | ||
1156 | if (rc) | ||
1157 | return rc; | ||
1158 | if (old_is_dir && new_dir != old_dir) { | ||
1159 | rc = avc_has_perm(tsec->sid, old_isec->sid, | ||
1160 | old_isec->sclass, DIR__REPARENT, &ad); | ||
1161 | if (rc) | ||
1162 | return rc; | ||
1163 | } | ||
1164 | |||
1165 | ad.u.fs.dentry = new_dentry; | ||
1166 | av = DIR__ADD_NAME | DIR__SEARCH; | ||
1167 | if (new_dentry->d_inode) | ||
1168 | av |= DIR__REMOVE_NAME; | ||
1169 | rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad); | ||
1170 | if (rc) | ||
1171 | return rc; | ||
1172 | if (new_dentry->d_inode) { | ||
1173 | new_isec = new_dentry->d_inode->i_security; | ||
1174 | new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode); | ||
1175 | rc = avc_has_perm(tsec->sid, new_isec->sid, | ||
1176 | new_isec->sclass, | ||
1177 | (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad); | ||
1178 | if (rc) | ||
1179 | return rc; | ||
1180 | } | ||
1181 | |||
1182 | return 0; | ||
1183 | } | ||
1184 | |||
1185 | /* Check whether a task can perform a filesystem operation. */ | ||
1186 | static int superblock_has_perm(struct task_struct *tsk, | ||
1187 | struct super_block *sb, | ||
1188 | u32 perms, | ||
1189 | struct avc_audit_data *ad) | ||
1190 | { | ||
1191 | struct task_security_struct *tsec; | ||
1192 | struct superblock_security_struct *sbsec; | ||
1193 | |||
1194 | tsec = tsk->security; | ||
1195 | sbsec = sb->s_security; | ||
1196 | return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, | ||
1197 | perms, ad); | ||
1198 | } | ||
1199 | |||
1200 | /* Convert a Linux mode and permission mask to an access vector. */ | ||
1201 | static inline u32 file_mask_to_av(int mode, int mask) | ||
1202 | { | ||
1203 | u32 av = 0; | ||
1204 | |||
1205 | if ((mode & S_IFMT) != S_IFDIR) { | ||
1206 | if (mask & MAY_EXEC) | ||
1207 | av |= FILE__EXECUTE; | ||
1208 | if (mask & MAY_READ) | ||
1209 | av |= FILE__READ; | ||
1210 | |||
1211 | if (mask & MAY_APPEND) | ||
1212 | av |= FILE__APPEND; | ||
1213 | else if (mask & MAY_WRITE) | ||
1214 | av |= FILE__WRITE; | ||
1215 | |||
1216 | } else { | ||
1217 | if (mask & MAY_EXEC) | ||
1218 | av |= DIR__SEARCH; | ||
1219 | if (mask & MAY_WRITE) | ||
1220 | av |= DIR__WRITE; | ||
1221 | if (mask & MAY_READ) | ||
1222 | av |= DIR__READ; | ||
1223 | } | ||
1224 | |||
1225 | return av; | ||
1226 | } | ||
1227 | |||
1228 | /* Convert a Linux file to an access vector. */ | ||
1229 | static inline u32 file_to_av(struct file *file) | ||
1230 | { | ||
1231 | u32 av = 0; | ||
1232 | |||
1233 | if (file->f_mode & FMODE_READ) | ||
1234 | av |= FILE__READ; | ||
1235 | if (file->f_mode & FMODE_WRITE) { | ||
1236 | if (file->f_flags & O_APPEND) | ||
1237 | av |= FILE__APPEND; | ||
1238 | else | ||
1239 | av |= FILE__WRITE; | ||
1240 | } | ||
1241 | |||
1242 | return av; | ||
1243 | } | ||
1244 | |||
1245 | /* Set an inode's SID to a specified value. */ | ||
1246 | static int inode_security_set_sid(struct inode *inode, u32 sid) | ||
1247 | { | ||
1248 | struct inode_security_struct *isec = inode->i_security; | ||
1249 | struct superblock_security_struct *sbsec = inode->i_sb->s_security; | ||
1250 | |||
1251 | if (!sbsec->initialized) { | ||
1252 | /* Defer initialization to selinux_complete_init. */ | ||
1253 | return 0; | ||
1254 | } | ||
1255 | |||
1256 | down(&isec->sem); | ||
1257 | isec->sclass = inode_mode_to_security_class(inode->i_mode); | ||
1258 | isec->sid = sid; | ||
1259 | isec->initialized = 1; | ||
1260 | up(&isec->sem); | ||
1261 | return 0; | ||
1262 | } | ||
1263 | |||
1264 | /* Set the security attributes on a newly created file. */ | ||
1265 | static int post_create(struct inode *dir, | ||
1266 | struct dentry *dentry) | ||
1267 | { | ||
1268 | |||
1269 | struct task_security_struct *tsec; | ||
1270 | struct inode *inode; | ||
1271 | struct inode_security_struct *dsec; | ||
1272 | struct superblock_security_struct *sbsec; | ||
1273 | u32 newsid; | ||
1274 | char *context; | ||
1275 | unsigned int len; | ||
1276 | int rc; | ||
1277 | |||
1278 | tsec = current->security; | ||
1279 | dsec = dir->i_security; | ||
1280 | sbsec = dir->i_sb->s_security; | ||
1281 | |||
1282 | inode = dentry->d_inode; | ||
1283 | if (!inode) { | ||
1284 | /* Some file system types (e.g. NFS) may not instantiate | ||
1285 | a dentry for all create operations (e.g. symlink), | ||
1286 | so we have to check to see if the inode is non-NULL. */ | ||
1287 | printk(KERN_WARNING "post_create: no inode, dir (dev=%s, " | ||
1288 | "ino=%ld)\n", dir->i_sb->s_id, dir->i_ino); | ||
1289 | return 0; | ||
1290 | } | ||
1291 | |||
1292 | if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) { | ||
1293 | newsid = tsec->create_sid; | ||
1294 | } else { | ||
1295 | rc = security_transition_sid(tsec->sid, dsec->sid, | ||
1296 | inode_mode_to_security_class(inode->i_mode), | ||
1297 | &newsid); | ||
1298 | if (rc) { | ||
1299 | printk(KERN_WARNING "post_create: " | ||
1300 | "security_transition_sid failed, rc=%d (dev=%s " | ||
1301 | "ino=%ld)\n", | ||
1302 | -rc, inode->i_sb->s_id, inode->i_ino); | ||
1303 | return rc; | ||
1304 | } | ||
1305 | } | ||
1306 | |||
1307 | rc = inode_security_set_sid(inode, newsid); | ||
1308 | if (rc) { | ||
1309 | printk(KERN_WARNING "post_create: inode_security_set_sid " | ||
1310 | "failed, rc=%d (dev=%s ino=%ld)\n", | ||
1311 | -rc, inode->i_sb->s_id, inode->i_ino); | ||
1312 | return rc; | ||
1313 | } | ||
1314 | |||
1315 | if (sbsec->behavior == SECURITY_FS_USE_XATTR && | ||
1316 | inode->i_op->setxattr) { | ||
1317 | /* Use extended attributes. */ | ||
1318 | rc = security_sid_to_context(newsid, &context, &len); | ||
1319 | if (rc) { | ||
1320 | printk(KERN_WARNING "post_create: sid_to_context " | ||
1321 | "failed, rc=%d (dev=%s ino=%ld)\n", | ||
1322 | -rc, inode->i_sb->s_id, inode->i_ino); | ||
1323 | return rc; | ||
1324 | } | ||
1325 | down(&inode->i_sem); | ||
1326 | rc = inode->i_op->setxattr(dentry, | ||
1327 | XATTR_NAME_SELINUX, | ||
1328 | context, len, 0); | ||
1329 | up(&inode->i_sem); | ||
1330 | kfree(context); | ||
1331 | if (rc < 0) { | ||
1332 | printk(KERN_WARNING "post_create: setxattr failed, " | ||
1333 | "rc=%d (dev=%s ino=%ld)\n", | ||
1334 | -rc, inode->i_sb->s_id, inode->i_ino); | ||
1335 | return rc; | ||
1336 | } | ||
1337 | } | ||
1338 | |||
1339 | return 0; | ||
1340 | } | ||
1341 | |||
1342 | |||
1343 | /* Hook functions begin here. */ | ||
1344 | |||
1345 | static int selinux_ptrace(struct task_struct *parent, struct task_struct *child) | ||
1346 | { | ||
1347 | struct task_security_struct *psec = parent->security; | ||
1348 | struct task_security_struct *csec = child->security; | ||
1349 | int rc; | ||
1350 | |||
1351 | rc = secondary_ops->ptrace(parent,child); | ||
1352 | if (rc) | ||
1353 | return rc; | ||
1354 | |||
1355 | rc = task_has_perm(parent, child, PROCESS__PTRACE); | ||
1356 | /* Save the SID of the tracing process for later use in apply_creds. */ | ||
1357 | if (!rc) | ||
1358 | csec->ptrace_sid = psec->sid; | ||
1359 | return rc; | ||
1360 | } | ||
1361 | |||
1362 | static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, | ||
1363 | kernel_cap_t *inheritable, kernel_cap_t *permitted) | ||
1364 | { | ||
1365 | int error; | ||
1366 | |||
1367 | error = task_has_perm(current, target, PROCESS__GETCAP); | ||
1368 | if (error) | ||
1369 | return error; | ||
1370 | |||
1371 | return secondary_ops->capget(target, effective, inheritable, permitted); | ||
1372 | } | ||
1373 | |||
1374 | static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effective, | ||
1375 | kernel_cap_t *inheritable, kernel_cap_t *permitted) | ||
1376 | { | ||
1377 | int error; | ||
1378 | |||
1379 | error = secondary_ops->capset_check(target, effective, inheritable, permitted); | ||
1380 | if (error) | ||
1381 | return error; | ||
1382 | |||
1383 | return task_has_perm(current, target, PROCESS__SETCAP); | ||
1384 | } | ||
1385 | |||
1386 | static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective, | ||
1387 | kernel_cap_t *inheritable, kernel_cap_t *permitted) | ||
1388 | { | ||
1389 | secondary_ops->capset_set(target, effective, inheritable, permitted); | ||
1390 | } | ||
1391 | |||
1392 | static int selinux_capable(struct task_struct *tsk, int cap) | ||
1393 | { | ||
1394 | int rc; | ||
1395 | |||
1396 | rc = secondary_ops->capable(tsk, cap); | ||
1397 | if (rc) | ||
1398 | return rc; | ||
1399 | |||
1400 | return task_has_capability(tsk,cap); | ||
1401 | } | ||
1402 | |||
1403 | static int selinux_sysctl(ctl_table *table, int op) | ||
1404 | { | ||
1405 | int error = 0; | ||
1406 | u32 av; | ||
1407 | struct task_security_struct *tsec; | ||
1408 | u32 tsid; | ||
1409 | int rc; | ||
1410 | |||
1411 | rc = secondary_ops->sysctl(table, op); | ||
1412 | if (rc) | ||
1413 | return rc; | ||
1414 | |||
1415 | tsec = current->security; | ||
1416 | |||
1417 | rc = selinux_proc_get_sid(table->de, (op == 001) ? | ||
1418 | SECCLASS_DIR : SECCLASS_FILE, &tsid); | ||
1419 | if (rc) { | ||
1420 | /* Default to the well-defined sysctl SID. */ | ||
1421 | tsid = SECINITSID_SYSCTL; | ||
1422 | } | ||
1423 | |||
1424 | /* The op values are "defined" in sysctl.c, thereby creating | ||
1425 | * a bad coupling between this module and sysctl.c */ | ||
1426 | if(op == 001) { | ||
1427 | error = avc_has_perm(tsec->sid, tsid, | ||
1428 | SECCLASS_DIR, DIR__SEARCH, NULL); | ||
1429 | } else { | ||
1430 | av = 0; | ||
1431 | if (op & 004) | ||
1432 | av |= FILE__READ; | ||
1433 | if (op & 002) | ||
1434 | av |= FILE__WRITE; | ||
1435 | if (av) | ||
1436 | error = avc_has_perm(tsec->sid, tsid, | ||
1437 | SECCLASS_FILE, av, NULL); | ||
1438 | } | ||
1439 | |||
1440 | return error; | ||
1441 | } | ||
1442 | |||
1443 | static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) | ||
1444 | { | ||
1445 | int rc = 0; | ||
1446 | |||
1447 | if (!sb) | ||
1448 | return 0; | ||
1449 | |||
1450 | switch (cmds) { | ||
1451 | case Q_SYNC: | ||
1452 | case Q_QUOTAON: | ||
1453 | case Q_QUOTAOFF: | ||
1454 | case Q_SETINFO: | ||
1455 | case Q_SETQUOTA: | ||
1456 | rc = superblock_has_perm(current, | ||
1457 | sb, | ||
1458 | FILESYSTEM__QUOTAMOD, NULL); | ||
1459 | break; | ||
1460 | case Q_GETFMT: | ||
1461 | case Q_GETINFO: | ||
1462 | case Q_GETQUOTA: | ||
1463 | rc = superblock_has_perm(current, | ||
1464 | sb, | ||
1465 | FILESYSTEM__QUOTAGET, NULL); | ||
1466 | break; | ||
1467 | default: | ||
1468 | rc = 0; /* let the kernel handle invalid cmds */ | ||
1469 | break; | ||
1470 | } | ||
1471 | return rc; | ||
1472 | } | ||
1473 | |||
1474 | static int selinux_quota_on(struct dentry *dentry) | ||
1475 | { | ||
1476 | return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON); | ||
1477 | } | ||
1478 | |||
1479 | static int selinux_syslog(int type) | ||
1480 | { | ||
1481 | int rc; | ||
1482 | |||
1483 | rc = secondary_ops->syslog(type); | ||
1484 | if (rc) | ||
1485 | return rc; | ||
1486 | |||
1487 | switch (type) { | ||
1488 | case 3: /* Read last kernel messages */ | ||
1489 | case 10: /* Return size of the log buffer */ | ||
1490 | rc = task_has_system(current, SYSTEM__SYSLOG_READ); | ||
1491 | break; | ||
1492 | case 6: /* Disable logging to console */ | ||
1493 | case 7: /* Enable logging to console */ | ||
1494 | case 8: /* Set level of messages printed to console */ | ||
1495 | rc = task_has_system(current, SYSTEM__SYSLOG_CONSOLE); | ||
1496 | break; | ||
1497 | case 0: /* Close log */ | ||
1498 | case 1: /* Open log */ | ||
1499 | case 2: /* Read from log */ | ||
1500 | case 4: /* Read/clear last kernel messages */ | ||
1501 | case 5: /* Clear ring buffer */ | ||
1502 | default: | ||
1503 | rc = task_has_system(current, SYSTEM__SYSLOG_MOD); | ||
1504 | break; | ||
1505 | } | ||
1506 | return rc; | ||
1507 | } | ||
1508 | |||
1509 | /* | ||
1510 | * Check that a process has enough memory to allocate a new virtual | ||
1511 | * mapping. 0 means there is enough memory for the allocation to | ||
1512 | * succeed and -ENOMEM implies there is not. | ||
1513 | * | ||
1514 | * Note that secondary_ops->capable and task_has_perm_noaudit return 0 | ||
1515 | * if the capability is granted, but __vm_enough_memory requires 1 if | ||
1516 | * the capability is granted. | ||
1517 | * | ||
1518 | * Do not audit the selinux permission check, as this is applied to all | ||
1519 | * processes that allocate mappings. | ||
1520 | */ | ||
1521 | static int selinux_vm_enough_memory(long pages) | ||
1522 | { | ||
1523 | int rc, cap_sys_admin = 0; | ||
1524 | struct task_security_struct *tsec = current->security; | ||
1525 | |||
1526 | rc = secondary_ops->capable(current, CAP_SYS_ADMIN); | ||
1527 | if (rc == 0) | ||
1528 | rc = avc_has_perm_noaudit(tsec->sid, tsec->sid, | ||
1529 | SECCLASS_CAPABILITY, | ||
1530 | CAP_TO_MASK(CAP_SYS_ADMIN), | ||
1531 | NULL); | ||
1532 | |||
1533 | if (rc == 0) | ||
1534 | cap_sys_admin = 1; | ||
1535 | |||
1536 | return __vm_enough_memory(pages, cap_sys_admin); | ||
1537 | } | ||
1538 | |||
1539 | /* binprm security operations */ | ||
1540 | |||
1541 | static int selinux_bprm_alloc_security(struct linux_binprm *bprm) | ||
1542 | { | ||
1543 | struct bprm_security_struct *bsec; | ||
1544 | |||
1545 | bsec = kmalloc(sizeof(struct bprm_security_struct), GFP_KERNEL); | ||
1546 | if (!bsec) | ||
1547 | return -ENOMEM; | ||
1548 | |||
1549 | memset(bsec, 0, sizeof *bsec); | ||
1550 | bsec->magic = SELINUX_MAGIC; | ||
1551 | bsec->bprm = bprm; | ||
1552 | bsec->sid = SECINITSID_UNLABELED; | ||
1553 | bsec->set = 0; | ||
1554 | |||
1555 | bprm->security = bsec; | ||
1556 | return 0; | ||
1557 | } | ||
1558 | |||
1559 | static int selinux_bprm_set_security(struct linux_binprm *bprm) | ||
1560 | { | ||
1561 | struct task_security_struct *tsec; | ||
1562 | struct inode *inode = bprm->file->f_dentry->d_inode; | ||
1563 | struct inode_security_struct *isec; | ||
1564 | struct bprm_security_struct *bsec; | ||
1565 | u32 newsid; | ||
1566 | struct avc_audit_data ad; | ||
1567 | int rc; | ||
1568 | |||
1569 | rc = secondary_ops->bprm_set_security(bprm); | ||
1570 | if (rc) | ||
1571 | return rc; | ||
1572 | |||
1573 | bsec = bprm->security; | ||
1574 | |||
1575 | if (bsec->set) | ||
1576 | return 0; | ||
1577 | |||
1578 | tsec = current->security; | ||
1579 | isec = inode->i_security; | ||
1580 | |||
1581 | /* Default to the current task SID. */ | ||
1582 | bsec->sid = tsec->sid; | ||
1583 | |||
1584 | /* Reset create SID on execve. */ | ||
1585 | tsec->create_sid = 0; | ||
1586 | |||
1587 | if (tsec->exec_sid) { | ||
1588 | newsid = tsec->exec_sid; | ||
1589 | /* Reset exec SID on execve. */ | ||
1590 | tsec->exec_sid = 0; | ||
1591 | } else { | ||
1592 | /* Check for a default transition on this program. */ | ||
1593 | rc = security_transition_sid(tsec->sid, isec->sid, | ||
1594 | SECCLASS_PROCESS, &newsid); | ||
1595 | if (rc) | ||
1596 | return rc; | ||
1597 | } | ||
1598 | |||
1599 | AVC_AUDIT_DATA_INIT(&ad, FS); | ||
1600 | ad.u.fs.mnt = bprm->file->f_vfsmnt; | ||
1601 | ad.u.fs.dentry = bprm->file->f_dentry; | ||
1602 | |||
1603 | if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) | ||
1604 | newsid = tsec->sid; | ||
1605 | |||
1606 | if (tsec->sid == newsid) { | ||
1607 | rc = avc_has_perm(tsec->sid, isec->sid, | ||
1608 | SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); | ||
1609 | if (rc) | ||
1610 | return rc; | ||
1611 | } else { | ||
1612 | /* Check permissions for the transition. */ | ||
1613 | rc = avc_has_perm(tsec->sid, newsid, | ||
1614 | SECCLASS_PROCESS, PROCESS__TRANSITION, &ad); | ||
1615 | if (rc) | ||
1616 | return rc; | ||
1617 | |||
1618 | rc = avc_has_perm(newsid, isec->sid, | ||
1619 | SECCLASS_FILE, FILE__ENTRYPOINT, &ad); | ||
1620 | if (rc) | ||
1621 | return rc; | ||
1622 | |||
1623 | /* Clear any possibly unsafe personality bits on exec: */ | ||
1624 | current->personality &= ~PER_CLEAR_ON_SETID; | ||
1625 | |||
1626 | /* Set the security field to the new SID. */ | ||
1627 | bsec->sid = newsid; | ||
1628 | } | ||
1629 | |||
1630 | bsec->set = 1; | ||
1631 | return 0; | ||
1632 | } | ||
1633 | |||
1634 | static int selinux_bprm_check_security (struct linux_binprm *bprm) | ||
1635 | { | ||
1636 | return secondary_ops->bprm_check_security(bprm); | ||
1637 | } | ||
1638 | |||
1639 | |||
1640 | static int selinux_bprm_secureexec (struct linux_binprm *bprm) | ||
1641 | { | ||
1642 | struct task_security_struct *tsec = current->security; | ||
1643 | int atsecure = 0; | ||
1644 | |||
1645 | if (tsec->osid != tsec->sid) { | ||
1646 | /* Enable secure mode for SIDs transitions unless | ||
1647 | the noatsecure permission is granted between | ||
1648 | the two SIDs, i.e. ahp returns 0. */ | ||
1649 | atsecure = avc_has_perm(tsec->osid, tsec->sid, | ||
1650 | SECCLASS_PROCESS, | ||
1651 | PROCESS__NOATSECURE, NULL); | ||
1652 | } | ||
1653 | |||
1654 | return (atsecure || secondary_ops->bprm_secureexec(bprm)); | ||
1655 | } | ||
1656 | |||
1657 | static void selinux_bprm_free_security(struct linux_binprm *bprm) | ||
1658 | { | ||
1659 | struct bprm_security_struct *bsec = bprm->security; | ||
1660 | bprm->security = NULL; | ||
1661 | kfree(bsec); | ||
1662 | } | ||
1663 | |||
1664 | extern struct vfsmount *selinuxfs_mount; | ||
1665 | extern struct dentry *selinux_null; | ||
1666 | |||
1667 | /* Derived from fs/exec.c:flush_old_files. */ | ||
1668 | static inline void flush_unauthorized_files(struct files_struct * files) | ||
1669 | { | ||
1670 | struct avc_audit_data ad; | ||
1671 | struct file *file, *devnull = NULL; | ||
1672 | struct tty_struct *tty = current->signal->tty; | ||
1673 | long j = -1; | ||
1674 | |||
1675 | if (tty) { | ||
1676 | file_list_lock(); | ||
1677 | file = list_entry(tty->tty_files.next, typeof(*file), f_list); | ||
1678 | if (file) { | ||
1679 | /* Revalidate access to controlling tty. | ||
1680 | Use inode_has_perm on the tty inode directly rather | ||
1681 | than using file_has_perm, as this particular open | ||
1682 | file may belong to another process and we are only | ||
1683 | interested in the inode-based check here. */ | ||
1684 | struct inode *inode = file->f_dentry->d_inode; | ||
1685 | if (inode_has_perm(current, inode, | ||
1686 | FILE__READ | FILE__WRITE, NULL)) { | ||
1687 | /* Reset controlling tty. */ | ||
1688 | current->signal->tty = NULL; | ||
1689 | current->signal->tty_old_pgrp = 0; | ||
1690 | } | ||
1691 | } | ||
1692 | file_list_unlock(); | ||
1693 | } | ||
1694 | |||
1695 | /* Revalidate access to inherited open files. */ | ||
1696 | |||
1697 | AVC_AUDIT_DATA_INIT(&ad,FS); | ||
1698 | |||
1699 | spin_lock(&files->file_lock); | ||
1700 | for (;;) { | ||
1701 | unsigned long set, i; | ||
1702 | int fd; | ||
1703 | |||
1704 | j++; | ||
1705 | i = j * __NFDBITS; | ||
1706 | if (i >= files->max_fds || i >= files->max_fdset) | ||
1707 | break; | ||
1708 | set = files->open_fds->fds_bits[j]; | ||
1709 | if (!set) | ||
1710 | continue; | ||
1711 | spin_unlock(&files->file_lock); | ||
1712 | for ( ; set ; i++,set >>= 1) { | ||
1713 | if (set & 1) { | ||
1714 | file = fget(i); | ||
1715 | if (!file) | ||
1716 | continue; | ||
1717 | if (file_has_perm(current, | ||
1718 | file, | ||
1719 | file_to_av(file))) { | ||
1720 | sys_close(i); | ||
1721 | fd = get_unused_fd(); | ||
1722 | if (fd != i) { | ||
1723 | if (fd >= 0) | ||
1724 | put_unused_fd(fd); | ||
1725 | fput(file); | ||
1726 | continue; | ||
1727 | } | ||
1728 | if (devnull) { | ||
1729 | atomic_inc(&devnull->f_count); | ||
1730 | } else { | ||
1731 | devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR); | ||
1732 | if (!devnull) { | ||
1733 | put_unused_fd(fd); | ||
1734 | fput(file); | ||
1735 | continue; | ||
1736 | } | ||
1737 | } | ||
1738 | fd_install(fd, devnull); | ||
1739 | } | ||
1740 | fput(file); | ||
1741 | } | ||
1742 | } | ||
1743 | spin_lock(&files->file_lock); | ||
1744 | |||
1745 | } | ||
1746 | spin_unlock(&files->file_lock); | ||
1747 | } | ||
1748 | |||
1749 | static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) | ||
1750 | { | ||
1751 | struct task_security_struct *tsec; | ||
1752 | struct bprm_security_struct *bsec; | ||
1753 | u32 sid; | ||
1754 | int rc; | ||
1755 | |||
1756 | secondary_ops->bprm_apply_creds(bprm, unsafe); | ||
1757 | |||
1758 | tsec = current->security; | ||
1759 | |||
1760 | bsec = bprm->security; | ||
1761 | sid = bsec->sid; | ||
1762 | |||
1763 | tsec->osid = tsec->sid; | ||
1764 | bsec->unsafe = 0; | ||
1765 | if (tsec->sid != sid) { | ||
1766 | /* Check for shared state. If not ok, leave SID | ||
1767 | unchanged and kill. */ | ||
1768 | if (unsafe & LSM_UNSAFE_SHARE) { | ||
1769 | rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, | ||
1770 | PROCESS__SHARE, NULL); | ||
1771 | if (rc) { | ||
1772 | bsec->unsafe = 1; | ||
1773 | return; | ||
1774 | } | ||
1775 | } | ||
1776 | |||
1777 | /* Check for ptracing, and update the task SID if ok. | ||
1778 | Otherwise, leave SID unchanged and kill. */ | ||
1779 | if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { | ||
1780 | rc = avc_has_perm(tsec->ptrace_sid, sid, | ||
1781 | SECCLASS_PROCESS, PROCESS__PTRACE, | ||
1782 | NULL); | ||
1783 | if (rc) { | ||
1784 | bsec->unsafe = 1; | ||
1785 | return; | ||
1786 | } | ||
1787 | } | ||
1788 | tsec->sid = sid; | ||
1789 | } | ||
1790 | } | ||
1791 | |||
1792 | /* | ||
1793 | * called after apply_creds without the task lock held | ||
1794 | */ | ||
1795 | static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm) | ||
1796 | { | ||
1797 | struct task_security_struct *tsec; | ||
1798 | struct rlimit *rlim, *initrlim; | ||
1799 | struct itimerval itimer; | ||
1800 | struct bprm_security_struct *bsec; | ||
1801 | int rc, i; | ||
1802 | |||
1803 | tsec = current->security; | ||
1804 | bsec = bprm->security; | ||
1805 | |||
1806 | if (bsec->unsafe) { | ||
1807 | force_sig_specific(SIGKILL, current); | ||
1808 | return; | ||
1809 | } | ||
1810 | if (tsec->osid == tsec->sid) | ||
1811 | return; | ||
1812 | |||
1813 | /* Close files for which the new task SID is not authorized. */ | ||
1814 | flush_unauthorized_files(current->files); | ||
1815 | |||
1816 | /* Check whether the new SID can inherit signal state | ||
1817 | from the old SID. If not, clear itimers to avoid | ||
1818 | subsequent signal generation and flush and unblock | ||
1819 | signals. This must occur _after_ the task SID has | ||
1820 | been updated so that any kill done after the flush | ||
1821 | will be checked against the new SID. */ | ||
1822 | rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS, | ||
1823 | PROCESS__SIGINH, NULL); | ||
1824 | if (rc) { | ||
1825 | memset(&itimer, 0, sizeof itimer); | ||
1826 | for (i = 0; i < 3; i++) | ||
1827 | do_setitimer(i, &itimer, NULL); | ||
1828 | flush_signals(current); | ||
1829 | spin_lock_irq(¤t->sighand->siglock); | ||
1830 | flush_signal_handlers(current, 1); | ||
1831 | sigemptyset(¤t->blocked); | ||
1832 | recalc_sigpending(); | ||
1833 | spin_unlock_irq(¤t->sighand->siglock); | ||
1834 | } | ||
1835 | |||
1836 | /* Check whether the new SID can inherit resource limits | ||
1837 | from the old SID. If not, reset all soft limits to | ||
1838 | the lower of the current task's hard limit and the init | ||
1839 | task's soft limit. Note that the setting of hard limits | ||
1840 | (even to lower them) can be controlled by the setrlimit | ||
1841 | check. The inclusion of the init task's soft limit into | ||
1842 | the computation is to avoid resetting soft limits higher | ||
1843 | than the default soft limit for cases where the default | ||
1844 | is lower than the hard limit, e.g. RLIMIT_CORE or | ||
1845 | RLIMIT_STACK.*/ | ||
1846 | rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS, | ||
1847 | PROCESS__RLIMITINH, NULL); | ||
1848 | if (rc) { | ||
1849 | for (i = 0; i < RLIM_NLIMITS; i++) { | ||
1850 | rlim = current->signal->rlim + i; | ||
1851 | initrlim = init_task.signal->rlim+i; | ||
1852 | rlim->rlim_cur = min(rlim->rlim_max,initrlim->rlim_cur); | ||
1853 | } | ||
1854 | if (current->signal->rlim[RLIMIT_CPU].rlim_cur != RLIM_INFINITY) { | ||
1855 | /* | ||
1856 | * This will cause RLIMIT_CPU calculations | ||
1857 | * to be refigured. | ||
1858 | */ | ||
1859 | current->it_prof_expires = jiffies_to_cputime(1); | ||
1860 | } | ||
1861 | } | ||
1862 | |||
1863 | /* Wake up the parent if it is waiting so that it can | ||
1864 | recheck wait permission to the new task SID. */ | ||
1865 | wake_up_interruptible(¤t->parent->signal->wait_chldexit); | ||
1866 | } | ||
1867 | |||
1868 | /* superblock security operations */ | ||
1869 | |||
1870 | static int selinux_sb_alloc_security(struct super_block *sb) | ||
1871 | { | ||
1872 | return superblock_alloc_security(sb); | ||
1873 | } | ||
1874 | |||
1875 | static void selinux_sb_free_security(struct super_block *sb) | ||
1876 | { | ||
1877 | superblock_free_security(sb); | ||
1878 | } | ||
1879 | |||
1880 | static inline int match_prefix(char *prefix, int plen, char *option, int olen) | ||
1881 | { | ||
1882 | if (plen > olen) | ||
1883 | return 0; | ||
1884 | |||
1885 | return !memcmp(prefix, option, plen); | ||
1886 | } | ||
1887 | |||
1888 | static inline int selinux_option(char *option, int len) | ||
1889 | { | ||
1890 | return (match_prefix("context=", sizeof("context=")-1, option, len) || | ||
1891 | match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) || | ||
1892 | match_prefix("defcontext=", sizeof("defcontext=")-1, option, len)); | ||
1893 | } | ||
1894 | |||
1895 | static inline void take_option(char **to, char *from, int *first, int len) | ||
1896 | { | ||
1897 | if (!*first) { | ||
1898 | **to = ','; | ||
1899 | *to += 1; | ||
1900 | } | ||
1901 | else | ||
1902 | *first = 0; | ||
1903 | memcpy(*to, from, len); | ||
1904 | *to += len; | ||
1905 | } | ||
1906 | |||
1907 | static int selinux_sb_copy_data(struct file_system_type *type, void *orig, void *copy) | ||
1908 | { | ||
1909 | int fnosec, fsec, rc = 0; | ||
1910 | char *in_save, *in_curr, *in_end; | ||
1911 | char *sec_curr, *nosec_save, *nosec; | ||
1912 | |||
1913 | in_curr = orig; | ||
1914 | sec_curr = copy; | ||
1915 | |||
1916 | /* Binary mount data: just copy */ | ||
1917 | if (type->fs_flags & FS_BINARY_MOUNTDATA) { | ||
1918 | copy_page(sec_curr, in_curr); | ||
1919 | goto out; | ||
1920 | } | ||
1921 | |||
1922 | nosec = (char *)get_zeroed_page(GFP_KERNEL); | ||
1923 | if (!nosec) { | ||
1924 | rc = -ENOMEM; | ||
1925 | goto out; | ||
1926 | } | ||
1927 | |||
1928 | nosec_save = nosec; | ||
1929 | fnosec = fsec = 1; | ||
1930 | in_save = in_end = orig; | ||
1931 | |||
1932 | do { | ||
1933 | if (*in_end == ',' || *in_end == '\0') { | ||
1934 | int len = in_end - in_curr; | ||
1935 | |||
1936 | if (selinux_option(in_curr, len)) | ||
1937 | take_option(&sec_curr, in_curr, &fsec, len); | ||
1938 | else | ||
1939 | take_option(&nosec, in_curr, &fnosec, len); | ||
1940 | |||
1941 | in_curr = in_end + 1; | ||
1942 | } | ||
1943 | } while (*in_end++); | ||
1944 | |||
1945 | copy_page(in_save, nosec_save); | ||
1946 | out: | ||
1947 | return rc; | ||
1948 | } | ||
1949 | |||
1950 | static int selinux_sb_kern_mount(struct super_block *sb, void *data) | ||
1951 | { | ||
1952 | struct avc_audit_data ad; | ||
1953 | int rc; | ||
1954 | |||
1955 | rc = superblock_doinit(sb, data); | ||
1956 | if (rc) | ||
1957 | return rc; | ||
1958 | |||
1959 | AVC_AUDIT_DATA_INIT(&ad,FS); | ||
1960 | ad.u.fs.dentry = sb->s_root; | ||
1961 | return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad); | ||
1962 | } | ||
1963 | |||
1964 | static int selinux_sb_statfs(struct super_block *sb) | ||
1965 | { | ||
1966 | struct avc_audit_data ad; | ||
1967 | |||
1968 | AVC_AUDIT_DATA_INIT(&ad,FS); | ||
1969 | ad.u.fs.dentry = sb->s_root; | ||
1970 | return superblock_has_perm(current, sb, FILESYSTEM__GETATTR, &ad); | ||
1971 | } | ||
1972 | |||
1973 | static int selinux_mount(char * dev_name, | ||
1974 | struct nameidata *nd, | ||
1975 | char * type, | ||
1976 | unsigned long flags, | ||
1977 | void * data) | ||
1978 | { | ||
1979 | int rc; | ||
1980 | |||
1981 | rc = secondary_ops->sb_mount(dev_name, nd, type, flags, data); | ||
1982 | if (rc) | ||
1983 | return rc; | ||
1984 | |||
1985 | if (flags & MS_REMOUNT) | ||
1986 | return superblock_has_perm(current, nd->mnt->mnt_sb, | ||
1987 | FILESYSTEM__REMOUNT, NULL); | ||
1988 | else | ||
1989 | return dentry_has_perm(current, nd->mnt, nd->dentry, | ||
1990 | FILE__MOUNTON); | ||
1991 | } | ||
1992 | |||
1993 | static int selinux_umount(struct vfsmount *mnt, int flags) | ||
1994 | { | ||
1995 | int rc; | ||
1996 | |||
1997 | rc = secondary_ops->sb_umount(mnt, flags); | ||
1998 | if (rc) | ||
1999 | return rc; | ||
2000 | |||
2001 | return superblock_has_perm(current,mnt->mnt_sb, | ||
2002 | FILESYSTEM__UNMOUNT,NULL); | ||
2003 | } | ||
2004 | |||
2005 | /* inode security operations */ | ||
2006 | |||
2007 | static int selinux_inode_alloc_security(struct inode *inode) | ||
2008 | { | ||
2009 | return inode_alloc_security(inode); | ||
2010 | } | ||
2011 | |||
2012 | static void selinux_inode_free_security(struct inode *inode) | ||
2013 | { | ||
2014 | inode_free_security(inode); | ||
2015 | } | ||
2016 | |||
2017 | static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask) | ||
2018 | { | ||
2019 | return may_create(dir, dentry, SECCLASS_FILE); | ||
2020 | } | ||
2021 | |||
2022 | static void selinux_inode_post_create(struct inode *dir, struct dentry *dentry, int mask) | ||
2023 | { | ||
2024 | post_create(dir, dentry); | ||
2025 | } | ||
2026 | |||
2027 | static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) | ||
2028 | { | ||
2029 | int rc; | ||
2030 | |||
2031 | rc = secondary_ops->inode_link(old_dentry,dir,new_dentry); | ||
2032 | if (rc) | ||
2033 | return rc; | ||
2034 | return may_link(dir, old_dentry, MAY_LINK); | ||
2035 | } | ||
2036 | |||
2037 | static void selinux_inode_post_link(struct dentry *old_dentry, struct inode *inode, struct dentry *new_dentry) | ||
2038 | { | ||
2039 | return; | ||
2040 | } | ||
2041 | |||
2042 | static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry) | ||
2043 | { | ||
2044 | int rc; | ||
2045 | |||
2046 | rc = secondary_ops->inode_unlink(dir, dentry); | ||
2047 | if (rc) | ||
2048 | return rc; | ||
2049 | return may_link(dir, dentry, MAY_UNLINK); | ||
2050 | } | ||
2051 | |||
2052 | static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name) | ||
2053 | { | ||
2054 | return may_create(dir, dentry, SECCLASS_LNK_FILE); | ||
2055 | } | ||
2056 | |||
2057 | static void selinux_inode_post_symlink(struct inode *dir, struct dentry *dentry, const char *name) | ||
2058 | { | ||
2059 | post_create(dir, dentry); | ||
2060 | } | ||
2061 | |||
2062 | static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, int mask) | ||
2063 | { | ||
2064 | return may_create(dir, dentry, SECCLASS_DIR); | ||
2065 | } | ||
2066 | |||
2067 | static void selinux_inode_post_mkdir(struct inode *dir, struct dentry *dentry, int mask) | ||
2068 | { | ||
2069 | post_create(dir, dentry); | ||
2070 | } | ||
2071 | |||
2072 | static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry) | ||
2073 | { | ||
2074 | return may_link(dir, dentry, MAY_RMDIR); | ||
2075 | } | ||
2076 | |||
2077 | static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) | ||
2078 | { | ||
2079 | int rc; | ||
2080 | |||
2081 | rc = secondary_ops->inode_mknod(dir, dentry, mode, dev); | ||
2082 | if (rc) | ||
2083 | return rc; | ||
2084 | |||
2085 | return may_create(dir, dentry, inode_mode_to_security_class(mode)); | ||
2086 | } | ||
2087 | |||
2088 | static void selinux_inode_post_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) | ||
2089 | { | ||
2090 | post_create(dir, dentry); | ||
2091 | } | ||
2092 | |||
2093 | static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry, | ||
2094 | struct inode *new_inode, struct dentry *new_dentry) | ||
2095 | { | ||
2096 | return may_rename(old_inode, old_dentry, new_inode, new_dentry); | ||
2097 | } | ||
2098 | |||
2099 | static void selinux_inode_post_rename(struct inode *old_inode, struct dentry *old_dentry, | ||
2100 | struct inode *new_inode, struct dentry *new_dentry) | ||
2101 | { | ||
2102 | return; | ||
2103 | } | ||
2104 | |||
2105 | static int selinux_inode_readlink(struct dentry *dentry) | ||
2106 | { | ||
2107 | return dentry_has_perm(current, NULL, dentry, FILE__READ); | ||
2108 | } | ||
2109 | |||
2110 | static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata) | ||
2111 | { | ||
2112 | int rc; | ||
2113 | |||
2114 | rc = secondary_ops->inode_follow_link(dentry,nameidata); | ||
2115 | if (rc) | ||
2116 | return rc; | ||
2117 | return dentry_has_perm(current, NULL, dentry, FILE__READ); | ||
2118 | } | ||
2119 | |||
2120 | static int selinux_inode_permission(struct inode *inode, int mask, | ||
2121 | struct nameidata *nd) | ||
2122 | { | ||
2123 | int rc; | ||
2124 | |||
2125 | rc = secondary_ops->inode_permission(inode, mask, nd); | ||
2126 | if (rc) | ||
2127 | return rc; | ||
2128 | |||
2129 | if (!mask) { | ||
2130 | /* No permission to check. Existence test. */ | ||
2131 | return 0; | ||
2132 | } | ||
2133 | |||
2134 | return inode_has_perm(current, inode, | ||
2135 | file_mask_to_av(inode->i_mode, mask), NULL); | ||
2136 | } | ||
2137 | |||
2138 | static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) | ||
2139 | { | ||
2140 | int rc; | ||
2141 | |||
2142 | rc = secondary_ops->inode_setattr(dentry, iattr); | ||
2143 | if (rc) | ||
2144 | return rc; | ||
2145 | |||
2146 | if (iattr->ia_valid & ATTR_FORCE) | ||
2147 | return 0; | ||
2148 | |||
2149 | if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | | ||
2150 | ATTR_ATIME_SET | ATTR_MTIME_SET)) | ||
2151 | return dentry_has_perm(current, NULL, dentry, FILE__SETATTR); | ||
2152 | |||
2153 | return dentry_has_perm(current, NULL, dentry, FILE__WRITE); | ||
2154 | } | ||
2155 | |||
2156 | static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) | ||
2157 | { | ||
2158 | return dentry_has_perm(current, mnt, dentry, FILE__GETATTR); | ||
2159 | } | ||
2160 | |||
2161 | static int selinux_inode_setxattr(struct dentry *dentry, char *name, void *value, size_t size, int flags) | ||
2162 | { | ||
2163 | struct task_security_struct *tsec = current->security; | ||
2164 | struct inode *inode = dentry->d_inode; | ||
2165 | struct inode_security_struct *isec = inode->i_security; | ||
2166 | struct superblock_security_struct *sbsec; | ||
2167 | struct avc_audit_data ad; | ||
2168 | u32 newsid; | ||
2169 | int rc = 0; | ||
2170 | |||
2171 | if (strcmp(name, XATTR_NAME_SELINUX)) { | ||
2172 | if (!strncmp(name, XATTR_SECURITY_PREFIX, | ||
2173 | sizeof XATTR_SECURITY_PREFIX - 1) && | ||
2174 | !capable(CAP_SYS_ADMIN)) { | ||
2175 | /* A different attribute in the security namespace. | ||
2176 | Restrict to administrator. */ | ||
2177 | return -EPERM; | ||
2178 | } | ||
2179 | |||
2180 | /* Not an attribute we recognize, so just check the | ||
2181 | ordinary setattr permission. */ | ||
2182 | return dentry_has_perm(current, NULL, dentry, FILE__SETATTR); | ||
2183 | } | ||
2184 | |||
2185 | sbsec = inode->i_sb->s_security; | ||
2186 | if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT) | ||
2187 | return -EOPNOTSUPP; | ||
2188 | |||
2189 | if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) | ||
2190 | return -EPERM; | ||
2191 | |||
2192 | AVC_AUDIT_DATA_INIT(&ad,FS); | ||
2193 | ad.u.fs.dentry = dentry; | ||
2194 | |||
2195 | rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, | ||
2196 | FILE__RELABELFROM, &ad); | ||
2197 | if (rc) | ||
2198 | return rc; | ||
2199 | |||
2200 | rc = security_context_to_sid(value, size, &newsid); | ||
2201 | if (rc) | ||
2202 | return rc; | ||
2203 | |||
2204 | rc = avc_has_perm(tsec->sid, newsid, isec->sclass, | ||
2205 | FILE__RELABELTO, &ad); | ||
2206 | if (rc) | ||
2207 | return rc; | ||
2208 | |||
2209 | rc = security_validate_transition(isec->sid, newsid, tsec->sid, | ||
2210 | isec->sclass); | ||
2211 | if (rc) | ||
2212 | return rc; | ||
2213 | |||
2214 | return avc_has_perm(newsid, | ||
2215 | sbsec->sid, | ||
2216 | SECCLASS_FILESYSTEM, | ||
2217 | FILESYSTEM__ASSOCIATE, | ||
2218 | &ad); | ||
2219 | } | ||
2220 | |||
2221 | static void selinux_inode_post_setxattr(struct dentry *dentry, char *name, | ||
2222 | void *value, size_t size, int flags) | ||
2223 | { | ||
2224 | struct inode *inode = dentry->d_inode; | ||
2225 | struct inode_security_struct *isec = inode->i_security; | ||
2226 | u32 newsid; | ||
2227 | int rc; | ||
2228 | |||
2229 | if (strcmp(name, XATTR_NAME_SELINUX)) { | ||
2230 | /* Not an attribute we recognize, so nothing to do. */ | ||
2231 | return; | ||
2232 | } | ||
2233 | |||
2234 | rc = security_context_to_sid(value, size, &newsid); | ||
2235 | if (rc) { | ||
2236 | printk(KERN_WARNING "%s: unable to obtain SID for context " | ||
2237 | "%s, rc=%d\n", __FUNCTION__, (char*)value, -rc); | ||
2238 | return; | ||
2239 | } | ||
2240 | |||
2241 | isec->sid = newsid; | ||
2242 | return; | ||
2243 | } | ||
2244 | |||
2245 | static int selinux_inode_getxattr (struct dentry *dentry, char *name) | ||
2246 | { | ||
2247 | struct inode *inode = dentry->d_inode; | ||
2248 | struct superblock_security_struct *sbsec = inode->i_sb->s_security; | ||
2249 | |||
2250 | if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT) | ||
2251 | return -EOPNOTSUPP; | ||
2252 | |||
2253 | return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); | ||
2254 | } | ||
2255 | |||
2256 | static int selinux_inode_listxattr (struct dentry *dentry) | ||
2257 | { | ||
2258 | return dentry_has_perm(current, NULL, dentry, FILE__GETATTR); | ||
2259 | } | ||
2260 | |||
2261 | static int selinux_inode_removexattr (struct dentry *dentry, char *name) | ||
2262 | { | ||
2263 | if (strcmp(name, XATTR_NAME_SELINUX)) { | ||
2264 | if (!strncmp(name, XATTR_SECURITY_PREFIX, | ||
2265 | sizeof XATTR_SECURITY_PREFIX - 1) && | ||
2266 | !capable(CAP_SYS_ADMIN)) { | ||
2267 | /* A different attribute in the security namespace. | ||
2268 | Restrict to administrator. */ | ||
2269 | return -EPERM; | ||
2270 | } | ||
2271 | |||
2272 | /* Not an attribute we recognize, so just check the | ||
2273 | ordinary setattr permission. Might want a separate | ||
2274 | permission for removexattr. */ | ||
2275 | return dentry_has_perm(current, NULL, dentry, FILE__SETATTR); | ||
2276 | } | ||
2277 | |||
2278 | /* No one is allowed to remove a SELinux security label. | ||
2279 | You can change the label, but all data must be labeled. */ | ||
2280 | return -EACCES; | ||
2281 | } | ||
2282 | |||
2283 | static int selinux_inode_getsecurity(struct inode *inode, const char *name, void *buffer, size_t size) | ||
2284 | { | ||
2285 | struct inode_security_struct *isec = inode->i_security; | ||
2286 | char *context; | ||
2287 | unsigned len; | ||
2288 | int rc; | ||
2289 | |||
2290 | /* Permission check handled by selinux_inode_getxattr hook.*/ | ||
2291 | |||
2292 | if (strcmp(name, XATTR_SELINUX_SUFFIX)) | ||
2293 | return -EOPNOTSUPP; | ||
2294 | |||
2295 | rc = security_sid_to_context(isec->sid, &context, &len); | ||
2296 | if (rc) | ||
2297 | return rc; | ||
2298 | |||
2299 | if (!buffer || !size) { | ||
2300 | kfree(context); | ||
2301 | return len; | ||
2302 | } | ||
2303 | if (size < len) { | ||
2304 | kfree(context); | ||
2305 | return -ERANGE; | ||
2306 | } | ||
2307 | memcpy(buffer, context, len); | ||
2308 | kfree(context); | ||
2309 | return len; | ||
2310 | } | ||
2311 | |||
2312 | static int selinux_inode_setsecurity(struct inode *inode, const char *name, | ||
2313 | const void *value, size_t size, int flags) | ||
2314 | { | ||
2315 | struct inode_security_struct *isec = inode->i_security; | ||
2316 | u32 newsid; | ||
2317 | int rc; | ||
2318 | |||
2319 | if (strcmp(name, XATTR_SELINUX_SUFFIX)) | ||
2320 | return -EOPNOTSUPP; | ||
2321 | |||
2322 | if (!value || !size) | ||
2323 | return -EACCES; | ||
2324 | |||
2325 | rc = security_context_to_sid((void*)value, size, &newsid); | ||
2326 | if (rc) | ||
2327 | return rc; | ||
2328 | |||
2329 | isec->sid = newsid; | ||
2330 | return 0; | ||
2331 | } | ||
2332 | |||
2333 | static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size) | ||
2334 | { | ||
2335 | const int len = sizeof(XATTR_NAME_SELINUX); | ||
2336 | if (buffer && len <= buffer_size) | ||
2337 | memcpy(buffer, XATTR_NAME_SELINUX, len); | ||
2338 | return len; | ||
2339 | } | ||
2340 | |||
2341 | /* file security operations */ | ||
2342 | |||
2343 | static int selinux_file_permission(struct file *file, int mask) | ||
2344 | { | ||
2345 | struct inode *inode = file->f_dentry->d_inode; | ||
2346 | |||
2347 | if (!mask) { | ||
2348 | /* No permission to check. Existence test. */ | ||
2349 | return 0; | ||
2350 | } | ||
2351 | |||
2352 | /* file_mask_to_av won't add FILE__WRITE if MAY_APPEND is set */ | ||
2353 | if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE)) | ||
2354 | mask |= MAY_APPEND; | ||
2355 | |||
2356 | return file_has_perm(current, file, | ||
2357 | file_mask_to_av(inode->i_mode, mask)); | ||
2358 | } | ||
2359 | |||
2360 | static int selinux_file_alloc_security(struct file *file) | ||
2361 | { | ||
2362 | return file_alloc_security(file); | ||
2363 | } | ||
2364 | |||
2365 | static void selinux_file_free_security(struct file *file) | ||
2366 | { | ||
2367 | file_free_security(file); | ||
2368 | } | ||
2369 | |||
2370 | static int selinux_file_ioctl(struct file *file, unsigned int cmd, | ||
2371 | unsigned long arg) | ||
2372 | { | ||
2373 | int error = 0; | ||
2374 | |||
2375 | switch (cmd) { | ||
2376 | case FIONREAD: | ||
2377 | /* fall through */ | ||
2378 | case FIBMAP: | ||
2379 | /* fall through */ | ||
2380 | case FIGETBSZ: | ||
2381 | /* fall through */ | ||
2382 | case EXT2_IOC_GETFLAGS: | ||
2383 | /* fall through */ | ||
2384 | case EXT2_IOC_GETVERSION: | ||
2385 | error = file_has_perm(current, file, FILE__GETATTR); | ||
2386 | break; | ||
2387 | |||
2388 | case EXT2_IOC_SETFLAGS: | ||
2389 | /* fall through */ | ||
2390 | case EXT2_IOC_SETVERSION: | ||
2391 | error = file_has_perm(current, file, FILE__SETATTR); | ||
2392 | break; | ||
2393 | |||
2394 | /* sys_ioctl() checks */ | ||
2395 | case FIONBIO: | ||
2396 | /* fall through */ | ||
2397 | case FIOASYNC: | ||
2398 | error = file_has_perm(current, file, 0); | ||
2399 | break; | ||
2400 | |||
2401 | case KDSKBENT: | ||
2402 | case KDSKBSENT: | ||
2403 | error = task_has_capability(current,CAP_SYS_TTY_CONFIG); | ||
2404 | break; | ||
2405 | |||
2406 | /* default case assumes that the command will go | ||
2407 | * to the file's ioctl() function. | ||
2408 | */ | ||
2409 | default: | ||
2410 | error = file_has_perm(current, file, FILE__IOCTL); | ||
2411 | |||
2412 | } | ||
2413 | return error; | ||
2414 | } | ||
2415 | |||
2416 | static int file_map_prot_check(struct file *file, unsigned long prot, int shared) | ||
2417 | { | ||
2418 | #ifndef CONFIG_PPC32 | ||
2419 | if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) { | ||
2420 | /* | ||
2421 | * We are making executable an anonymous mapping or a | ||
2422 | * private file mapping that will also be writable. | ||
2423 | * This has an additional check. | ||
2424 | */ | ||
2425 | int rc = task_has_perm(current, current, PROCESS__EXECMEM); | ||
2426 | if (rc) | ||
2427 | return rc; | ||
2428 | } | ||
2429 | #endif | ||
2430 | |||
2431 | if (file) { | ||
2432 | /* read access is always possible with a mapping */ | ||
2433 | u32 av = FILE__READ; | ||
2434 | |||
2435 | /* write access only matters if the mapping is shared */ | ||
2436 | if (shared && (prot & PROT_WRITE)) | ||
2437 | av |= FILE__WRITE; | ||
2438 | |||
2439 | if (prot & PROT_EXEC) | ||
2440 | av |= FILE__EXECUTE; | ||
2441 | |||
2442 | return file_has_perm(current, file, av); | ||
2443 | } | ||
2444 | return 0; | ||
2445 | } | ||
2446 | |||
2447 | static int selinux_file_mmap(struct file *file, unsigned long reqprot, | ||
2448 | unsigned long prot, unsigned long flags) | ||
2449 | { | ||
2450 | int rc; | ||
2451 | |||
2452 | rc = secondary_ops->file_mmap(file, reqprot, prot, flags); | ||
2453 | if (rc) | ||
2454 | return rc; | ||
2455 | |||
2456 | if (selinux_checkreqprot) | ||
2457 | prot = reqprot; | ||
2458 | |||
2459 | return file_map_prot_check(file, prot, | ||
2460 | (flags & MAP_TYPE) == MAP_SHARED); | ||
2461 | } | ||
2462 | |||
2463 | static int selinux_file_mprotect(struct vm_area_struct *vma, | ||
2464 | unsigned long reqprot, | ||
2465 | unsigned long prot) | ||
2466 | { | ||
2467 | int rc; | ||
2468 | |||
2469 | rc = secondary_ops->file_mprotect(vma, reqprot, prot); | ||
2470 | if (rc) | ||
2471 | return rc; | ||
2472 | |||
2473 | if (selinux_checkreqprot) | ||
2474 | prot = reqprot; | ||
2475 | |||
2476 | #ifndef CONFIG_PPC32 | ||
2477 | if (vma->vm_file != NULL && vma->anon_vma != NULL && (prot & PROT_EXEC)) { | ||
2478 | /* | ||
2479 | * We are making executable a file mapping that has | ||
2480 | * had some COW done. Since pages might have been written, | ||
2481 | * check ability to execute the possibly modified content. | ||
2482 | * This typically should only occur for text relocations. | ||
2483 | */ | ||
2484 | int rc = file_has_perm(current, vma->vm_file, FILE__EXECMOD); | ||
2485 | if (rc) | ||
2486 | return rc; | ||
2487 | } | ||
2488 | #endif | ||
2489 | |||
2490 | return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED); | ||
2491 | } | ||
2492 | |||
2493 | static int selinux_file_lock(struct file *file, unsigned int cmd) | ||
2494 | { | ||
2495 | return file_has_perm(current, file, FILE__LOCK); | ||
2496 | } | ||
2497 | |||
2498 | static int selinux_file_fcntl(struct file *file, unsigned int cmd, | ||
2499 | unsigned long arg) | ||
2500 | { | ||
2501 | int err = 0; | ||
2502 | |||
2503 | switch (cmd) { | ||
2504 | case F_SETFL: | ||
2505 | if (!file->f_dentry || !file->f_dentry->d_inode) { | ||
2506 | err = -EINVAL; | ||
2507 | break; | ||
2508 | } | ||
2509 | |||
2510 | if ((file->f_flags & O_APPEND) && !(arg & O_APPEND)) { | ||
2511 | err = file_has_perm(current, file,FILE__WRITE); | ||
2512 | break; | ||
2513 | } | ||
2514 | /* fall through */ | ||
2515 | case F_SETOWN: | ||
2516 | case F_SETSIG: | ||
2517 | case F_GETFL: | ||
2518 | case F_GETOWN: | ||
2519 | case F_GETSIG: | ||
2520 | /* Just check FD__USE permission */ | ||
2521 | err = file_has_perm(current, file, 0); | ||
2522 | break; | ||
2523 | case F_GETLK: | ||
2524 | case F_SETLK: | ||
2525 | case F_SETLKW: | ||
2526 | #if BITS_PER_LONG == 32 | ||
2527 | case F_GETLK64: | ||
2528 | case F_SETLK64: | ||
2529 | case F_SETLKW64: | ||
2530 | #endif | ||
2531 | if (!file->f_dentry || !file->f_dentry->d_inode) { | ||
2532 | err = -EINVAL; | ||
2533 | break; | ||
2534 | } | ||
2535 | err = file_has_perm(current, file, FILE__LOCK); | ||
2536 | break; | ||
2537 | } | ||
2538 | |||
2539 | return err; | ||
2540 | } | ||
2541 | |||
2542 | static int selinux_file_set_fowner(struct file *file) | ||
2543 | { | ||
2544 | struct task_security_struct *tsec; | ||
2545 | struct file_security_struct *fsec; | ||
2546 | |||
2547 | tsec = current->security; | ||
2548 | fsec = file->f_security; | ||
2549 | fsec->fown_sid = tsec->sid; | ||
2550 | |||
2551 | return 0; | ||
2552 | } | ||
2553 | |||
2554 | static int selinux_file_send_sigiotask(struct task_struct *tsk, | ||
2555 | struct fown_struct *fown, int signum) | ||
2556 | { | ||
2557 | struct file *file; | ||
2558 | u32 perm; | ||
2559 | struct task_security_struct *tsec; | ||
2560 | struct file_security_struct *fsec; | ||
2561 | |||
2562 | /* struct fown_struct is never outside the context of a struct file */ | ||
2563 | file = (struct file *)((long)fown - offsetof(struct file,f_owner)); | ||
2564 | |||
2565 | tsec = tsk->security; | ||
2566 | fsec = file->f_security; | ||
2567 | |||
2568 | if (!signum) | ||
2569 | perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */ | ||
2570 | else | ||
2571 | perm = signal_to_av(signum); | ||
2572 | |||
2573 | return avc_has_perm(fsec->fown_sid, tsec->sid, | ||
2574 | SECCLASS_PROCESS, perm, NULL); | ||
2575 | } | ||
2576 | |||
2577 | static int selinux_file_receive(struct file *file) | ||
2578 | { | ||
2579 | return file_has_perm(current, file, file_to_av(file)); | ||
2580 | } | ||
2581 | |||
2582 | /* task security operations */ | ||
2583 | |||
2584 | static int selinux_task_create(unsigned long clone_flags) | ||
2585 | { | ||
2586 | int rc; | ||
2587 | |||
2588 | rc = secondary_ops->task_create(clone_flags); | ||
2589 | if (rc) | ||
2590 | return rc; | ||
2591 | |||
2592 | return task_has_perm(current, current, PROCESS__FORK); | ||
2593 | } | ||
2594 | |||
2595 | static int selinux_task_alloc_security(struct task_struct *tsk) | ||
2596 | { | ||
2597 | struct task_security_struct *tsec1, *tsec2; | ||
2598 | int rc; | ||
2599 | |||
2600 | tsec1 = current->security; | ||
2601 | |||
2602 | rc = task_alloc_security(tsk); | ||
2603 | if (rc) | ||
2604 | return rc; | ||
2605 | tsec2 = tsk->security; | ||
2606 | |||
2607 | tsec2->osid = tsec1->osid; | ||
2608 | tsec2->sid = tsec1->sid; | ||
2609 | |||
2610 | /* Retain the exec and create SIDs across fork */ | ||
2611 | tsec2->exec_sid = tsec1->exec_sid; | ||
2612 | tsec2->create_sid = tsec1->create_sid; | ||
2613 | |||
2614 | /* Retain ptracer SID across fork, if any. | ||
2615 | This will be reset by the ptrace hook upon any | ||
2616 | subsequent ptrace_attach operations. */ | ||
2617 | tsec2->ptrace_sid = tsec1->ptrace_sid; | ||
2618 | |||
2619 | return 0; | ||
2620 | } | ||
2621 | |||
2622 | static void selinux_task_free_security(struct task_struct *tsk) | ||
2623 | { | ||
2624 | task_free_security(tsk); | ||
2625 | } | ||
2626 | |||
2627 | static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags) | ||
2628 | { | ||
2629 | /* Since setuid only affects the current process, and | ||
2630 | since the SELinux controls are not based on the Linux | ||
2631 | identity attributes, SELinux does not need to control | ||
2632 | this operation. However, SELinux does control the use | ||
2633 | of the CAP_SETUID and CAP_SETGID capabilities using the | ||
2634 | capable hook. */ | ||
2635 | return 0; | ||
2636 | } | ||
2637 | |||
2638 | static int selinux_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, int flags) | ||
2639 | { | ||
2640 | return secondary_ops->task_post_setuid(id0,id1,id2,flags); | ||
2641 | } | ||
2642 | |||
2643 | static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags) | ||
2644 | { | ||
2645 | /* See the comment for setuid above. */ | ||
2646 | return 0; | ||
2647 | } | ||
2648 | |||
2649 | static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) | ||
2650 | { | ||
2651 | return task_has_perm(current, p, PROCESS__SETPGID); | ||
2652 | } | ||
2653 | |||
2654 | static int selinux_task_getpgid(struct task_struct *p) | ||
2655 | { | ||
2656 | return task_has_perm(current, p, PROCESS__GETPGID); | ||
2657 | } | ||
2658 | |||
2659 | static int selinux_task_getsid(struct task_struct *p) | ||
2660 | { | ||
2661 | return task_has_perm(current, p, PROCESS__GETSESSION); | ||
2662 | } | ||
2663 | |||
2664 | static int selinux_task_setgroups(struct group_info *group_info) | ||
2665 | { | ||
2666 | /* See the comment for setuid above. */ | ||
2667 | return 0; | ||
2668 | } | ||
2669 | |||
2670 | static int selinux_task_setnice(struct task_struct *p, int nice) | ||
2671 | { | ||
2672 | int rc; | ||
2673 | |||
2674 | rc = secondary_ops->task_setnice(p, nice); | ||
2675 | if (rc) | ||
2676 | return rc; | ||
2677 | |||
2678 | return task_has_perm(current,p, PROCESS__SETSCHED); | ||
2679 | } | ||
2680 | |||
2681 | static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) | ||
2682 | { | ||
2683 | struct rlimit *old_rlim = current->signal->rlim + resource; | ||
2684 | int rc; | ||
2685 | |||
2686 | rc = secondary_ops->task_setrlimit(resource, new_rlim); | ||
2687 | if (rc) | ||
2688 | return rc; | ||
2689 | |||
2690 | /* Control the ability to change the hard limit (whether | ||
2691 | lowering or raising it), so that the hard limit can | ||
2692 | later be used as a safe reset point for the soft limit | ||
2693 | upon context transitions. See selinux_bprm_apply_creds. */ | ||
2694 | if (old_rlim->rlim_max != new_rlim->rlim_max) | ||
2695 | return task_has_perm(current, current, PROCESS__SETRLIMIT); | ||
2696 | |||
2697 | return 0; | ||
2698 | } | ||
2699 | |||
2700 | static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp) | ||
2701 | { | ||
2702 | return task_has_perm(current, p, PROCESS__SETSCHED); | ||
2703 | } | ||
2704 | |||
2705 | static int selinux_task_getscheduler(struct task_struct *p) | ||
2706 | { | ||
2707 | return task_has_perm(current, p, PROCESS__GETSCHED); | ||
2708 | } | ||
2709 | |||
2710 | static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig) | ||
2711 | { | ||
2712 | u32 perm; | ||
2713 | int rc; | ||
2714 | |||
2715 | rc = secondary_ops->task_kill(p, info, sig); | ||
2716 | if (rc) | ||
2717 | return rc; | ||
2718 | |||
2719 | if (info && ((unsigned long)info == 1 || | ||
2720 | (unsigned long)info == 2 || SI_FROMKERNEL(info))) | ||
2721 | return 0; | ||
2722 | |||
2723 | if (!sig) | ||
2724 | perm = PROCESS__SIGNULL; /* null signal; existence test */ | ||
2725 | else | ||
2726 | perm = signal_to_av(sig); | ||
2727 | |||
2728 | return task_has_perm(current, p, perm); | ||
2729 | } | ||
2730 | |||
2731 | static int selinux_task_prctl(int option, | ||
2732 | unsigned long arg2, | ||
2733 | unsigned long arg3, | ||
2734 | unsigned long arg4, | ||
2735 | unsigned long arg5) | ||
2736 | { | ||
2737 | /* The current prctl operations do not appear to require | ||
2738 | any SELinux controls since they merely observe or modify | ||
2739 | the state of the current process. */ | ||
2740 | return 0; | ||
2741 | } | ||
2742 | |||
2743 | static int selinux_task_wait(struct task_struct *p) | ||
2744 | { | ||
2745 | u32 perm; | ||
2746 | |||
2747 | perm = signal_to_av(p->exit_signal); | ||
2748 | |||
2749 | return task_has_perm(p, current, perm); | ||
2750 | } | ||
2751 | |||
2752 | static void selinux_task_reparent_to_init(struct task_struct *p) | ||
2753 | { | ||
2754 | struct task_security_struct *tsec; | ||
2755 | |||
2756 | secondary_ops->task_reparent_to_init(p); | ||
2757 | |||
2758 | tsec = p->security; | ||
2759 | tsec->osid = tsec->sid; | ||
2760 | tsec->sid = SECINITSID_KERNEL; | ||
2761 | return; | ||
2762 | } | ||
2763 | |||
2764 | static void selinux_task_to_inode(struct task_struct *p, | ||
2765 | struct inode *inode) | ||
2766 | { | ||
2767 | struct task_security_struct *tsec = p->security; | ||
2768 | struct inode_security_struct *isec = inode->i_security; | ||
2769 | |||
2770 | isec->sid = tsec->sid; | ||
2771 | isec->initialized = 1; | ||
2772 | return; | ||
2773 | } | ||
2774 | |||
2775 | #ifdef CONFIG_SECURITY_NETWORK | ||
2776 | |||
2777 | /* Returns error only if unable to parse addresses */ | ||
2778 | static int selinux_parse_skb_ipv4(struct sk_buff *skb, struct avc_audit_data *ad) | ||
2779 | { | ||
2780 | int offset, ihlen, ret = -EINVAL; | ||
2781 | struct iphdr _iph, *ih; | ||
2782 | |||
2783 | offset = skb->nh.raw - skb->data; | ||
2784 | ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph); | ||
2785 | if (ih == NULL) | ||
2786 | goto out; | ||
2787 | |||
2788 | ihlen = ih->ihl * 4; | ||
2789 | if (ihlen < sizeof(_iph)) | ||
2790 | goto out; | ||
2791 | |||
2792 | ad->u.net.v4info.saddr = ih->saddr; | ||
2793 | ad->u.net.v4info.daddr = ih->daddr; | ||
2794 | ret = 0; | ||
2795 | |||
2796 | switch (ih->protocol) { | ||
2797 | case IPPROTO_TCP: { | ||
2798 | struct tcphdr _tcph, *th; | ||
2799 | |||
2800 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
2801 | break; | ||
2802 | |||
2803 | offset += ihlen; | ||
2804 | th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); | ||
2805 | if (th == NULL) | ||
2806 | break; | ||
2807 | |||
2808 | ad->u.net.sport = th->source; | ||
2809 | ad->u.net.dport = th->dest; | ||
2810 | break; | ||
2811 | } | ||
2812 | |||
2813 | case IPPROTO_UDP: { | ||
2814 | struct udphdr _udph, *uh; | ||
2815 | |||
2816 | if (ntohs(ih->frag_off) & IP_OFFSET) | ||
2817 | break; | ||
2818 | |||
2819 | offset += ihlen; | ||
2820 | uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); | ||
2821 | if (uh == NULL) | ||
2822 | break; | ||
2823 | |||
2824 | ad->u.net.sport = uh->source; | ||
2825 | ad->u.net.dport = uh->dest; | ||
2826 | break; | ||
2827 | } | ||
2828 | |||
2829 | default: | ||
2830 | break; | ||
2831 | } | ||
2832 | out: | ||
2833 | return ret; | ||
2834 | } | ||
2835 | |||
2836 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
2837 | |||
2838 | /* Returns error only if unable to parse addresses */ | ||
2839 | static int selinux_parse_skb_ipv6(struct sk_buff *skb, struct avc_audit_data *ad) | ||
2840 | { | ||
2841 | u8 nexthdr; | ||
2842 | int ret = -EINVAL, offset; | ||
2843 | struct ipv6hdr _ipv6h, *ip6; | ||
2844 | |||
2845 | offset = skb->nh.raw - skb->data; | ||
2846 | ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h); | ||
2847 | if (ip6 == NULL) | ||
2848 | goto out; | ||
2849 | |||
2850 | ipv6_addr_copy(&ad->u.net.v6info.saddr, &ip6->saddr); | ||
2851 | ipv6_addr_copy(&ad->u.net.v6info.daddr, &ip6->daddr); | ||
2852 | ret = 0; | ||
2853 | |||
2854 | nexthdr = ip6->nexthdr; | ||
2855 | offset += sizeof(_ipv6h); | ||
2856 | offset = ipv6_skip_exthdr(skb, offset, &nexthdr, | ||
2857 | skb->tail - skb->head - offset); | ||
2858 | if (offset < 0) | ||
2859 | goto out; | ||
2860 | |||
2861 | switch (nexthdr) { | ||
2862 | case IPPROTO_TCP: { | ||
2863 | struct tcphdr _tcph, *th; | ||
2864 | |||
2865 | th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); | ||
2866 | if (th == NULL) | ||
2867 | break; | ||
2868 | |||
2869 | ad->u.net.sport = th->source; | ||
2870 | ad->u.net.dport = th->dest; | ||
2871 | break; | ||
2872 | } | ||
2873 | |||
2874 | case IPPROTO_UDP: { | ||
2875 | struct udphdr _udph, *uh; | ||
2876 | |||
2877 | uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); | ||
2878 | if (uh == NULL) | ||
2879 | break; | ||
2880 | |||
2881 | ad->u.net.sport = uh->source; | ||
2882 | ad->u.net.dport = uh->dest; | ||
2883 | break; | ||
2884 | } | ||
2885 | |||
2886 | /* includes fragments */ | ||
2887 | default: | ||
2888 | break; | ||
2889 | } | ||
2890 | out: | ||
2891 | return ret; | ||
2892 | } | ||
2893 | |||
2894 | #endif /* IPV6 */ | ||
2895 | |||
2896 | static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, | ||
2897 | char **addrp, int *len, int src) | ||
2898 | { | ||
2899 | int ret = 0; | ||
2900 | |||
2901 | switch (ad->u.net.family) { | ||
2902 | case PF_INET: | ||
2903 | ret = selinux_parse_skb_ipv4(skb, ad); | ||
2904 | if (ret || !addrp) | ||
2905 | break; | ||
2906 | *len = 4; | ||
2907 | *addrp = (char *)(src ? &ad->u.net.v4info.saddr : | ||
2908 | &ad->u.net.v4info.daddr); | ||
2909 | break; | ||
2910 | |||
2911 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
2912 | case PF_INET6: | ||
2913 | ret = selinux_parse_skb_ipv6(skb, ad); | ||
2914 | if (ret || !addrp) | ||
2915 | break; | ||
2916 | *len = 16; | ||
2917 | *addrp = (char *)(src ? &ad->u.net.v6info.saddr : | ||
2918 | &ad->u.net.v6info.daddr); | ||
2919 | break; | ||
2920 | #endif /* IPV6 */ | ||
2921 | default: | ||
2922 | break; | ||
2923 | } | ||
2924 | |||
2925 | return ret; | ||
2926 | } | ||
2927 | |||
2928 | /* socket security operations */ | ||
2929 | static int socket_has_perm(struct task_struct *task, struct socket *sock, | ||
2930 | u32 perms) | ||
2931 | { | ||
2932 | struct inode_security_struct *isec; | ||
2933 | struct task_security_struct *tsec; | ||
2934 | struct avc_audit_data ad; | ||
2935 | int err = 0; | ||
2936 | |||
2937 | tsec = task->security; | ||
2938 | isec = SOCK_INODE(sock)->i_security; | ||
2939 | |||
2940 | if (isec->sid == SECINITSID_KERNEL) | ||
2941 | goto out; | ||
2942 | |||
2943 | AVC_AUDIT_DATA_INIT(&ad,NET); | ||
2944 | ad.u.net.sk = sock->sk; | ||
2945 | err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad); | ||
2946 | |||
2947 | out: | ||
2948 | return err; | ||
2949 | } | ||
2950 | |||
2951 | static int selinux_socket_create(int family, int type, | ||
2952 | int protocol, int kern) | ||
2953 | { | ||
2954 | int err = 0; | ||
2955 | struct task_security_struct *tsec; | ||
2956 | |||
2957 | if (kern) | ||
2958 | goto out; | ||
2959 | |||
2960 | tsec = current->security; | ||
2961 | err = avc_has_perm(tsec->sid, tsec->sid, | ||
2962 | socket_type_to_security_class(family, type, | ||
2963 | protocol), SOCKET__CREATE, NULL); | ||
2964 | |||
2965 | out: | ||
2966 | return err; | ||
2967 | } | ||
2968 | |||
2969 | static void selinux_socket_post_create(struct socket *sock, int family, | ||
2970 | int type, int protocol, int kern) | ||
2971 | { | ||
2972 | struct inode_security_struct *isec; | ||
2973 | struct task_security_struct *tsec; | ||
2974 | |||
2975 | isec = SOCK_INODE(sock)->i_security; | ||
2976 | |||
2977 | tsec = current->security; | ||
2978 | isec->sclass = socket_type_to_security_class(family, type, protocol); | ||
2979 | isec->sid = kern ? SECINITSID_KERNEL : tsec->sid; | ||
2980 | isec->initialized = 1; | ||
2981 | |||
2982 | return; | ||
2983 | } | ||
2984 | |||
2985 | /* Range of port numbers used to automatically bind. | ||
2986 | Need to determine whether we should perform a name_bind | ||
2987 | permission check between the socket and the port number. */ | ||
2988 | #define ip_local_port_range_0 sysctl_local_port_range[0] | ||
2989 | #define ip_local_port_range_1 sysctl_local_port_range[1] | ||
2990 | |||
2991 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) | ||
2992 | { | ||
2993 | u16 family; | ||
2994 | int err; | ||
2995 | |||
2996 | err = socket_has_perm(current, sock, SOCKET__BIND); | ||
2997 | if (err) | ||
2998 | goto out; | ||
2999 | |||
3000 | /* | ||
3001 | * If PF_INET or PF_INET6, check name_bind permission for the port. | ||
3002 | */ | ||
3003 | family = sock->sk->sk_family; | ||
3004 | if (family == PF_INET || family == PF_INET6) { | ||
3005 | char *addrp; | ||
3006 | struct inode_security_struct *isec; | ||
3007 | struct task_security_struct *tsec; | ||
3008 | struct avc_audit_data ad; | ||
3009 | struct sockaddr_in *addr4 = NULL; | ||
3010 | struct sockaddr_in6 *addr6 = NULL; | ||
3011 | unsigned short snum; | ||
3012 | struct sock *sk = sock->sk; | ||
3013 | u32 sid, node_perm, addrlen; | ||
3014 | |||
3015 | tsec = current->security; | ||
3016 | isec = SOCK_INODE(sock)->i_security; | ||
3017 | |||
3018 | if (family == PF_INET) { | ||
3019 | addr4 = (struct sockaddr_in *)address; | ||
3020 | snum = ntohs(addr4->sin_port); | ||
3021 | addrlen = sizeof(addr4->sin_addr.s_addr); | ||
3022 | addrp = (char *)&addr4->sin_addr.s_addr; | ||
3023 | } else { | ||
3024 | addr6 = (struct sockaddr_in6 *)address; | ||
3025 | snum = ntohs(addr6->sin6_port); | ||
3026 | addrlen = sizeof(addr6->sin6_addr.s6_addr); | ||
3027 | addrp = (char *)&addr6->sin6_addr.s6_addr; | ||
3028 | } | ||
3029 | |||
3030 | if (snum&&(snum < max(PROT_SOCK,ip_local_port_range_0) || | ||
3031 | snum > ip_local_port_range_1)) { | ||
3032 | err = security_port_sid(sk->sk_family, sk->sk_type, | ||
3033 | sk->sk_protocol, snum, &sid); | ||
3034 | if (err) | ||
3035 | goto out; | ||
3036 | AVC_AUDIT_DATA_INIT(&ad,NET); | ||
3037 | ad.u.net.sport = htons(snum); | ||
3038 | ad.u.net.family = family; | ||
3039 | err = avc_has_perm(isec->sid, sid, | ||
3040 | isec->sclass, | ||
3041 | SOCKET__NAME_BIND, &ad); | ||
3042 | if (err) | ||
3043 | goto out; | ||
3044 | } | ||
3045 | |||
3046 | switch(sk->sk_protocol) { | ||
3047 | case IPPROTO_TCP: | ||
3048 | node_perm = TCP_SOCKET__NODE_BIND; | ||
3049 | break; | ||
3050 | |||
3051 | case IPPROTO_UDP: | ||
3052 | node_perm = UDP_SOCKET__NODE_BIND; | ||
3053 | break; | ||
3054 | |||
3055 | default: | ||
3056 | node_perm = RAWIP_SOCKET__NODE_BIND; | ||
3057 | break; | ||
3058 | } | ||
3059 | |||
3060 | err = security_node_sid(family, addrp, addrlen, &sid); | ||
3061 | if (err) | ||
3062 | goto out; | ||
3063 | |||
3064 | AVC_AUDIT_DATA_INIT(&ad,NET); | ||
3065 | ad.u.net.sport = htons(snum); | ||
3066 | ad.u.net.family = family; | ||
3067 | |||
3068 | if (family == PF_INET) | ||
3069 | ad.u.net.v4info.saddr = addr4->sin_addr.s_addr; | ||
3070 | else | ||
3071 | ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr); | ||
3072 | |||
3073 | err = avc_has_perm(isec->sid, sid, | ||
3074 | isec->sclass, node_perm, &ad); | ||
3075 | if (err) | ||
3076 | goto out; | ||
3077 | } | ||
3078 | out: | ||
3079 | return err; | ||
3080 | } | ||
3081 | |||
3082 | static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) | ||
3083 | { | ||
3084 | struct inode_security_struct *isec; | ||
3085 | int err; | ||
3086 | |||
3087 | err = socket_has_perm(current, sock, SOCKET__CONNECT); | ||
3088 | if (err) | ||
3089 | return err; | ||
3090 | |||
3091 | /* | ||
3092 | * If a TCP socket, check name_connect permission for the port. | ||
3093 | */ | ||
3094 | isec = SOCK_INODE(sock)->i_security; | ||
3095 | if (isec->sclass == SECCLASS_TCP_SOCKET) { | ||
3096 | struct sock *sk = sock->sk; | ||
3097 | struct avc_audit_data ad; | ||
3098 | struct sockaddr_in *addr4 = NULL; | ||
3099 | struct sockaddr_in6 *addr6 = NULL; | ||
3100 | unsigned short snum; | ||
3101 | u32 sid; | ||
3102 | |||
3103 | if (sk->sk_family == PF_INET) { | ||
3104 | addr4 = (struct sockaddr_in *)address; | ||
3105 | if (addrlen != sizeof(struct sockaddr_in)) | ||
3106 | return -EINVAL; | ||
3107 | snum = ntohs(addr4->sin_port); | ||
3108 | } else { | ||
3109 | addr6 = (struct sockaddr_in6 *)address; | ||
3110 | if (addrlen != sizeof(struct sockaddr_in6)) | ||
3111 | return -EINVAL; | ||
3112 | snum = ntohs(addr6->sin6_port); | ||
3113 | } | ||
3114 | |||
3115 | err = security_port_sid(sk->sk_family, sk->sk_type, | ||
3116 | sk->sk_protocol, snum, &sid); | ||
3117 | if (err) | ||
3118 | goto out; | ||
3119 | |||
3120 | AVC_AUDIT_DATA_INIT(&ad,NET); | ||
3121 | ad.u.net.dport = htons(snum); | ||
3122 | ad.u.net.family = sk->sk_family; | ||
3123 | err = avc_has_perm(isec->sid, sid, isec->sclass, | ||
3124 | TCP_SOCKET__NAME_CONNECT, &ad); | ||
3125 | if (err) | ||
3126 | goto out; | ||
3127 | } | ||
3128 | |||
3129 | out: | ||
3130 | return err; | ||
3131 | } | ||
3132 | |||
3133 | static int selinux_socket_listen(struct socket *sock, int backlog) | ||
3134 | { | ||
3135 | return socket_has_perm(current, sock, SOCKET__LISTEN); | ||
3136 | } | ||
3137 | |||
3138 | static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | ||
3139 | { | ||
3140 | int err; | ||
3141 | struct inode_security_struct *isec; | ||
3142 | struct inode_security_struct *newisec; | ||
3143 | |||
3144 | err = socket_has_perm(current, sock, SOCKET__ACCEPT); | ||
3145 | if (err) | ||
3146 | return err; | ||
3147 | |||
3148 | newisec = SOCK_INODE(newsock)->i_security; | ||
3149 | |||
3150 | isec = SOCK_INODE(sock)->i_security; | ||
3151 | newisec->sclass = isec->sclass; | ||
3152 | newisec->sid = isec->sid; | ||
3153 | newisec->initialized = 1; | ||
3154 | |||
3155 | return 0; | ||
3156 | } | ||
3157 | |||
3158 | static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, | ||
3159 | int size) | ||
3160 | { | ||
3161 | return socket_has_perm(current, sock, SOCKET__WRITE); | ||
3162 | } | ||
3163 | |||
3164 | static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, | ||
3165 | int size, int flags) | ||
3166 | { | ||
3167 | return socket_has_perm(current, sock, SOCKET__READ); | ||
3168 | } | ||
3169 | |||
3170 | static int selinux_socket_getsockname(struct socket *sock) | ||
3171 | { | ||
3172 | return socket_has_perm(current, sock, SOCKET__GETATTR); | ||
3173 | } | ||
3174 | |||
3175 | static int selinux_socket_getpeername(struct socket *sock) | ||
3176 | { | ||
3177 | return socket_has_perm(current, sock, SOCKET__GETATTR); | ||
3178 | } | ||
3179 | |||
3180 | static int selinux_socket_setsockopt(struct socket *sock,int level,int optname) | ||
3181 | { | ||
3182 | return socket_has_perm(current, sock, SOCKET__SETOPT); | ||
3183 | } | ||
3184 | |||
3185 | static int selinux_socket_getsockopt(struct socket *sock, int level, | ||
3186 | int optname) | ||
3187 | { | ||
3188 | return socket_has_perm(current, sock, SOCKET__GETOPT); | ||
3189 | } | ||
3190 | |||
3191 | static int selinux_socket_shutdown(struct socket *sock, int how) | ||
3192 | { | ||
3193 | return socket_has_perm(current, sock, SOCKET__SHUTDOWN); | ||
3194 | } | ||
3195 | |||
3196 | static int selinux_socket_unix_stream_connect(struct socket *sock, | ||
3197 | struct socket *other, | ||
3198 | struct sock *newsk) | ||
3199 | { | ||
3200 | struct sk_security_struct *ssec; | ||
3201 | struct inode_security_struct *isec; | ||
3202 | struct inode_security_struct *other_isec; | ||
3203 | struct avc_audit_data ad; | ||
3204 | int err; | ||
3205 | |||
3206 | err = secondary_ops->unix_stream_connect(sock, other, newsk); | ||
3207 | if (err) | ||
3208 | return err; | ||
3209 | |||
3210 | isec = SOCK_INODE(sock)->i_security; | ||
3211 | other_isec = SOCK_INODE(other)->i_security; | ||
3212 | |||
3213 | AVC_AUDIT_DATA_INIT(&ad,NET); | ||
3214 | ad.u.net.sk = other->sk; | ||
3215 | |||
3216 | err = avc_has_perm(isec->sid, other_isec->sid, | ||
3217 | isec->sclass, | ||
3218 | UNIX_STREAM_SOCKET__CONNECTTO, &ad); | ||
3219 | if (err) | ||
3220 | return err; | ||
3221 | |||
3222 | /* connecting socket */ | ||
3223 | ssec = sock->sk->sk_security; | ||
3224 | ssec->peer_sid = other_isec->sid; | ||
3225 | |||
3226 | /* server child socket */ | ||
3227 | ssec = newsk->sk_security; | ||
3228 | ssec->peer_sid = isec->sid; | ||
3229 | |||
3230 | return 0; | ||
3231 | } | ||
3232 | |||
3233 | static int selinux_socket_unix_may_send(struct socket *sock, | ||
3234 | struct socket *other) | ||
3235 | { | ||
3236 | struct inode_security_struct *isec; | ||
3237 | struct inode_security_struct *other_isec; | ||
3238 | struct avc_audit_data ad; | ||
3239 | int err; | ||
3240 | |||
3241 | isec = SOCK_INODE(sock)->i_security; | ||
3242 | other_isec = SOCK_INODE(other)->i_security; | ||
3243 | |||
3244 | AVC_AUDIT_DATA_INIT(&ad,NET); | ||
3245 | ad.u.net.sk = other->sk; | ||
3246 | |||
3247 | err = avc_has_perm(isec->sid, other_isec->sid, | ||
3248 | isec->sclass, SOCKET__SENDTO, &ad); | ||
3249 | if (err) | ||
3250 | return err; | ||
3251 | |||
3252 | return 0; | ||
3253 | } | ||
3254 | |||
3255 | static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) | ||
3256 | { | ||
3257 | u16 family; | ||
3258 | char *addrp; | ||
3259 | int len, err = 0; | ||
3260 | u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0; | ||
3261 | u32 sock_sid = 0; | ||
3262 | u16 sock_class = 0; | ||
3263 | struct socket *sock; | ||
3264 | struct net_device *dev; | ||
3265 | struct avc_audit_data ad; | ||
3266 | |||
3267 | family = sk->sk_family; | ||
3268 | if (family != PF_INET && family != PF_INET6) | ||
3269 | goto out; | ||
3270 | |||
3271 | /* Handle mapped IPv4 packets arriving via IPv6 sockets */ | ||
3272 | if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP)) | ||
3273 | family = PF_INET; | ||
3274 | |||
3275 | read_lock_bh(&sk->sk_callback_lock); | ||
3276 | sock = sk->sk_socket; | ||
3277 | if (sock) { | ||
3278 | struct inode *inode; | ||
3279 | inode = SOCK_INODE(sock); | ||
3280 | if (inode) { | ||
3281 | struct inode_security_struct *isec; | ||
3282 | isec = inode->i_security; | ||
3283 | sock_sid = isec->sid; | ||
3284 | sock_class = isec->sclass; | ||
3285 | } | ||
3286 | } | ||
3287 | read_unlock_bh(&sk->sk_callback_lock); | ||
3288 | if (!sock_sid) | ||
3289 | goto out; | ||
3290 | |||
3291 | dev = skb->dev; | ||
3292 | if (!dev) | ||
3293 | goto out; | ||
3294 | |||
3295 | err = sel_netif_sids(dev, &if_sid, NULL); | ||
3296 | if (err) | ||
3297 | goto out; | ||
3298 | |||
3299 | switch (sock_class) { | ||
3300 | case SECCLASS_UDP_SOCKET: | ||
3301 | netif_perm = NETIF__UDP_RECV; | ||
3302 | node_perm = NODE__UDP_RECV; | ||
3303 | recv_perm = UDP_SOCKET__RECV_MSG; | ||
3304 | break; | ||
3305 | |||
3306 | case SECCLASS_TCP_SOCKET: | ||
3307 | netif_perm = NETIF__TCP_RECV; | ||
3308 | node_perm = NODE__TCP_RECV; | ||
3309 | recv_perm = TCP_SOCKET__RECV_MSG; | ||
3310 | break; | ||
3311 | |||
3312 | default: | ||
3313 | netif_perm = NETIF__RAWIP_RECV; | ||
3314 | node_perm = NODE__RAWIP_RECV; | ||
3315 | break; | ||
3316 | } | ||
3317 | |||
3318 | AVC_AUDIT_DATA_INIT(&ad, NET); | ||
3319 | ad.u.net.netif = dev->name; | ||
3320 | ad.u.net.family = family; | ||
3321 | |||
3322 | err = selinux_parse_skb(skb, &ad, &addrp, &len, 1); | ||
3323 | if (err) | ||
3324 | goto out; | ||
3325 | |||
3326 | err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, &ad); | ||
3327 | if (err) | ||
3328 | goto out; | ||
3329 | |||
3330 | /* Fixme: this lookup is inefficient */ | ||
3331 | err = security_node_sid(family, addrp, len, &node_sid); | ||
3332 | if (err) | ||
3333 | goto out; | ||
3334 | |||
3335 | err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, &ad); | ||
3336 | if (err) | ||
3337 | goto out; | ||
3338 | |||
3339 | if (recv_perm) { | ||
3340 | u32 port_sid; | ||
3341 | |||
3342 | /* Fixme: make this more efficient */ | ||
3343 | err = security_port_sid(sk->sk_family, sk->sk_type, | ||
3344 | sk->sk_protocol, ntohs(ad.u.net.sport), | ||
3345 | &port_sid); | ||
3346 | if (err) | ||
3347 | goto out; | ||
3348 | |||
3349 | err = avc_has_perm(sock_sid, port_sid, | ||
3350 | sock_class, recv_perm, &ad); | ||
3351 | } | ||
3352 | out: | ||
3353 | return err; | ||
3354 | } | ||
3355 | |||
3356 | static int selinux_socket_getpeersec(struct socket *sock, char __user *optval, | ||
3357 | int __user *optlen, unsigned len) | ||
3358 | { | ||
3359 | int err = 0; | ||
3360 | char *scontext; | ||
3361 | u32 scontext_len; | ||
3362 | struct sk_security_struct *ssec; | ||
3363 | struct inode_security_struct *isec; | ||
3364 | |||
3365 | isec = SOCK_INODE(sock)->i_security; | ||
3366 | if (isec->sclass != SECCLASS_UNIX_STREAM_SOCKET) { | ||
3367 | err = -ENOPROTOOPT; | ||
3368 | goto out; | ||
3369 | } | ||
3370 | |||
3371 | ssec = sock->sk->sk_security; | ||
3372 | |||
3373 | err = security_sid_to_context(ssec->peer_sid, &scontext, &scontext_len); | ||
3374 | if (err) | ||
3375 | goto out; | ||
3376 | |||
3377 | if (scontext_len > len) { | ||
3378 | err = -ERANGE; | ||
3379 | goto out_len; | ||
3380 | } | ||
3381 | |||
3382 | if (copy_to_user(optval, scontext, scontext_len)) | ||
3383 | err = -EFAULT; | ||
3384 | |||
3385 | out_len: | ||
3386 | if (put_user(scontext_len, optlen)) | ||
3387 | err = -EFAULT; | ||
3388 | |||
3389 | kfree(scontext); | ||
3390 | out: | ||
3391 | return err; | ||
3392 | } | ||
3393 | |||
3394 | static int selinux_sk_alloc_security(struct sock *sk, int family, int priority) | ||
3395 | { | ||
3396 | return sk_alloc_security(sk, family, priority); | ||
3397 | } | ||
3398 | |||
3399 | static void selinux_sk_free_security(struct sock *sk) | ||
3400 | { | ||
3401 | sk_free_security(sk); | ||
3402 | } | ||
3403 | |||
3404 | static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) | ||
3405 | { | ||
3406 | int err = 0; | ||
3407 | u32 perm; | ||
3408 | struct nlmsghdr *nlh; | ||
3409 | struct socket *sock = sk->sk_socket; | ||
3410 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; | ||
3411 | |||
3412 | if (skb->len < NLMSG_SPACE(0)) { | ||
3413 | err = -EINVAL; | ||
3414 | goto out; | ||
3415 | } | ||
3416 | nlh = (struct nlmsghdr *)skb->data; | ||
3417 | |||
3418 | err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm); | ||
3419 | if (err) { | ||
3420 | if (err == -EINVAL) { | ||
3421 | audit_log(current->audit_context, | ||
3422 | "SELinux: unrecognized netlink message" | ||
3423 | " type=%hu for sclass=%hu\n", | ||
3424 | nlh->nlmsg_type, isec->sclass); | ||
3425 | if (!selinux_enforcing) | ||
3426 | err = 0; | ||
3427 | } | ||
3428 | |||
3429 | /* Ignore */ | ||
3430 | if (err == -ENOENT) | ||
3431 | err = 0; | ||
3432 | goto out; | ||
3433 | } | ||
3434 | |||
3435 | err = socket_has_perm(current, sock, perm); | ||
3436 | out: | ||
3437 | return err; | ||
3438 | } | ||
3439 | |||
3440 | #ifdef CONFIG_NETFILTER | ||
3441 | |||
3442 | static unsigned int selinux_ip_postroute_last(unsigned int hooknum, | ||
3443 | struct sk_buff **pskb, | ||
3444 | const struct net_device *in, | ||
3445 | const struct net_device *out, | ||
3446 | int (*okfn)(struct sk_buff *), | ||
3447 | u16 family) | ||
3448 | { | ||
3449 | char *addrp; | ||
3450 | int len, err = NF_ACCEPT; | ||
3451 | u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0; | ||
3452 | struct sock *sk; | ||
3453 | struct socket *sock; | ||
3454 | struct inode *inode; | ||
3455 | struct sk_buff *skb = *pskb; | ||
3456 | struct inode_security_struct *isec; | ||
3457 | struct avc_audit_data ad; | ||
3458 | struct net_device *dev = (struct net_device *)out; | ||
3459 | |||
3460 | sk = skb->sk; | ||
3461 | if (!sk) | ||
3462 | goto out; | ||
3463 | |||
3464 | sock = sk->sk_socket; | ||
3465 | if (!sock) | ||
3466 | goto out; | ||
3467 | |||
3468 | inode = SOCK_INODE(sock); | ||
3469 | if (!inode) | ||
3470 | goto out; | ||
3471 | |||
3472 | err = sel_netif_sids(dev, &if_sid, NULL); | ||
3473 | if (err) | ||
3474 | goto out; | ||
3475 | |||
3476 | isec = inode->i_security; | ||
3477 | |||
3478 | switch (isec->sclass) { | ||
3479 | case SECCLASS_UDP_SOCKET: | ||
3480 | netif_perm = NETIF__UDP_SEND; | ||
3481 | node_perm = NODE__UDP_SEND; | ||
3482 | send_perm = UDP_SOCKET__SEND_MSG; | ||
3483 | break; | ||
3484 | |||
3485 | case SECCLASS_TCP_SOCKET: | ||
3486 | netif_perm = NETIF__TCP_SEND; | ||
3487 | node_perm = NODE__TCP_SEND; | ||
3488 | send_perm = TCP_SOCKET__SEND_MSG; | ||
3489 | break; | ||
3490 | |||
3491 | default: | ||
3492 | netif_perm = NETIF__RAWIP_SEND; | ||
3493 | node_perm = NODE__RAWIP_SEND; | ||
3494 | break; | ||
3495 | } | ||
3496 | |||
3497 | |||
3498 | AVC_AUDIT_DATA_INIT(&ad, NET); | ||
3499 | ad.u.net.netif = dev->name; | ||
3500 | ad.u.net.family = family; | ||
3501 | |||
3502 | err = selinux_parse_skb(skb, &ad, &addrp, | ||
3503 | &len, 0) ? NF_DROP : NF_ACCEPT; | ||
3504 | if (err != NF_ACCEPT) | ||
3505 | goto out; | ||
3506 | |||
3507 | err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, | ||
3508 | netif_perm, &ad) ? NF_DROP : NF_ACCEPT; | ||
3509 | if (err != NF_ACCEPT) | ||
3510 | goto out; | ||
3511 | |||
3512 | /* Fixme: this lookup is inefficient */ | ||
3513 | err = security_node_sid(family, addrp, len, | ||
3514 | &node_sid) ? NF_DROP : NF_ACCEPT; | ||
3515 | if (err != NF_ACCEPT) | ||
3516 | goto out; | ||
3517 | |||
3518 | err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, | ||
3519 | node_perm, &ad) ? NF_DROP : NF_ACCEPT; | ||
3520 | if (err != NF_ACCEPT) | ||
3521 | goto out; | ||
3522 | |||
3523 | if (send_perm) { | ||
3524 | u32 port_sid; | ||
3525 | |||
3526 | /* Fixme: make this more efficient */ | ||
3527 | err = security_port_sid(sk->sk_family, | ||
3528 | sk->sk_type, | ||
3529 | sk->sk_protocol, | ||
3530 | ntohs(ad.u.net.dport), | ||
3531 | &port_sid) ? NF_DROP : NF_ACCEPT; | ||
3532 | if (err != NF_ACCEPT) | ||
3533 | goto out; | ||
3534 | |||
3535 | err = avc_has_perm(isec->sid, port_sid, isec->sclass, | ||
3536 | send_perm, &ad) ? NF_DROP : NF_ACCEPT; | ||
3537 | } | ||
3538 | |||
3539 | out: | ||
3540 | return err; | ||
3541 | } | ||
3542 | |||
3543 | static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum, | ||
3544 | struct sk_buff **pskb, | ||
3545 | const struct net_device *in, | ||
3546 | const struct net_device *out, | ||
3547 | int (*okfn)(struct sk_buff *)) | ||
3548 | { | ||
3549 | return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET); | ||
3550 | } | ||
3551 | |||
3552 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
3553 | |||
3554 | static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum, | ||
3555 | struct sk_buff **pskb, | ||
3556 | const struct net_device *in, | ||
3557 | const struct net_device *out, | ||
3558 | int (*okfn)(struct sk_buff *)) | ||
3559 | { | ||
3560 | return selinux_ip_postroute_last(hooknum, pskb, in, out, okfn, PF_INET6); | ||
3561 | } | ||
3562 | |||
3563 | #endif /* IPV6 */ | ||
3564 | |||
3565 | #endif /* CONFIG_NETFILTER */ | ||
3566 | |||
3567 | #else | ||
3568 | |||
3569 | static inline int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) | ||
3570 | { | ||
3571 | return 0; | ||
3572 | } | ||
3573 | |||
3574 | #endif /* CONFIG_SECURITY_NETWORK */ | ||
3575 | |||
3576 | static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) | ||
3577 | { | ||
3578 | struct task_security_struct *tsec; | ||
3579 | struct av_decision avd; | ||
3580 | int err; | ||
3581 | |||
3582 | err = secondary_ops->netlink_send(sk, skb); | ||
3583 | if (err) | ||
3584 | return err; | ||
3585 | |||
3586 | tsec = current->security; | ||
3587 | |||
3588 | avd.allowed = 0; | ||
3589 | avc_has_perm_noaudit(tsec->sid, tsec->sid, | ||
3590 | SECCLASS_CAPABILITY, ~0, &avd); | ||
3591 | cap_mask(NETLINK_CB(skb).eff_cap, avd.allowed); | ||
3592 | |||
3593 | if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS) | ||
3594 | err = selinux_nlmsg_perm(sk, skb); | ||
3595 | |||
3596 | return err; | ||
3597 | } | ||
3598 | |||
3599 | static int selinux_netlink_recv(struct sk_buff *skb) | ||
3600 | { | ||
3601 | if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) | ||
3602 | return -EPERM; | ||
3603 | return 0; | ||
3604 | } | ||
3605 | |||
3606 | static int ipc_alloc_security(struct task_struct *task, | ||
3607 | struct kern_ipc_perm *perm, | ||
3608 | u16 sclass) | ||
3609 | { | ||
3610 | struct task_security_struct *tsec = task->security; | ||
3611 | struct ipc_security_struct *isec; | ||
3612 | |||
3613 | isec = kmalloc(sizeof(struct ipc_security_struct), GFP_KERNEL); | ||
3614 | if (!isec) | ||
3615 | return -ENOMEM; | ||
3616 | |||
3617 | memset(isec, 0, sizeof(struct ipc_security_struct)); | ||
3618 | isec->magic = SELINUX_MAGIC; | ||
3619 | isec->sclass = sclass; | ||
3620 | isec->ipc_perm = perm; | ||
3621 | if (tsec) { | ||
3622 | isec->sid = tsec->sid; | ||
3623 | } else { | ||
3624 | isec->sid = SECINITSID_UNLABELED; | ||
3625 | } | ||
3626 | perm->security = isec; | ||
3627 | |||
3628 | return 0; | ||
3629 | } | ||
3630 | |||
3631 | static void ipc_free_security(struct kern_ipc_perm *perm) | ||
3632 | { | ||
3633 | struct ipc_security_struct *isec = perm->security; | ||
3634 | if (!isec || isec->magic != SELINUX_MAGIC) | ||
3635 | return; | ||
3636 | |||
3637 | perm->security = NULL; | ||
3638 | kfree(isec); | ||
3639 | } | ||
3640 | |||
3641 | static int msg_msg_alloc_security(struct msg_msg *msg) | ||
3642 | { | ||
3643 | struct msg_security_struct *msec; | ||
3644 | |||
3645 | msec = kmalloc(sizeof(struct msg_security_struct), GFP_KERNEL); | ||
3646 | if (!msec) | ||
3647 | return -ENOMEM; | ||
3648 | |||
3649 | memset(msec, 0, sizeof(struct msg_security_struct)); | ||
3650 | msec->magic = SELINUX_MAGIC; | ||
3651 | msec->msg = msg; | ||
3652 | msec->sid = SECINITSID_UNLABELED; | ||
3653 | msg->security = msec; | ||
3654 | |||
3655 | return 0; | ||
3656 | } | ||
3657 | |||
3658 | static void msg_msg_free_security(struct msg_msg *msg) | ||
3659 | { | ||
3660 | struct msg_security_struct *msec = msg->security; | ||
3661 | if (!msec || msec->magic != SELINUX_MAGIC) | ||
3662 | return; | ||
3663 | |||
3664 | msg->security = NULL; | ||
3665 | kfree(msec); | ||
3666 | } | ||
3667 | |||
3668 | static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, | ||
3669 | u16 sclass, u32 perms) | ||
3670 | { | ||
3671 | struct task_security_struct *tsec; | ||
3672 | struct ipc_security_struct *isec; | ||
3673 | struct avc_audit_data ad; | ||
3674 | |||
3675 | tsec = current->security; | ||
3676 | isec = ipc_perms->security; | ||
3677 | |||
3678 | AVC_AUDIT_DATA_INIT(&ad, IPC); | ||
3679 | ad.u.ipc_id = ipc_perms->key; | ||
3680 | |||
3681 | return avc_has_perm(tsec->sid, isec->sid, sclass, perms, &ad); | ||
3682 | } | ||
3683 | |||
3684 | static int selinux_msg_msg_alloc_security(struct msg_msg *msg) | ||
3685 | { | ||
3686 | return msg_msg_alloc_security(msg); | ||
3687 | } | ||
3688 | |||
3689 | static void selinux_msg_msg_free_security(struct msg_msg *msg) | ||
3690 | { | ||
3691 | msg_msg_free_security(msg); | ||
3692 | } | ||
3693 | |||
3694 | /* message queue security operations */ | ||
3695 | static int selinux_msg_queue_alloc_security(struct msg_queue *msq) | ||
3696 | { | ||
3697 | struct task_security_struct *tsec; | ||
3698 | struct ipc_security_struct *isec; | ||
3699 | struct avc_audit_data ad; | ||
3700 | int rc; | ||
3701 | |||
3702 | rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ); | ||
3703 | if (rc) | ||
3704 | return rc; | ||
3705 | |||
3706 | tsec = current->security; | ||
3707 | isec = msq->q_perm.security; | ||
3708 | |||
3709 | AVC_AUDIT_DATA_INIT(&ad, IPC); | ||
3710 | ad.u.ipc_id = msq->q_perm.key; | ||
3711 | |||
3712 | rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ, | ||
3713 | MSGQ__CREATE, &ad); | ||
3714 | if (rc) { | ||
3715 | ipc_free_security(&msq->q_perm); | ||
3716 | return rc; | ||
3717 | } | ||
3718 | return 0; | ||
3719 | } | ||
3720 | |||
3721 | static void selinux_msg_queue_free_security(struct msg_queue *msq) | ||
3722 | { | ||
3723 | ipc_free_security(&msq->q_perm); | ||
3724 | } | ||
3725 | |||
3726 | static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg) | ||
3727 | { | ||
3728 | struct task_security_struct *tsec; | ||
3729 | struct ipc_security_struct *isec; | ||
3730 | struct avc_audit_data ad; | ||
3731 | |||
3732 | tsec = current->security; | ||
3733 | isec = msq->q_perm.security; | ||
3734 | |||
3735 | AVC_AUDIT_DATA_INIT(&ad, IPC); | ||
3736 | ad.u.ipc_id = msq->q_perm.key; | ||
3737 | |||
3738 | return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ, | ||
3739 | MSGQ__ASSOCIATE, &ad); | ||
3740 | } | ||
3741 | |||
3742 | static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd) | ||
3743 | { | ||
3744 | int err; | ||
3745 | int perms; | ||
3746 | |||
3747 | switch(cmd) { | ||
3748 | case IPC_INFO: | ||
3749 | case MSG_INFO: | ||
3750 | /* No specific object, just general system-wide information. */ | ||
3751 | return task_has_system(current, SYSTEM__IPC_INFO); | ||
3752 | case IPC_STAT: | ||
3753 | case MSG_STAT: | ||
3754 | perms = MSGQ__GETATTR | MSGQ__ASSOCIATE; | ||
3755 | break; | ||
3756 | case IPC_SET: | ||
3757 | perms = MSGQ__SETATTR; | ||
3758 | break; | ||
3759 | case IPC_RMID: | ||
3760 | perms = MSGQ__DESTROY; | ||
3761 | break; | ||
3762 | default: | ||
3763 | return 0; | ||
3764 | } | ||
3765 | |||
3766 | err = ipc_has_perm(&msq->q_perm, SECCLASS_MSGQ, perms); | ||
3767 | return err; | ||
3768 | } | ||
3769 | |||
3770 | static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg) | ||
3771 | { | ||
3772 | struct task_security_struct *tsec; | ||
3773 | struct ipc_security_struct *isec; | ||
3774 | struct msg_security_struct *msec; | ||
3775 | struct avc_audit_data ad; | ||
3776 | int rc; | ||
3777 | |||
3778 | tsec = current->security; | ||
3779 | isec = msq->q_perm.security; | ||
3780 | msec = msg->security; | ||
3781 | |||
3782 | /* | ||
3783 | * First time through, need to assign label to the message | ||
3784 | */ | ||
3785 | if (msec->sid == SECINITSID_UNLABELED) { | ||
3786 | /* | ||
3787 | * Compute new sid based on current process and | ||
3788 | * message queue this message will be stored in | ||
3789 | */ | ||
3790 | rc = security_transition_sid(tsec->sid, | ||
3791 | isec->sid, | ||
3792 | SECCLASS_MSG, | ||
3793 | &msec->sid); | ||
3794 | if (rc) | ||
3795 | return rc; | ||
3796 | } | ||
3797 | |||
3798 | AVC_AUDIT_DATA_INIT(&ad, IPC); | ||
3799 | ad.u.ipc_id = msq->q_perm.key; | ||
3800 | |||
3801 | /* Can this process write to the queue? */ | ||
3802 | rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ, | ||
3803 | MSGQ__WRITE, &ad); | ||
3804 | if (!rc) | ||
3805 | /* Can this process send the message */ | ||
3806 | rc = avc_has_perm(tsec->sid, msec->sid, | ||
3807 | SECCLASS_MSG, MSG__SEND, &ad); | ||
3808 | if (!rc) | ||
3809 | /* Can the message be put in the queue? */ | ||
3810 | rc = avc_has_perm(msec->sid, isec->sid, | ||
3811 | SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad); | ||
3812 | |||
3813 | return rc; | ||
3814 | } | ||
3815 | |||
3816 | static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, | ||
3817 | struct task_struct *target, | ||
3818 | long type, int mode) | ||
3819 | { | ||
3820 | struct task_security_struct *tsec; | ||
3821 | struct ipc_security_struct *isec; | ||
3822 | struct msg_security_struct *msec; | ||
3823 | struct avc_audit_data ad; | ||
3824 | int rc; | ||
3825 | |||
3826 | tsec = target->security; | ||
3827 | isec = msq->q_perm.security; | ||
3828 | msec = msg->security; | ||
3829 | |||
3830 | AVC_AUDIT_DATA_INIT(&ad, IPC); | ||
3831 | ad.u.ipc_id = msq->q_perm.key; | ||
3832 | |||
3833 | rc = avc_has_perm(tsec->sid, isec->sid, | ||
3834 | SECCLASS_MSGQ, MSGQ__READ, &ad); | ||
3835 | if (!rc) | ||
3836 | rc = avc_has_perm(tsec->sid, msec->sid, | ||
3837 | SECCLASS_MSG, MSG__RECEIVE, &ad); | ||
3838 | return rc; | ||
3839 | } | ||
3840 | |||
3841 | /* Shared Memory security operations */ | ||
3842 | static int selinux_shm_alloc_security(struct shmid_kernel *shp) | ||
3843 | { | ||
3844 | struct task_security_struct *tsec; | ||
3845 | struct ipc_security_struct *isec; | ||
3846 | struct avc_audit_data ad; | ||
3847 | int rc; | ||
3848 | |||
3849 | rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM); | ||
3850 | if (rc) | ||
3851 | return rc; | ||
3852 | |||
3853 | tsec = current->security; | ||
3854 | isec = shp->shm_perm.security; | ||
3855 | |||
3856 | AVC_AUDIT_DATA_INIT(&ad, IPC); | ||
3857 | ad.u.ipc_id = shp->shm_perm.key; | ||
3858 | |||
3859 | rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM, | ||
3860 | SHM__CREATE, &ad); | ||
3861 | if (rc) { | ||
3862 | ipc_free_security(&shp->shm_perm); | ||
3863 | return rc; | ||
3864 | } | ||
3865 | return 0; | ||
3866 | } | ||
3867 | |||
3868 | static void selinux_shm_free_security(struct shmid_kernel *shp) | ||
3869 | { | ||
3870 | ipc_free_security(&shp->shm_perm); | ||
3871 | } | ||
3872 | |||
3873 | static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg) | ||
3874 | { | ||
3875 | struct task_security_struct *tsec; | ||
3876 | struct ipc_security_struct *isec; | ||
3877 | struct avc_audit_data ad; | ||
3878 | |||
3879 | tsec = current->security; | ||
3880 | isec = shp->shm_perm.security; | ||
3881 | |||
3882 | AVC_AUDIT_DATA_INIT(&ad, IPC); | ||
3883 | ad.u.ipc_id = shp->shm_perm.key; | ||
3884 | |||
3885 | return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM, | ||
3886 | SHM__ASSOCIATE, &ad); | ||
3887 | } | ||
3888 | |||
3889 | /* Note, at this point, shp is locked down */ | ||
3890 | static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd) | ||
3891 | { | ||
3892 | int perms; | ||
3893 | int err; | ||
3894 | |||
3895 | switch(cmd) { | ||
3896 | case IPC_INFO: | ||
3897 | case SHM_INFO: | ||
3898 | /* No specific object, just general system-wide information. */ | ||
3899 | return task_has_system(current, SYSTEM__IPC_INFO); | ||
3900 | case IPC_STAT: | ||
3901 | case SHM_STAT: | ||
3902 | perms = SHM__GETATTR | SHM__ASSOCIATE; | ||
3903 | break; | ||
3904 | case IPC_SET: | ||
3905 | perms = SHM__SETATTR; | ||
3906 | break; | ||
3907 | case SHM_LOCK: | ||
3908 | case SHM_UNLOCK: | ||
3909 | perms = SHM__LOCK; | ||
3910 | break; | ||
3911 | case IPC_RMID: | ||
3912 | perms = SHM__DESTROY; | ||
3913 | break; | ||
3914 | default: | ||
3915 | return 0; | ||
3916 | } | ||
3917 | |||
3918 | err = ipc_has_perm(&shp->shm_perm, SECCLASS_SHM, perms); | ||
3919 | return err; | ||
3920 | } | ||
3921 | |||
3922 | static int selinux_shm_shmat(struct shmid_kernel *shp, | ||
3923 | char __user *shmaddr, int shmflg) | ||
3924 | { | ||
3925 | u32 perms; | ||
3926 | int rc; | ||
3927 | |||
3928 | rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg); | ||
3929 | if (rc) | ||
3930 | return rc; | ||
3931 | |||
3932 | if (shmflg & SHM_RDONLY) | ||
3933 | perms = SHM__READ; | ||
3934 | else | ||
3935 | perms = SHM__READ | SHM__WRITE; | ||
3936 | |||
3937 | return ipc_has_perm(&shp->shm_perm, SECCLASS_SHM, perms); | ||
3938 | } | ||
3939 | |||
3940 | /* Semaphore security operations */ | ||
3941 | static int selinux_sem_alloc_security(struct sem_array *sma) | ||
3942 | { | ||
3943 | struct task_security_struct *tsec; | ||
3944 | struct ipc_security_struct *isec; | ||
3945 | struct avc_audit_data ad; | ||
3946 | int rc; | ||
3947 | |||
3948 | rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM); | ||
3949 | if (rc) | ||
3950 | return rc; | ||
3951 | |||
3952 | tsec = current->security; | ||
3953 | isec = sma->sem_perm.security; | ||
3954 | |||
3955 | AVC_AUDIT_DATA_INIT(&ad, IPC); | ||
3956 | ad.u.ipc_id = sma->sem_perm.key; | ||
3957 | |||
3958 | rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM, | ||
3959 | SEM__CREATE, &ad); | ||
3960 | if (rc) { | ||
3961 | ipc_free_security(&sma->sem_perm); | ||
3962 | return rc; | ||
3963 | } | ||
3964 | return 0; | ||
3965 | } | ||
3966 | |||
3967 | static void selinux_sem_free_security(struct sem_array *sma) | ||
3968 | { | ||
3969 | ipc_free_security(&sma->sem_perm); | ||
3970 | } | ||
3971 | |||
3972 | static int selinux_sem_associate(struct sem_array *sma, int semflg) | ||
3973 | { | ||
3974 | struct task_security_struct *tsec; | ||
3975 | struct ipc_security_struct *isec; | ||
3976 | struct avc_audit_data ad; | ||
3977 | |||
3978 | tsec = current->security; | ||
3979 | isec = sma->sem_perm.security; | ||
3980 | |||
3981 | AVC_AUDIT_DATA_INIT(&ad, IPC); | ||
3982 | ad.u.ipc_id = sma->sem_perm.key; | ||
3983 | |||
3984 | return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM, | ||
3985 | SEM__ASSOCIATE, &ad); | ||
3986 | } | ||
3987 | |||
3988 | /* Note, at this point, sma is locked down */ | ||
3989 | static int selinux_sem_semctl(struct sem_array *sma, int cmd) | ||
3990 | { | ||
3991 | int err; | ||
3992 | u32 perms; | ||
3993 | |||
3994 | switch(cmd) { | ||
3995 | case IPC_INFO: | ||
3996 | case SEM_INFO: | ||
3997 | /* No specific object, just general system-wide information. */ | ||
3998 | return task_has_system(current, SYSTEM__IPC_INFO); | ||
3999 | case GETPID: | ||
4000 | case GETNCNT: | ||
4001 | case GETZCNT: | ||
4002 | perms = SEM__GETATTR; | ||
4003 | break; | ||
4004 | case GETVAL: | ||
4005 | case GETALL: | ||
4006 | perms = SEM__READ; | ||
4007 | break; | ||
4008 | case SETVAL: | ||
4009 | case SETALL: | ||
4010 | perms = SEM__WRITE; | ||
4011 | break; | ||
4012 | case IPC_RMID: | ||
4013 | perms = SEM__DESTROY; | ||
4014 | break; | ||
4015 | case IPC_SET: | ||
4016 | perms = SEM__SETATTR; | ||
4017 | break; | ||
4018 | case IPC_STAT: | ||
4019 | case SEM_STAT: | ||
4020 | perms = SEM__GETATTR | SEM__ASSOCIATE; | ||
4021 | break; | ||
4022 | default: | ||
4023 | return 0; | ||
4024 | } | ||
4025 | |||
4026 | err = ipc_has_perm(&sma->sem_perm, SECCLASS_SEM, perms); | ||
4027 | return err; | ||
4028 | } | ||
4029 | |||
4030 | static int selinux_sem_semop(struct sem_array *sma, | ||
4031 | struct sembuf *sops, unsigned nsops, int alter) | ||
4032 | { | ||
4033 | u32 perms; | ||
4034 | |||
4035 | if (alter) | ||
4036 | perms = SEM__READ | SEM__WRITE; | ||
4037 | else | ||
4038 | perms = SEM__READ; | ||
4039 | |||
4040 | return ipc_has_perm(&sma->sem_perm, SECCLASS_SEM, perms); | ||
4041 | } | ||
4042 | |||
4043 | static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag) | ||
4044 | { | ||
4045 | struct ipc_security_struct *isec = ipcp->security; | ||
4046 | u16 sclass = SECCLASS_IPC; | ||
4047 | u32 av = 0; | ||
4048 | |||
4049 | if (isec && isec->magic == SELINUX_MAGIC) | ||
4050 | sclass = isec->sclass; | ||
4051 | |||
4052 | av = 0; | ||
4053 | if (flag & S_IRUGO) | ||
4054 | av |= IPC__UNIX_READ; | ||
4055 | if (flag & S_IWUGO) | ||
4056 | av |= IPC__UNIX_WRITE; | ||
4057 | |||
4058 | if (av == 0) | ||
4059 | return 0; | ||
4060 | |||
4061 | return ipc_has_perm(ipcp, sclass, av); | ||
4062 | } | ||
4063 | |||
4064 | /* module stacking operations */ | ||
4065 | static int selinux_register_security (const char *name, struct security_operations *ops) | ||
4066 | { | ||
4067 | if (secondary_ops != original_ops) { | ||
4068 | printk(KERN_INFO "%s: There is already a secondary security " | ||
4069 | "module registered.\n", __FUNCTION__); | ||
4070 | return -EINVAL; | ||
4071 | } | ||
4072 | |||
4073 | secondary_ops = ops; | ||
4074 | |||
4075 | printk(KERN_INFO "%s: Registering secondary module %s\n", | ||
4076 | __FUNCTION__, | ||
4077 | name); | ||
4078 | |||
4079 | return 0; | ||
4080 | } | ||
4081 | |||
4082 | static int selinux_unregister_security (const char *name, struct security_operations *ops) | ||
4083 | { | ||
4084 | if (ops != secondary_ops) { | ||
4085 | printk (KERN_INFO "%s: trying to unregister a security module " | ||
4086 | "that is not registered.\n", __FUNCTION__); | ||
4087 | return -EINVAL; | ||
4088 | } | ||
4089 | |||
4090 | secondary_ops = original_ops; | ||
4091 | |||
4092 | return 0; | ||
4093 | } | ||
4094 | |||
4095 | static void selinux_d_instantiate (struct dentry *dentry, struct inode *inode) | ||
4096 | { | ||
4097 | if (inode) | ||
4098 | inode_doinit_with_dentry(inode, dentry); | ||
4099 | } | ||
4100 | |||
4101 | static int selinux_getprocattr(struct task_struct *p, | ||
4102 | char *name, void *value, size_t size) | ||
4103 | { | ||
4104 | struct task_security_struct *tsec; | ||
4105 | u32 sid, len; | ||
4106 | char *context; | ||
4107 | int error; | ||
4108 | |||
4109 | if (current != p) { | ||
4110 | error = task_has_perm(current, p, PROCESS__GETATTR); | ||
4111 | if (error) | ||
4112 | return error; | ||
4113 | } | ||
4114 | |||
4115 | if (!size) | ||
4116 | return -ERANGE; | ||
4117 | |||
4118 | tsec = p->security; | ||
4119 | |||
4120 | if (!strcmp(name, "current")) | ||
4121 | sid = tsec->sid; | ||
4122 | else if (!strcmp(name, "prev")) | ||
4123 | sid = tsec->osid; | ||
4124 | else if (!strcmp(name, "exec")) | ||
4125 | sid = tsec->exec_sid; | ||
4126 | else if (!strcmp(name, "fscreate")) | ||
4127 | sid = tsec->create_sid; | ||
4128 | else | ||
4129 | return -EINVAL; | ||
4130 | |||
4131 | if (!sid) | ||
4132 | return 0; | ||
4133 | |||
4134 | error = security_sid_to_context(sid, &context, &len); | ||
4135 | if (error) | ||
4136 | return error; | ||
4137 | if (len > size) { | ||
4138 | kfree(context); | ||
4139 | return -ERANGE; | ||
4140 | } | ||
4141 | memcpy(value, context, len); | ||
4142 | kfree(context); | ||
4143 | return len; | ||
4144 | } | ||
4145 | |||
4146 | static int selinux_setprocattr(struct task_struct *p, | ||
4147 | char *name, void *value, size_t size) | ||
4148 | { | ||
4149 | struct task_security_struct *tsec; | ||
4150 | u32 sid = 0; | ||
4151 | int error; | ||
4152 | char *str = value; | ||
4153 | |||
4154 | if (current != p) { | ||
4155 | /* SELinux only allows a process to change its own | ||
4156 | security attributes. */ | ||
4157 | return -EACCES; | ||
4158 | } | ||
4159 | |||
4160 | /* | ||
4161 | * Basic control over ability to set these attributes at all. | ||
4162 | * current == p, but we'll pass them separately in case the | ||
4163 | * above restriction is ever removed. | ||
4164 | */ | ||
4165 | if (!strcmp(name, "exec")) | ||
4166 | error = task_has_perm(current, p, PROCESS__SETEXEC); | ||
4167 | else if (!strcmp(name, "fscreate")) | ||
4168 | error = task_has_perm(current, p, PROCESS__SETFSCREATE); | ||
4169 | else if (!strcmp(name, "current")) | ||
4170 | error = task_has_perm(current, p, PROCESS__SETCURRENT); | ||
4171 | else | ||
4172 | error = -EINVAL; | ||
4173 | if (error) | ||
4174 | return error; | ||
4175 | |||
4176 | /* Obtain a SID for the context, if one was specified. */ | ||
4177 | if (size && str[1] && str[1] != '\n') { | ||
4178 | if (str[size-1] == '\n') { | ||
4179 | str[size-1] = 0; | ||
4180 | size--; | ||
4181 | } | ||
4182 | error = security_context_to_sid(value, size, &sid); | ||
4183 | if (error) | ||
4184 | return error; | ||
4185 | } | ||
4186 | |||
4187 | /* Permission checking based on the specified context is | ||
4188 | performed during the actual operation (execve, | ||
4189 | open/mkdir/...), when we know the full context of the | ||
4190 | operation. See selinux_bprm_set_security for the execve | ||
4191 | checks and may_create for the file creation checks. The | ||
4192 | operation will then fail if the context is not permitted. */ | ||
4193 | tsec = p->security; | ||
4194 | if (!strcmp(name, "exec")) | ||
4195 | tsec->exec_sid = sid; | ||
4196 | else if (!strcmp(name, "fscreate")) | ||
4197 | tsec->create_sid = sid; | ||
4198 | else if (!strcmp(name, "current")) { | ||
4199 | struct av_decision avd; | ||
4200 | |||
4201 | if (sid == 0) | ||
4202 | return -EINVAL; | ||
4203 | |||
4204 | /* Only allow single threaded processes to change context */ | ||
4205 | if (atomic_read(&p->mm->mm_users) != 1) { | ||
4206 | struct task_struct *g, *t; | ||
4207 | struct mm_struct *mm = p->mm; | ||
4208 | read_lock(&tasklist_lock); | ||
4209 | do_each_thread(g, t) | ||
4210 | if (t->mm == mm && t != p) { | ||
4211 | read_unlock(&tasklist_lock); | ||
4212 | return -EPERM; | ||
4213 | } | ||
4214 | while_each_thread(g, t); | ||
4215 | read_unlock(&tasklist_lock); | ||
4216 | } | ||
4217 | |||
4218 | /* Check permissions for the transition. */ | ||
4219 | error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, | ||
4220 | PROCESS__DYNTRANSITION, NULL); | ||
4221 | if (error) | ||
4222 | return error; | ||
4223 | |||
4224 | /* Check for ptracing, and update the task SID if ok. | ||
4225 | Otherwise, leave SID unchanged and fail. */ | ||
4226 | task_lock(p); | ||
4227 | if (p->ptrace & PT_PTRACED) { | ||
4228 | error = avc_has_perm_noaudit(tsec->ptrace_sid, sid, | ||
4229 | SECCLASS_PROCESS, | ||
4230 | PROCESS__PTRACE, &avd); | ||
4231 | if (!error) | ||
4232 | tsec->sid = sid; | ||
4233 | task_unlock(p); | ||
4234 | avc_audit(tsec->ptrace_sid, sid, SECCLASS_PROCESS, | ||
4235 | PROCESS__PTRACE, &avd, error, NULL); | ||
4236 | if (error) | ||
4237 | return error; | ||
4238 | } else { | ||
4239 | tsec->sid = sid; | ||
4240 | task_unlock(p); | ||
4241 | } | ||
4242 | } | ||
4243 | else | ||
4244 | return -EINVAL; | ||
4245 | |||
4246 | return size; | ||
4247 | } | ||
4248 | |||
4249 | static struct security_operations selinux_ops = { | ||
4250 | .ptrace = selinux_ptrace, | ||
4251 | .capget = selinux_capget, | ||
4252 | .capset_check = selinux_capset_check, | ||
4253 | .capset_set = selinux_capset_set, | ||
4254 | .sysctl = selinux_sysctl, | ||
4255 | .capable = selinux_capable, | ||
4256 | .quotactl = selinux_quotactl, | ||
4257 | .quota_on = selinux_quota_on, | ||
4258 | .syslog = selinux_syslog, | ||
4259 | .vm_enough_memory = selinux_vm_enough_memory, | ||
4260 | |||
4261 | .netlink_send = selinux_netlink_send, | ||
4262 | .netlink_recv = selinux_netlink_recv, | ||
4263 | |||
4264 | .bprm_alloc_security = selinux_bprm_alloc_security, | ||
4265 | .bprm_free_security = selinux_bprm_free_security, | ||
4266 | .bprm_apply_creds = selinux_bprm_apply_creds, | ||
4267 | .bprm_post_apply_creds = selinux_bprm_post_apply_creds, | ||
4268 | .bprm_set_security = selinux_bprm_set_security, | ||
4269 | .bprm_check_security = selinux_bprm_check_security, | ||
4270 | .bprm_secureexec = selinux_bprm_secureexec, | ||
4271 | |||
4272 | .sb_alloc_security = selinux_sb_alloc_security, | ||
4273 | .sb_free_security = selinux_sb_free_security, | ||
4274 | .sb_copy_data = selinux_sb_copy_data, | ||
4275 | .sb_kern_mount = selinux_sb_kern_mount, | ||
4276 | .sb_statfs = selinux_sb_statfs, | ||
4277 | .sb_mount = selinux_mount, | ||
4278 | .sb_umount = selinux_umount, | ||
4279 | |||
4280 | .inode_alloc_security = selinux_inode_alloc_security, | ||
4281 | .inode_free_security = selinux_inode_free_security, | ||
4282 | .inode_create = selinux_inode_create, | ||
4283 | .inode_post_create = selinux_inode_post_create, | ||
4284 | .inode_link = selinux_inode_link, | ||
4285 | .inode_post_link = selinux_inode_post_link, | ||
4286 | .inode_unlink = selinux_inode_unlink, | ||
4287 | .inode_symlink = selinux_inode_symlink, | ||
4288 | .inode_post_symlink = selinux_inode_post_symlink, | ||
4289 | .inode_mkdir = selinux_inode_mkdir, | ||
4290 | .inode_post_mkdir = selinux_inode_post_mkdir, | ||
4291 | .inode_rmdir = selinux_inode_rmdir, | ||
4292 | .inode_mknod = selinux_inode_mknod, | ||
4293 | .inode_post_mknod = selinux_inode_post_mknod, | ||
4294 | .inode_rename = selinux_inode_rename, | ||
4295 | .inode_post_rename = selinux_inode_post_rename, | ||
4296 | .inode_readlink = selinux_inode_readlink, | ||
4297 | .inode_follow_link = selinux_inode_follow_link, | ||
4298 | .inode_permission = selinux_inode_permission, | ||
4299 | .inode_setattr = selinux_inode_setattr, | ||
4300 | .inode_getattr = selinux_inode_getattr, | ||
4301 | .inode_setxattr = selinux_inode_setxattr, | ||
4302 | .inode_post_setxattr = selinux_inode_post_setxattr, | ||
4303 | .inode_getxattr = selinux_inode_getxattr, | ||
4304 | .inode_listxattr = selinux_inode_listxattr, | ||
4305 | .inode_removexattr = selinux_inode_removexattr, | ||
4306 | .inode_getsecurity = selinux_inode_getsecurity, | ||
4307 | .inode_setsecurity = selinux_inode_setsecurity, | ||
4308 | .inode_listsecurity = selinux_inode_listsecurity, | ||
4309 | |||
4310 | .file_permission = selinux_file_permission, | ||
4311 | .file_alloc_security = selinux_file_alloc_security, | ||
4312 | .file_free_security = selinux_file_free_security, | ||
4313 | .file_ioctl = selinux_file_ioctl, | ||
4314 | .file_mmap = selinux_file_mmap, | ||
4315 | .file_mprotect = selinux_file_mprotect, | ||
4316 | .file_lock = selinux_file_lock, | ||
4317 | .file_fcntl = selinux_file_fcntl, | ||
4318 | .file_set_fowner = selinux_file_set_fowner, | ||
4319 | .file_send_sigiotask = selinux_file_send_sigiotask, | ||
4320 | .file_receive = selinux_file_receive, | ||
4321 | |||
4322 | .task_create = selinux_task_create, | ||
4323 | .task_alloc_security = selinux_task_alloc_security, | ||
4324 | .task_free_security = selinux_task_free_security, | ||
4325 | .task_setuid = selinux_task_setuid, | ||
4326 | .task_post_setuid = selinux_task_post_setuid, | ||
4327 | .task_setgid = selinux_task_setgid, | ||
4328 | .task_setpgid = selinux_task_setpgid, | ||
4329 | .task_getpgid = selinux_task_getpgid, | ||
4330 | .task_getsid = selinux_task_getsid, | ||
4331 | .task_setgroups = selinux_task_setgroups, | ||
4332 | .task_setnice = selinux_task_setnice, | ||
4333 | .task_setrlimit = selinux_task_setrlimit, | ||
4334 | .task_setscheduler = selinux_task_setscheduler, | ||
4335 | .task_getscheduler = selinux_task_getscheduler, | ||
4336 | .task_kill = selinux_task_kill, | ||
4337 | .task_wait = selinux_task_wait, | ||
4338 | .task_prctl = selinux_task_prctl, | ||
4339 | .task_reparent_to_init = selinux_task_reparent_to_init, | ||
4340 | .task_to_inode = selinux_task_to_inode, | ||
4341 | |||
4342 | .ipc_permission = selinux_ipc_permission, | ||
4343 | |||
4344 | .msg_msg_alloc_security = selinux_msg_msg_alloc_security, | ||
4345 | .msg_msg_free_security = selinux_msg_msg_free_security, | ||
4346 | |||
4347 | .msg_queue_alloc_security = selinux_msg_queue_alloc_security, | ||
4348 | .msg_queue_free_security = selinux_msg_queue_free_security, | ||
4349 | .msg_queue_associate = selinux_msg_queue_associate, | ||
4350 | .msg_queue_msgctl = selinux_msg_queue_msgctl, | ||
4351 | .msg_queue_msgsnd = selinux_msg_queue_msgsnd, | ||
4352 | .msg_queue_msgrcv = selinux_msg_queue_msgrcv, | ||
4353 | |||
4354 | .shm_alloc_security = selinux_shm_alloc_security, | ||
4355 | .shm_free_security = selinux_shm_free_security, | ||
4356 | .shm_associate = selinux_shm_associate, | ||
4357 | .shm_shmctl = selinux_shm_shmctl, | ||
4358 | .shm_shmat = selinux_shm_shmat, | ||
4359 | |||
4360 | .sem_alloc_security = selinux_sem_alloc_security, | ||
4361 | .sem_free_security = selinux_sem_free_security, | ||
4362 | .sem_associate = selinux_sem_associate, | ||
4363 | .sem_semctl = selinux_sem_semctl, | ||
4364 | .sem_semop = selinux_sem_semop, | ||
4365 | |||
4366 | .register_security = selinux_register_security, | ||
4367 | .unregister_security = selinux_unregister_security, | ||
4368 | |||
4369 | .d_instantiate = selinux_d_instantiate, | ||
4370 | |||
4371 | .getprocattr = selinux_getprocattr, | ||
4372 | .setprocattr = selinux_setprocattr, | ||
4373 | |||
4374 | #ifdef CONFIG_SECURITY_NETWORK | ||
4375 | .unix_stream_connect = selinux_socket_unix_stream_connect, | ||
4376 | .unix_may_send = selinux_socket_unix_may_send, | ||
4377 | |||
4378 | .socket_create = selinux_socket_create, | ||
4379 | .socket_post_create = selinux_socket_post_create, | ||
4380 | .socket_bind = selinux_socket_bind, | ||
4381 | .socket_connect = selinux_socket_connect, | ||
4382 | .socket_listen = selinux_socket_listen, | ||
4383 | .socket_accept = selinux_socket_accept, | ||
4384 | .socket_sendmsg = selinux_socket_sendmsg, | ||
4385 | .socket_recvmsg = selinux_socket_recvmsg, | ||
4386 | .socket_getsockname = selinux_socket_getsockname, | ||
4387 | .socket_getpeername = selinux_socket_getpeername, | ||
4388 | .socket_getsockopt = selinux_socket_getsockopt, | ||
4389 | .socket_setsockopt = selinux_socket_setsockopt, | ||
4390 | .socket_shutdown = selinux_socket_shutdown, | ||
4391 | .socket_sock_rcv_skb = selinux_socket_sock_rcv_skb, | ||
4392 | .socket_getpeersec = selinux_socket_getpeersec, | ||
4393 | .sk_alloc_security = selinux_sk_alloc_security, | ||
4394 | .sk_free_security = selinux_sk_free_security, | ||
4395 | #endif | ||
4396 | }; | ||
4397 | |||
4398 | static __init int selinux_init(void) | ||
4399 | { | ||
4400 | struct task_security_struct *tsec; | ||
4401 | |||
4402 | if (!selinux_enabled) { | ||
4403 | printk(KERN_INFO "SELinux: Disabled at boot.\n"); | ||
4404 | return 0; | ||
4405 | } | ||
4406 | |||
4407 | printk(KERN_INFO "SELinux: Initializing.\n"); | ||
4408 | |||
4409 | /* Set the security state for the initial task. */ | ||
4410 | if (task_alloc_security(current)) | ||
4411 | panic("SELinux: Failed to initialize initial task.\n"); | ||
4412 | tsec = current->security; | ||
4413 | tsec->osid = tsec->sid = SECINITSID_KERNEL; | ||
4414 | |||
4415 | avc_init(); | ||
4416 | |||
4417 | original_ops = secondary_ops = security_ops; | ||
4418 | if (!secondary_ops) | ||
4419 | panic ("SELinux: No initial security operations\n"); | ||
4420 | if (register_security (&selinux_ops)) | ||
4421 | panic("SELinux: Unable to register with kernel.\n"); | ||
4422 | |||
4423 | if (selinux_enforcing) { | ||
4424 | printk(KERN_INFO "SELinux: Starting in enforcing mode\n"); | ||
4425 | } else { | ||
4426 | printk(KERN_INFO "SELinux: Starting in permissive mode\n"); | ||
4427 | } | ||
4428 | return 0; | ||
4429 | } | ||
4430 | |||
4431 | void selinux_complete_init(void) | ||
4432 | { | ||
4433 | printk(KERN_INFO "SELinux: Completing initialization.\n"); | ||
4434 | |||
4435 | /* Set up any superblocks initialized prior to the policy load. */ | ||
4436 | printk(KERN_INFO "SELinux: Setting up existing superblocks.\n"); | ||
4437 | spin_lock(&sb_security_lock); | ||
4438 | next_sb: | ||
4439 | if (!list_empty(&superblock_security_head)) { | ||
4440 | struct superblock_security_struct *sbsec = | ||
4441 | list_entry(superblock_security_head.next, | ||
4442 | struct superblock_security_struct, | ||
4443 | list); | ||
4444 | struct super_block *sb = sbsec->sb; | ||
4445 | spin_lock(&sb_lock); | ||
4446 | sb->s_count++; | ||
4447 | spin_unlock(&sb_lock); | ||
4448 | spin_unlock(&sb_security_lock); | ||
4449 | down_read(&sb->s_umount); | ||
4450 | if (sb->s_root) | ||
4451 | superblock_doinit(sb, NULL); | ||
4452 | drop_super(sb); | ||
4453 | spin_lock(&sb_security_lock); | ||
4454 | list_del_init(&sbsec->list); | ||
4455 | goto next_sb; | ||
4456 | } | ||
4457 | spin_unlock(&sb_security_lock); | ||
4458 | } | ||
4459 | |||
4460 | /* SELinux requires early initialization in order to label | ||
4461 | all processes and objects when they are created. */ | ||
4462 | security_initcall(selinux_init); | ||
4463 | |||
4464 | #if defined(CONFIG_SECURITY_NETWORK) && defined(CONFIG_NETFILTER) | ||
4465 | |||
4466 | static struct nf_hook_ops selinux_ipv4_op = { | ||
4467 | .hook = selinux_ipv4_postroute_last, | ||
4468 | .owner = THIS_MODULE, | ||
4469 | .pf = PF_INET, | ||
4470 | .hooknum = NF_IP_POST_ROUTING, | ||
4471 | .priority = NF_IP_PRI_SELINUX_LAST, | ||
4472 | }; | ||
4473 | |||
4474 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
4475 | |||
4476 | static struct nf_hook_ops selinux_ipv6_op = { | ||
4477 | .hook = selinux_ipv6_postroute_last, | ||
4478 | .owner = THIS_MODULE, | ||
4479 | .pf = PF_INET6, | ||
4480 | .hooknum = NF_IP6_POST_ROUTING, | ||
4481 | .priority = NF_IP6_PRI_SELINUX_LAST, | ||
4482 | }; | ||
4483 | |||
4484 | #endif /* IPV6 */ | ||
4485 | |||
4486 | static int __init selinux_nf_ip_init(void) | ||
4487 | { | ||
4488 | int err = 0; | ||
4489 | |||
4490 | if (!selinux_enabled) | ||
4491 | goto out; | ||
4492 | |||
4493 | printk(KERN_INFO "SELinux: Registering netfilter hooks\n"); | ||
4494 | |||
4495 | err = nf_register_hook(&selinux_ipv4_op); | ||
4496 | if (err) | ||
4497 | panic("SELinux: nf_register_hook for IPv4: error %d\n", err); | ||
4498 | |||
4499 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
4500 | |||
4501 | err = nf_register_hook(&selinux_ipv6_op); | ||
4502 | if (err) | ||
4503 | panic("SELinux: nf_register_hook for IPv6: error %d\n", err); | ||
4504 | |||
4505 | #endif /* IPV6 */ | ||
4506 | out: | ||
4507 | return err; | ||
4508 | } | ||
4509 | |||
4510 | __initcall(selinux_nf_ip_init); | ||
4511 | |||
4512 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE | ||
4513 | static void selinux_nf_ip_exit(void) | ||
4514 | { | ||
4515 | printk(KERN_INFO "SELinux: Unregistering netfilter hooks\n"); | ||
4516 | |||
4517 | nf_unregister_hook(&selinux_ipv4_op); | ||
4518 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
4519 | nf_unregister_hook(&selinux_ipv6_op); | ||
4520 | #endif /* IPV6 */ | ||
4521 | } | ||
4522 | #endif | ||
4523 | |||
4524 | #else /* CONFIG_SECURITY_NETWORK && CONFIG_NETFILTER */ | ||
4525 | |||
4526 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE | ||
4527 | #define selinux_nf_ip_exit() | ||
4528 | #endif | ||
4529 | |||
4530 | #endif /* CONFIG_SECURITY_NETWORK && CONFIG_NETFILTER */ | ||
4531 | |||
4532 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE | ||
4533 | int selinux_disable(void) | ||
4534 | { | ||
4535 | extern void exit_sel_fs(void); | ||
4536 | static int selinux_disabled = 0; | ||
4537 | |||
4538 | if (ss_initialized) { | ||
4539 | /* Not permitted after initial policy load. */ | ||
4540 | return -EINVAL; | ||
4541 | } | ||
4542 | |||
4543 | if (selinux_disabled) { | ||
4544 | /* Only do this once. */ | ||
4545 | return -EINVAL; | ||
4546 | } | ||
4547 | |||
4548 | printk(KERN_INFO "SELinux: Disabled at runtime.\n"); | ||
4549 | |||
4550 | selinux_disabled = 1; | ||
4551 | |||
4552 | /* Reset security_ops to the secondary module, dummy or capability. */ | ||
4553 | security_ops = secondary_ops; | ||
4554 | |||
4555 | /* Unregister netfilter hooks. */ | ||
4556 | selinux_nf_ip_exit(); | ||
4557 | |||
4558 | /* Unregister selinuxfs. */ | ||
4559 | exit_sel_fs(); | ||
4560 | |||
4561 | return 0; | ||
4562 | } | ||
4563 | #endif | ||
4564 | |||
4565 | |||
diff --git a/security/selinux/include/av_inherit.h b/security/selinux/include/av_inherit.h new file mode 100644 index 000000000000..9facb27822a1 --- /dev/null +++ b/security/selinux/include/av_inherit.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* This file is automatically generated. Do not edit. */ | ||
2 | S_(SECCLASS_DIR, file, 0x00020000UL) | ||
3 | S_(SECCLASS_FILE, file, 0x00020000UL) | ||
4 | S_(SECCLASS_LNK_FILE, file, 0x00020000UL) | ||
5 | S_(SECCLASS_CHR_FILE, file, 0x00020000UL) | ||
6 | S_(SECCLASS_BLK_FILE, file, 0x00020000UL) | ||
7 | S_(SECCLASS_SOCK_FILE, file, 0x00020000UL) | ||
8 | S_(SECCLASS_FIFO_FILE, file, 0x00020000UL) | ||
9 | S_(SECCLASS_SOCKET, socket, 0x00400000UL) | ||
10 | S_(SECCLASS_TCP_SOCKET, socket, 0x00400000UL) | ||
11 | S_(SECCLASS_UDP_SOCKET, socket, 0x00400000UL) | ||
12 | S_(SECCLASS_RAWIP_SOCKET, socket, 0x00400000UL) | ||
13 | S_(SECCLASS_NETLINK_SOCKET, socket, 0x00400000UL) | ||
14 | S_(SECCLASS_PACKET_SOCKET, socket, 0x00400000UL) | ||
15 | S_(SECCLASS_KEY_SOCKET, socket, 0x00400000UL) | ||
16 | S_(SECCLASS_UNIX_STREAM_SOCKET, socket, 0x00400000UL) | ||
17 | S_(SECCLASS_UNIX_DGRAM_SOCKET, socket, 0x00400000UL) | ||
18 | S_(SECCLASS_IPC, ipc, 0x00000200UL) | ||
19 | S_(SECCLASS_SEM, ipc, 0x00000200UL) | ||
20 | S_(SECCLASS_MSGQ, ipc, 0x00000200UL) | ||
21 | S_(SECCLASS_SHM, ipc, 0x00000200UL) | ||
22 | S_(SECCLASS_NETLINK_ROUTE_SOCKET, socket, 0x00400000UL) | ||
23 | S_(SECCLASS_NETLINK_FIREWALL_SOCKET, socket, 0x00400000UL) | ||
24 | S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, socket, 0x00400000UL) | ||
25 | S_(SECCLASS_NETLINK_NFLOG_SOCKET, socket, 0x00400000UL) | ||
26 | S_(SECCLASS_NETLINK_XFRM_SOCKET, socket, 0x00400000UL) | ||
27 | S_(SECCLASS_NETLINK_SELINUX_SOCKET, socket, 0x00400000UL) | ||
28 | S_(SECCLASS_NETLINK_AUDIT_SOCKET, socket, 0x00400000UL) | ||
29 | S_(SECCLASS_NETLINK_IP6FW_SOCKET, socket, 0x00400000UL) | ||
30 | S_(SECCLASS_NETLINK_DNRT_SOCKET, socket, 0x00400000UL) | ||
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h new file mode 100644 index 000000000000..903e8b3cc2e9 --- /dev/null +++ b/security/selinux/include/av_perm_to_string.h | |||
@@ -0,0 +1,232 @@ | |||
1 | /* This file is automatically generated. Do not edit. */ | ||
2 | S_(SECCLASS_FILESYSTEM, FILESYSTEM__MOUNT, "mount") | ||
3 | S_(SECCLASS_FILESYSTEM, FILESYSTEM__REMOUNT, "remount") | ||
4 | S_(SECCLASS_FILESYSTEM, FILESYSTEM__UNMOUNT, "unmount") | ||
5 | S_(SECCLASS_FILESYSTEM, FILESYSTEM__GETATTR, "getattr") | ||
6 | S_(SECCLASS_FILESYSTEM, FILESYSTEM__RELABELFROM, "relabelfrom") | ||
7 | S_(SECCLASS_FILESYSTEM, FILESYSTEM__RELABELTO, "relabelto") | ||
8 | S_(SECCLASS_FILESYSTEM, FILESYSTEM__TRANSITION, "transition") | ||
9 | S_(SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, "associate") | ||
10 | S_(SECCLASS_FILESYSTEM, FILESYSTEM__QUOTAMOD, "quotamod") | ||
11 | S_(SECCLASS_FILESYSTEM, FILESYSTEM__QUOTAGET, "quotaget") | ||
12 | S_(SECCLASS_DIR, DIR__ADD_NAME, "add_name") | ||
13 | S_(SECCLASS_DIR, DIR__REMOVE_NAME, "remove_name") | ||
14 | S_(SECCLASS_DIR, DIR__REPARENT, "reparent") | ||
15 | S_(SECCLASS_DIR, DIR__SEARCH, "search") | ||
16 | S_(SECCLASS_DIR, DIR__RMDIR, "rmdir") | ||
17 | S_(SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, "execute_no_trans") | ||
18 | S_(SECCLASS_FILE, FILE__ENTRYPOINT, "entrypoint") | ||
19 | S_(SECCLASS_FILE, FILE__EXECMOD, "execmod") | ||
20 | S_(SECCLASS_CHR_FILE, CHR_FILE__EXECUTE_NO_TRANS, "execute_no_trans") | ||
21 | S_(SECCLASS_CHR_FILE, CHR_FILE__ENTRYPOINT, "entrypoint") | ||
22 | S_(SECCLASS_CHR_FILE, CHR_FILE__EXECMOD, "execmod") | ||
23 | S_(SECCLASS_FD, FD__USE, "use") | ||
24 | S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__CONNECTTO, "connectto") | ||
25 | S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NEWCONN, "newconn") | ||
26 | S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__ACCEPTFROM, "acceptfrom") | ||
27 | S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NODE_BIND, "node_bind") | ||
28 | S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NAME_CONNECT, "name_connect") | ||
29 | S_(SECCLASS_UDP_SOCKET, UDP_SOCKET__NODE_BIND, "node_bind") | ||
30 | S_(SECCLASS_RAWIP_SOCKET, RAWIP_SOCKET__NODE_BIND, "node_bind") | ||
31 | S_(SECCLASS_NODE, NODE__TCP_RECV, "tcp_recv") | ||
32 | S_(SECCLASS_NODE, NODE__TCP_SEND, "tcp_send") | ||
33 | S_(SECCLASS_NODE, NODE__UDP_RECV, "udp_recv") | ||
34 | S_(SECCLASS_NODE, NODE__UDP_SEND, "udp_send") | ||
35 | S_(SECCLASS_NODE, NODE__RAWIP_RECV, "rawip_recv") | ||
36 | S_(SECCLASS_NODE, NODE__RAWIP_SEND, "rawip_send") | ||
37 | S_(SECCLASS_NODE, NODE__ENFORCE_DEST, "enforce_dest") | ||
38 | S_(SECCLASS_NETIF, NETIF__TCP_RECV, "tcp_recv") | ||
39 | S_(SECCLASS_NETIF, NETIF__TCP_SEND, "tcp_send") | ||
40 | S_(SECCLASS_NETIF, NETIF__UDP_RECV, "udp_recv") | ||
41 | S_(SECCLASS_NETIF, NETIF__UDP_SEND, "udp_send") | ||
42 | S_(SECCLASS_NETIF, NETIF__RAWIP_RECV, "rawip_recv") | ||
43 | S_(SECCLASS_NETIF, NETIF__RAWIP_SEND, "rawip_send") | ||
44 | S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__CONNECTTO, "connectto") | ||
45 | S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__NEWCONN, "newconn") | ||
46 | S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__ACCEPTFROM, "acceptfrom") | ||
47 | S_(SECCLASS_PROCESS, PROCESS__FORK, "fork") | ||
48 | S_(SECCLASS_PROCESS, PROCESS__TRANSITION, "transition") | ||
49 | S_(SECCLASS_PROCESS, PROCESS__SIGCHLD, "sigchld") | ||
50 | S_(SECCLASS_PROCESS, PROCESS__SIGKILL, "sigkill") | ||
51 | S_(SECCLASS_PROCESS, PROCESS__SIGSTOP, "sigstop") | ||
52 | S_(SECCLASS_PROCESS, PROCESS__SIGNULL, "signull") | ||
53 | S_(SECCLASS_PROCESS, PROCESS__SIGNAL, "signal") | ||
54 | S_(SECCLASS_PROCESS, PROCESS__PTRACE, "ptrace") | ||
55 | S_(SECCLASS_PROCESS, PROCESS__GETSCHED, "getsched") | ||
56 | S_(SECCLASS_PROCESS, PROCESS__SETSCHED, "setsched") | ||
57 | S_(SECCLASS_PROCESS, PROCESS__GETSESSION, "getsession") | ||
58 | S_(SECCLASS_PROCESS, PROCESS__GETPGID, "getpgid") | ||
59 | S_(SECCLASS_PROCESS, PROCESS__SETPGID, "setpgid") | ||
60 | S_(SECCLASS_PROCESS, PROCESS__GETCAP, "getcap") | ||
61 | S_(SECCLASS_PROCESS, PROCESS__SETCAP, "setcap") | ||
62 | S_(SECCLASS_PROCESS, PROCESS__SHARE, "share") | ||
63 | S_(SECCLASS_PROCESS, PROCESS__GETATTR, "getattr") | ||
64 | S_(SECCLASS_PROCESS, PROCESS__SETEXEC, "setexec") | ||
65 | S_(SECCLASS_PROCESS, PROCESS__SETFSCREATE, "setfscreate") | ||
66 | S_(SECCLASS_PROCESS, PROCESS__NOATSECURE, "noatsecure") | ||
67 | S_(SECCLASS_PROCESS, PROCESS__SIGINH, "siginh") | ||
68 | S_(SECCLASS_PROCESS, PROCESS__SETRLIMIT, "setrlimit") | ||
69 | S_(SECCLASS_PROCESS, PROCESS__RLIMITINH, "rlimitinh") | ||
70 | S_(SECCLASS_PROCESS, PROCESS__DYNTRANSITION, "dyntransition") | ||
71 | S_(SECCLASS_PROCESS, PROCESS__SETCURRENT, "setcurrent") | ||
72 | S_(SECCLASS_PROCESS, PROCESS__EXECMEM, "execmem") | ||
73 | S_(SECCLASS_MSGQ, MSGQ__ENQUEUE, "enqueue") | ||
74 | S_(SECCLASS_MSG, MSG__SEND, "send") | ||
75 | S_(SECCLASS_MSG, MSG__RECEIVE, "receive") | ||
76 | S_(SECCLASS_SHM, SHM__LOCK, "lock") | ||
77 | S_(SECCLASS_SECURITY, SECURITY__COMPUTE_AV, "compute_av") | ||
78 | S_(SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE, "compute_create") | ||
79 | S_(SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER, "compute_member") | ||
80 | S_(SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, "check_context") | ||
81 | S_(SECCLASS_SECURITY, SECURITY__LOAD_POLICY, "load_policy") | ||
82 | S_(SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL, "compute_relabel") | ||
83 | S_(SECCLASS_SECURITY, SECURITY__COMPUTE_USER, "compute_user") | ||
84 | S_(SECCLASS_SECURITY, SECURITY__SETENFORCE, "setenforce") | ||
85 | S_(SECCLASS_SECURITY, SECURITY__SETBOOL, "setbool") | ||
86 | S_(SECCLASS_SECURITY, SECURITY__SETSECPARAM, "setsecparam") | ||
87 | S_(SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT, "setcheckreqprot") | ||
88 | S_(SECCLASS_SYSTEM, SYSTEM__IPC_INFO, "ipc_info") | ||
89 | S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read") | ||
90 | S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod") | ||
91 | S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console") | ||
92 | S_(SECCLASS_CAPABILITY, CAPABILITY__CHOWN, "chown") | ||
93 | S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_OVERRIDE, "dac_override") | ||
94 | S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_READ_SEARCH, "dac_read_search") | ||
95 | S_(SECCLASS_CAPABILITY, CAPABILITY__FOWNER, "fowner") | ||
96 | S_(SECCLASS_CAPABILITY, CAPABILITY__FSETID, "fsetid") | ||
97 | S_(SECCLASS_CAPABILITY, CAPABILITY__KILL, "kill") | ||
98 | S_(SECCLASS_CAPABILITY, CAPABILITY__SETGID, "setgid") | ||
99 | S_(SECCLASS_CAPABILITY, CAPABILITY__SETUID, "setuid") | ||
100 | S_(SECCLASS_CAPABILITY, CAPABILITY__SETPCAP, "setpcap") | ||
101 | S_(SECCLASS_CAPABILITY, CAPABILITY__LINUX_IMMUTABLE, "linux_immutable") | ||
102 | S_(SECCLASS_CAPABILITY, CAPABILITY__NET_BIND_SERVICE, "net_bind_service") | ||
103 | S_(SECCLASS_CAPABILITY, CAPABILITY__NET_BROADCAST, "net_broadcast") | ||
104 | S_(SECCLASS_CAPABILITY, CAPABILITY__NET_ADMIN, "net_admin") | ||
105 | S_(SECCLASS_CAPABILITY, CAPABILITY__NET_RAW, "net_raw") | ||
106 | S_(SECCLASS_CAPABILITY, CAPABILITY__IPC_LOCK, "ipc_lock") | ||
107 | S_(SECCLASS_CAPABILITY, CAPABILITY__IPC_OWNER, "ipc_owner") | ||
108 | S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_MODULE, "sys_module") | ||
109 | S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_RAWIO, "sys_rawio") | ||
110 | S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_CHROOT, "sys_chroot") | ||
111 | S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_PTRACE, "sys_ptrace") | ||
112 | S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_PACCT, "sys_pacct") | ||
113 | S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_ADMIN, "sys_admin") | ||
114 | S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_BOOT, "sys_boot") | ||
115 | S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_NICE, "sys_nice") | ||
116 | S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_RESOURCE, "sys_resource") | ||
117 | S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_TIME, "sys_time") | ||
118 | S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_TTY_CONFIG, "sys_tty_config") | ||
119 | S_(SECCLASS_CAPABILITY, CAPABILITY__MKNOD, "mknod") | ||
120 | S_(SECCLASS_CAPABILITY, CAPABILITY__LEASE, "lease") | ||
121 | S_(SECCLASS_PASSWD, PASSWD__PASSWD, "passwd") | ||
122 | S_(SECCLASS_PASSWD, PASSWD__CHFN, "chfn") | ||
123 | S_(SECCLASS_PASSWD, PASSWD__CHSH, "chsh") | ||
124 | S_(SECCLASS_PASSWD, PASSWD__ROOTOK, "rootok") | ||
125 | S_(SECCLASS_PASSWD, PASSWD__CRONTAB, "crontab") | ||
126 | S_(SECCLASS_DRAWABLE, DRAWABLE__CREATE, "create") | ||
127 | S_(SECCLASS_DRAWABLE, DRAWABLE__DESTROY, "destroy") | ||
128 | S_(SECCLASS_DRAWABLE, DRAWABLE__DRAW, "draw") | ||
129 | S_(SECCLASS_DRAWABLE, DRAWABLE__COPY, "copy") | ||
130 | S_(SECCLASS_DRAWABLE, DRAWABLE__GETATTR, "getattr") | ||
131 | S_(SECCLASS_GC, GC__CREATE, "create") | ||
132 | S_(SECCLASS_GC, GC__FREE, "free") | ||
133 | S_(SECCLASS_GC, GC__GETATTR, "getattr") | ||
134 | S_(SECCLASS_GC, GC__SETATTR, "setattr") | ||
135 | S_(SECCLASS_WINDOW, WINDOW__ADDCHILD, "addchild") | ||
136 | S_(SECCLASS_WINDOW, WINDOW__CREATE, "create") | ||
137 | S_(SECCLASS_WINDOW, WINDOW__DESTROY, "destroy") | ||
138 | S_(SECCLASS_WINDOW, WINDOW__MAP, "map") | ||
139 | S_(SECCLASS_WINDOW, WINDOW__UNMAP, "unmap") | ||
140 | S_(SECCLASS_WINDOW, WINDOW__CHSTACK, "chstack") | ||
141 | S_(SECCLASS_WINDOW, WINDOW__CHPROPLIST, "chproplist") | ||
142 | S_(SECCLASS_WINDOW, WINDOW__CHPROP, "chprop") | ||
143 | S_(SECCLASS_WINDOW, WINDOW__LISTPROP, "listprop") | ||
144 | S_(SECCLASS_WINDOW, WINDOW__GETATTR, "getattr") | ||
145 | S_(SECCLASS_WINDOW, WINDOW__SETATTR, "setattr") | ||
146 | S_(SECCLASS_WINDOW, WINDOW__SETFOCUS, "setfocus") | ||
147 | S_(SECCLASS_WINDOW, WINDOW__MOVE, "move") | ||
148 | S_(SECCLASS_WINDOW, WINDOW__CHSELECTION, "chselection") | ||
149 | S_(SECCLASS_WINDOW, WINDOW__CHPARENT, "chparent") | ||
150 | S_(SECCLASS_WINDOW, WINDOW__CTRLLIFE, "ctrllife") | ||
151 | S_(SECCLASS_WINDOW, WINDOW__ENUMERATE, "enumerate") | ||
152 | S_(SECCLASS_WINDOW, WINDOW__TRANSPARENT, "transparent") | ||
153 | S_(SECCLASS_WINDOW, WINDOW__MOUSEMOTION, "mousemotion") | ||
154 | S_(SECCLASS_WINDOW, WINDOW__CLIENTCOMEVENT, "clientcomevent") | ||
155 | S_(SECCLASS_WINDOW, WINDOW__INPUTEVENT, "inputevent") | ||
156 | S_(SECCLASS_WINDOW, WINDOW__DRAWEVENT, "drawevent") | ||
157 | S_(SECCLASS_WINDOW, WINDOW__WINDOWCHANGEEVENT, "windowchangeevent") | ||
158 | S_(SECCLASS_WINDOW, WINDOW__WINDOWCHANGEREQUEST, "windowchangerequest") | ||
159 | S_(SECCLASS_WINDOW, WINDOW__SERVERCHANGEEVENT, "serverchangeevent") | ||
160 | S_(SECCLASS_WINDOW, WINDOW__EXTENSIONEVENT, "extensionevent") | ||
161 | S_(SECCLASS_FONT, FONT__LOAD, "load") | ||
162 | S_(SECCLASS_FONT, FONT__FREE, "free") | ||
163 | S_(SECCLASS_FONT, FONT__GETATTR, "getattr") | ||
164 | S_(SECCLASS_FONT, FONT__USE, "use") | ||
165 | S_(SECCLASS_COLORMAP, COLORMAP__CREATE, "create") | ||
166 | S_(SECCLASS_COLORMAP, COLORMAP__FREE, "free") | ||
167 | S_(SECCLASS_COLORMAP, COLORMAP__INSTALL, "install") | ||
168 | S_(SECCLASS_COLORMAP, COLORMAP__UNINSTALL, "uninstall") | ||
169 | S_(SECCLASS_COLORMAP, COLORMAP__LIST, "list") | ||
170 | S_(SECCLASS_COLORMAP, COLORMAP__READ, "read") | ||
171 | S_(SECCLASS_COLORMAP, COLORMAP__STORE, "store") | ||
172 | S_(SECCLASS_COLORMAP, COLORMAP__GETATTR, "getattr") | ||
173 | S_(SECCLASS_COLORMAP, COLORMAP__SETATTR, "setattr") | ||
174 | S_(SECCLASS_PROPERTY, PROPERTY__CREATE, "create") | ||
175 | S_(SECCLASS_PROPERTY, PROPERTY__FREE, "free") | ||
176 | S_(SECCLASS_PROPERTY, PROPERTY__READ, "read") | ||
177 | S_(SECCLASS_PROPERTY, PROPERTY__WRITE, "write") | ||
178 | S_(SECCLASS_CURSOR, CURSOR__CREATE, "create") | ||
179 | S_(SECCLASS_CURSOR, CURSOR__CREATEGLYPH, "createglyph") | ||
180 | S_(SECCLASS_CURSOR, CURSOR__FREE, "free") | ||
181 | S_(SECCLASS_CURSOR, CURSOR__ASSIGN, "assign") | ||
182 | S_(SECCLASS_CURSOR, CURSOR__SETATTR, "setattr") | ||
183 | S_(SECCLASS_XCLIENT, XCLIENT__KILL, "kill") | ||
184 | S_(SECCLASS_XINPUT, XINPUT__LOOKUP, "lookup") | ||
185 | S_(SECCLASS_XINPUT, XINPUT__GETATTR, "getattr") | ||
186 | S_(SECCLASS_XINPUT, XINPUT__SETATTR, "setattr") | ||
187 | S_(SECCLASS_XINPUT, XINPUT__SETFOCUS, "setfocus") | ||
188 | S_(SECCLASS_XINPUT, XINPUT__WARPPOINTER, "warppointer") | ||
189 | S_(SECCLASS_XINPUT, XINPUT__ACTIVEGRAB, "activegrab") | ||
190 | S_(SECCLASS_XINPUT, XINPUT__PASSIVEGRAB, "passivegrab") | ||
191 | S_(SECCLASS_XINPUT, XINPUT__UNGRAB, "ungrab") | ||
192 | S_(SECCLASS_XINPUT, XINPUT__BELL, "bell") | ||
193 | S_(SECCLASS_XINPUT, XINPUT__MOUSEMOTION, "mousemotion") | ||
194 | S_(SECCLASS_XINPUT, XINPUT__RELABELINPUT, "relabelinput") | ||
195 | S_(SECCLASS_XSERVER, XSERVER__SCREENSAVER, "screensaver") | ||
196 | S_(SECCLASS_XSERVER, XSERVER__GETHOSTLIST, "gethostlist") | ||
197 | S_(SECCLASS_XSERVER, XSERVER__SETHOSTLIST, "sethostlist") | ||
198 | S_(SECCLASS_XSERVER, XSERVER__GETFONTPATH, "getfontpath") | ||
199 | S_(SECCLASS_XSERVER, XSERVER__SETFONTPATH, "setfontpath") | ||
200 | S_(SECCLASS_XSERVER, XSERVER__GETATTR, "getattr") | ||
201 | S_(SECCLASS_XSERVER, XSERVER__GRAB, "grab") | ||
202 | S_(SECCLASS_XSERVER, XSERVER__UNGRAB, "ungrab") | ||
203 | S_(SECCLASS_XEXTENSION, XEXTENSION__QUERY, "query") | ||
204 | S_(SECCLASS_XEXTENSION, XEXTENSION__USE, "use") | ||
205 | S_(SECCLASS_PAX, PAX__PAGEEXEC, "pageexec") | ||
206 | S_(SECCLASS_PAX, PAX__EMUTRAMP, "emutramp") | ||
207 | S_(SECCLASS_PAX, PAX__MPROTECT, "mprotect") | ||
208 | S_(SECCLASS_PAX, PAX__RANDMMAP, "randmmap") | ||
209 | S_(SECCLASS_PAX, PAX__RANDEXEC, "randexec") | ||
210 | S_(SECCLASS_PAX, PAX__SEGMEXEC, "segmexec") | ||
211 | S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_READ, "nlmsg_read") | ||
212 | S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE, "nlmsg_write") | ||
213 | S_(SECCLASS_NETLINK_FIREWALL_SOCKET, NETLINK_FIREWALL_SOCKET__NLMSG_READ, "nlmsg_read") | ||
214 | S_(SECCLASS_NETLINK_FIREWALL_SOCKET, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE, "nlmsg_write") | ||
215 | S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, NETLINK_TCPDIAG_SOCKET__NLMSG_READ, "nlmsg_read") | ||
216 | S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE, "nlmsg_write") | ||
217 | S_(SECCLASS_NETLINK_XFRM_SOCKET, NETLINK_XFRM_SOCKET__NLMSG_READ, "nlmsg_read") | ||
218 | S_(SECCLASS_NETLINK_XFRM_SOCKET, NETLINK_XFRM_SOCKET__NLMSG_WRITE, "nlmsg_write") | ||
219 | S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READ, "nlmsg_read") | ||
220 | S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE, "nlmsg_write") | ||
221 | S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_READ, "nlmsg_read") | ||
222 | S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_WRITE, "nlmsg_write") | ||
223 | S_(SECCLASS_DBUS, DBUS__ACQUIRE_SVC, "acquire_svc") | ||
224 | S_(SECCLASS_DBUS, DBUS__SEND_MSG, "send_msg") | ||
225 | S_(SECCLASS_NSCD, NSCD__GETPWD, "getpwd") | ||
226 | S_(SECCLASS_NSCD, NSCD__GETGRP, "getgrp") | ||
227 | S_(SECCLASS_NSCD, NSCD__GETHOST, "gethost") | ||
228 | S_(SECCLASS_NSCD, NSCD__GETSTAT, "getstat") | ||
229 | S_(SECCLASS_NSCD, NSCD__ADMIN, "admin") | ||
230 | S_(SECCLASS_NSCD, NSCD__SHMEMPWD, "shmempwd") | ||
231 | S_(SECCLASS_NSCD, NSCD__SHMEMGRP, "shmemgrp") | ||
232 | S_(SECCLASS_NSCD, NSCD__SHMEMHOST, "shmemhost") | ||
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h new file mode 100644 index 000000000000..b0a12ac8f7ee --- /dev/null +++ b/security/selinux/include/av_permissions.h | |||
@@ -0,0 +1,902 @@ | |||
1 | /* This file is automatically generated. Do not edit. */ | ||
2 | #define COMMON_FILE__IOCTL 0x00000001UL | ||
3 | #define COMMON_FILE__READ 0x00000002UL | ||
4 | #define COMMON_FILE__WRITE 0x00000004UL | ||
5 | #define COMMON_FILE__CREATE 0x00000008UL | ||
6 | #define COMMON_FILE__GETATTR 0x00000010UL | ||
7 | #define COMMON_FILE__SETATTR 0x00000020UL | ||
8 | #define COMMON_FILE__LOCK 0x00000040UL | ||
9 | #define COMMON_FILE__RELABELFROM 0x00000080UL | ||
10 | #define COMMON_FILE__RELABELTO 0x00000100UL | ||
11 | #define COMMON_FILE__APPEND 0x00000200UL | ||
12 | #define COMMON_FILE__UNLINK 0x00000400UL | ||
13 | #define COMMON_FILE__LINK 0x00000800UL | ||
14 | #define COMMON_FILE__RENAME 0x00001000UL | ||
15 | #define COMMON_FILE__EXECUTE 0x00002000UL | ||
16 | #define COMMON_FILE__SWAPON 0x00004000UL | ||
17 | #define COMMON_FILE__QUOTAON 0x00008000UL | ||
18 | #define COMMON_FILE__MOUNTON 0x00010000UL | ||
19 | |||
20 | #define COMMON_SOCKET__IOCTL 0x00000001UL | ||
21 | #define COMMON_SOCKET__READ 0x00000002UL | ||
22 | #define COMMON_SOCKET__WRITE 0x00000004UL | ||
23 | #define COMMON_SOCKET__CREATE 0x00000008UL | ||
24 | #define COMMON_SOCKET__GETATTR 0x00000010UL | ||
25 | #define COMMON_SOCKET__SETATTR 0x00000020UL | ||
26 | #define COMMON_SOCKET__LOCK 0x00000040UL | ||
27 | #define COMMON_SOCKET__RELABELFROM 0x00000080UL | ||
28 | #define COMMON_SOCKET__RELABELTO 0x00000100UL | ||
29 | #define COMMON_SOCKET__APPEND 0x00000200UL | ||
30 | #define COMMON_SOCKET__BIND 0x00000400UL | ||
31 | #define COMMON_SOCKET__CONNECT 0x00000800UL | ||
32 | #define COMMON_SOCKET__LISTEN 0x00001000UL | ||
33 | #define COMMON_SOCKET__ACCEPT 0x00002000UL | ||
34 | #define COMMON_SOCKET__GETOPT 0x00004000UL | ||
35 | #define COMMON_SOCKET__SETOPT 0x00008000UL | ||
36 | #define COMMON_SOCKET__SHUTDOWN 0x00010000UL | ||
37 | #define COMMON_SOCKET__RECVFROM 0x00020000UL | ||
38 | #define COMMON_SOCKET__SENDTO 0x00040000UL | ||
39 | #define COMMON_SOCKET__RECV_MSG 0x00080000UL | ||
40 | #define COMMON_SOCKET__SEND_MSG 0x00100000UL | ||
41 | #define COMMON_SOCKET__NAME_BIND 0x00200000UL | ||
42 | |||
43 | #define COMMON_IPC__CREATE 0x00000001UL | ||
44 | #define COMMON_IPC__DESTROY 0x00000002UL | ||
45 | #define COMMON_IPC__GETATTR 0x00000004UL | ||
46 | #define COMMON_IPC__SETATTR 0x00000008UL | ||
47 | #define COMMON_IPC__READ 0x00000010UL | ||
48 | #define COMMON_IPC__WRITE 0x00000020UL | ||
49 | #define COMMON_IPC__ASSOCIATE 0x00000040UL | ||
50 | #define COMMON_IPC__UNIX_READ 0x00000080UL | ||
51 | #define COMMON_IPC__UNIX_WRITE 0x00000100UL | ||
52 | |||
53 | #define FILESYSTEM__MOUNT 0x00000001UL | ||
54 | #define FILESYSTEM__REMOUNT 0x00000002UL | ||
55 | #define FILESYSTEM__UNMOUNT 0x00000004UL | ||
56 | #define FILESYSTEM__GETATTR 0x00000008UL | ||
57 | #define FILESYSTEM__RELABELFROM 0x00000010UL | ||
58 | #define FILESYSTEM__RELABELTO 0x00000020UL | ||
59 | #define FILESYSTEM__TRANSITION 0x00000040UL | ||
60 | #define FILESYSTEM__ASSOCIATE 0x00000080UL | ||
61 | #define FILESYSTEM__QUOTAMOD 0x00000100UL | ||
62 | #define FILESYSTEM__QUOTAGET 0x00000200UL | ||
63 | |||
64 | #define DIR__IOCTL 0x00000001UL | ||
65 | #define DIR__READ 0x00000002UL | ||
66 | #define DIR__WRITE 0x00000004UL | ||
67 | #define DIR__CREATE 0x00000008UL | ||
68 | #define DIR__GETATTR 0x00000010UL | ||
69 | #define DIR__SETATTR 0x00000020UL | ||
70 | #define DIR__LOCK 0x00000040UL | ||
71 | #define DIR__RELABELFROM 0x00000080UL | ||
72 | #define DIR__RELABELTO 0x00000100UL | ||
73 | #define DIR__APPEND 0x00000200UL | ||
74 | #define DIR__UNLINK 0x00000400UL | ||
75 | #define DIR__LINK 0x00000800UL | ||
76 | #define DIR__RENAME 0x00001000UL | ||
77 | #define DIR__EXECUTE 0x00002000UL | ||
78 | #define DIR__SWAPON 0x00004000UL | ||
79 | #define DIR__QUOTAON 0x00008000UL | ||
80 | #define DIR__MOUNTON 0x00010000UL | ||
81 | |||
82 | #define DIR__ADD_NAME 0x00020000UL | ||
83 | #define DIR__REMOVE_NAME 0x00040000UL | ||
84 | #define DIR__REPARENT 0x00080000UL | ||
85 | #define DIR__SEARCH 0x00100000UL | ||
86 | #define DIR__RMDIR 0x00200000UL | ||
87 | |||
88 | #define FILE__IOCTL 0x00000001UL | ||
89 | #define FILE__READ 0x00000002UL | ||
90 | #define FILE__WRITE 0x00000004UL | ||
91 | #define FILE__CREATE 0x00000008UL | ||
92 | #define FILE__GETATTR 0x00000010UL | ||
93 | #define FILE__SETATTR 0x00000020UL | ||
94 | #define FILE__LOCK 0x00000040UL | ||
95 | #define FILE__RELABELFROM 0x00000080UL | ||
96 | #define FILE__RELABELTO 0x00000100UL | ||
97 | #define FILE__APPEND 0x00000200UL | ||
98 | #define FILE__UNLINK 0x00000400UL | ||
99 | #define FILE__LINK 0x00000800UL | ||
100 | #define FILE__RENAME 0x00001000UL | ||
101 | #define FILE__EXECUTE 0x00002000UL | ||
102 | #define FILE__SWAPON 0x00004000UL | ||
103 | #define FILE__QUOTAON 0x00008000UL | ||
104 | #define FILE__MOUNTON 0x00010000UL | ||
105 | |||
106 | #define FILE__EXECUTE_NO_TRANS 0x00020000UL | ||
107 | #define FILE__ENTRYPOINT 0x00040000UL | ||
108 | #define FILE__EXECMOD 0x00080000UL | ||
109 | |||
110 | #define LNK_FILE__IOCTL 0x00000001UL | ||
111 | #define LNK_FILE__READ 0x00000002UL | ||
112 | #define LNK_FILE__WRITE 0x00000004UL | ||
113 | #define LNK_FILE__CREATE 0x00000008UL | ||
114 | #define LNK_FILE__GETATTR 0x00000010UL | ||
115 | #define LNK_FILE__SETATTR 0x00000020UL | ||
116 | #define LNK_FILE__LOCK 0x00000040UL | ||
117 | #define LNK_FILE__RELABELFROM 0x00000080UL | ||
118 | #define LNK_FILE__RELABELTO 0x00000100UL | ||
119 | #define LNK_FILE__APPEND 0x00000200UL | ||
120 | #define LNK_FILE__UNLINK 0x00000400UL | ||
121 | #define LNK_FILE__LINK 0x00000800UL | ||
122 | #define LNK_FILE__RENAME 0x00001000UL | ||
123 | #define LNK_FILE__EXECUTE 0x00002000UL | ||
124 | #define LNK_FILE__SWAPON 0x00004000UL | ||
125 | #define LNK_FILE__QUOTAON 0x00008000UL | ||
126 | #define LNK_FILE__MOUNTON 0x00010000UL | ||
127 | |||
128 | #define CHR_FILE__IOCTL 0x00000001UL | ||
129 | #define CHR_FILE__READ 0x00000002UL | ||
130 | #define CHR_FILE__WRITE 0x00000004UL | ||
131 | #define CHR_FILE__CREATE 0x00000008UL | ||
132 | #define CHR_FILE__GETATTR 0x00000010UL | ||
133 | #define CHR_FILE__SETATTR 0x00000020UL | ||
134 | #define CHR_FILE__LOCK 0x00000040UL | ||
135 | #define CHR_FILE__RELABELFROM 0x00000080UL | ||
136 | #define CHR_FILE__RELABELTO 0x00000100UL | ||
137 | #define CHR_FILE__APPEND 0x00000200UL | ||
138 | #define CHR_FILE__UNLINK 0x00000400UL | ||
139 | #define CHR_FILE__LINK 0x00000800UL | ||
140 | #define CHR_FILE__RENAME 0x00001000UL | ||
141 | #define CHR_FILE__EXECUTE 0x00002000UL | ||
142 | #define CHR_FILE__SWAPON 0x00004000UL | ||
143 | #define CHR_FILE__QUOTAON 0x00008000UL | ||
144 | #define CHR_FILE__MOUNTON 0x00010000UL | ||
145 | |||
146 | #define CHR_FILE__EXECUTE_NO_TRANS 0x00020000UL | ||
147 | #define CHR_FILE__ENTRYPOINT 0x00040000UL | ||
148 | #define CHR_FILE__EXECMOD 0x00080000UL | ||
149 | |||
150 | #define BLK_FILE__IOCTL 0x00000001UL | ||
151 | #define BLK_FILE__READ 0x00000002UL | ||
152 | #define BLK_FILE__WRITE 0x00000004UL | ||
153 | #define BLK_FILE__CREATE 0x00000008UL | ||
154 | #define BLK_FILE__GETATTR 0x00000010UL | ||
155 | #define BLK_FILE__SETATTR 0x00000020UL | ||
156 | #define BLK_FILE__LOCK 0x00000040UL | ||
157 | #define BLK_FILE__RELABELFROM 0x00000080UL | ||
158 | #define BLK_FILE__RELABELTO 0x00000100UL | ||
159 | #define BLK_FILE__APPEND 0x00000200UL | ||
160 | #define BLK_FILE__UNLINK 0x00000400UL | ||
161 | #define BLK_FILE__LINK 0x00000800UL | ||
162 | #define BLK_FILE__RENAME 0x00001000UL | ||
163 | #define BLK_FILE__EXECUTE 0x00002000UL | ||
164 | #define BLK_FILE__SWAPON 0x00004000UL | ||
165 | #define BLK_FILE__QUOTAON 0x00008000UL | ||
166 | #define BLK_FILE__MOUNTON 0x00010000UL | ||
167 | |||
168 | #define SOCK_FILE__IOCTL 0x00000001UL | ||
169 | #define SOCK_FILE__READ 0x00000002UL | ||
170 | #define SOCK_FILE__WRITE 0x00000004UL | ||
171 | #define SOCK_FILE__CREATE 0x00000008UL | ||
172 | #define SOCK_FILE__GETATTR 0x00000010UL | ||
173 | #define SOCK_FILE__SETATTR 0x00000020UL | ||
174 | #define SOCK_FILE__LOCK 0x00000040UL | ||
175 | #define SOCK_FILE__RELABELFROM 0x00000080UL | ||
176 | #define SOCK_FILE__RELABELTO 0x00000100UL | ||
177 | #define SOCK_FILE__APPEND 0x00000200UL | ||
178 | #define SOCK_FILE__UNLINK 0x00000400UL | ||
179 | #define SOCK_FILE__LINK 0x00000800UL | ||
180 | #define SOCK_FILE__RENAME 0x00001000UL | ||
181 | #define SOCK_FILE__EXECUTE 0x00002000UL | ||
182 | #define SOCK_FILE__SWAPON 0x00004000UL | ||
183 | #define SOCK_FILE__QUOTAON 0x00008000UL | ||
184 | #define SOCK_FILE__MOUNTON 0x00010000UL | ||
185 | |||
186 | #define FIFO_FILE__IOCTL 0x00000001UL | ||
187 | #define FIFO_FILE__READ 0x00000002UL | ||
188 | #define FIFO_FILE__WRITE 0x00000004UL | ||
189 | #define FIFO_FILE__CREATE 0x00000008UL | ||
190 | #define FIFO_FILE__GETATTR 0x00000010UL | ||
191 | #define FIFO_FILE__SETATTR 0x00000020UL | ||
192 | #define FIFO_FILE__LOCK 0x00000040UL | ||
193 | #define FIFO_FILE__RELABELFROM 0x00000080UL | ||
194 | #define FIFO_FILE__RELABELTO 0x00000100UL | ||
195 | #define FIFO_FILE__APPEND 0x00000200UL | ||
196 | #define FIFO_FILE__UNLINK 0x00000400UL | ||
197 | #define FIFO_FILE__LINK 0x00000800UL | ||
198 | #define FIFO_FILE__RENAME 0x00001000UL | ||
199 | #define FIFO_FILE__EXECUTE 0x00002000UL | ||
200 | #define FIFO_FILE__SWAPON 0x00004000UL | ||
201 | #define FIFO_FILE__QUOTAON 0x00008000UL | ||
202 | #define FIFO_FILE__MOUNTON 0x00010000UL | ||
203 | |||
204 | #define FD__USE 0x00000001UL | ||
205 | |||
206 | #define SOCKET__IOCTL 0x00000001UL | ||
207 | #define SOCKET__READ 0x00000002UL | ||
208 | #define SOCKET__WRITE 0x00000004UL | ||
209 | #define SOCKET__CREATE 0x00000008UL | ||
210 | #define SOCKET__GETATTR 0x00000010UL | ||
211 | #define SOCKET__SETATTR 0x00000020UL | ||
212 | #define SOCKET__LOCK 0x00000040UL | ||
213 | #define SOCKET__RELABELFROM 0x00000080UL | ||
214 | #define SOCKET__RELABELTO 0x00000100UL | ||
215 | #define SOCKET__APPEND 0x00000200UL | ||
216 | #define SOCKET__BIND 0x00000400UL | ||
217 | #define SOCKET__CONNECT 0x00000800UL | ||
218 | #define SOCKET__LISTEN 0x00001000UL | ||
219 | #define SOCKET__ACCEPT 0x00002000UL | ||
220 | #define SOCKET__GETOPT 0x00004000UL | ||
221 | #define SOCKET__SETOPT 0x00008000UL | ||
222 | #define SOCKET__SHUTDOWN 0x00010000UL | ||
223 | #define SOCKET__RECVFROM 0x00020000UL | ||
224 | #define SOCKET__SENDTO 0x00040000UL | ||
225 | #define SOCKET__RECV_MSG 0x00080000UL | ||
226 | #define SOCKET__SEND_MSG 0x00100000UL | ||
227 | #define SOCKET__NAME_BIND 0x00200000UL | ||
228 | |||
229 | #define TCP_SOCKET__IOCTL 0x00000001UL | ||
230 | #define TCP_SOCKET__READ 0x00000002UL | ||
231 | #define TCP_SOCKET__WRITE 0x00000004UL | ||
232 | #define TCP_SOCKET__CREATE 0x00000008UL | ||
233 | #define TCP_SOCKET__GETATTR 0x00000010UL | ||
234 | #define TCP_SOCKET__SETATTR 0x00000020UL | ||
235 | #define TCP_SOCKET__LOCK 0x00000040UL | ||
236 | #define TCP_SOCKET__RELABELFROM 0x00000080UL | ||
237 | #define TCP_SOCKET__RELABELTO 0x00000100UL | ||
238 | #define TCP_SOCKET__APPEND 0x00000200UL | ||
239 | #define TCP_SOCKET__BIND 0x00000400UL | ||
240 | #define TCP_SOCKET__CONNECT 0x00000800UL | ||
241 | #define TCP_SOCKET__LISTEN 0x00001000UL | ||
242 | #define TCP_SOCKET__ACCEPT 0x00002000UL | ||
243 | #define TCP_SOCKET__GETOPT 0x00004000UL | ||
244 | #define TCP_SOCKET__SETOPT 0x00008000UL | ||
245 | #define TCP_SOCKET__SHUTDOWN 0x00010000UL | ||
246 | #define TCP_SOCKET__RECVFROM 0x00020000UL | ||
247 | #define TCP_SOCKET__SENDTO 0x00040000UL | ||
248 | #define TCP_SOCKET__RECV_MSG 0x00080000UL | ||
249 | #define TCP_SOCKET__SEND_MSG 0x00100000UL | ||
250 | #define TCP_SOCKET__NAME_BIND 0x00200000UL | ||
251 | |||
252 | #define TCP_SOCKET__CONNECTTO 0x00400000UL | ||
253 | #define TCP_SOCKET__NEWCONN 0x00800000UL | ||
254 | #define TCP_SOCKET__ACCEPTFROM 0x01000000UL | ||
255 | #define TCP_SOCKET__NODE_BIND 0x02000000UL | ||
256 | #define TCP_SOCKET__NAME_CONNECT 0x04000000UL | ||
257 | |||
258 | #define UDP_SOCKET__IOCTL 0x00000001UL | ||
259 | #define UDP_SOCKET__READ 0x00000002UL | ||
260 | #define UDP_SOCKET__WRITE 0x00000004UL | ||
261 | #define UDP_SOCKET__CREATE 0x00000008UL | ||
262 | #define UDP_SOCKET__GETATTR 0x00000010UL | ||
263 | #define UDP_SOCKET__SETATTR 0x00000020UL | ||
264 | #define UDP_SOCKET__LOCK 0x00000040UL | ||
265 | #define UDP_SOCKET__RELABELFROM 0x00000080UL | ||
266 | #define UDP_SOCKET__RELABELTO 0x00000100UL | ||
267 | #define UDP_SOCKET__APPEND 0x00000200UL | ||
268 | #define UDP_SOCKET__BIND 0x00000400UL | ||
269 | #define UDP_SOCKET__CONNECT 0x00000800UL | ||
270 | #define UDP_SOCKET__LISTEN 0x00001000UL | ||
271 | #define UDP_SOCKET__ACCEPT 0x00002000UL | ||
272 | #define UDP_SOCKET__GETOPT 0x00004000UL | ||
273 | #define UDP_SOCKET__SETOPT 0x00008000UL | ||
274 | #define UDP_SOCKET__SHUTDOWN 0x00010000UL | ||
275 | #define UDP_SOCKET__RECVFROM 0x00020000UL | ||
276 | #define UDP_SOCKET__SENDTO 0x00040000UL | ||
277 | #define UDP_SOCKET__RECV_MSG 0x00080000UL | ||
278 | #define UDP_SOCKET__SEND_MSG 0x00100000UL | ||
279 | #define UDP_SOCKET__NAME_BIND 0x00200000UL | ||
280 | |||
281 | #define UDP_SOCKET__NODE_BIND 0x00400000UL | ||
282 | |||
283 | #define RAWIP_SOCKET__IOCTL 0x00000001UL | ||
284 | #define RAWIP_SOCKET__READ 0x00000002UL | ||
285 | #define RAWIP_SOCKET__WRITE 0x00000004UL | ||
286 | #define RAWIP_SOCKET__CREATE 0x00000008UL | ||
287 | #define RAWIP_SOCKET__GETATTR 0x00000010UL | ||
288 | #define RAWIP_SOCKET__SETATTR 0x00000020UL | ||
289 | #define RAWIP_SOCKET__LOCK 0x00000040UL | ||
290 | #define RAWIP_SOCKET__RELABELFROM 0x00000080UL | ||
291 | #define RAWIP_SOCKET__RELABELTO 0x00000100UL | ||
292 | #define RAWIP_SOCKET__APPEND 0x00000200UL | ||
293 | #define RAWIP_SOCKET__BIND 0x00000400UL | ||
294 | #define RAWIP_SOCKET__CONNECT 0x00000800UL | ||
295 | #define RAWIP_SOCKET__LISTEN 0x00001000UL | ||
296 | #define RAWIP_SOCKET__ACCEPT 0x00002000UL | ||
297 | #define RAWIP_SOCKET__GETOPT 0x00004000UL | ||
298 | #define RAWIP_SOCKET__SETOPT 0x00008000UL | ||
299 | #define RAWIP_SOCKET__SHUTDOWN 0x00010000UL | ||
300 | #define RAWIP_SOCKET__RECVFROM 0x00020000UL | ||
301 | #define RAWIP_SOCKET__SENDTO 0x00040000UL | ||
302 | #define RAWIP_SOCKET__RECV_MSG 0x00080000UL | ||
303 | #define RAWIP_SOCKET__SEND_MSG 0x00100000UL | ||
304 | #define RAWIP_SOCKET__NAME_BIND 0x00200000UL | ||
305 | |||
306 | #define RAWIP_SOCKET__NODE_BIND 0x00400000UL | ||
307 | |||
308 | #define NODE__TCP_RECV 0x00000001UL | ||
309 | #define NODE__TCP_SEND 0x00000002UL | ||
310 | #define NODE__UDP_RECV 0x00000004UL | ||
311 | #define NODE__UDP_SEND 0x00000008UL | ||
312 | #define NODE__RAWIP_RECV 0x00000010UL | ||
313 | #define NODE__RAWIP_SEND 0x00000020UL | ||
314 | #define NODE__ENFORCE_DEST 0x00000040UL | ||
315 | |||
316 | #define NETIF__TCP_RECV 0x00000001UL | ||
317 | #define NETIF__TCP_SEND 0x00000002UL | ||
318 | #define NETIF__UDP_RECV 0x00000004UL | ||
319 | #define NETIF__UDP_SEND 0x00000008UL | ||
320 | #define NETIF__RAWIP_RECV 0x00000010UL | ||
321 | #define NETIF__RAWIP_SEND 0x00000020UL | ||
322 | |||
323 | #define NETLINK_SOCKET__IOCTL 0x00000001UL | ||
324 | #define NETLINK_SOCKET__READ 0x00000002UL | ||
325 | #define NETLINK_SOCKET__WRITE 0x00000004UL | ||
326 | #define NETLINK_SOCKET__CREATE 0x00000008UL | ||
327 | #define NETLINK_SOCKET__GETATTR 0x00000010UL | ||
328 | #define NETLINK_SOCKET__SETATTR 0x00000020UL | ||
329 | #define NETLINK_SOCKET__LOCK 0x00000040UL | ||
330 | #define NETLINK_SOCKET__RELABELFROM 0x00000080UL | ||
331 | #define NETLINK_SOCKET__RELABELTO 0x00000100UL | ||
332 | #define NETLINK_SOCKET__APPEND 0x00000200UL | ||
333 | #define NETLINK_SOCKET__BIND 0x00000400UL | ||
334 | #define NETLINK_SOCKET__CONNECT 0x00000800UL | ||
335 | #define NETLINK_SOCKET__LISTEN 0x00001000UL | ||
336 | #define NETLINK_SOCKET__ACCEPT 0x00002000UL | ||
337 | #define NETLINK_SOCKET__GETOPT 0x00004000UL | ||
338 | #define NETLINK_SOCKET__SETOPT 0x00008000UL | ||
339 | #define NETLINK_SOCKET__SHUTDOWN 0x00010000UL | ||
340 | #define NETLINK_SOCKET__RECVFROM 0x00020000UL | ||
341 | #define NETLINK_SOCKET__SENDTO 0x00040000UL | ||
342 | #define NETLINK_SOCKET__RECV_MSG 0x00080000UL | ||
343 | #define NETLINK_SOCKET__SEND_MSG 0x00100000UL | ||
344 | #define NETLINK_SOCKET__NAME_BIND 0x00200000UL | ||
345 | |||
346 | #define PACKET_SOCKET__IOCTL 0x00000001UL | ||
347 | #define PACKET_SOCKET__READ 0x00000002UL | ||
348 | #define PACKET_SOCKET__WRITE 0x00000004UL | ||
349 | #define PACKET_SOCKET__CREATE 0x00000008UL | ||
350 | #define PACKET_SOCKET__GETATTR 0x00000010UL | ||
351 | #define PACKET_SOCKET__SETATTR 0x00000020UL | ||
352 | #define PACKET_SOCKET__LOCK 0x00000040UL | ||
353 | #define PACKET_SOCKET__RELABELFROM 0x00000080UL | ||
354 | #define PACKET_SOCKET__RELABELTO 0x00000100UL | ||
355 | #define PACKET_SOCKET__APPEND 0x00000200UL | ||
356 | #define PACKET_SOCKET__BIND 0x00000400UL | ||
357 | #define PACKET_SOCKET__CONNECT 0x00000800UL | ||
358 | #define PACKET_SOCKET__LISTEN 0x00001000UL | ||
359 | #define PACKET_SOCKET__ACCEPT 0x00002000UL | ||
360 | #define PACKET_SOCKET__GETOPT 0x00004000UL | ||
361 | #define PACKET_SOCKET__SETOPT 0x00008000UL | ||
362 | #define PACKET_SOCKET__SHUTDOWN 0x00010000UL | ||
363 | #define PACKET_SOCKET__RECVFROM 0x00020000UL | ||
364 | #define PACKET_SOCKET__SENDTO 0x00040000UL | ||
365 | #define PACKET_SOCKET__RECV_MSG 0x00080000UL | ||
366 | #define PACKET_SOCKET__SEND_MSG 0x00100000UL | ||
367 | #define PACKET_SOCKET__NAME_BIND 0x00200000UL | ||
368 | |||
369 | #define KEY_SOCKET__IOCTL 0x00000001UL | ||
370 | #define KEY_SOCKET__READ 0x00000002UL | ||
371 | #define KEY_SOCKET__WRITE 0x00000004UL | ||
372 | #define KEY_SOCKET__CREATE 0x00000008UL | ||
373 | #define KEY_SOCKET__GETATTR 0x00000010UL | ||
374 | #define KEY_SOCKET__SETATTR 0x00000020UL | ||
375 | #define KEY_SOCKET__LOCK 0x00000040UL | ||
376 | #define KEY_SOCKET__RELABELFROM 0x00000080UL | ||
377 | #define KEY_SOCKET__RELABELTO 0x00000100UL | ||
378 | #define KEY_SOCKET__APPEND 0x00000200UL | ||
379 | #define KEY_SOCKET__BIND 0x00000400UL | ||
380 | #define KEY_SOCKET__CONNECT 0x00000800UL | ||
381 | #define KEY_SOCKET__LISTEN 0x00001000UL | ||
382 | #define KEY_SOCKET__ACCEPT 0x00002000UL | ||
383 | #define KEY_SOCKET__GETOPT 0x00004000UL | ||
384 | #define KEY_SOCKET__SETOPT 0x00008000UL | ||
385 | #define KEY_SOCKET__SHUTDOWN 0x00010000UL | ||
386 | #define KEY_SOCKET__RECVFROM 0x00020000UL | ||
387 | #define KEY_SOCKET__SENDTO 0x00040000UL | ||
388 | #define KEY_SOCKET__RECV_MSG 0x00080000UL | ||
389 | #define KEY_SOCKET__SEND_MSG 0x00100000UL | ||
390 | #define KEY_SOCKET__NAME_BIND 0x00200000UL | ||
391 | |||
392 | #define UNIX_STREAM_SOCKET__IOCTL 0x00000001UL | ||
393 | #define UNIX_STREAM_SOCKET__READ 0x00000002UL | ||
394 | #define UNIX_STREAM_SOCKET__WRITE 0x00000004UL | ||
395 | #define UNIX_STREAM_SOCKET__CREATE 0x00000008UL | ||
396 | #define UNIX_STREAM_SOCKET__GETATTR 0x00000010UL | ||
397 | #define UNIX_STREAM_SOCKET__SETATTR 0x00000020UL | ||
398 | #define UNIX_STREAM_SOCKET__LOCK 0x00000040UL | ||
399 | #define UNIX_STREAM_SOCKET__RELABELFROM 0x00000080UL | ||
400 | #define UNIX_STREAM_SOCKET__RELABELTO 0x00000100UL | ||
401 | #define UNIX_STREAM_SOCKET__APPEND 0x00000200UL | ||
402 | #define UNIX_STREAM_SOCKET__BIND 0x00000400UL | ||
403 | #define UNIX_STREAM_SOCKET__CONNECT 0x00000800UL | ||
404 | #define UNIX_STREAM_SOCKET__LISTEN 0x00001000UL | ||
405 | #define UNIX_STREAM_SOCKET__ACCEPT 0x00002000UL | ||
406 | #define UNIX_STREAM_SOCKET__GETOPT 0x00004000UL | ||
407 | #define UNIX_STREAM_SOCKET__SETOPT 0x00008000UL | ||
408 | #define UNIX_STREAM_SOCKET__SHUTDOWN 0x00010000UL | ||
409 | #define UNIX_STREAM_SOCKET__RECVFROM 0x00020000UL | ||
410 | #define UNIX_STREAM_SOCKET__SENDTO 0x00040000UL | ||
411 | #define UNIX_STREAM_SOCKET__RECV_MSG 0x00080000UL | ||
412 | #define UNIX_STREAM_SOCKET__SEND_MSG 0x00100000UL | ||
413 | #define UNIX_STREAM_SOCKET__NAME_BIND 0x00200000UL | ||
414 | |||
415 | #define UNIX_STREAM_SOCKET__CONNECTTO 0x00400000UL | ||
416 | #define UNIX_STREAM_SOCKET__NEWCONN 0x00800000UL | ||
417 | #define UNIX_STREAM_SOCKET__ACCEPTFROM 0x01000000UL | ||
418 | |||
419 | #define UNIX_DGRAM_SOCKET__IOCTL 0x00000001UL | ||
420 | #define UNIX_DGRAM_SOCKET__READ 0x00000002UL | ||
421 | #define UNIX_DGRAM_SOCKET__WRITE 0x00000004UL | ||
422 | #define UNIX_DGRAM_SOCKET__CREATE 0x00000008UL | ||
423 | #define UNIX_DGRAM_SOCKET__GETATTR 0x00000010UL | ||
424 | #define UNIX_DGRAM_SOCKET__SETATTR 0x00000020UL | ||
425 | #define UNIX_DGRAM_SOCKET__LOCK 0x00000040UL | ||
426 | #define UNIX_DGRAM_SOCKET__RELABELFROM 0x00000080UL | ||
427 | #define UNIX_DGRAM_SOCKET__RELABELTO 0x00000100UL | ||
428 | #define UNIX_DGRAM_SOCKET__APPEND 0x00000200UL | ||
429 | #define UNIX_DGRAM_SOCKET__BIND 0x00000400UL | ||
430 | #define UNIX_DGRAM_SOCKET__CONNECT 0x00000800UL | ||
431 | #define UNIX_DGRAM_SOCKET__LISTEN 0x00001000UL | ||
432 | #define UNIX_DGRAM_SOCKET__ACCEPT 0x00002000UL | ||
433 | #define UNIX_DGRAM_SOCKET__GETOPT 0x00004000UL | ||
434 | #define UNIX_DGRAM_SOCKET__SETOPT 0x00008000UL | ||
435 | #define UNIX_DGRAM_SOCKET__SHUTDOWN 0x00010000UL | ||
436 | #define UNIX_DGRAM_SOCKET__RECVFROM 0x00020000UL | ||
437 | #define UNIX_DGRAM_SOCKET__SENDTO 0x00040000UL | ||
438 | #define UNIX_DGRAM_SOCKET__RECV_MSG 0x00080000UL | ||
439 | #define UNIX_DGRAM_SOCKET__SEND_MSG 0x00100000UL | ||
440 | #define UNIX_DGRAM_SOCKET__NAME_BIND 0x00200000UL | ||
441 | |||
442 | #define PROCESS__FORK 0x00000001UL | ||
443 | #define PROCESS__TRANSITION 0x00000002UL | ||
444 | #define PROCESS__SIGCHLD 0x00000004UL | ||
445 | #define PROCESS__SIGKILL 0x00000008UL | ||
446 | #define PROCESS__SIGSTOP 0x00000010UL | ||
447 | #define PROCESS__SIGNULL 0x00000020UL | ||
448 | #define PROCESS__SIGNAL 0x00000040UL | ||
449 | #define PROCESS__PTRACE 0x00000080UL | ||
450 | #define PROCESS__GETSCHED 0x00000100UL | ||
451 | #define PROCESS__SETSCHED 0x00000200UL | ||
452 | #define PROCESS__GETSESSION 0x00000400UL | ||
453 | #define PROCESS__GETPGID 0x00000800UL | ||
454 | #define PROCESS__SETPGID 0x00001000UL | ||
455 | #define PROCESS__GETCAP 0x00002000UL | ||
456 | #define PROCESS__SETCAP 0x00004000UL | ||
457 | #define PROCESS__SHARE 0x00008000UL | ||
458 | #define PROCESS__GETATTR 0x00010000UL | ||
459 | #define PROCESS__SETEXEC 0x00020000UL | ||
460 | #define PROCESS__SETFSCREATE 0x00040000UL | ||
461 | #define PROCESS__NOATSECURE 0x00080000UL | ||
462 | #define PROCESS__SIGINH 0x00100000UL | ||
463 | #define PROCESS__SETRLIMIT 0x00200000UL | ||
464 | #define PROCESS__RLIMITINH 0x00400000UL | ||
465 | #define PROCESS__DYNTRANSITION 0x00800000UL | ||
466 | #define PROCESS__SETCURRENT 0x01000000UL | ||
467 | #define PROCESS__EXECMEM 0x02000000UL | ||
468 | |||
469 | #define IPC__CREATE 0x00000001UL | ||
470 | #define IPC__DESTROY 0x00000002UL | ||
471 | #define IPC__GETATTR 0x00000004UL | ||
472 | #define IPC__SETATTR 0x00000008UL | ||
473 | #define IPC__READ 0x00000010UL | ||
474 | #define IPC__WRITE 0x00000020UL | ||
475 | #define IPC__ASSOCIATE 0x00000040UL | ||
476 | #define IPC__UNIX_READ 0x00000080UL | ||
477 | #define IPC__UNIX_WRITE 0x00000100UL | ||
478 | |||
479 | #define SEM__CREATE 0x00000001UL | ||
480 | #define SEM__DESTROY 0x00000002UL | ||
481 | #define SEM__GETATTR 0x00000004UL | ||
482 | #define SEM__SETATTR 0x00000008UL | ||
483 | #define SEM__READ 0x00000010UL | ||
484 | #define SEM__WRITE 0x00000020UL | ||
485 | #define SEM__ASSOCIATE 0x00000040UL | ||
486 | #define SEM__UNIX_READ 0x00000080UL | ||
487 | #define SEM__UNIX_WRITE 0x00000100UL | ||
488 | |||
489 | #define MSGQ__CREATE 0x00000001UL | ||
490 | #define MSGQ__DESTROY 0x00000002UL | ||
491 | #define MSGQ__GETATTR 0x00000004UL | ||
492 | #define MSGQ__SETATTR 0x00000008UL | ||
493 | #define MSGQ__READ 0x00000010UL | ||
494 | #define MSGQ__WRITE 0x00000020UL | ||
495 | #define MSGQ__ASSOCIATE 0x00000040UL | ||
496 | #define MSGQ__UNIX_READ 0x00000080UL | ||
497 | #define MSGQ__UNIX_WRITE 0x00000100UL | ||
498 | |||
499 | #define MSGQ__ENQUEUE 0x00000200UL | ||
500 | |||
501 | #define MSG__SEND 0x00000001UL | ||
502 | #define MSG__RECEIVE 0x00000002UL | ||
503 | |||
504 | #define SHM__CREATE 0x00000001UL | ||
505 | #define SHM__DESTROY 0x00000002UL | ||
506 | #define SHM__GETATTR 0x00000004UL | ||
507 | #define SHM__SETATTR 0x00000008UL | ||
508 | #define SHM__READ 0x00000010UL | ||
509 | #define SHM__WRITE 0x00000020UL | ||
510 | #define SHM__ASSOCIATE 0x00000040UL | ||
511 | #define SHM__UNIX_READ 0x00000080UL | ||
512 | #define SHM__UNIX_WRITE 0x00000100UL | ||
513 | |||
514 | #define SHM__LOCK 0x00000200UL | ||
515 | |||
516 | #define SECURITY__COMPUTE_AV 0x00000001UL | ||
517 | #define SECURITY__COMPUTE_CREATE 0x00000002UL | ||
518 | #define SECURITY__COMPUTE_MEMBER 0x00000004UL | ||
519 | #define SECURITY__CHECK_CONTEXT 0x00000008UL | ||
520 | #define SECURITY__LOAD_POLICY 0x00000010UL | ||
521 | #define SECURITY__COMPUTE_RELABEL 0x00000020UL | ||
522 | #define SECURITY__COMPUTE_USER 0x00000040UL | ||
523 | #define SECURITY__SETENFORCE 0x00000080UL | ||
524 | #define SECURITY__SETBOOL 0x00000100UL | ||
525 | #define SECURITY__SETSECPARAM 0x00000200UL | ||
526 | #define SECURITY__SETCHECKREQPROT 0x00000400UL | ||
527 | |||
528 | #define SYSTEM__IPC_INFO 0x00000001UL | ||
529 | #define SYSTEM__SYSLOG_READ 0x00000002UL | ||
530 | #define SYSTEM__SYSLOG_MOD 0x00000004UL | ||
531 | #define SYSTEM__SYSLOG_CONSOLE 0x00000008UL | ||
532 | |||
533 | #define CAPABILITY__CHOWN 0x00000001UL | ||
534 | #define CAPABILITY__DAC_OVERRIDE 0x00000002UL | ||
535 | #define CAPABILITY__DAC_READ_SEARCH 0x00000004UL | ||
536 | #define CAPABILITY__FOWNER 0x00000008UL | ||
537 | #define CAPABILITY__FSETID 0x00000010UL | ||
538 | #define CAPABILITY__KILL 0x00000020UL | ||
539 | #define CAPABILITY__SETGID 0x00000040UL | ||
540 | #define CAPABILITY__SETUID 0x00000080UL | ||
541 | #define CAPABILITY__SETPCAP 0x00000100UL | ||
542 | #define CAPABILITY__LINUX_IMMUTABLE 0x00000200UL | ||
543 | #define CAPABILITY__NET_BIND_SERVICE 0x00000400UL | ||
544 | #define CAPABILITY__NET_BROADCAST 0x00000800UL | ||
545 | #define CAPABILITY__NET_ADMIN 0x00001000UL | ||
546 | #define CAPABILITY__NET_RAW 0x00002000UL | ||
547 | #define CAPABILITY__IPC_LOCK 0x00004000UL | ||
548 | #define CAPABILITY__IPC_OWNER 0x00008000UL | ||
549 | #define CAPABILITY__SYS_MODULE 0x00010000UL | ||
550 | #define CAPABILITY__SYS_RAWIO 0x00020000UL | ||
551 | #define CAPABILITY__SYS_CHROOT 0x00040000UL | ||
552 | #define CAPABILITY__SYS_PTRACE 0x00080000UL | ||
553 | #define CAPABILITY__SYS_PACCT 0x00100000UL | ||
554 | #define CAPABILITY__SYS_ADMIN 0x00200000UL | ||
555 | #define CAPABILITY__SYS_BOOT 0x00400000UL | ||
556 | #define CAPABILITY__SYS_NICE 0x00800000UL | ||
557 | #define CAPABILITY__SYS_RESOURCE 0x01000000UL | ||
558 | #define CAPABILITY__SYS_TIME 0x02000000UL | ||
559 | #define CAPABILITY__SYS_TTY_CONFIG 0x04000000UL | ||
560 | #define CAPABILITY__MKNOD 0x08000000UL | ||
561 | #define CAPABILITY__LEASE 0x10000000UL | ||
562 | |||
563 | #define PASSWD__PASSWD 0x00000001UL | ||
564 | #define PASSWD__CHFN 0x00000002UL | ||
565 | #define PASSWD__CHSH 0x00000004UL | ||
566 | #define PASSWD__ROOTOK 0x00000008UL | ||
567 | #define PASSWD__CRONTAB 0x00000010UL | ||
568 | |||
569 | #define DRAWABLE__CREATE 0x00000001UL | ||
570 | #define DRAWABLE__DESTROY 0x00000002UL | ||
571 | #define DRAWABLE__DRAW 0x00000004UL | ||
572 | #define DRAWABLE__COPY 0x00000008UL | ||
573 | #define DRAWABLE__GETATTR 0x00000010UL | ||
574 | |||
575 | #define GC__CREATE 0x00000001UL | ||
576 | #define GC__FREE 0x00000002UL | ||
577 | #define GC__GETATTR 0x00000004UL | ||
578 | #define GC__SETATTR 0x00000008UL | ||
579 | |||
580 | #define WINDOW__ADDCHILD 0x00000001UL | ||
581 | #define WINDOW__CREATE 0x00000002UL | ||
582 | #define WINDOW__DESTROY 0x00000004UL | ||
583 | #define WINDOW__MAP 0x00000008UL | ||
584 | #define WINDOW__UNMAP 0x00000010UL | ||
585 | #define WINDOW__CHSTACK 0x00000020UL | ||
586 | #define WINDOW__CHPROPLIST 0x00000040UL | ||
587 | #define WINDOW__CHPROP 0x00000080UL | ||
588 | #define WINDOW__LISTPROP 0x00000100UL | ||
589 | #define WINDOW__GETATTR 0x00000200UL | ||
590 | #define WINDOW__SETATTR 0x00000400UL | ||
591 | #define WINDOW__SETFOCUS 0x00000800UL | ||
592 | #define WINDOW__MOVE 0x00001000UL | ||
593 | #define WINDOW__CHSELECTION 0x00002000UL | ||
594 | #define WINDOW__CHPARENT 0x00004000UL | ||
595 | #define WINDOW__CTRLLIFE 0x00008000UL | ||
596 | #define WINDOW__ENUMERATE 0x00010000UL | ||
597 | #define WINDOW__TRANSPARENT 0x00020000UL | ||
598 | #define WINDOW__MOUSEMOTION 0x00040000UL | ||
599 | #define WINDOW__CLIENTCOMEVENT 0x00080000UL | ||
600 | #define WINDOW__INPUTEVENT 0x00100000UL | ||
601 | #define WINDOW__DRAWEVENT 0x00200000UL | ||
602 | #define WINDOW__WINDOWCHANGEEVENT 0x00400000UL | ||
603 | #define WINDOW__WINDOWCHANGEREQUEST 0x00800000UL | ||
604 | #define WINDOW__SERVERCHANGEEVENT 0x01000000UL | ||
605 | #define WINDOW__EXTENSIONEVENT 0x02000000UL | ||
606 | |||
607 | #define FONT__LOAD 0x00000001UL | ||
608 | #define FONT__FREE 0x00000002UL | ||
609 | #define FONT__GETATTR 0x00000004UL | ||
610 | #define FONT__USE 0x00000008UL | ||
611 | |||
612 | #define COLORMAP__CREATE 0x00000001UL | ||
613 | #define COLORMAP__FREE 0x00000002UL | ||
614 | #define COLORMAP__INSTALL 0x00000004UL | ||
615 | #define COLORMAP__UNINSTALL 0x00000008UL | ||
616 | #define COLORMAP__LIST 0x00000010UL | ||
617 | #define COLORMAP__READ 0x00000020UL | ||
618 | #define COLORMAP__STORE 0x00000040UL | ||
619 | #define COLORMAP__GETATTR 0x00000080UL | ||
620 | #define COLORMAP__SETATTR 0x00000100UL | ||
621 | |||
622 | #define PROPERTY__CREATE 0x00000001UL | ||
623 | #define PROPERTY__FREE 0x00000002UL | ||
624 | #define PROPERTY__READ 0x00000004UL | ||
625 | #define PROPERTY__WRITE 0x00000008UL | ||
626 | |||
627 | #define CURSOR__CREATE 0x00000001UL | ||
628 | #define CURSOR__CREATEGLYPH 0x00000002UL | ||
629 | #define CURSOR__FREE 0x00000004UL | ||
630 | #define CURSOR__ASSIGN 0x00000008UL | ||
631 | #define CURSOR__SETATTR 0x00000010UL | ||
632 | |||
633 | #define XCLIENT__KILL 0x00000001UL | ||
634 | |||
635 | #define XINPUT__LOOKUP 0x00000001UL | ||
636 | #define XINPUT__GETATTR 0x00000002UL | ||
637 | #define XINPUT__SETATTR 0x00000004UL | ||
638 | #define XINPUT__SETFOCUS 0x00000008UL | ||
639 | #define XINPUT__WARPPOINTER 0x00000010UL | ||
640 | #define XINPUT__ACTIVEGRAB 0x00000020UL | ||
641 | #define XINPUT__PASSIVEGRAB 0x00000040UL | ||
642 | #define XINPUT__UNGRAB 0x00000080UL | ||
643 | #define XINPUT__BELL 0x00000100UL | ||
644 | #define XINPUT__MOUSEMOTION 0x00000200UL | ||
645 | #define XINPUT__RELABELINPUT 0x00000400UL | ||
646 | |||
647 | #define XSERVER__SCREENSAVER 0x00000001UL | ||
648 | #define XSERVER__GETHOSTLIST 0x00000002UL | ||
649 | #define XSERVER__SETHOSTLIST 0x00000004UL | ||
650 | #define XSERVER__GETFONTPATH 0x00000008UL | ||
651 | #define XSERVER__SETFONTPATH 0x00000010UL | ||
652 | #define XSERVER__GETATTR 0x00000020UL | ||
653 | #define XSERVER__GRAB 0x00000040UL | ||
654 | #define XSERVER__UNGRAB 0x00000080UL | ||
655 | |||
656 | #define XEXTENSION__QUERY 0x00000001UL | ||
657 | #define XEXTENSION__USE 0x00000002UL | ||
658 | |||
659 | #define PAX__PAGEEXEC 0x00000001UL | ||
660 | #define PAX__EMUTRAMP 0x00000002UL | ||
661 | #define PAX__MPROTECT 0x00000004UL | ||
662 | #define PAX__RANDMMAP 0x00000008UL | ||
663 | #define PAX__RANDEXEC 0x00000010UL | ||
664 | #define PAX__SEGMEXEC 0x00000020UL | ||
665 | |||
666 | #define NETLINK_ROUTE_SOCKET__IOCTL 0x00000001UL | ||
667 | #define NETLINK_ROUTE_SOCKET__READ 0x00000002UL | ||
668 | #define NETLINK_ROUTE_SOCKET__WRITE 0x00000004UL | ||
669 | #define NETLINK_ROUTE_SOCKET__CREATE 0x00000008UL | ||
670 | #define NETLINK_ROUTE_SOCKET__GETATTR 0x00000010UL | ||
671 | #define NETLINK_ROUTE_SOCKET__SETATTR 0x00000020UL | ||
672 | #define NETLINK_ROUTE_SOCKET__LOCK 0x00000040UL | ||
673 | #define NETLINK_ROUTE_SOCKET__RELABELFROM 0x00000080UL | ||
674 | #define NETLINK_ROUTE_SOCKET__RELABELTO 0x00000100UL | ||
675 | #define NETLINK_ROUTE_SOCKET__APPEND 0x00000200UL | ||
676 | #define NETLINK_ROUTE_SOCKET__BIND 0x00000400UL | ||
677 | #define NETLINK_ROUTE_SOCKET__CONNECT 0x00000800UL | ||
678 | #define NETLINK_ROUTE_SOCKET__LISTEN 0x00001000UL | ||
679 | #define NETLINK_ROUTE_SOCKET__ACCEPT 0x00002000UL | ||
680 | #define NETLINK_ROUTE_SOCKET__GETOPT 0x00004000UL | ||
681 | #define NETLINK_ROUTE_SOCKET__SETOPT 0x00008000UL | ||
682 | #define NETLINK_ROUTE_SOCKET__SHUTDOWN 0x00010000UL | ||
683 | #define NETLINK_ROUTE_SOCKET__RECVFROM 0x00020000UL | ||
684 | #define NETLINK_ROUTE_SOCKET__SENDTO 0x00040000UL | ||
685 | #define NETLINK_ROUTE_SOCKET__RECV_MSG 0x00080000UL | ||
686 | #define NETLINK_ROUTE_SOCKET__SEND_MSG 0x00100000UL | ||
687 | #define NETLINK_ROUTE_SOCKET__NAME_BIND 0x00200000UL | ||
688 | |||
689 | #define NETLINK_ROUTE_SOCKET__NLMSG_READ 0x00400000UL | ||
690 | #define NETLINK_ROUTE_SOCKET__NLMSG_WRITE 0x00800000UL | ||
691 | |||
692 | #define NETLINK_FIREWALL_SOCKET__IOCTL 0x00000001UL | ||
693 | #define NETLINK_FIREWALL_SOCKET__READ 0x00000002UL | ||
694 | #define NETLINK_FIREWALL_SOCKET__WRITE 0x00000004UL | ||
695 | #define NETLINK_FIREWALL_SOCKET__CREATE 0x00000008UL | ||
696 | #define NETLINK_FIREWALL_SOCKET__GETATTR 0x00000010UL | ||
697 | #define NETLINK_FIREWALL_SOCKET__SETATTR 0x00000020UL | ||
698 | #define NETLINK_FIREWALL_SOCKET__LOCK 0x00000040UL | ||
699 | #define NETLINK_FIREWALL_SOCKET__RELABELFROM 0x00000080UL | ||
700 | #define NETLINK_FIREWALL_SOCKET__RELABELTO 0x00000100UL | ||
701 | #define NETLINK_FIREWALL_SOCKET__APPEND 0x00000200UL | ||
702 | #define NETLINK_FIREWALL_SOCKET__BIND 0x00000400UL | ||
703 | #define NETLINK_FIREWALL_SOCKET__CONNECT 0x00000800UL | ||
704 | #define NETLINK_FIREWALL_SOCKET__LISTEN 0x00001000UL | ||
705 | #define NETLINK_FIREWALL_SOCKET__ACCEPT 0x00002000UL | ||
706 | #define NETLINK_FIREWALL_SOCKET__GETOPT 0x00004000UL | ||
707 | #define NETLINK_FIREWALL_SOCKET__SETOPT 0x00008000UL | ||
708 | #define NETLINK_FIREWALL_SOCKET__SHUTDOWN 0x00010000UL | ||
709 | #define NETLINK_FIREWALL_SOCKET__RECVFROM 0x00020000UL | ||
710 | #define NETLINK_FIREWALL_SOCKET__SENDTO 0x00040000UL | ||
711 | #define NETLINK_FIREWALL_SOCKET__RECV_MSG 0x00080000UL | ||
712 | #define NETLINK_FIREWALL_SOCKET__SEND_MSG 0x00100000UL | ||
713 | #define NETLINK_FIREWALL_SOCKET__NAME_BIND 0x00200000UL | ||
714 | |||
715 | #define NETLINK_FIREWALL_SOCKET__NLMSG_READ 0x00400000UL | ||
716 | #define NETLINK_FIREWALL_SOCKET__NLMSG_WRITE 0x00800000UL | ||
717 | |||
718 | #define NETLINK_TCPDIAG_SOCKET__IOCTL 0x00000001UL | ||
719 | #define NETLINK_TCPDIAG_SOCKET__READ 0x00000002UL | ||
720 | #define NETLINK_TCPDIAG_SOCKET__WRITE 0x00000004UL | ||
721 | #define NETLINK_TCPDIAG_SOCKET__CREATE 0x00000008UL | ||
722 | #define NETLINK_TCPDIAG_SOCKET__GETATTR 0x00000010UL | ||
723 | #define NETLINK_TCPDIAG_SOCKET__SETATTR 0x00000020UL | ||
724 | #define NETLINK_TCPDIAG_SOCKET__LOCK 0x00000040UL | ||
725 | #define NETLINK_TCPDIAG_SOCKET__RELABELFROM 0x00000080UL | ||
726 | #define NETLINK_TCPDIAG_SOCKET__RELABELTO 0x00000100UL | ||
727 | #define NETLINK_TCPDIAG_SOCKET__APPEND 0x00000200UL | ||
728 | #define NETLINK_TCPDIAG_SOCKET__BIND 0x00000400UL | ||
729 | #define NETLINK_TCPDIAG_SOCKET__CONNECT 0x00000800UL | ||
730 | #define NETLINK_TCPDIAG_SOCKET__LISTEN 0x00001000UL | ||
731 | #define NETLINK_TCPDIAG_SOCKET__ACCEPT 0x00002000UL | ||
732 | #define NETLINK_TCPDIAG_SOCKET__GETOPT 0x00004000UL | ||
733 | #define NETLINK_TCPDIAG_SOCKET__SETOPT 0x00008000UL | ||
734 | #define NETLINK_TCPDIAG_SOCKET__SHUTDOWN 0x00010000UL | ||
735 | #define NETLINK_TCPDIAG_SOCKET__RECVFROM 0x00020000UL | ||
736 | #define NETLINK_TCPDIAG_SOCKET__SENDTO 0x00040000UL | ||
737 | #define NETLINK_TCPDIAG_SOCKET__RECV_MSG 0x00080000UL | ||
738 | #define NETLINK_TCPDIAG_SOCKET__SEND_MSG 0x00100000UL | ||
739 | #define NETLINK_TCPDIAG_SOCKET__NAME_BIND 0x00200000UL | ||
740 | |||
741 | #define NETLINK_TCPDIAG_SOCKET__NLMSG_READ 0x00400000UL | ||
742 | #define NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE 0x00800000UL | ||
743 | |||
744 | #define NETLINK_NFLOG_SOCKET__IOCTL 0x00000001UL | ||
745 | #define NETLINK_NFLOG_SOCKET__READ 0x00000002UL | ||
746 | #define NETLINK_NFLOG_SOCKET__WRITE 0x00000004UL | ||
747 | #define NETLINK_NFLOG_SOCKET__CREATE 0x00000008UL | ||
748 | #define NETLINK_NFLOG_SOCKET__GETATTR 0x00000010UL | ||
749 | #define NETLINK_NFLOG_SOCKET__SETATTR 0x00000020UL | ||
750 | #define NETLINK_NFLOG_SOCKET__LOCK 0x00000040UL | ||
751 | #define NETLINK_NFLOG_SOCKET__RELABELFROM 0x00000080UL | ||
752 | #define NETLINK_NFLOG_SOCKET__RELABELTO 0x00000100UL | ||
753 | #define NETLINK_NFLOG_SOCKET__APPEND 0x00000200UL | ||
754 | #define NETLINK_NFLOG_SOCKET__BIND 0x00000400UL | ||
755 | #define NETLINK_NFLOG_SOCKET__CONNECT 0x00000800UL | ||
756 | #define NETLINK_NFLOG_SOCKET__LISTEN 0x00001000UL | ||
757 | #define NETLINK_NFLOG_SOCKET__ACCEPT 0x00002000UL | ||
758 | #define NETLINK_NFLOG_SOCKET__GETOPT 0x00004000UL | ||
759 | #define NETLINK_NFLOG_SOCKET__SETOPT 0x00008000UL | ||
760 | #define NETLINK_NFLOG_SOCKET__SHUTDOWN 0x00010000UL | ||
761 | #define NETLINK_NFLOG_SOCKET__RECVFROM 0x00020000UL | ||
762 | #define NETLINK_NFLOG_SOCKET__SENDTO 0x00040000UL | ||
763 | #define NETLINK_NFLOG_SOCKET__RECV_MSG 0x00080000UL | ||
764 | #define NETLINK_NFLOG_SOCKET__SEND_MSG 0x00100000UL | ||
765 | #define NETLINK_NFLOG_SOCKET__NAME_BIND 0x00200000UL | ||
766 | |||
767 | #define NETLINK_XFRM_SOCKET__IOCTL 0x00000001UL | ||
768 | #define NETLINK_XFRM_SOCKET__READ 0x00000002UL | ||
769 | #define NETLINK_XFRM_SOCKET__WRITE 0x00000004UL | ||
770 | #define NETLINK_XFRM_SOCKET__CREATE 0x00000008UL | ||
771 | #define NETLINK_XFRM_SOCKET__GETATTR 0x00000010UL | ||
772 | #define NETLINK_XFRM_SOCKET__SETATTR 0x00000020UL | ||
773 | #define NETLINK_XFRM_SOCKET__LOCK 0x00000040UL | ||
774 | #define NETLINK_XFRM_SOCKET__RELABELFROM 0x00000080UL | ||
775 | #define NETLINK_XFRM_SOCKET__RELABELTO 0x00000100UL | ||
776 | #define NETLINK_XFRM_SOCKET__APPEND 0x00000200UL | ||
777 | #define NETLINK_XFRM_SOCKET__BIND 0x00000400UL | ||
778 | #define NETLINK_XFRM_SOCKET__CONNECT 0x00000800UL | ||
779 | #define NETLINK_XFRM_SOCKET__LISTEN 0x00001000UL | ||
780 | #define NETLINK_XFRM_SOCKET__ACCEPT 0x00002000UL | ||
781 | #define NETLINK_XFRM_SOCKET__GETOPT 0x00004000UL | ||
782 | #define NETLINK_XFRM_SOCKET__SETOPT 0x00008000UL | ||
783 | #define NETLINK_XFRM_SOCKET__SHUTDOWN 0x00010000UL | ||
784 | #define NETLINK_XFRM_SOCKET__RECVFROM 0x00020000UL | ||
785 | #define NETLINK_XFRM_SOCKET__SENDTO 0x00040000UL | ||
786 | #define NETLINK_XFRM_SOCKET__RECV_MSG 0x00080000UL | ||
787 | #define NETLINK_XFRM_SOCKET__SEND_MSG 0x00100000UL | ||
788 | #define NETLINK_XFRM_SOCKET__NAME_BIND 0x00200000UL | ||
789 | |||
790 | #define NETLINK_XFRM_SOCKET__NLMSG_READ 0x00400000UL | ||
791 | #define NETLINK_XFRM_SOCKET__NLMSG_WRITE 0x00800000UL | ||
792 | |||
793 | #define NETLINK_SELINUX_SOCKET__IOCTL 0x00000001UL | ||
794 | #define NETLINK_SELINUX_SOCKET__READ 0x00000002UL | ||
795 | #define NETLINK_SELINUX_SOCKET__WRITE 0x00000004UL | ||
796 | #define NETLINK_SELINUX_SOCKET__CREATE 0x00000008UL | ||
797 | #define NETLINK_SELINUX_SOCKET__GETATTR 0x00000010UL | ||
798 | #define NETLINK_SELINUX_SOCKET__SETATTR 0x00000020UL | ||
799 | #define NETLINK_SELINUX_SOCKET__LOCK 0x00000040UL | ||
800 | #define NETLINK_SELINUX_SOCKET__RELABELFROM 0x00000080UL | ||
801 | #define NETLINK_SELINUX_SOCKET__RELABELTO 0x00000100UL | ||
802 | #define NETLINK_SELINUX_SOCKET__APPEND 0x00000200UL | ||
803 | #define NETLINK_SELINUX_SOCKET__BIND 0x00000400UL | ||
804 | #define NETLINK_SELINUX_SOCKET__CONNECT 0x00000800UL | ||
805 | #define NETLINK_SELINUX_SOCKET__LISTEN 0x00001000UL | ||
806 | #define NETLINK_SELINUX_SOCKET__ACCEPT 0x00002000UL | ||
807 | #define NETLINK_SELINUX_SOCKET__GETOPT 0x00004000UL | ||
808 | #define NETLINK_SELINUX_SOCKET__SETOPT 0x00008000UL | ||
809 | #define NETLINK_SELINUX_SOCKET__SHUTDOWN 0x00010000UL | ||
810 | #define NETLINK_SELINUX_SOCKET__RECVFROM 0x00020000UL | ||
811 | #define NETLINK_SELINUX_SOCKET__SENDTO 0x00040000UL | ||
812 | #define NETLINK_SELINUX_SOCKET__RECV_MSG 0x00080000UL | ||
813 | #define NETLINK_SELINUX_SOCKET__SEND_MSG 0x00100000UL | ||
814 | #define NETLINK_SELINUX_SOCKET__NAME_BIND 0x00200000UL | ||
815 | |||
816 | #define NETLINK_AUDIT_SOCKET__IOCTL 0x00000001UL | ||
817 | #define NETLINK_AUDIT_SOCKET__READ 0x00000002UL | ||
818 | #define NETLINK_AUDIT_SOCKET__WRITE 0x00000004UL | ||
819 | #define NETLINK_AUDIT_SOCKET__CREATE 0x00000008UL | ||
820 | #define NETLINK_AUDIT_SOCKET__GETATTR 0x00000010UL | ||
821 | #define NETLINK_AUDIT_SOCKET__SETATTR 0x00000020UL | ||
822 | #define NETLINK_AUDIT_SOCKET__LOCK 0x00000040UL | ||
823 | #define NETLINK_AUDIT_SOCKET__RELABELFROM 0x00000080UL | ||
824 | #define NETLINK_AUDIT_SOCKET__RELABELTO 0x00000100UL | ||
825 | #define NETLINK_AUDIT_SOCKET__APPEND 0x00000200UL | ||
826 | #define NETLINK_AUDIT_SOCKET__BIND 0x00000400UL | ||
827 | #define NETLINK_AUDIT_SOCKET__CONNECT 0x00000800UL | ||
828 | #define NETLINK_AUDIT_SOCKET__LISTEN 0x00001000UL | ||
829 | #define NETLINK_AUDIT_SOCKET__ACCEPT 0x00002000UL | ||
830 | #define NETLINK_AUDIT_SOCKET__GETOPT 0x00004000UL | ||
831 | #define NETLINK_AUDIT_SOCKET__SETOPT 0x00008000UL | ||
832 | #define NETLINK_AUDIT_SOCKET__SHUTDOWN 0x00010000UL | ||
833 | #define NETLINK_AUDIT_SOCKET__RECVFROM 0x00020000UL | ||
834 | #define NETLINK_AUDIT_SOCKET__SENDTO 0x00040000UL | ||
835 | #define NETLINK_AUDIT_SOCKET__RECV_MSG 0x00080000UL | ||
836 | #define NETLINK_AUDIT_SOCKET__SEND_MSG 0x00100000UL | ||
837 | #define NETLINK_AUDIT_SOCKET__NAME_BIND 0x00200000UL | ||
838 | |||
839 | #define NETLINK_AUDIT_SOCKET__NLMSG_READ 0x00400000UL | ||
840 | #define NETLINK_AUDIT_SOCKET__NLMSG_WRITE 0x00800000UL | ||
841 | |||
842 | #define NETLINK_IP6FW_SOCKET__IOCTL 0x00000001UL | ||
843 | #define NETLINK_IP6FW_SOCKET__READ 0x00000002UL | ||
844 | #define NETLINK_IP6FW_SOCKET__WRITE 0x00000004UL | ||
845 | #define NETLINK_IP6FW_SOCKET__CREATE 0x00000008UL | ||
846 | #define NETLINK_IP6FW_SOCKET__GETATTR 0x00000010UL | ||
847 | #define NETLINK_IP6FW_SOCKET__SETATTR 0x00000020UL | ||
848 | #define NETLINK_IP6FW_SOCKET__LOCK 0x00000040UL | ||
849 | #define NETLINK_IP6FW_SOCKET__RELABELFROM 0x00000080UL | ||
850 | #define NETLINK_IP6FW_SOCKET__RELABELTO 0x00000100UL | ||
851 | #define NETLINK_IP6FW_SOCKET__APPEND 0x00000200UL | ||
852 | #define NETLINK_IP6FW_SOCKET__BIND 0x00000400UL | ||
853 | #define NETLINK_IP6FW_SOCKET__CONNECT 0x00000800UL | ||
854 | #define NETLINK_IP6FW_SOCKET__LISTEN 0x00001000UL | ||
855 | #define NETLINK_IP6FW_SOCKET__ACCEPT 0x00002000UL | ||
856 | #define NETLINK_IP6FW_SOCKET__GETOPT 0x00004000UL | ||
857 | #define NETLINK_IP6FW_SOCKET__SETOPT 0x00008000UL | ||
858 | #define NETLINK_IP6FW_SOCKET__SHUTDOWN 0x00010000UL | ||
859 | #define NETLINK_IP6FW_SOCKET__RECVFROM 0x00020000UL | ||
860 | #define NETLINK_IP6FW_SOCKET__SENDTO 0x00040000UL | ||
861 | #define NETLINK_IP6FW_SOCKET__RECV_MSG 0x00080000UL | ||
862 | #define NETLINK_IP6FW_SOCKET__SEND_MSG 0x00100000UL | ||
863 | #define NETLINK_IP6FW_SOCKET__NAME_BIND 0x00200000UL | ||
864 | |||
865 | #define NETLINK_IP6FW_SOCKET__NLMSG_READ 0x00400000UL | ||
866 | #define NETLINK_IP6FW_SOCKET__NLMSG_WRITE 0x00800000UL | ||
867 | |||
868 | #define NETLINK_DNRT_SOCKET__IOCTL 0x00000001UL | ||
869 | #define NETLINK_DNRT_SOCKET__READ 0x00000002UL | ||
870 | #define NETLINK_DNRT_SOCKET__WRITE 0x00000004UL | ||
871 | #define NETLINK_DNRT_SOCKET__CREATE 0x00000008UL | ||
872 | #define NETLINK_DNRT_SOCKET__GETATTR 0x00000010UL | ||
873 | #define NETLINK_DNRT_SOCKET__SETATTR 0x00000020UL | ||
874 | #define NETLINK_DNRT_SOCKET__LOCK 0x00000040UL | ||
875 | #define NETLINK_DNRT_SOCKET__RELABELFROM 0x00000080UL | ||
876 | #define NETLINK_DNRT_SOCKET__RELABELTO 0x00000100UL | ||
877 | #define NETLINK_DNRT_SOCKET__APPEND 0x00000200UL | ||
878 | #define NETLINK_DNRT_SOCKET__BIND 0x00000400UL | ||
879 | #define NETLINK_DNRT_SOCKET__CONNECT 0x00000800UL | ||
880 | #define NETLINK_DNRT_SOCKET__LISTEN 0x00001000UL | ||
881 | #define NETLINK_DNRT_SOCKET__ACCEPT 0x00002000UL | ||
882 | #define NETLINK_DNRT_SOCKET__GETOPT 0x00004000UL | ||
883 | #define NETLINK_DNRT_SOCKET__SETOPT 0x00008000UL | ||
884 | #define NETLINK_DNRT_SOCKET__SHUTDOWN 0x00010000UL | ||
885 | #define NETLINK_DNRT_SOCKET__RECVFROM 0x00020000UL | ||
886 | #define NETLINK_DNRT_SOCKET__SENDTO 0x00040000UL | ||
887 | #define NETLINK_DNRT_SOCKET__RECV_MSG 0x00080000UL | ||
888 | #define NETLINK_DNRT_SOCKET__SEND_MSG 0x00100000UL | ||
889 | #define NETLINK_DNRT_SOCKET__NAME_BIND 0x00200000UL | ||
890 | |||
891 | #define DBUS__ACQUIRE_SVC 0x00000001UL | ||
892 | #define DBUS__SEND_MSG 0x00000002UL | ||
893 | |||
894 | #define NSCD__GETPWD 0x00000001UL | ||
895 | #define NSCD__GETGRP 0x00000002UL | ||
896 | #define NSCD__GETHOST 0x00000004UL | ||
897 | #define NSCD__GETSTAT 0x00000008UL | ||
898 | #define NSCD__ADMIN 0x00000010UL | ||
899 | #define NSCD__SHMEMPWD 0x00000020UL | ||
900 | #define NSCD__SHMEMGRP 0x00000040UL | ||
901 | #define NSCD__SHMEMHOST 0x00000080UL | ||
902 | |||
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h new file mode 100644 index 000000000000..960ef18ddc41 --- /dev/null +++ b/security/selinux/include/avc.h | |||
@@ -0,0 +1,137 @@ | |||
1 | /* | ||
2 | * Access vector cache interface for object managers. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | #ifndef _SELINUX_AVC_H_ | ||
7 | #define _SELINUX_AVC_H_ | ||
8 | |||
9 | #include <linux/stddef.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/kdev_t.h> | ||
13 | #include <linux/spinlock.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/in6.h> | ||
16 | #include <asm/system.h> | ||
17 | #include "flask.h" | ||
18 | #include "av_permissions.h" | ||
19 | #include "security.h" | ||
20 | |||
21 | #ifdef CONFIG_SECURITY_SELINUX_DEVELOP | ||
22 | extern int selinux_enforcing; | ||
23 | #else | ||
24 | #define selinux_enforcing 1 | ||
25 | #endif | ||
26 | |||
27 | /* | ||
28 | * An entry in the AVC. | ||
29 | */ | ||
30 | struct avc_entry; | ||
31 | |||
32 | struct task_struct; | ||
33 | struct vfsmount; | ||
34 | struct dentry; | ||
35 | struct inode; | ||
36 | struct sock; | ||
37 | struct sk_buff; | ||
38 | |||
39 | /* Auxiliary data to use in generating the audit record. */ | ||
40 | struct avc_audit_data { | ||
41 | char type; | ||
42 | #define AVC_AUDIT_DATA_FS 1 | ||
43 | #define AVC_AUDIT_DATA_NET 2 | ||
44 | #define AVC_AUDIT_DATA_CAP 3 | ||
45 | #define AVC_AUDIT_DATA_IPC 4 | ||
46 | struct task_struct *tsk; | ||
47 | union { | ||
48 | struct { | ||
49 | struct vfsmount *mnt; | ||
50 | struct dentry *dentry; | ||
51 | struct inode *inode; | ||
52 | } fs; | ||
53 | struct { | ||
54 | char *netif; | ||
55 | struct sock *sk; | ||
56 | u16 family; | ||
57 | u16 dport; | ||
58 | u16 sport; | ||
59 | union { | ||
60 | struct { | ||
61 | u32 daddr; | ||
62 | u32 saddr; | ||
63 | } v4; | ||
64 | struct { | ||
65 | struct in6_addr daddr; | ||
66 | struct in6_addr saddr; | ||
67 | } v6; | ||
68 | } fam; | ||
69 | } net; | ||
70 | int cap; | ||
71 | int ipc_id; | ||
72 | } u; | ||
73 | }; | ||
74 | |||
75 | #define v4info fam.v4 | ||
76 | #define v6info fam.v6 | ||
77 | |||
78 | /* Initialize an AVC audit data structure. */ | ||
79 | #define AVC_AUDIT_DATA_INIT(_d,_t) \ | ||
80 | { memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; } | ||
81 | |||
82 | /* | ||
83 | * AVC statistics | ||
84 | */ | ||
85 | struct avc_cache_stats | ||
86 | { | ||
87 | unsigned int lookups; | ||
88 | unsigned int hits; | ||
89 | unsigned int misses; | ||
90 | unsigned int allocations; | ||
91 | unsigned int reclaims; | ||
92 | unsigned int frees; | ||
93 | }; | ||
94 | |||
95 | /* | ||
96 | * AVC operations | ||
97 | */ | ||
98 | |||
99 | void __init avc_init(void); | ||
100 | |||
101 | void avc_audit(u32 ssid, u32 tsid, | ||
102 | u16 tclass, u32 requested, | ||
103 | struct av_decision *avd, int result, struct avc_audit_data *auditdata); | ||
104 | |||
105 | int avc_has_perm_noaudit(u32 ssid, u32 tsid, | ||
106 | u16 tclass, u32 requested, | ||
107 | struct av_decision *avd); | ||
108 | |||
109 | int avc_has_perm(u32 ssid, u32 tsid, | ||
110 | u16 tclass, u32 requested, | ||
111 | struct avc_audit_data *auditdata); | ||
112 | |||
113 | #define AVC_CALLBACK_GRANT 1 | ||
114 | #define AVC_CALLBACK_TRY_REVOKE 2 | ||
115 | #define AVC_CALLBACK_REVOKE 4 | ||
116 | #define AVC_CALLBACK_RESET 8 | ||
117 | #define AVC_CALLBACK_AUDITALLOW_ENABLE 16 | ||
118 | #define AVC_CALLBACK_AUDITALLOW_DISABLE 32 | ||
119 | #define AVC_CALLBACK_AUDITDENY_ENABLE 64 | ||
120 | #define AVC_CALLBACK_AUDITDENY_DISABLE 128 | ||
121 | |||
122 | int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, | ||
123 | u16 tclass, u32 perms, | ||
124 | u32 *out_retained), | ||
125 | u32 events, u32 ssid, u32 tsid, | ||
126 | u16 tclass, u32 perms); | ||
127 | |||
128 | /* Exported to selinuxfs */ | ||
129 | int avc_get_hash_stats(char *page); | ||
130 | extern unsigned int avc_cache_threshold; | ||
131 | |||
132 | #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS | ||
133 | DECLARE_PER_CPU(struct avc_cache_stats, avc_cache_stats); | ||
134 | #endif | ||
135 | |||
136 | #endif /* _SELINUX_AVC_H_ */ | ||
137 | |||
diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h new file mode 100644 index 000000000000..450a2831e2e3 --- /dev/null +++ b/security/selinux/include/avc_ss.h | |||
@@ -0,0 +1,14 @@ | |||
1 | /* | ||
2 | * Access vector cache interface for the security server. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | #ifndef _SELINUX_AVC_SS_H_ | ||
7 | #define _SELINUX_AVC_SS_H_ | ||
8 | |||
9 | #include "flask.h" | ||
10 | |||
11 | int avc_ss_reset(u32 seqno); | ||
12 | |||
13 | #endif /* _SELINUX_AVC_SS_H_ */ | ||
14 | |||
diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h new file mode 100644 index 000000000000..519a77d7394a --- /dev/null +++ b/security/selinux/include/class_to_string.h | |||
@@ -0,0 +1,58 @@ | |||
1 | /* This file is automatically generated. Do not edit. */ | ||
2 | /* | ||
3 | * Security object class definitions | ||
4 | */ | ||
5 | S_("null") | ||
6 | S_("security") | ||
7 | S_("process") | ||
8 | S_("system") | ||
9 | S_("capability") | ||
10 | S_("filesystem") | ||
11 | S_("file") | ||
12 | S_("dir") | ||
13 | S_("fd") | ||
14 | S_("lnk_file") | ||
15 | S_("chr_file") | ||
16 | S_("blk_file") | ||
17 | S_("sock_file") | ||
18 | S_("fifo_file") | ||
19 | S_("socket") | ||
20 | S_("tcp_socket") | ||
21 | S_("udp_socket") | ||
22 | S_("rawip_socket") | ||
23 | S_("node") | ||
24 | S_("netif") | ||
25 | S_("netlink_socket") | ||
26 | S_("packet_socket") | ||
27 | S_("key_socket") | ||
28 | S_("unix_stream_socket") | ||
29 | S_("unix_dgram_socket") | ||
30 | S_("sem") | ||
31 | S_("msg") | ||
32 | S_("msgq") | ||
33 | S_("shm") | ||
34 | S_("ipc") | ||
35 | S_("passwd") | ||
36 | S_("drawable") | ||
37 | S_("window") | ||
38 | S_("gc") | ||
39 | S_("font") | ||
40 | S_("colormap") | ||
41 | S_("property") | ||
42 | S_("cursor") | ||
43 | S_("xclient") | ||
44 | S_("xinput") | ||
45 | S_("xserver") | ||
46 | S_("xextension") | ||
47 | S_("pax") | ||
48 | S_("netlink_route_socket") | ||
49 | S_("netlink_firewall_socket") | ||
50 | S_("netlink_tcpdiag_socket") | ||
51 | S_("netlink_nflog_socket") | ||
52 | S_("netlink_xfrm_socket") | ||
53 | S_("netlink_selinux_socket") | ||
54 | S_("netlink_audit_socket") | ||
55 | S_("netlink_ip6fw_socket") | ||
56 | S_("netlink_dnrt_socket") | ||
57 | S_("dbus") | ||
58 | S_("nscd") | ||
diff --git a/security/selinux/include/common_perm_to_string.h b/security/selinux/include/common_perm_to_string.h new file mode 100644 index 000000000000..ce5b6e2fe9dd --- /dev/null +++ b/security/selinux/include/common_perm_to_string.h | |||
@@ -0,0 +1,58 @@ | |||
1 | /* This file is automatically generated. Do not edit. */ | ||
2 | TB_(common_file_perm_to_string) | ||
3 | S_("ioctl") | ||
4 | S_("read") | ||
5 | S_("write") | ||
6 | S_("create") | ||
7 | S_("getattr") | ||
8 | S_("setattr") | ||
9 | S_("lock") | ||
10 | S_("relabelfrom") | ||
11 | S_("relabelto") | ||
12 | S_("append") | ||
13 | S_("unlink") | ||
14 | S_("link") | ||
15 | S_("rename") | ||
16 | S_("execute") | ||
17 | S_("swapon") | ||
18 | S_("quotaon") | ||
19 | S_("mounton") | ||
20 | TE_(common_file_perm_to_string) | ||
21 | |||
22 | TB_(common_socket_perm_to_string) | ||
23 | S_("ioctl") | ||
24 | S_("read") | ||
25 | S_("write") | ||
26 | S_("create") | ||
27 | S_("getattr") | ||
28 | S_("setattr") | ||
29 | S_("lock") | ||
30 | S_("relabelfrom") | ||
31 | S_("relabelto") | ||
32 | S_("append") | ||
33 | S_("bind") | ||
34 | S_("connect") | ||
35 | S_("listen") | ||
36 | S_("accept") | ||
37 | S_("getopt") | ||
38 | S_("setopt") | ||
39 | S_("shutdown") | ||
40 | S_("recvfrom") | ||
41 | S_("sendto") | ||
42 | S_("recv_msg") | ||
43 | S_("send_msg") | ||
44 | S_("name_bind") | ||
45 | TE_(common_socket_perm_to_string) | ||
46 | |||
47 | TB_(common_ipc_perm_to_string) | ||
48 | S_("create") | ||
49 | S_("destroy") | ||
50 | S_("getattr") | ||
51 | S_("setattr") | ||
52 | S_("read") | ||
53 | S_("write") | ||
54 | S_("associate") | ||
55 | S_("unix_read") | ||
56 | S_("unix_write") | ||
57 | TE_(common_ipc_perm_to_string) | ||
58 | |||
diff --git a/security/selinux/include/conditional.h b/security/selinux/include/conditional.h new file mode 100644 index 000000000000..67ce7a8d8301 --- /dev/null +++ b/security/selinux/include/conditional.h | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * Interface to booleans in the security server. This is exported | ||
3 | * for the selinuxfs. | ||
4 | * | ||
5 | * Author: Karl MacMillan <kmacmillan@tresys.com> | ||
6 | * | ||
7 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation, version 2. | ||
11 | */ | ||
12 | |||
13 | #ifndef _SELINUX_CONDITIONAL_H_ | ||
14 | #define _SELINUX_CONDITIONAL_H_ | ||
15 | |||
16 | int security_get_bools(int *len, char ***names, int **values); | ||
17 | |||
18 | int security_set_bools(int len, int *values); | ||
19 | |||
20 | int security_get_bool_value(int bool); | ||
21 | |||
22 | #endif | ||
diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h new file mode 100644 index 000000000000..4eef1b654e92 --- /dev/null +++ b/security/selinux/include/flask.h | |||
@@ -0,0 +1,95 @@ | |||
1 | /* This file is automatically generated. Do not edit. */ | ||
2 | #ifndef _SELINUX_FLASK_H_ | ||
3 | #define _SELINUX_FLASK_H_ | ||
4 | |||
5 | /* | ||
6 | * Security object class definitions | ||
7 | */ | ||
8 | #define SECCLASS_SECURITY 1 | ||
9 | #define SECCLASS_PROCESS 2 | ||
10 | #define SECCLASS_SYSTEM 3 | ||
11 | #define SECCLASS_CAPABILITY 4 | ||
12 | #define SECCLASS_FILESYSTEM 5 | ||
13 | #define SECCLASS_FILE 6 | ||
14 | #define SECCLASS_DIR 7 | ||
15 | #define SECCLASS_FD 8 | ||
16 | #define SECCLASS_LNK_FILE 9 | ||
17 | #define SECCLASS_CHR_FILE 10 | ||
18 | #define SECCLASS_BLK_FILE 11 | ||
19 | #define SECCLASS_SOCK_FILE 12 | ||
20 | #define SECCLASS_FIFO_FILE 13 | ||
21 | #define SECCLASS_SOCKET 14 | ||
22 | #define SECCLASS_TCP_SOCKET 15 | ||
23 | #define SECCLASS_UDP_SOCKET 16 | ||
24 | #define SECCLASS_RAWIP_SOCKET 17 | ||
25 | #define SECCLASS_NODE 18 | ||
26 | #define SECCLASS_NETIF 19 | ||
27 | #define SECCLASS_NETLINK_SOCKET 20 | ||
28 | #define SECCLASS_PACKET_SOCKET 21 | ||
29 | #define SECCLASS_KEY_SOCKET 22 | ||
30 | #define SECCLASS_UNIX_STREAM_SOCKET 23 | ||
31 | #define SECCLASS_UNIX_DGRAM_SOCKET 24 | ||
32 | #define SECCLASS_SEM 25 | ||
33 | #define SECCLASS_MSG 26 | ||
34 | #define SECCLASS_MSGQ 27 | ||
35 | #define SECCLASS_SHM 28 | ||
36 | #define SECCLASS_IPC 29 | ||
37 | #define SECCLASS_PASSWD 30 | ||
38 | #define SECCLASS_DRAWABLE 31 | ||
39 | #define SECCLASS_WINDOW 32 | ||
40 | #define SECCLASS_GC 33 | ||
41 | #define SECCLASS_FONT 34 | ||
42 | #define SECCLASS_COLORMAP 35 | ||
43 | #define SECCLASS_PROPERTY 36 | ||
44 | #define SECCLASS_CURSOR 37 | ||
45 | #define SECCLASS_XCLIENT 38 | ||
46 | #define SECCLASS_XINPUT 39 | ||
47 | #define SECCLASS_XSERVER 40 | ||
48 | #define SECCLASS_XEXTENSION 41 | ||
49 | #define SECCLASS_PAX 42 | ||
50 | #define SECCLASS_NETLINK_ROUTE_SOCKET 43 | ||
51 | #define SECCLASS_NETLINK_FIREWALL_SOCKET 44 | ||
52 | #define SECCLASS_NETLINK_TCPDIAG_SOCKET 45 | ||
53 | #define SECCLASS_NETLINK_NFLOG_SOCKET 46 | ||
54 | #define SECCLASS_NETLINK_XFRM_SOCKET 47 | ||
55 | #define SECCLASS_NETLINK_SELINUX_SOCKET 48 | ||
56 | #define SECCLASS_NETLINK_AUDIT_SOCKET 49 | ||
57 | #define SECCLASS_NETLINK_IP6FW_SOCKET 50 | ||
58 | #define SECCLASS_NETLINK_DNRT_SOCKET 51 | ||
59 | #define SECCLASS_DBUS 52 | ||
60 | #define SECCLASS_NSCD 53 | ||
61 | |||
62 | /* | ||
63 | * Security identifier indices for initial entities | ||
64 | */ | ||
65 | #define SECINITSID_KERNEL 1 | ||
66 | #define SECINITSID_SECURITY 2 | ||
67 | #define SECINITSID_UNLABELED 3 | ||
68 | #define SECINITSID_FS 4 | ||
69 | #define SECINITSID_FILE 5 | ||
70 | #define SECINITSID_FILE_LABELS 6 | ||
71 | #define SECINITSID_INIT 7 | ||
72 | #define SECINITSID_ANY_SOCKET 8 | ||
73 | #define SECINITSID_PORT 9 | ||
74 | #define SECINITSID_NETIF 10 | ||
75 | #define SECINITSID_NETMSG 11 | ||
76 | #define SECINITSID_NODE 12 | ||
77 | #define SECINITSID_IGMP_PACKET 13 | ||
78 | #define SECINITSID_ICMP_SOCKET 14 | ||
79 | #define SECINITSID_TCP_SOCKET 15 | ||
80 | #define SECINITSID_SYSCTL_MODPROBE 16 | ||
81 | #define SECINITSID_SYSCTL 17 | ||
82 | #define SECINITSID_SYSCTL_FS 18 | ||
83 | #define SECINITSID_SYSCTL_KERNEL 19 | ||
84 | #define SECINITSID_SYSCTL_NET 20 | ||
85 | #define SECINITSID_SYSCTL_NET_UNIX 21 | ||
86 | #define SECINITSID_SYSCTL_VM 22 | ||
87 | #define SECINITSID_SYSCTL_DEV 23 | ||
88 | #define SECINITSID_KMOD 24 | ||
89 | #define SECINITSID_POLICY 25 | ||
90 | #define SECINITSID_SCMP_PACKET 26 | ||
91 | #define SECINITSID_DEVNULL 27 | ||
92 | |||
93 | #define SECINITSID_NUM 27 | ||
94 | |||
95 | #endif | ||
diff --git a/security/selinux/include/initial_sid_to_string.h b/security/selinux/include/initial_sid_to_string.h new file mode 100644 index 000000000000..d4fac82793ae --- /dev/null +++ b/security/selinux/include/initial_sid_to_string.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* This file is automatically generated. Do not edit. */ | ||
2 | static char *initial_sid_to_string[] = | ||
3 | { | ||
4 | "null", | ||
5 | "kernel", | ||
6 | "security", | ||
7 | "unlabeled", | ||
8 | "fs", | ||
9 | "file", | ||
10 | "file_labels", | ||
11 | "init", | ||
12 | "any_socket", | ||
13 | "port", | ||
14 | "netif", | ||
15 | "netmsg", | ||
16 | "node", | ||
17 | "igmp_packet", | ||
18 | "icmp_socket", | ||
19 | "tcp_socket", | ||
20 | "sysctl_modprobe", | ||
21 | "sysctl", | ||
22 | "sysctl_fs", | ||
23 | "sysctl_kernel", | ||
24 | "sysctl_net", | ||
25 | "sysctl_net_unix", | ||
26 | "sysctl_vm", | ||
27 | "sysctl_dev", | ||
28 | "kmod", | ||
29 | "policy", | ||
30 | "scmp_packet", | ||
31 | "devnull", | ||
32 | }; | ||
33 | |||
diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h new file mode 100644 index 000000000000..8bd6f9992d2b --- /dev/null +++ b/security/selinux/include/netif.h | |||
@@ -0,0 +1,21 @@ | |||
1 | /* | ||
2 | * Network interface table. | ||
3 | * | ||
4 | * Network interfaces (devices) do not have a security field, so we | ||
5 | * maintain a table associating each interface with a SID. | ||
6 | * | ||
7 | * Author: James Morris <jmorris@redhat.com> | ||
8 | * | ||
9 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2, | ||
13 | * as published by the Free Software Foundation. | ||
14 | */ | ||
15 | #ifndef _SELINUX_NETIF_H_ | ||
16 | #define _SELINUX_NETIF_H_ | ||
17 | |||
18 | int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid); | ||
19 | |||
20 | #endif /* _SELINUX_NETIF_H_ */ | ||
21 | |||
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h new file mode 100644 index 000000000000..887937c8134a --- /dev/null +++ b/security/selinux/include/objsec.h | |||
@@ -0,0 +1,112 @@ | |||
1 | /* | ||
2 | * NSA Security-Enhanced Linux (SELinux) security module | ||
3 | * | ||
4 | * This file contains the SELinux security data structures for kernel objects. | ||
5 | * | ||
6 | * Author(s): Stephen Smalley, <sds@epoch.ncsc.mil> | ||
7 | * Chris Vance, <cvance@nai.com> | ||
8 | * Wayne Salamon, <wsalamon@nai.com> | ||
9 | * James Morris <jmorris@redhat.com> | ||
10 | * | ||
11 | * Copyright (C) 2001,2002 Networks Associates Technology, Inc. | ||
12 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License version 2, | ||
16 | * as published by the Free Software Foundation. | ||
17 | */ | ||
18 | #ifndef _SELINUX_OBJSEC_H_ | ||
19 | #define _SELINUX_OBJSEC_H_ | ||
20 | |||
21 | #include <linux/list.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/fs.h> | ||
24 | #include <linux/binfmts.h> | ||
25 | #include <linux/in.h> | ||
26 | #include "flask.h" | ||
27 | #include "avc.h" | ||
28 | |||
29 | struct task_security_struct { | ||
30 | unsigned long magic; /* magic number for this module */ | ||
31 | struct task_struct *task; /* back pointer to task object */ | ||
32 | u32 osid; /* SID prior to last execve */ | ||
33 | u32 sid; /* current SID */ | ||
34 | u32 exec_sid; /* exec SID */ | ||
35 | u32 create_sid; /* fscreate SID */ | ||
36 | u32 ptrace_sid; /* SID of ptrace parent */ | ||
37 | }; | ||
38 | |||
39 | struct inode_security_struct { | ||
40 | unsigned long magic; /* magic number for this module */ | ||
41 | struct inode *inode; /* back pointer to inode object */ | ||
42 | struct list_head list; /* list of inode_security_struct */ | ||
43 | u32 task_sid; /* SID of creating task */ | ||
44 | u32 sid; /* SID of this object */ | ||
45 | u16 sclass; /* security class of this object */ | ||
46 | unsigned char initialized; /* initialization flag */ | ||
47 | struct semaphore sem; | ||
48 | unsigned char inherit; /* inherit SID from parent entry */ | ||
49 | }; | ||
50 | |||
51 | struct file_security_struct { | ||
52 | unsigned long magic; /* magic number for this module */ | ||
53 | struct file *file; /* back pointer to file object */ | ||
54 | u32 sid; /* SID of open file description */ | ||
55 | u32 fown_sid; /* SID of file owner (for SIGIO) */ | ||
56 | }; | ||
57 | |||
58 | struct superblock_security_struct { | ||
59 | unsigned long magic; /* magic number for this module */ | ||
60 | struct super_block *sb; /* back pointer to sb object */ | ||
61 | struct list_head list; /* list of superblock_security_struct */ | ||
62 | u32 sid; /* SID of file system */ | ||
63 | u32 def_sid; /* default SID for labeling */ | ||
64 | unsigned int behavior; /* labeling behavior */ | ||
65 | unsigned char initialized; /* initialization flag */ | ||
66 | unsigned char proc; /* proc fs */ | ||
67 | struct semaphore sem; | ||
68 | struct list_head isec_head; | ||
69 | spinlock_t isec_lock; | ||
70 | }; | ||
71 | |||
72 | struct msg_security_struct { | ||
73 | unsigned long magic; /* magic number for this module */ | ||
74 | struct msg_msg *msg; /* back pointer */ | ||
75 | u32 sid; /* SID of message */ | ||
76 | }; | ||
77 | |||
78 | struct ipc_security_struct { | ||
79 | unsigned long magic; /* magic number for this module */ | ||
80 | struct kern_ipc_perm *ipc_perm; /* back pointer */ | ||
81 | u16 sclass; /* security class of this object */ | ||
82 | u32 sid; /* SID of IPC resource */ | ||
83 | }; | ||
84 | |||
85 | struct bprm_security_struct { | ||
86 | unsigned long magic; /* magic number for this module */ | ||
87 | struct linux_binprm *bprm; /* back pointer to bprm object */ | ||
88 | u32 sid; /* SID for transformed process */ | ||
89 | unsigned char set; | ||
90 | |||
91 | /* | ||
92 | * unsafe is used to share failure information from bprm_apply_creds() | ||
93 | * to bprm_post_apply_creds(). | ||
94 | */ | ||
95 | char unsafe; | ||
96 | }; | ||
97 | |||
98 | struct netif_security_struct { | ||
99 | struct net_device *dev; /* back pointer */ | ||
100 | u32 if_sid; /* SID for this interface */ | ||
101 | u32 msg_sid; /* default SID for messages received on this interface */ | ||
102 | }; | ||
103 | |||
104 | struct sk_security_struct { | ||
105 | unsigned long magic; /* magic number for this module */ | ||
106 | struct sock *sk; /* back pointer to sk object */ | ||
107 | u32 peer_sid; /* SID of peer */ | ||
108 | }; | ||
109 | |||
110 | extern unsigned int selinux_checkreqprot; | ||
111 | |||
112 | #endif /* _SELINUX_OBJSEC_H_ */ | ||
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h new file mode 100644 index 000000000000..fa187c9a351d --- /dev/null +++ b/security/selinux/include/security.h | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * Security server interface. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | * | ||
6 | */ | ||
7 | |||
8 | #ifndef _SELINUX_SECURITY_H_ | ||
9 | #define _SELINUX_SECURITY_H_ | ||
10 | |||
11 | #include "flask.h" | ||
12 | |||
13 | #define SECSID_NULL 0x00000000 /* unspecified SID */ | ||
14 | #define SECSID_WILD 0xffffffff /* wildcard SID */ | ||
15 | #define SECCLASS_NULL 0x0000 /* no class */ | ||
16 | |||
17 | #define SELINUX_MAGIC 0xf97cff8c | ||
18 | |||
19 | /* Identify specific policy version changes */ | ||
20 | #define POLICYDB_VERSION_BASE 15 | ||
21 | #define POLICYDB_VERSION_BOOL 16 | ||
22 | #define POLICYDB_VERSION_IPV6 17 | ||
23 | #define POLICYDB_VERSION_NLCLASS 18 | ||
24 | #define POLICYDB_VERSION_VALIDATETRANS 19 | ||
25 | #define POLICYDB_VERSION_MLS 19 | ||
26 | |||
27 | /* Range of policy versions we understand*/ | ||
28 | #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE | ||
29 | #define POLICYDB_VERSION_MAX POLICYDB_VERSION_MLS | ||
30 | |||
31 | #ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM | ||
32 | extern int selinux_enabled; | ||
33 | #else | ||
34 | #define selinux_enabled 1 | ||
35 | #endif | ||
36 | |||
37 | extern int selinux_mls_enabled; | ||
38 | |||
39 | int security_load_policy(void * data, size_t len); | ||
40 | |||
41 | struct av_decision { | ||
42 | u32 allowed; | ||
43 | u32 decided; | ||
44 | u32 auditallow; | ||
45 | u32 auditdeny; | ||
46 | u32 seqno; | ||
47 | }; | ||
48 | |||
49 | int security_compute_av(u32 ssid, u32 tsid, | ||
50 | u16 tclass, u32 requested, | ||
51 | struct av_decision *avd); | ||
52 | |||
53 | int security_transition_sid(u32 ssid, u32 tsid, | ||
54 | u16 tclass, u32 *out_sid); | ||
55 | |||
56 | int security_member_sid(u32 ssid, u32 tsid, | ||
57 | u16 tclass, u32 *out_sid); | ||
58 | |||
59 | int security_change_sid(u32 ssid, u32 tsid, | ||
60 | u16 tclass, u32 *out_sid); | ||
61 | |||
62 | int security_sid_to_context(u32 sid, char **scontext, | ||
63 | u32 *scontext_len); | ||
64 | |||
65 | int security_context_to_sid(char *scontext, u32 scontext_len, | ||
66 | u32 *out_sid); | ||
67 | |||
68 | int security_get_user_sids(u32 callsid, char *username, | ||
69 | u32 **sids, u32 *nel); | ||
70 | |||
71 | int security_port_sid(u16 domain, u16 type, u8 protocol, u16 port, | ||
72 | u32 *out_sid); | ||
73 | |||
74 | int security_netif_sid(char *name, u32 *if_sid, | ||
75 | u32 *msg_sid); | ||
76 | |||
77 | int security_node_sid(u16 domain, void *addr, u32 addrlen, | ||
78 | u32 *out_sid); | ||
79 | |||
80 | int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, | ||
81 | u16 tclass); | ||
82 | |||
83 | #define SECURITY_FS_USE_XATTR 1 /* use xattr */ | ||
84 | #define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */ | ||
85 | #define SECURITY_FS_USE_TASK 3 /* use task SIDs, e.g. pipefs/sockfs */ | ||
86 | #define SECURITY_FS_USE_GENFS 4 /* use the genfs support */ | ||
87 | #define SECURITY_FS_USE_NONE 5 /* no labeling support */ | ||
88 | #define SECURITY_FS_USE_MNTPOINT 6 /* use mountpoint labeling */ | ||
89 | |||
90 | int security_fs_use(const char *fstype, unsigned int *behavior, | ||
91 | u32 *sid); | ||
92 | |||
93 | int security_genfs_sid(const char *fstype, char *name, u16 sclass, | ||
94 | u32 *sid); | ||
95 | |||
96 | #endif /* _SELINUX_SECURITY_H_ */ | ||
97 | |||
diff --git a/security/selinux/netif.c b/security/selinux/netif.c new file mode 100644 index 000000000000..718d7be9f4dd --- /dev/null +++ b/security/selinux/netif.c | |||
@@ -0,0 +1,270 @@ | |||
1 | /* | ||
2 | * Network interface table. | ||
3 | * | ||
4 | * Network interfaces (devices) do not have a security field, so we | ||
5 | * maintain a table associating each interface with a SID. | ||
6 | * | ||
7 | * Author: James Morris <jmorris@redhat.com> | ||
8 | * | ||
9 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2, | ||
13 | * as published by the Free Software Foundation. | ||
14 | */ | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/stddef.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/list.h> | ||
20 | #include <linux/notifier.h> | ||
21 | #include <linux/netdevice.h> | ||
22 | #include <linux/rcupdate.h> | ||
23 | |||
24 | #include "security.h" | ||
25 | #include "objsec.h" | ||
26 | #include "netif.h" | ||
27 | |||
28 | #define SEL_NETIF_HASH_SIZE 64 | ||
29 | #define SEL_NETIF_HASH_MAX 1024 | ||
30 | |||
31 | #undef DEBUG | ||
32 | |||
33 | #ifdef DEBUG | ||
34 | #define DEBUGP printk | ||
35 | #else | ||
36 | #define DEBUGP(format, args...) | ||
37 | #endif | ||
38 | |||
39 | struct sel_netif | ||
40 | { | ||
41 | struct list_head list; | ||
42 | struct netif_security_struct nsec; | ||
43 | struct rcu_head rcu_head; | ||
44 | }; | ||
45 | |||
46 | static u32 sel_netif_total; | ||
47 | static LIST_HEAD(sel_netif_list); | ||
48 | static DEFINE_SPINLOCK(sel_netif_lock); | ||
49 | static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; | ||
50 | |||
51 | static inline u32 sel_netif_hasfn(struct net_device *dev) | ||
52 | { | ||
53 | return (dev->ifindex & (SEL_NETIF_HASH_SIZE - 1)); | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * All of the devices should normally fit in the hash, so we optimize | ||
58 | * for that case. | ||
59 | */ | ||
60 | static inline struct sel_netif *sel_netif_find(struct net_device *dev) | ||
61 | { | ||
62 | struct list_head *pos; | ||
63 | int idx = sel_netif_hasfn(dev); | ||
64 | |||
65 | __list_for_each_rcu(pos, &sel_netif_hash[idx]) { | ||
66 | struct sel_netif *netif = list_entry(pos, | ||
67 | struct sel_netif, list); | ||
68 | if (likely(netif->nsec.dev == dev)) | ||
69 | return netif; | ||
70 | } | ||
71 | return NULL; | ||
72 | } | ||
73 | |||
74 | static int sel_netif_insert(struct sel_netif *netif) | ||
75 | { | ||
76 | int idx, ret = 0; | ||
77 | |||
78 | if (sel_netif_total >= SEL_NETIF_HASH_MAX) { | ||
79 | ret = -ENOSPC; | ||
80 | goto out; | ||
81 | } | ||
82 | |||
83 | idx = sel_netif_hasfn(netif->nsec.dev); | ||
84 | list_add_rcu(&netif->list, &sel_netif_hash[idx]); | ||
85 | sel_netif_total++; | ||
86 | out: | ||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | static void sel_netif_free(struct rcu_head *p) | ||
91 | { | ||
92 | struct sel_netif *netif = container_of(p, struct sel_netif, rcu_head); | ||
93 | |||
94 | DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name); | ||
95 | kfree(netif); | ||
96 | } | ||
97 | |||
98 | static void sel_netif_destroy(struct sel_netif *netif) | ||
99 | { | ||
100 | DEBUGP("%s: %s\n", __FUNCTION__, netif->nsec.dev->name); | ||
101 | |||
102 | list_del_rcu(&netif->list); | ||
103 | sel_netif_total--; | ||
104 | call_rcu(&netif->rcu_head, sel_netif_free); | ||
105 | } | ||
106 | |||
107 | static struct sel_netif *sel_netif_lookup(struct net_device *dev) | ||
108 | { | ||
109 | int ret; | ||
110 | struct sel_netif *netif, *new; | ||
111 | struct netif_security_struct *nsec; | ||
112 | |||
113 | netif = sel_netif_find(dev); | ||
114 | if (likely(netif != NULL)) | ||
115 | goto out; | ||
116 | |||
117 | new = kmalloc(sizeof(*new), GFP_ATOMIC); | ||
118 | if (!new) { | ||
119 | netif = ERR_PTR(-ENOMEM); | ||
120 | goto out; | ||
121 | } | ||
122 | |||
123 | memset(new, 0, sizeof(*new)); | ||
124 | nsec = &new->nsec; | ||
125 | |||
126 | ret = security_netif_sid(dev->name, &nsec->if_sid, &nsec->msg_sid); | ||
127 | if (ret < 0) { | ||
128 | kfree(new); | ||
129 | netif = ERR_PTR(ret); | ||
130 | goto out; | ||
131 | } | ||
132 | |||
133 | nsec->dev = dev; | ||
134 | |||
135 | spin_lock_bh(&sel_netif_lock); | ||
136 | |||
137 | netif = sel_netif_find(dev); | ||
138 | if (netif) { | ||
139 | spin_unlock_bh(&sel_netif_lock); | ||
140 | kfree(new); | ||
141 | goto out; | ||
142 | } | ||
143 | |||
144 | ret = sel_netif_insert(new); | ||
145 | spin_unlock_bh(&sel_netif_lock); | ||
146 | |||
147 | if (ret) { | ||
148 | kfree(new); | ||
149 | netif = ERR_PTR(ret); | ||
150 | goto out; | ||
151 | } | ||
152 | |||
153 | netif = new; | ||
154 | |||
155 | DEBUGP("new: ifindex=%u name=%s if_sid=%u msg_sid=%u\n", dev->ifindex, dev->name, | ||
156 | nsec->if_sid, nsec->msg_sid); | ||
157 | out: | ||
158 | return netif; | ||
159 | } | ||
160 | |||
161 | static void sel_netif_assign_sids(u32 if_sid_in, u32 msg_sid_in, u32 *if_sid_out, u32 *msg_sid_out) | ||
162 | { | ||
163 | if (if_sid_out) | ||
164 | *if_sid_out = if_sid_in; | ||
165 | if (msg_sid_out) | ||
166 | *msg_sid_out = msg_sid_in; | ||
167 | } | ||
168 | |||
169 | static int sel_netif_sids_slow(struct net_device *dev, u32 *if_sid, u32 *msg_sid) | ||
170 | { | ||
171 | int ret = 0; | ||
172 | u32 tmp_if_sid, tmp_msg_sid; | ||
173 | |||
174 | ret = security_netif_sid(dev->name, &tmp_if_sid, &tmp_msg_sid); | ||
175 | if (!ret) | ||
176 | sel_netif_assign_sids(tmp_if_sid, tmp_msg_sid, if_sid, msg_sid); | ||
177 | return ret; | ||
178 | } | ||
179 | |||
180 | int sel_netif_sids(struct net_device *dev, u32 *if_sid, u32 *msg_sid) | ||
181 | { | ||
182 | int ret = 0; | ||
183 | struct sel_netif *netif; | ||
184 | |||
185 | rcu_read_lock(); | ||
186 | netif = sel_netif_lookup(dev); | ||
187 | if (IS_ERR(netif)) { | ||
188 | rcu_read_unlock(); | ||
189 | ret = sel_netif_sids_slow(dev, if_sid, msg_sid); | ||
190 | goto out; | ||
191 | } | ||
192 | sel_netif_assign_sids(netif->nsec.if_sid, netif->nsec.msg_sid, if_sid, msg_sid); | ||
193 | rcu_read_unlock(); | ||
194 | out: | ||
195 | return ret; | ||
196 | } | ||
197 | |||
198 | static void sel_netif_kill(struct net_device *dev) | ||
199 | { | ||
200 | struct sel_netif *netif; | ||
201 | |||
202 | spin_lock_bh(&sel_netif_lock); | ||
203 | netif = sel_netif_find(dev); | ||
204 | if (netif) | ||
205 | sel_netif_destroy(netif); | ||
206 | spin_unlock_bh(&sel_netif_lock); | ||
207 | } | ||
208 | |||
209 | static void sel_netif_flush(void) | ||
210 | { | ||
211 | int idx; | ||
212 | |||
213 | spin_lock_bh(&sel_netif_lock); | ||
214 | for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) { | ||
215 | struct sel_netif *netif; | ||
216 | |||
217 | list_for_each_entry(netif, &sel_netif_hash[idx], list) | ||
218 | sel_netif_destroy(netif); | ||
219 | } | ||
220 | spin_unlock_bh(&sel_netif_lock); | ||
221 | } | ||
222 | |||
223 | static int sel_netif_avc_callback(u32 event, u32 ssid, u32 tsid, | ||
224 | u16 class, u32 perms, u32 *retained) | ||
225 | { | ||
226 | if (event == AVC_CALLBACK_RESET) { | ||
227 | sel_netif_flush(); | ||
228 | synchronize_net(); | ||
229 | } | ||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static int sel_netif_netdev_notifier_handler(struct notifier_block *this, | ||
234 | unsigned long event, void *ptr) | ||
235 | { | ||
236 | struct net_device *dev = ptr; | ||
237 | |||
238 | if (event == NETDEV_DOWN) | ||
239 | sel_netif_kill(dev); | ||
240 | |||
241 | return NOTIFY_DONE; | ||
242 | } | ||
243 | |||
244 | static struct notifier_block sel_netif_netdev_notifier = { | ||
245 | .notifier_call = sel_netif_netdev_notifier_handler, | ||
246 | }; | ||
247 | |||
248 | static __init int sel_netif_init(void) | ||
249 | { | ||
250 | int i, err = 0; | ||
251 | |||
252 | if (!selinux_enabled) | ||
253 | goto out; | ||
254 | |||
255 | for (i = 0; i < SEL_NETIF_HASH_SIZE; i++) | ||
256 | INIT_LIST_HEAD(&sel_netif_hash[i]); | ||
257 | |||
258 | register_netdevice_notifier(&sel_netif_netdev_notifier); | ||
259 | |||
260 | err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET, | ||
261 | SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); | ||
262 | if (err) | ||
263 | panic("avc_add_callback() failed, error %d\n", err); | ||
264 | |||
265 | out: | ||
266 | return err; | ||
267 | } | ||
268 | |||
269 | __initcall(sel_netif_init); | ||
270 | |||
diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c new file mode 100644 index 000000000000..18d08acafa78 --- /dev/null +++ b/security/selinux/netlink.c | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * Netlink event notifications for SELinux. | ||
3 | * | ||
4 | * Author: James Morris <jmorris@redhat.com> | ||
5 | * | ||
6 | * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2, | ||
10 | * as published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/stddef.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/list.h> | ||
17 | #include <linux/skbuff.h> | ||
18 | #include <linux/netlink.h> | ||
19 | #include <linux/selinux_netlink.h> | ||
20 | |||
21 | static struct sock *selnl; | ||
22 | |||
23 | static int selnl_msglen(int msgtype) | ||
24 | { | ||
25 | int ret = 0; | ||
26 | |||
27 | switch (msgtype) { | ||
28 | case SELNL_MSG_SETENFORCE: | ||
29 | ret = sizeof(struct selnl_msg_setenforce); | ||
30 | break; | ||
31 | |||
32 | case SELNL_MSG_POLICYLOAD: | ||
33 | ret = sizeof(struct selnl_msg_policyload); | ||
34 | break; | ||
35 | |||
36 | default: | ||
37 | BUG(); | ||
38 | } | ||
39 | return ret; | ||
40 | } | ||
41 | |||
42 | static void selnl_add_payload(struct nlmsghdr *nlh, int len, int msgtype, void *data) | ||
43 | { | ||
44 | switch (msgtype) { | ||
45 | case SELNL_MSG_SETENFORCE: { | ||
46 | struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh); | ||
47 | |||
48 | memset(msg, 0, len); | ||
49 | msg->val = *((int *)data); | ||
50 | break; | ||
51 | } | ||
52 | |||
53 | case SELNL_MSG_POLICYLOAD: { | ||
54 | struct selnl_msg_policyload *msg = NLMSG_DATA(nlh); | ||
55 | |||
56 | memset(msg, 0, len); | ||
57 | msg->seqno = *((u32 *)data); | ||
58 | break; | ||
59 | } | ||
60 | |||
61 | default: | ||
62 | BUG(); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | static void selnl_notify(int msgtype, void *data) | ||
67 | { | ||
68 | int len; | ||
69 | unsigned char *tmp; | ||
70 | struct sk_buff *skb; | ||
71 | struct nlmsghdr *nlh; | ||
72 | |||
73 | len = selnl_msglen(msgtype); | ||
74 | |||
75 | skb = alloc_skb(NLMSG_SPACE(len), GFP_USER); | ||
76 | if (!skb) | ||
77 | goto oom; | ||
78 | |||
79 | tmp = skb->tail; | ||
80 | nlh = NLMSG_PUT(skb, 0, 0, msgtype, len); | ||
81 | selnl_add_payload(nlh, len, msgtype, data); | ||
82 | nlh->nlmsg_len = skb->tail - tmp; | ||
83 | netlink_broadcast(selnl, skb, 0, SELNL_GRP_AVC, GFP_USER); | ||
84 | out: | ||
85 | return; | ||
86 | |||
87 | nlmsg_failure: | ||
88 | kfree_skb(skb); | ||
89 | oom: | ||
90 | printk(KERN_ERR "SELinux: OOM in %s\n", __FUNCTION__); | ||
91 | goto out; | ||
92 | } | ||
93 | |||
94 | void selnl_notify_setenforce(int val) | ||
95 | { | ||
96 | selnl_notify(SELNL_MSG_SETENFORCE, &val); | ||
97 | } | ||
98 | |||
99 | void selnl_notify_policyload(u32 seqno) | ||
100 | { | ||
101 | selnl_notify(SELNL_MSG_POLICYLOAD, &seqno); | ||
102 | } | ||
103 | |||
104 | static int __init selnl_init(void) | ||
105 | { | ||
106 | selnl = netlink_kernel_create(NETLINK_SELINUX, NULL); | ||
107 | if (selnl == NULL) | ||
108 | panic("SELinux: Cannot create netlink socket."); | ||
109 | netlink_set_nonroot(NETLINK_SELINUX, NL_NONROOT_RECV); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | __initcall(selnl_init); | ||
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c new file mode 100644 index 000000000000..fa7fa030e6eb --- /dev/null +++ b/security/selinux/nlmsgtab.c | |||
@@ -0,0 +1,156 @@ | |||
1 | /* | ||
2 | * Netlink message type permission tables, for user generated messages. | ||
3 | * | ||
4 | * Author: James Morris <jmorris@redhat.com> | ||
5 | * | ||
6 | * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2, | ||
10 | * as published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/skbuff.h> | ||
15 | #include <linux/netlink.h> | ||
16 | #include <linux/rtnetlink.h> | ||
17 | #include <linux/if.h> | ||
18 | #include <linux/netfilter_ipv4/ip_queue.h> | ||
19 | #include <linux/tcp_diag.h> | ||
20 | #include <linux/xfrm.h> | ||
21 | #include <linux/audit.h> | ||
22 | |||
23 | #include "flask.h" | ||
24 | #include "av_permissions.h" | ||
25 | |||
26 | struct nlmsg_perm | ||
27 | { | ||
28 | u16 nlmsg_type; | ||
29 | u32 perm; | ||
30 | }; | ||
31 | |||
32 | static struct nlmsg_perm nlmsg_route_perms[] = | ||
33 | { | ||
34 | { RTM_NEWLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
35 | { RTM_DELLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
36 | { RTM_GETLINK, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | ||
37 | { RTM_SETLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
38 | { RTM_NEWADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
39 | { RTM_DELADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
40 | { RTM_GETADDR, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | ||
41 | { RTM_NEWROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
42 | { RTM_DELROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
43 | { RTM_GETROUTE, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | ||
44 | { RTM_NEWNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
45 | { RTM_DELNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
46 | { RTM_GETNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | ||
47 | { RTM_NEWRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
48 | { RTM_DELRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
49 | { RTM_GETRULE, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | ||
50 | { RTM_NEWQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
51 | { RTM_DELQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
52 | { RTM_GETQDISC, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | ||
53 | { RTM_NEWTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
54 | { RTM_DELTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
55 | { RTM_GETTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | ||
56 | { RTM_NEWTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
57 | { RTM_DELTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
58 | { RTM_GETTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | ||
59 | { RTM_NEWACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
60 | { RTM_DELACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
61 | { RTM_GETACTION, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | ||
62 | { RTM_NEWPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, | ||
63 | { RTM_GETPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | ||
64 | { RTM_GETMULTICAST, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | ||
65 | { RTM_GETANYCAST, NETLINK_ROUTE_SOCKET__NLMSG_READ }, | ||
66 | }; | ||
67 | |||
68 | static struct nlmsg_perm nlmsg_firewall_perms[] = | ||
69 | { | ||
70 | { IPQM_MODE, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE }, | ||
71 | { IPQM_VERDICT, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE }, | ||
72 | }; | ||
73 | |||
74 | static struct nlmsg_perm nlmsg_tcpdiag_perms[] = | ||
75 | { | ||
76 | { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, | ||
77 | }; | ||
78 | |||
79 | static struct nlmsg_perm nlmsg_xfrm_perms[] = | ||
80 | { | ||
81 | { XFRM_MSG_NEWSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, | ||
82 | { XFRM_MSG_DELSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, | ||
83 | { XFRM_MSG_GETSA, NETLINK_XFRM_SOCKET__NLMSG_READ }, | ||
84 | { XFRM_MSG_NEWPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, | ||
85 | { XFRM_MSG_DELPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, | ||
86 | { XFRM_MSG_GETPOLICY, NETLINK_XFRM_SOCKET__NLMSG_READ }, | ||
87 | { XFRM_MSG_ALLOCSPI, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, | ||
88 | { XFRM_MSG_UPDPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, | ||
89 | { XFRM_MSG_UPDSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, | ||
90 | }; | ||
91 | |||
92 | static struct nlmsg_perm nlmsg_audit_perms[] = | ||
93 | { | ||
94 | { AUDIT_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ }, | ||
95 | { AUDIT_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, | ||
96 | { AUDIT_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READ }, | ||
97 | { AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, | ||
98 | { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, | ||
99 | { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, | ||
100 | { AUDIT_LOGIN, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, | ||
101 | }; | ||
102 | |||
103 | |||
104 | static int nlmsg_perm(u16 nlmsg_type, u32 *perm, struct nlmsg_perm *tab, size_t tabsize) | ||
105 | { | ||
106 | int i, err = -EINVAL; | ||
107 | |||
108 | for (i = 0; i < tabsize/sizeof(struct nlmsg_perm); i++) | ||
109 | if (nlmsg_type == tab[i].nlmsg_type) { | ||
110 | *perm = tab[i].perm; | ||
111 | err = 0; | ||
112 | break; | ||
113 | } | ||
114 | |||
115 | return err; | ||
116 | } | ||
117 | |||
118 | int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm) | ||
119 | { | ||
120 | int err = 0; | ||
121 | |||
122 | switch (sclass) { | ||
123 | case SECCLASS_NETLINK_ROUTE_SOCKET: | ||
124 | err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms, | ||
125 | sizeof(nlmsg_route_perms)); | ||
126 | break; | ||
127 | |||
128 | case SECCLASS_NETLINK_FIREWALL_SOCKET: | ||
129 | case NETLINK_IP6_FW: | ||
130 | err = nlmsg_perm(nlmsg_type, perm, nlmsg_firewall_perms, | ||
131 | sizeof(nlmsg_firewall_perms)); | ||
132 | break; | ||
133 | |||
134 | case SECCLASS_NETLINK_TCPDIAG_SOCKET: | ||
135 | err = nlmsg_perm(nlmsg_type, perm, nlmsg_tcpdiag_perms, | ||
136 | sizeof(nlmsg_tcpdiag_perms)); | ||
137 | break; | ||
138 | |||
139 | case SECCLASS_NETLINK_XFRM_SOCKET: | ||
140 | err = nlmsg_perm(nlmsg_type, perm, nlmsg_xfrm_perms, | ||
141 | sizeof(nlmsg_xfrm_perms)); | ||
142 | break; | ||
143 | |||
144 | case SECCLASS_NETLINK_AUDIT_SOCKET: | ||
145 | err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms, | ||
146 | sizeof(nlmsg_audit_perms)); | ||
147 | break; | ||
148 | |||
149 | /* No messaging from userspace, or class unknown/unhandled */ | ||
150 | default: | ||
151 | err = -ENOENT; | ||
152 | break; | ||
153 | } | ||
154 | |||
155 | return err; | ||
156 | } | ||
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c new file mode 100644 index 000000000000..07221568b505 --- /dev/null +++ b/security/selinux/selinuxfs.c | |||
@@ -0,0 +1,1340 @@ | |||
1 | /* Updated: Karl MacMillan <kmacmillan@tresys.com> | ||
2 | * | ||
3 | * Added conditional policy language extensions | ||
4 | * | ||
5 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | ||
6 | * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com> | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, version 2. | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/pagemap.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/vmalloc.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/security.h> | ||
21 | #include <linux/major.h> | ||
22 | #include <linux/seq_file.h> | ||
23 | #include <linux/percpu.h> | ||
24 | #include <asm/uaccess.h> | ||
25 | #include <asm/semaphore.h> | ||
26 | |||
27 | /* selinuxfs pseudo filesystem for exporting the security policy API. | ||
28 | Based on the proc code and the fs/nfsd/nfsctl.c code. */ | ||
29 | |||
30 | #include "flask.h" | ||
31 | #include "avc.h" | ||
32 | #include "avc_ss.h" | ||
33 | #include "security.h" | ||
34 | #include "objsec.h" | ||
35 | #include "conditional.h" | ||
36 | |||
37 | unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; | ||
38 | |||
39 | static int __init checkreqprot_setup(char *str) | ||
40 | { | ||
41 | selinux_checkreqprot = simple_strtoul(str,NULL,0) ? 1 : 0; | ||
42 | return 1; | ||
43 | } | ||
44 | __setup("checkreqprot=", checkreqprot_setup); | ||
45 | |||
46 | |||
47 | static DECLARE_MUTEX(sel_sem); | ||
48 | |||
49 | /* global data for booleans */ | ||
50 | static struct dentry *bool_dir = NULL; | ||
51 | static int bool_num = 0; | ||
52 | static int *bool_pending_values = NULL; | ||
53 | |||
54 | extern void selnl_notify_setenforce(int val); | ||
55 | |||
56 | /* Check whether a task is allowed to use a security operation. */ | ||
57 | static int task_has_security(struct task_struct *tsk, | ||
58 | u32 perms) | ||
59 | { | ||
60 | struct task_security_struct *tsec; | ||
61 | |||
62 | tsec = tsk->security; | ||
63 | if (!tsec) | ||
64 | return -EACCES; | ||
65 | |||
66 | return avc_has_perm(tsec->sid, SECINITSID_SECURITY, | ||
67 | SECCLASS_SECURITY, perms, NULL); | ||
68 | } | ||
69 | |||
70 | enum sel_inos { | ||
71 | SEL_ROOT_INO = 2, | ||
72 | SEL_LOAD, /* load policy */ | ||
73 | SEL_ENFORCE, /* get or set enforcing status */ | ||
74 | SEL_CONTEXT, /* validate context */ | ||
75 | SEL_ACCESS, /* compute access decision */ | ||
76 | SEL_CREATE, /* compute create labeling decision */ | ||
77 | SEL_RELABEL, /* compute relabeling decision */ | ||
78 | SEL_USER, /* compute reachable user contexts */ | ||
79 | SEL_POLICYVERS, /* return policy version for this kernel */ | ||
80 | SEL_COMMIT_BOOLS, /* commit new boolean values */ | ||
81 | SEL_MLS, /* return if MLS policy is enabled */ | ||
82 | SEL_DISABLE, /* disable SELinux until next reboot */ | ||
83 | SEL_AVC, /* AVC management directory */ | ||
84 | SEL_MEMBER, /* compute polyinstantiation membership decision */ | ||
85 | SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */ | ||
86 | }; | ||
87 | |||
88 | #define TMPBUFLEN 12 | ||
89 | static ssize_t sel_read_enforce(struct file *filp, char __user *buf, | ||
90 | size_t count, loff_t *ppos) | ||
91 | { | ||
92 | char tmpbuf[TMPBUFLEN]; | ||
93 | ssize_t length; | ||
94 | |||
95 | length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_enforcing); | ||
96 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); | ||
97 | } | ||
98 | |||
99 | #ifdef CONFIG_SECURITY_SELINUX_DEVELOP | ||
100 | static ssize_t sel_write_enforce(struct file * file, const char __user * buf, | ||
101 | size_t count, loff_t *ppos) | ||
102 | |||
103 | { | ||
104 | char *page; | ||
105 | ssize_t length; | ||
106 | int new_value; | ||
107 | |||
108 | if (count < 0 || count >= PAGE_SIZE) | ||
109 | return -ENOMEM; | ||
110 | if (*ppos != 0) { | ||
111 | /* No partial writes. */ | ||
112 | return -EINVAL; | ||
113 | } | ||
114 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
115 | if (!page) | ||
116 | return -ENOMEM; | ||
117 | length = -EFAULT; | ||
118 | if (copy_from_user(page, buf, count)) | ||
119 | goto out; | ||
120 | |||
121 | length = -EINVAL; | ||
122 | if (sscanf(page, "%d", &new_value) != 1) | ||
123 | goto out; | ||
124 | |||
125 | if (new_value != selinux_enforcing) { | ||
126 | length = task_has_security(current, SECURITY__SETENFORCE); | ||
127 | if (length) | ||
128 | goto out; | ||
129 | selinux_enforcing = new_value; | ||
130 | if (selinux_enforcing) | ||
131 | avc_ss_reset(0); | ||
132 | selnl_notify_setenforce(selinux_enforcing); | ||
133 | } | ||
134 | length = count; | ||
135 | out: | ||
136 | free_page((unsigned long) page); | ||
137 | return length; | ||
138 | } | ||
139 | #else | ||
140 | #define sel_write_enforce NULL | ||
141 | #endif | ||
142 | |||
143 | static struct file_operations sel_enforce_ops = { | ||
144 | .read = sel_read_enforce, | ||
145 | .write = sel_write_enforce, | ||
146 | }; | ||
147 | |||
148 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE | ||
149 | static ssize_t sel_write_disable(struct file * file, const char __user * buf, | ||
150 | size_t count, loff_t *ppos) | ||
151 | |||
152 | { | ||
153 | char *page; | ||
154 | ssize_t length; | ||
155 | int new_value; | ||
156 | extern int selinux_disable(void); | ||
157 | |||
158 | if (count < 0 || count >= PAGE_SIZE) | ||
159 | return -ENOMEM; | ||
160 | if (*ppos != 0) { | ||
161 | /* No partial writes. */ | ||
162 | return -EINVAL; | ||
163 | } | ||
164 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
165 | if (!page) | ||
166 | return -ENOMEM; | ||
167 | length = -EFAULT; | ||
168 | if (copy_from_user(page, buf, count)) | ||
169 | goto out; | ||
170 | |||
171 | length = -EINVAL; | ||
172 | if (sscanf(page, "%d", &new_value) != 1) | ||
173 | goto out; | ||
174 | |||
175 | if (new_value) { | ||
176 | length = selinux_disable(); | ||
177 | if (length < 0) | ||
178 | goto out; | ||
179 | } | ||
180 | |||
181 | length = count; | ||
182 | out: | ||
183 | free_page((unsigned long) page); | ||
184 | return length; | ||
185 | } | ||
186 | #else | ||
187 | #define sel_write_disable NULL | ||
188 | #endif | ||
189 | |||
190 | static struct file_operations sel_disable_ops = { | ||
191 | .write = sel_write_disable, | ||
192 | }; | ||
193 | |||
194 | static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, | ||
195 | size_t count, loff_t *ppos) | ||
196 | { | ||
197 | char tmpbuf[TMPBUFLEN]; | ||
198 | ssize_t length; | ||
199 | |||
200 | length = scnprintf(tmpbuf, TMPBUFLEN, "%u", POLICYDB_VERSION_MAX); | ||
201 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); | ||
202 | } | ||
203 | |||
204 | static struct file_operations sel_policyvers_ops = { | ||
205 | .read = sel_read_policyvers, | ||
206 | }; | ||
207 | |||
208 | /* declaration for sel_write_load */ | ||
209 | static int sel_make_bools(void); | ||
210 | |||
211 | static ssize_t sel_read_mls(struct file *filp, char __user *buf, | ||
212 | size_t count, loff_t *ppos) | ||
213 | { | ||
214 | char tmpbuf[TMPBUFLEN]; | ||
215 | ssize_t length; | ||
216 | |||
217 | length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_mls_enabled); | ||
218 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); | ||
219 | } | ||
220 | |||
221 | static struct file_operations sel_mls_ops = { | ||
222 | .read = sel_read_mls, | ||
223 | }; | ||
224 | |||
225 | static ssize_t sel_write_load(struct file * file, const char __user * buf, | ||
226 | size_t count, loff_t *ppos) | ||
227 | |||
228 | { | ||
229 | int ret; | ||
230 | ssize_t length; | ||
231 | void *data = NULL; | ||
232 | |||
233 | down(&sel_sem); | ||
234 | |||
235 | length = task_has_security(current, SECURITY__LOAD_POLICY); | ||
236 | if (length) | ||
237 | goto out; | ||
238 | |||
239 | if (*ppos != 0) { | ||
240 | /* No partial writes. */ | ||
241 | length = -EINVAL; | ||
242 | goto out; | ||
243 | } | ||
244 | |||
245 | if ((count < 0) || (count > 64 * 1024 * 1024) | ||
246 | || (data = vmalloc(count)) == NULL) { | ||
247 | length = -ENOMEM; | ||
248 | goto out; | ||
249 | } | ||
250 | |||
251 | length = -EFAULT; | ||
252 | if (copy_from_user(data, buf, count) != 0) | ||
253 | goto out; | ||
254 | |||
255 | length = security_load_policy(data, count); | ||
256 | if (length) | ||
257 | goto out; | ||
258 | |||
259 | ret = sel_make_bools(); | ||
260 | if (ret) | ||
261 | length = ret; | ||
262 | else | ||
263 | length = count; | ||
264 | out: | ||
265 | up(&sel_sem); | ||
266 | vfree(data); | ||
267 | return length; | ||
268 | } | ||
269 | |||
270 | static struct file_operations sel_load_ops = { | ||
271 | .write = sel_write_load, | ||
272 | }; | ||
273 | |||
274 | |||
275 | static ssize_t sel_write_context(struct file * file, const char __user * buf, | ||
276 | size_t count, loff_t *ppos) | ||
277 | |||
278 | { | ||
279 | char *page; | ||
280 | u32 sid; | ||
281 | ssize_t length; | ||
282 | |||
283 | length = task_has_security(current, SECURITY__CHECK_CONTEXT); | ||
284 | if (length) | ||
285 | return length; | ||
286 | |||
287 | if (count < 0 || count >= PAGE_SIZE) | ||
288 | return -ENOMEM; | ||
289 | if (*ppos != 0) { | ||
290 | /* No partial writes. */ | ||
291 | return -EINVAL; | ||
292 | } | ||
293 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
294 | if (!page) | ||
295 | return -ENOMEM; | ||
296 | length = -EFAULT; | ||
297 | if (copy_from_user(page, buf, count)) | ||
298 | goto out; | ||
299 | |||
300 | length = security_context_to_sid(page, count, &sid); | ||
301 | if (length < 0) | ||
302 | goto out; | ||
303 | |||
304 | length = count; | ||
305 | out: | ||
306 | free_page((unsigned long) page); | ||
307 | return length; | ||
308 | } | ||
309 | |||
310 | static struct file_operations sel_context_ops = { | ||
311 | .write = sel_write_context, | ||
312 | }; | ||
313 | |||
314 | static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf, | ||
315 | size_t count, loff_t *ppos) | ||
316 | { | ||
317 | char tmpbuf[TMPBUFLEN]; | ||
318 | ssize_t length; | ||
319 | |||
320 | length = scnprintf(tmpbuf, TMPBUFLEN, "%u", selinux_checkreqprot); | ||
321 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); | ||
322 | } | ||
323 | |||
324 | static ssize_t sel_write_checkreqprot(struct file * file, const char __user * buf, | ||
325 | size_t count, loff_t *ppos) | ||
326 | { | ||
327 | char *page; | ||
328 | ssize_t length; | ||
329 | unsigned int new_value; | ||
330 | |||
331 | length = task_has_security(current, SECURITY__SETCHECKREQPROT); | ||
332 | if (length) | ||
333 | return length; | ||
334 | |||
335 | if (count < 0 || count >= PAGE_SIZE) | ||
336 | return -ENOMEM; | ||
337 | if (*ppos != 0) { | ||
338 | /* No partial writes. */ | ||
339 | return -EINVAL; | ||
340 | } | ||
341 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
342 | if (!page) | ||
343 | return -ENOMEM; | ||
344 | length = -EFAULT; | ||
345 | if (copy_from_user(page, buf, count)) | ||
346 | goto out; | ||
347 | |||
348 | length = -EINVAL; | ||
349 | if (sscanf(page, "%u", &new_value) != 1) | ||
350 | goto out; | ||
351 | |||
352 | selinux_checkreqprot = new_value ? 1 : 0; | ||
353 | length = count; | ||
354 | out: | ||
355 | free_page((unsigned long) page); | ||
356 | return length; | ||
357 | } | ||
358 | static struct file_operations sel_checkreqprot_ops = { | ||
359 | .read = sel_read_checkreqprot, | ||
360 | .write = sel_write_checkreqprot, | ||
361 | }; | ||
362 | |||
363 | /* | ||
364 | * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c | ||
365 | */ | ||
366 | static ssize_t sel_write_access(struct file * file, char *buf, size_t size); | ||
367 | static ssize_t sel_write_create(struct file * file, char *buf, size_t size); | ||
368 | static ssize_t sel_write_relabel(struct file * file, char *buf, size_t size); | ||
369 | static ssize_t sel_write_user(struct file * file, char *buf, size_t size); | ||
370 | static ssize_t sel_write_member(struct file * file, char *buf, size_t size); | ||
371 | |||
372 | static ssize_t (*write_op[])(struct file *, char *, size_t) = { | ||
373 | [SEL_ACCESS] = sel_write_access, | ||
374 | [SEL_CREATE] = sel_write_create, | ||
375 | [SEL_RELABEL] = sel_write_relabel, | ||
376 | [SEL_USER] = sel_write_user, | ||
377 | [SEL_MEMBER] = sel_write_member, | ||
378 | }; | ||
379 | |||
380 | static ssize_t selinux_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) | ||
381 | { | ||
382 | ino_t ino = file->f_dentry->d_inode->i_ino; | ||
383 | char *data; | ||
384 | ssize_t rv; | ||
385 | |||
386 | if (ino >= sizeof(write_op)/sizeof(write_op[0]) || !write_op[ino]) | ||
387 | return -EINVAL; | ||
388 | |||
389 | data = simple_transaction_get(file, buf, size); | ||
390 | if (IS_ERR(data)) | ||
391 | return PTR_ERR(data); | ||
392 | |||
393 | rv = write_op[ino](file, data, size); | ||
394 | if (rv>0) { | ||
395 | simple_transaction_set(file, rv); | ||
396 | rv = size; | ||
397 | } | ||
398 | return rv; | ||
399 | } | ||
400 | |||
401 | static struct file_operations transaction_ops = { | ||
402 | .write = selinux_transaction_write, | ||
403 | .read = simple_transaction_read, | ||
404 | .release = simple_transaction_release, | ||
405 | }; | ||
406 | |||
407 | /* | ||
408 | * payload - write methods | ||
409 | * If the method has a response, the response should be put in buf, | ||
410 | * and the length returned. Otherwise return 0 or and -error. | ||
411 | */ | ||
412 | |||
413 | static ssize_t sel_write_access(struct file * file, char *buf, size_t size) | ||
414 | { | ||
415 | char *scon, *tcon; | ||
416 | u32 ssid, tsid; | ||
417 | u16 tclass; | ||
418 | u32 req; | ||
419 | struct av_decision avd; | ||
420 | ssize_t length; | ||
421 | |||
422 | length = task_has_security(current, SECURITY__COMPUTE_AV); | ||
423 | if (length) | ||
424 | return length; | ||
425 | |||
426 | length = -ENOMEM; | ||
427 | scon = kmalloc(size+1, GFP_KERNEL); | ||
428 | if (!scon) | ||
429 | return length; | ||
430 | memset(scon, 0, size+1); | ||
431 | |||
432 | tcon = kmalloc(size+1, GFP_KERNEL); | ||
433 | if (!tcon) | ||
434 | goto out; | ||
435 | memset(tcon, 0, size+1); | ||
436 | |||
437 | length = -EINVAL; | ||
438 | if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4) | ||
439 | goto out2; | ||
440 | |||
441 | length = security_context_to_sid(scon, strlen(scon)+1, &ssid); | ||
442 | if (length < 0) | ||
443 | goto out2; | ||
444 | length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid); | ||
445 | if (length < 0) | ||
446 | goto out2; | ||
447 | |||
448 | length = security_compute_av(ssid, tsid, tclass, req, &avd); | ||
449 | if (length < 0) | ||
450 | goto out2; | ||
451 | |||
452 | length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, | ||
453 | "%x %x %x %x %u", | ||
454 | avd.allowed, avd.decided, | ||
455 | avd.auditallow, avd.auditdeny, | ||
456 | avd.seqno); | ||
457 | out2: | ||
458 | kfree(tcon); | ||
459 | out: | ||
460 | kfree(scon); | ||
461 | return length; | ||
462 | } | ||
463 | |||
464 | static ssize_t sel_write_create(struct file * file, char *buf, size_t size) | ||
465 | { | ||
466 | char *scon, *tcon; | ||
467 | u32 ssid, tsid, newsid; | ||
468 | u16 tclass; | ||
469 | ssize_t length; | ||
470 | char *newcon; | ||
471 | u32 len; | ||
472 | |||
473 | length = task_has_security(current, SECURITY__COMPUTE_CREATE); | ||
474 | if (length) | ||
475 | return length; | ||
476 | |||
477 | length = -ENOMEM; | ||
478 | scon = kmalloc(size+1, GFP_KERNEL); | ||
479 | if (!scon) | ||
480 | return length; | ||
481 | memset(scon, 0, size+1); | ||
482 | |||
483 | tcon = kmalloc(size+1, GFP_KERNEL); | ||
484 | if (!tcon) | ||
485 | goto out; | ||
486 | memset(tcon, 0, size+1); | ||
487 | |||
488 | length = -EINVAL; | ||
489 | if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) | ||
490 | goto out2; | ||
491 | |||
492 | length = security_context_to_sid(scon, strlen(scon)+1, &ssid); | ||
493 | if (length < 0) | ||
494 | goto out2; | ||
495 | length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid); | ||
496 | if (length < 0) | ||
497 | goto out2; | ||
498 | |||
499 | length = security_transition_sid(ssid, tsid, tclass, &newsid); | ||
500 | if (length < 0) | ||
501 | goto out2; | ||
502 | |||
503 | length = security_sid_to_context(newsid, &newcon, &len); | ||
504 | if (length < 0) | ||
505 | goto out2; | ||
506 | |||
507 | if (len > SIMPLE_TRANSACTION_LIMIT) { | ||
508 | printk(KERN_ERR "%s: context size (%u) exceeds payload " | ||
509 | "max\n", __FUNCTION__, len); | ||
510 | length = -ERANGE; | ||
511 | goto out3; | ||
512 | } | ||
513 | |||
514 | memcpy(buf, newcon, len); | ||
515 | length = len; | ||
516 | out3: | ||
517 | kfree(newcon); | ||
518 | out2: | ||
519 | kfree(tcon); | ||
520 | out: | ||
521 | kfree(scon); | ||
522 | return length; | ||
523 | } | ||
524 | |||
525 | static ssize_t sel_write_relabel(struct file * file, char *buf, size_t size) | ||
526 | { | ||
527 | char *scon, *tcon; | ||
528 | u32 ssid, tsid, newsid; | ||
529 | u16 tclass; | ||
530 | ssize_t length; | ||
531 | char *newcon; | ||
532 | u32 len; | ||
533 | |||
534 | length = task_has_security(current, SECURITY__COMPUTE_RELABEL); | ||
535 | if (length) | ||
536 | return length; | ||
537 | |||
538 | length = -ENOMEM; | ||
539 | scon = kmalloc(size+1, GFP_KERNEL); | ||
540 | if (!scon) | ||
541 | return length; | ||
542 | memset(scon, 0, size+1); | ||
543 | |||
544 | tcon = kmalloc(size+1, GFP_KERNEL); | ||
545 | if (!tcon) | ||
546 | goto out; | ||
547 | memset(tcon, 0, size+1); | ||
548 | |||
549 | length = -EINVAL; | ||
550 | if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) | ||
551 | goto out2; | ||
552 | |||
553 | length = security_context_to_sid(scon, strlen(scon)+1, &ssid); | ||
554 | if (length < 0) | ||
555 | goto out2; | ||
556 | length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid); | ||
557 | if (length < 0) | ||
558 | goto out2; | ||
559 | |||
560 | length = security_change_sid(ssid, tsid, tclass, &newsid); | ||
561 | if (length < 0) | ||
562 | goto out2; | ||
563 | |||
564 | length = security_sid_to_context(newsid, &newcon, &len); | ||
565 | if (length < 0) | ||
566 | goto out2; | ||
567 | |||
568 | if (len > SIMPLE_TRANSACTION_LIMIT) { | ||
569 | length = -ERANGE; | ||
570 | goto out3; | ||
571 | } | ||
572 | |||
573 | memcpy(buf, newcon, len); | ||
574 | length = len; | ||
575 | out3: | ||
576 | kfree(newcon); | ||
577 | out2: | ||
578 | kfree(tcon); | ||
579 | out: | ||
580 | kfree(scon); | ||
581 | return length; | ||
582 | } | ||
583 | |||
584 | static ssize_t sel_write_user(struct file * file, char *buf, size_t size) | ||
585 | { | ||
586 | char *con, *user, *ptr; | ||
587 | u32 sid, *sids; | ||
588 | ssize_t length; | ||
589 | char *newcon; | ||
590 | int i, rc; | ||
591 | u32 len, nsids; | ||
592 | |||
593 | length = task_has_security(current, SECURITY__COMPUTE_USER); | ||
594 | if (length) | ||
595 | return length; | ||
596 | |||
597 | length = -ENOMEM; | ||
598 | con = kmalloc(size+1, GFP_KERNEL); | ||
599 | if (!con) | ||
600 | return length; | ||
601 | memset(con, 0, size+1); | ||
602 | |||
603 | user = kmalloc(size+1, GFP_KERNEL); | ||
604 | if (!user) | ||
605 | goto out; | ||
606 | memset(user, 0, size+1); | ||
607 | |||
608 | length = -EINVAL; | ||
609 | if (sscanf(buf, "%s %s", con, user) != 2) | ||
610 | goto out2; | ||
611 | |||
612 | length = security_context_to_sid(con, strlen(con)+1, &sid); | ||
613 | if (length < 0) | ||
614 | goto out2; | ||
615 | |||
616 | length = security_get_user_sids(sid, user, &sids, &nsids); | ||
617 | if (length < 0) | ||
618 | goto out2; | ||
619 | |||
620 | length = sprintf(buf, "%u", nsids) + 1; | ||
621 | ptr = buf + length; | ||
622 | for (i = 0; i < nsids; i++) { | ||
623 | rc = security_sid_to_context(sids[i], &newcon, &len); | ||
624 | if (rc) { | ||
625 | length = rc; | ||
626 | goto out3; | ||
627 | } | ||
628 | if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) { | ||
629 | kfree(newcon); | ||
630 | length = -ERANGE; | ||
631 | goto out3; | ||
632 | } | ||
633 | memcpy(ptr, newcon, len); | ||
634 | kfree(newcon); | ||
635 | ptr += len; | ||
636 | length += len; | ||
637 | } | ||
638 | out3: | ||
639 | kfree(sids); | ||
640 | out2: | ||
641 | kfree(user); | ||
642 | out: | ||
643 | kfree(con); | ||
644 | return length; | ||
645 | } | ||
646 | |||
647 | static ssize_t sel_write_member(struct file * file, char *buf, size_t size) | ||
648 | { | ||
649 | char *scon, *tcon; | ||
650 | u32 ssid, tsid, newsid; | ||
651 | u16 tclass; | ||
652 | ssize_t length; | ||
653 | char *newcon; | ||
654 | u32 len; | ||
655 | |||
656 | length = task_has_security(current, SECURITY__COMPUTE_MEMBER); | ||
657 | if (length) | ||
658 | return length; | ||
659 | |||
660 | length = -ENOMEM; | ||
661 | scon = kmalloc(size+1, GFP_KERNEL); | ||
662 | if (!scon) | ||
663 | return length; | ||
664 | memset(scon, 0, size+1); | ||
665 | |||
666 | tcon = kmalloc(size+1, GFP_KERNEL); | ||
667 | if (!tcon) | ||
668 | goto out; | ||
669 | memset(tcon, 0, size+1); | ||
670 | |||
671 | length = -EINVAL; | ||
672 | if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) | ||
673 | goto out2; | ||
674 | |||
675 | length = security_context_to_sid(scon, strlen(scon)+1, &ssid); | ||
676 | if (length < 0) | ||
677 | goto out2; | ||
678 | length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid); | ||
679 | if (length < 0) | ||
680 | goto out2; | ||
681 | |||
682 | length = security_member_sid(ssid, tsid, tclass, &newsid); | ||
683 | if (length < 0) | ||
684 | goto out2; | ||
685 | |||
686 | length = security_sid_to_context(newsid, &newcon, &len); | ||
687 | if (length < 0) | ||
688 | goto out2; | ||
689 | |||
690 | if (len > SIMPLE_TRANSACTION_LIMIT) { | ||
691 | printk(KERN_ERR "%s: context size (%u) exceeds payload " | ||
692 | "max\n", __FUNCTION__, len); | ||
693 | length = -ERANGE; | ||
694 | goto out3; | ||
695 | } | ||
696 | |||
697 | memcpy(buf, newcon, len); | ||
698 | length = len; | ||
699 | out3: | ||
700 | kfree(newcon); | ||
701 | out2: | ||
702 | kfree(tcon); | ||
703 | out: | ||
704 | kfree(scon); | ||
705 | return length; | ||
706 | } | ||
707 | |||
708 | static struct inode *sel_make_inode(struct super_block *sb, int mode) | ||
709 | { | ||
710 | struct inode *ret = new_inode(sb); | ||
711 | |||
712 | if (ret) { | ||
713 | ret->i_mode = mode; | ||
714 | ret->i_uid = ret->i_gid = 0; | ||
715 | ret->i_blksize = PAGE_CACHE_SIZE; | ||
716 | ret->i_blocks = 0; | ||
717 | ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME; | ||
718 | } | ||
719 | return ret; | ||
720 | } | ||
721 | |||
722 | #define BOOL_INO_OFFSET 30 | ||
723 | |||
724 | static ssize_t sel_read_bool(struct file *filep, char __user *buf, | ||
725 | size_t count, loff_t *ppos) | ||
726 | { | ||
727 | char *page = NULL; | ||
728 | ssize_t length; | ||
729 | ssize_t end; | ||
730 | ssize_t ret; | ||
731 | int cur_enforcing; | ||
732 | struct inode *inode; | ||
733 | |||
734 | down(&sel_sem); | ||
735 | |||
736 | ret = -EFAULT; | ||
737 | |||
738 | /* check to see if this file has been deleted */ | ||
739 | if (!filep->f_op) | ||
740 | goto out; | ||
741 | |||
742 | if (count < 0 || count > PAGE_SIZE) { | ||
743 | ret = -EINVAL; | ||
744 | goto out; | ||
745 | } | ||
746 | if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) { | ||
747 | ret = -ENOMEM; | ||
748 | goto out; | ||
749 | } | ||
750 | |||
751 | inode = filep->f_dentry->d_inode; | ||
752 | cur_enforcing = security_get_bool_value(inode->i_ino - BOOL_INO_OFFSET); | ||
753 | if (cur_enforcing < 0) { | ||
754 | ret = cur_enforcing; | ||
755 | goto out; | ||
756 | } | ||
757 | |||
758 | length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing, | ||
759 | bool_pending_values[inode->i_ino - BOOL_INO_OFFSET]); | ||
760 | if (length < 0) { | ||
761 | ret = length; | ||
762 | goto out; | ||
763 | } | ||
764 | |||
765 | if (*ppos >= length) { | ||
766 | ret = 0; | ||
767 | goto out; | ||
768 | } | ||
769 | if (count + *ppos > length) | ||
770 | count = length - *ppos; | ||
771 | end = count + *ppos; | ||
772 | if (copy_to_user(buf, (char *) page + *ppos, count)) { | ||
773 | ret = -EFAULT; | ||
774 | goto out; | ||
775 | } | ||
776 | *ppos = end; | ||
777 | ret = count; | ||
778 | out: | ||
779 | up(&sel_sem); | ||
780 | if (page) | ||
781 | free_page((unsigned long)page); | ||
782 | return ret; | ||
783 | } | ||
784 | |||
785 | static ssize_t sel_write_bool(struct file *filep, const char __user *buf, | ||
786 | size_t count, loff_t *ppos) | ||
787 | { | ||
788 | char *page = NULL; | ||
789 | ssize_t length = -EFAULT; | ||
790 | int new_value; | ||
791 | struct inode *inode; | ||
792 | |||
793 | down(&sel_sem); | ||
794 | |||
795 | length = task_has_security(current, SECURITY__SETBOOL); | ||
796 | if (length) | ||
797 | goto out; | ||
798 | |||
799 | /* check to see if this file has been deleted */ | ||
800 | if (!filep->f_op) | ||
801 | goto out; | ||
802 | |||
803 | if (count < 0 || count >= PAGE_SIZE) { | ||
804 | length = -ENOMEM; | ||
805 | goto out; | ||
806 | } | ||
807 | if (*ppos != 0) { | ||
808 | /* No partial writes. */ | ||
809 | goto out; | ||
810 | } | ||
811 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
812 | if (!page) { | ||
813 | length = -ENOMEM; | ||
814 | goto out; | ||
815 | } | ||
816 | |||
817 | if (copy_from_user(page, buf, count)) | ||
818 | goto out; | ||
819 | |||
820 | length = -EINVAL; | ||
821 | if (sscanf(page, "%d", &new_value) != 1) | ||
822 | goto out; | ||
823 | |||
824 | if (new_value) | ||
825 | new_value = 1; | ||
826 | |||
827 | inode = filep->f_dentry->d_inode; | ||
828 | bool_pending_values[inode->i_ino - BOOL_INO_OFFSET] = new_value; | ||
829 | length = count; | ||
830 | |||
831 | out: | ||
832 | up(&sel_sem); | ||
833 | if (page) | ||
834 | free_page((unsigned long) page); | ||
835 | return length; | ||
836 | } | ||
837 | |||
838 | static struct file_operations sel_bool_ops = { | ||
839 | .read = sel_read_bool, | ||
840 | .write = sel_write_bool, | ||
841 | }; | ||
842 | |||
843 | static ssize_t sel_commit_bools_write(struct file *filep, | ||
844 | const char __user *buf, | ||
845 | size_t count, loff_t *ppos) | ||
846 | { | ||
847 | char *page = NULL; | ||
848 | ssize_t length = -EFAULT; | ||
849 | int new_value; | ||
850 | |||
851 | down(&sel_sem); | ||
852 | |||
853 | length = task_has_security(current, SECURITY__SETBOOL); | ||
854 | if (length) | ||
855 | goto out; | ||
856 | |||
857 | /* check to see if this file has been deleted */ | ||
858 | if (!filep->f_op) | ||
859 | goto out; | ||
860 | |||
861 | if (count < 0 || count >= PAGE_SIZE) { | ||
862 | length = -ENOMEM; | ||
863 | goto out; | ||
864 | } | ||
865 | if (*ppos != 0) { | ||
866 | /* No partial writes. */ | ||
867 | goto out; | ||
868 | } | ||
869 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
870 | if (!page) { | ||
871 | length = -ENOMEM; | ||
872 | goto out; | ||
873 | } | ||
874 | |||
875 | if (copy_from_user(page, buf, count)) | ||
876 | goto out; | ||
877 | |||
878 | length = -EINVAL; | ||
879 | if (sscanf(page, "%d", &new_value) != 1) | ||
880 | goto out; | ||
881 | |||
882 | if (new_value) { | ||
883 | security_set_bools(bool_num, bool_pending_values); | ||
884 | } | ||
885 | |||
886 | length = count; | ||
887 | |||
888 | out: | ||
889 | up(&sel_sem); | ||
890 | if (page) | ||
891 | free_page((unsigned long) page); | ||
892 | return length; | ||
893 | } | ||
894 | |||
895 | static struct file_operations sel_commit_bools_ops = { | ||
896 | .write = sel_commit_bools_write, | ||
897 | }; | ||
898 | |||
899 | /* delete booleans - partial revoke() from | ||
900 | * fs/proc/generic.c proc_kill_inodes */ | ||
901 | static void sel_remove_bools(struct dentry *de) | ||
902 | { | ||
903 | struct list_head *p, *node; | ||
904 | struct super_block *sb = de->d_sb; | ||
905 | |||
906 | spin_lock(&dcache_lock); | ||
907 | node = de->d_subdirs.next; | ||
908 | while (node != &de->d_subdirs) { | ||
909 | struct dentry *d = list_entry(node, struct dentry, d_child); | ||
910 | list_del_init(node); | ||
911 | |||
912 | if (d->d_inode) { | ||
913 | d = dget_locked(d); | ||
914 | spin_unlock(&dcache_lock); | ||
915 | d_delete(d); | ||
916 | simple_unlink(de->d_inode, d); | ||
917 | dput(d); | ||
918 | spin_lock(&dcache_lock); | ||
919 | } | ||
920 | node = de->d_subdirs.next; | ||
921 | } | ||
922 | |||
923 | spin_unlock(&dcache_lock); | ||
924 | |||
925 | file_list_lock(); | ||
926 | list_for_each(p, &sb->s_files) { | ||
927 | struct file * filp = list_entry(p, struct file, f_list); | ||
928 | struct dentry * dentry = filp->f_dentry; | ||
929 | |||
930 | if (dentry->d_parent != de) { | ||
931 | continue; | ||
932 | } | ||
933 | filp->f_op = NULL; | ||
934 | } | ||
935 | file_list_unlock(); | ||
936 | } | ||
937 | |||
938 | #define BOOL_DIR_NAME "booleans" | ||
939 | |||
940 | static int sel_make_bools(void) | ||
941 | { | ||
942 | int i, ret = 0; | ||
943 | ssize_t len; | ||
944 | struct dentry *dentry = NULL; | ||
945 | struct dentry *dir = bool_dir; | ||
946 | struct inode *inode = NULL; | ||
947 | struct inode_security_struct *isec; | ||
948 | char **names = NULL, *page; | ||
949 | int num; | ||
950 | int *values = NULL; | ||
951 | u32 sid; | ||
952 | |||
953 | /* remove any existing files */ | ||
954 | if (bool_pending_values) | ||
955 | kfree(bool_pending_values); | ||
956 | |||
957 | sel_remove_bools(dir); | ||
958 | |||
959 | if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) | ||
960 | return -ENOMEM; | ||
961 | |||
962 | ret = security_get_bools(&num, &names, &values); | ||
963 | if (ret != 0) | ||
964 | goto out; | ||
965 | |||
966 | for (i = 0; i < num; i++) { | ||
967 | dentry = d_alloc_name(dir, names[i]); | ||
968 | if (!dentry) { | ||
969 | ret = -ENOMEM; | ||
970 | goto err; | ||
971 | } | ||
972 | inode = sel_make_inode(dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR); | ||
973 | if (!inode) { | ||
974 | ret = -ENOMEM; | ||
975 | goto err; | ||
976 | } | ||
977 | |||
978 | len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]); | ||
979 | if (len < 0) { | ||
980 | ret = -EINVAL; | ||
981 | goto err; | ||
982 | } else if (len >= PAGE_SIZE) { | ||
983 | ret = -ENAMETOOLONG; | ||
984 | goto err; | ||
985 | } | ||
986 | isec = (struct inode_security_struct*)inode->i_security; | ||
987 | if ((ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid))) | ||
988 | goto err; | ||
989 | isec->sid = sid; | ||
990 | isec->initialized = 1; | ||
991 | inode->i_fop = &sel_bool_ops; | ||
992 | inode->i_ino = i + BOOL_INO_OFFSET; | ||
993 | d_add(dentry, inode); | ||
994 | } | ||
995 | bool_num = num; | ||
996 | bool_pending_values = values; | ||
997 | out: | ||
998 | free_page((unsigned long)page); | ||
999 | if (names) { | ||
1000 | for (i = 0; i < num; i++) { | ||
1001 | if (names[i]) | ||
1002 | kfree(names[i]); | ||
1003 | } | ||
1004 | kfree(names); | ||
1005 | } | ||
1006 | return ret; | ||
1007 | err: | ||
1008 | d_genocide(dir); | ||
1009 | ret = -ENOMEM; | ||
1010 | goto out; | ||
1011 | } | ||
1012 | |||
1013 | #define NULL_FILE_NAME "null" | ||
1014 | |||
1015 | struct dentry *selinux_null = NULL; | ||
1016 | |||
1017 | static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf, | ||
1018 | size_t count, loff_t *ppos) | ||
1019 | { | ||
1020 | char tmpbuf[TMPBUFLEN]; | ||
1021 | ssize_t length; | ||
1022 | |||
1023 | length = scnprintf(tmpbuf, TMPBUFLEN, "%u", avc_cache_threshold); | ||
1024 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); | ||
1025 | } | ||
1026 | |||
1027 | static ssize_t sel_write_avc_cache_threshold(struct file * file, | ||
1028 | const char __user * buf, | ||
1029 | size_t count, loff_t *ppos) | ||
1030 | |||
1031 | { | ||
1032 | char *page; | ||
1033 | ssize_t ret; | ||
1034 | int new_value; | ||
1035 | |||
1036 | if (count < 0 || count >= PAGE_SIZE) { | ||
1037 | ret = -ENOMEM; | ||
1038 | goto out; | ||
1039 | } | ||
1040 | |||
1041 | if (*ppos != 0) { | ||
1042 | /* No partial writes. */ | ||
1043 | ret = -EINVAL; | ||
1044 | goto out; | ||
1045 | } | ||
1046 | |||
1047 | page = (char*)get_zeroed_page(GFP_KERNEL); | ||
1048 | if (!page) { | ||
1049 | ret = -ENOMEM; | ||
1050 | goto out; | ||
1051 | } | ||
1052 | |||
1053 | if (copy_from_user(page, buf, count)) { | ||
1054 | ret = -EFAULT; | ||
1055 | goto out_free; | ||
1056 | } | ||
1057 | |||
1058 | if (sscanf(page, "%u", &new_value) != 1) { | ||
1059 | ret = -EINVAL; | ||
1060 | goto out; | ||
1061 | } | ||
1062 | |||
1063 | if (new_value != avc_cache_threshold) { | ||
1064 | ret = task_has_security(current, SECURITY__SETSECPARAM); | ||
1065 | if (ret) | ||
1066 | goto out_free; | ||
1067 | avc_cache_threshold = new_value; | ||
1068 | } | ||
1069 | ret = count; | ||
1070 | out_free: | ||
1071 | free_page((unsigned long)page); | ||
1072 | out: | ||
1073 | return ret; | ||
1074 | } | ||
1075 | |||
1076 | static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf, | ||
1077 | size_t count, loff_t *ppos) | ||
1078 | { | ||
1079 | char *page; | ||
1080 | ssize_t ret = 0; | ||
1081 | |||
1082 | page = (char *)__get_free_page(GFP_KERNEL); | ||
1083 | if (!page) { | ||
1084 | ret = -ENOMEM; | ||
1085 | goto out; | ||
1086 | } | ||
1087 | ret = avc_get_hash_stats(page); | ||
1088 | if (ret >= 0) | ||
1089 | ret = simple_read_from_buffer(buf, count, ppos, page, ret); | ||
1090 | free_page((unsigned long)page); | ||
1091 | out: | ||
1092 | return ret; | ||
1093 | } | ||
1094 | |||
1095 | static struct file_operations sel_avc_cache_threshold_ops = { | ||
1096 | .read = sel_read_avc_cache_threshold, | ||
1097 | .write = sel_write_avc_cache_threshold, | ||
1098 | }; | ||
1099 | |||
1100 | static struct file_operations sel_avc_hash_stats_ops = { | ||
1101 | .read = sel_read_avc_hash_stats, | ||
1102 | }; | ||
1103 | |||
1104 | #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS | ||
1105 | static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx) | ||
1106 | { | ||
1107 | int cpu; | ||
1108 | |||
1109 | for (cpu = *idx; cpu < NR_CPUS; ++cpu) { | ||
1110 | if (!cpu_possible(cpu)) | ||
1111 | continue; | ||
1112 | *idx = cpu + 1; | ||
1113 | return &per_cpu(avc_cache_stats, cpu); | ||
1114 | } | ||
1115 | return NULL; | ||
1116 | } | ||
1117 | |||
1118 | static void *sel_avc_stats_seq_start(struct seq_file *seq, loff_t *pos) | ||
1119 | { | ||
1120 | loff_t n = *pos - 1; | ||
1121 | |||
1122 | if (*pos == 0) | ||
1123 | return SEQ_START_TOKEN; | ||
1124 | |||
1125 | return sel_avc_get_stat_idx(&n); | ||
1126 | } | ||
1127 | |||
1128 | static void *sel_avc_stats_seq_next(struct seq_file *seq, void *v, loff_t *pos) | ||
1129 | { | ||
1130 | return sel_avc_get_stat_idx(pos); | ||
1131 | } | ||
1132 | |||
1133 | static int sel_avc_stats_seq_show(struct seq_file *seq, void *v) | ||
1134 | { | ||
1135 | struct avc_cache_stats *st = v; | ||
1136 | |||
1137 | if (v == SEQ_START_TOKEN) | ||
1138 | seq_printf(seq, "lookups hits misses allocations reclaims " | ||
1139 | "frees\n"); | ||
1140 | else | ||
1141 | seq_printf(seq, "%u %u %u %u %u %u\n", st->lookups, | ||
1142 | st->hits, st->misses, st->allocations, | ||
1143 | st->reclaims, st->frees); | ||
1144 | return 0; | ||
1145 | } | ||
1146 | |||
1147 | static void sel_avc_stats_seq_stop(struct seq_file *seq, void *v) | ||
1148 | { } | ||
1149 | |||
1150 | static struct seq_operations sel_avc_cache_stats_seq_ops = { | ||
1151 | .start = sel_avc_stats_seq_start, | ||
1152 | .next = sel_avc_stats_seq_next, | ||
1153 | .show = sel_avc_stats_seq_show, | ||
1154 | .stop = sel_avc_stats_seq_stop, | ||
1155 | }; | ||
1156 | |||
1157 | static int sel_open_avc_cache_stats(struct inode *inode, struct file *file) | ||
1158 | { | ||
1159 | return seq_open(file, &sel_avc_cache_stats_seq_ops); | ||
1160 | } | ||
1161 | |||
1162 | static struct file_operations sel_avc_cache_stats_ops = { | ||
1163 | .open = sel_open_avc_cache_stats, | ||
1164 | .read = seq_read, | ||
1165 | .llseek = seq_lseek, | ||
1166 | .release = seq_release, | ||
1167 | }; | ||
1168 | #endif | ||
1169 | |||
1170 | static int sel_make_avc_files(struct dentry *dir) | ||
1171 | { | ||
1172 | int i, ret = 0; | ||
1173 | static struct tree_descr files[] = { | ||
1174 | { "cache_threshold", | ||
1175 | &sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR }, | ||
1176 | { "hash_stats", &sel_avc_hash_stats_ops, S_IRUGO }, | ||
1177 | #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS | ||
1178 | { "cache_stats", &sel_avc_cache_stats_ops, S_IRUGO }, | ||
1179 | #endif | ||
1180 | }; | ||
1181 | |||
1182 | for (i = 0; i < sizeof (files) / sizeof (files[0]); i++) { | ||
1183 | struct inode *inode; | ||
1184 | struct dentry *dentry; | ||
1185 | |||
1186 | dentry = d_alloc_name(dir, files[i].name); | ||
1187 | if (!dentry) { | ||
1188 | ret = -ENOMEM; | ||
1189 | goto err; | ||
1190 | } | ||
1191 | |||
1192 | inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); | ||
1193 | if (!inode) { | ||
1194 | ret = -ENOMEM; | ||
1195 | goto err; | ||
1196 | } | ||
1197 | inode->i_fop = files[i].ops; | ||
1198 | d_add(dentry, inode); | ||
1199 | } | ||
1200 | out: | ||
1201 | return ret; | ||
1202 | err: | ||
1203 | d_genocide(dir); | ||
1204 | goto out; | ||
1205 | } | ||
1206 | |||
1207 | static int sel_make_dir(struct super_block *sb, struct dentry *dentry) | ||
1208 | { | ||
1209 | int ret = 0; | ||
1210 | struct inode *inode; | ||
1211 | |||
1212 | inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO); | ||
1213 | if (!inode) { | ||
1214 | ret = -ENOMEM; | ||
1215 | goto out; | ||
1216 | } | ||
1217 | inode->i_op = &simple_dir_inode_operations; | ||
1218 | inode->i_fop = &simple_dir_operations; | ||
1219 | d_add(dentry, inode); | ||
1220 | out: | ||
1221 | return ret; | ||
1222 | } | ||
1223 | |||
1224 | static int sel_fill_super(struct super_block * sb, void * data, int silent) | ||
1225 | { | ||
1226 | int ret; | ||
1227 | struct dentry *dentry; | ||
1228 | struct inode *inode; | ||
1229 | struct inode_security_struct *isec; | ||
1230 | |||
1231 | static struct tree_descr selinux_files[] = { | ||
1232 | [SEL_LOAD] = {"load", &sel_load_ops, S_IRUSR|S_IWUSR}, | ||
1233 | [SEL_ENFORCE] = {"enforce", &sel_enforce_ops, S_IRUGO|S_IWUSR}, | ||
1234 | [SEL_CONTEXT] = {"context", &sel_context_ops, S_IRUGO|S_IWUGO}, | ||
1235 | [SEL_ACCESS] = {"access", &transaction_ops, S_IRUGO|S_IWUGO}, | ||
1236 | [SEL_CREATE] = {"create", &transaction_ops, S_IRUGO|S_IWUGO}, | ||
1237 | [SEL_RELABEL] = {"relabel", &transaction_ops, S_IRUGO|S_IWUGO}, | ||
1238 | [SEL_USER] = {"user", &transaction_ops, S_IRUGO|S_IWUGO}, | ||
1239 | [SEL_POLICYVERS] = {"policyvers", &sel_policyvers_ops, S_IRUGO}, | ||
1240 | [SEL_COMMIT_BOOLS] = {"commit_pending_bools", &sel_commit_bools_ops, S_IWUSR}, | ||
1241 | [SEL_MLS] = {"mls", &sel_mls_ops, S_IRUGO}, | ||
1242 | [SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR}, | ||
1243 | [SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO}, | ||
1244 | [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR}, | ||
1245 | /* last one */ {""} | ||
1246 | }; | ||
1247 | ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); | ||
1248 | if (ret) | ||
1249 | return ret; | ||
1250 | |||
1251 | dentry = d_alloc_name(sb->s_root, BOOL_DIR_NAME); | ||
1252 | if (!dentry) | ||
1253 | return -ENOMEM; | ||
1254 | |||
1255 | inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO); | ||
1256 | if (!inode) | ||
1257 | goto out; | ||
1258 | inode->i_op = &simple_dir_inode_operations; | ||
1259 | inode->i_fop = &simple_dir_operations; | ||
1260 | d_add(dentry, inode); | ||
1261 | bool_dir = dentry; | ||
1262 | ret = sel_make_bools(); | ||
1263 | if (ret) | ||
1264 | goto out; | ||
1265 | |||
1266 | dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME); | ||
1267 | if (!dentry) | ||
1268 | return -ENOMEM; | ||
1269 | |||
1270 | inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO); | ||
1271 | if (!inode) | ||
1272 | goto out; | ||
1273 | isec = (struct inode_security_struct*)inode->i_security; | ||
1274 | isec->sid = SECINITSID_DEVNULL; | ||
1275 | isec->sclass = SECCLASS_CHR_FILE; | ||
1276 | isec->initialized = 1; | ||
1277 | |||
1278 | init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3)); | ||
1279 | d_add(dentry, inode); | ||
1280 | selinux_null = dentry; | ||
1281 | |||
1282 | dentry = d_alloc_name(sb->s_root, "avc"); | ||
1283 | if (!dentry) | ||
1284 | return -ENOMEM; | ||
1285 | |||
1286 | ret = sel_make_dir(sb, dentry); | ||
1287 | if (ret) | ||
1288 | goto out; | ||
1289 | |||
1290 | ret = sel_make_avc_files(dentry); | ||
1291 | if (ret) | ||
1292 | goto out; | ||
1293 | |||
1294 | return 0; | ||
1295 | out: | ||
1296 | dput(dentry); | ||
1297 | printk(KERN_ERR "%s: failed while creating inodes\n", __FUNCTION__); | ||
1298 | return -ENOMEM; | ||
1299 | } | ||
1300 | |||
1301 | static struct super_block *sel_get_sb(struct file_system_type *fs_type, | ||
1302 | int flags, const char *dev_name, void *data) | ||
1303 | { | ||
1304 | return get_sb_single(fs_type, flags, data, sel_fill_super); | ||
1305 | } | ||
1306 | |||
1307 | static struct file_system_type sel_fs_type = { | ||
1308 | .name = "selinuxfs", | ||
1309 | .get_sb = sel_get_sb, | ||
1310 | .kill_sb = kill_litter_super, | ||
1311 | }; | ||
1312 | |||
1313 | struct vfsmount *selinuxfs_mount; | ||
1314 | |||
1315 | static int __init init_sel_fs(void) | ||
1316 | { | ||
1317 | int err; | ||
1318 | |||
1319 | if (!selinux_enabled) | ||
1320 | return 0; | ||
1321 | err = register_filesystem(&sel_fs_type); | ||
1322 | if (!err) { | ||
1323 | selinuxfs_mount = kern_mount(&sel_fs_type); | ||
1324 | if (IS_ERR(selinuxfs_mount)) { | ||
1325 | printk(KERN_ERR "selinuxfs: could not mount!\n"); | ||
1326 | err = PTR_ERR(selinuxfs_mount); | ||
1327 | selinuxfs_mount = NULL; | ||
1328 | } | ||
1329 | } | ||
1330 | return err; | ||
1331 | } | ||
1332 | |||
1333 | __initcall(init_sel_fs); | ||
1334 | |||
1335 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE | ||
1336 | void exit_sel_fs(void) | ||
1337 | { | ||
1338 | unregister_filesystem(&sel_fs_type); | ||
1339 | } | ||
1340 | #endif | ||
diff --git a/security/selinux/ss/Makefile b/security/selinux/ss/Makefile new file mode 100644 index 000000000000..bad78779b9b0 --- /dev/null +++ b/security/selinux/ss/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # | ||
2 | # Makefile for building the SELinux security server as part of the kernel tree. | ||
3 | # | ||
4 | |||
5 | EXTRA_CFLAGS += -Isecurity/selinux/include | ||
6 | obj-y := ss.o | ||
7 | |||
8 | ss-y := ebitmap.o hashtab.o symtab.o sidtab.o avtab.o policydb.o services.o conditional.o mls.o | ||
9 | |||
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c new file mode 100644 index 000000000000..f238c034c44e --- /dev/null +++ b/security/selinux/ss/avtab.c | |||
@@ -0,0 +1,399 @@ | |||
1 | /* | ||
2 | * Implementation of the access vector table type. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | |||
7 | /* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> | ||
8 | * | ||
9 | * Added conditional policy language extensions | ||
10 | * | ||
11 | * Copyright (C) 2003 Tresys Technology, LLC | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation, version 2. | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/vmalloc.h> | ||
20 | #include <linux/errno.h> | ||
21 | |||
22 | #include "avtab.h" | ||
23 | #include "policydb.h" | ||
24 | |||
25 | #define AVTAB_HASH(keyp) \ | ||
26 | ((keyp->target_class + \ | ||
27 | (keyp->target_type << 2) + \ | ||
28 | (keyp->source_type << 9)) & \ | ||
29 | AVTAB_HASH_MASK) | ||
30 | |||
31 | static kmem_cache_t *avtab_node_cachep; | ||
32 | |||
33 | static struct avtab_node* | ||
34 | avtab_insert_node(struct avtab *h, int hvalue, | ||
35 | struct avtab_node * prev, struct avtab_node * cur, | ||
36 | struct avtab_key *key, struct avtab_datum *datum) | ||
37 | { | ||
38 | struct avtab_node * newnode; | ||
39 | newnode = kmem_cache_alloc(avtab_node_cachep, SLAB_KERNEL); | ||
40 | if (newnode == NULL) | ||
41 | return NULL; | ||
42 | memset(newnode, 0, sizeof(struct avtab_node)); | ||
43 | newnode->key = *key; | ||
44 | newnode->datum = *datum; | ||
45 | if (prev) { | ||
46 | newnode->next = prev->next; | ||
47 | prev->next = newnode; | ||
48 | } else { | ||
49 | newnode->next = h->htable[hvalue]; | ||
50 | h->htable[hvalue] = newnode; | ||
51 | } | ||
52 | |||
53 | h->nel++; | ||
54 | return newnode; | ||
55 | } | ||
56 | |||
57 | static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum) | ||
58 | { | ||
59 | int hvalue; | ||
60 | struct avtab_node *prev, *cur, *newnode; | ||
61 | |||
62 | if (!h) | ||
63 | return -EINVAL; | ||
64 | |||
65 | hvalue = AVTAB_HASH(key); | ||
66 | for (prev = NULL, cur = h->htable[hvalue]; | ||
67 | cur; | ||
68 | prev = cur, cur = cur->next) { | ||
69 | if (key->source_type == cur->key.source_type && | ||
70 | key->target_type == cur->key.target_type && | ||
71 | key->target_class == cur->key.target_class && | ||
72 | (datum->specified & cur->datum.specified)) | ||
73 | return -EEXIST; | ||
74 | if (key->source_type < cur->key.source_type) | ||
75 | break; | ||
76 | if (key->source_type == cur->key.source_type && | ||
77 | key->target_type < cur->key.target_type) | ||
78 | break; | ||
79 | if (key->source_type == cur->key.source_type && | ||
80 | key->target_type == cur->key.target_type && | ||
81 | key->target_class < cur->key.target_class) | ||
82 | break; | ||
83 | } | ||
84 | |||
85 | newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum); | ||
86 | if(!newnode) | ||
87 | return -ENOMEM; | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | /* Unlike avtab_insert(), this function allow multiple insertions of the same | ||
93 | * key/specified mask into the table, as needed by the conditional avtab. | ||
94 | * It also returns a pointer to the node inserted. | ||
95 | */ | ||
96 | struct avtab_node * | ||
97 | avtab_insert_nonunique(struct avtab * h, struct avtab_key * key, struct avtab_datum * datum) | ||
98 | { | ||
99 | int hvalue; | ||
100 | struct avtab_node *prev, *cur, *newnode; | ||
101 | |||
102 | if (!h) | ||
103 | return NULL; | ||
104 | hvalue = AVTAB_HASH(key); | ||
105 | for (prev = NULL, cur = h->htable[hvalue]; | ||
106 | cur; | ||
107 | prev = cur, cur = cur->next) { | ||
108 | if (key->source_type == cur->key.source_type && | ||
109 | key->target_type == cur->key.target_type && | ||
110 | key->target_class == cur->key.target_class && | ||
111 | (datum->specified & cur->datum.specified)) | ||
112 | break; | ||
113 | if (key->source_type < cur->key.source_type) | ||
114 | break; | ||
115 | if (key->source_type == cur->key.source_type && | ||
116 | key->target_type < cur->key.target_type) | ||
117 | break; | ||
118 | if (key->source_type == cur->key.source_type && | ||
119 | key->target_type == cur->key.target_type && | ||
120 | key->target_class < cur->key.target_class) | ||
121 | break; | ||
122 | } | ||
123 | newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum); | ||
124 | |||
125 | return newnode; | ||
126 | } | ||
127 | |||
128 | struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key, int specified) | ||
129 | { | ||
130 | int hvalue; | ||
131 | struct avtab_node *cur; | ||
132 | |||
133 | if (!h) | ||
134 | return NULL; | ||
135 | |||
136 | hvalue = AVTAB_HASH(key); | ||
137 | for (cur = h->htable[hvalue]; cur; cur = cur->next) { | ||
138 | if (key->source_type == cur->key.source_type && | ||
139 | key->target_type == cur->key.target_type && | ||
140 | key->target_class == cur->key.target_class && | ||
141 | (specified & cur->datum.specified)) | ||
142 | return &cur->datum; | ||
143 | |||
144 | if (key->source_type < cur->key.source_type) | ||
145 | break; | ||
146 | if (key->source_type == cur->key.source_type && | ||
147 | key->target_type < cur->key.target_type) | ||
148 | break; | ||
149 | if (key->source_type == cur->key.source_type && | ||
150 | key->target_type == cur->key.target_type && | ||
151 | key->target_class < cur->key.target_class) | ||
152 | break; | ||
153 | } | ||
154 | |||
155 | return NULL; | ||
156 | } | ||
157 | |||
158 | /* This search function returns a node pointer, and can be used in | ||
159 | * conjunction with avtab_search_next_node() | ||
160 | */ | ||
161 | struct avtab_node* | ||
162 | avtab_search_node(struct avtab *h, struct avtab_key *key, int specified) | ||
163 | { | ||
164 | int hvalue; | ||
165 | struct avtab_node *cur; | ||
166 | |||
167 | if (!h) | ||
168 | return NULL; | ||
169 | |||
170 | hvalue = AVTAB_HASH(key); | ||
171 | for (cur = h->htable[hvalue]; cur; cur = cur->next) { | ||
172 | if (key->source_type == cur->key.source_type && | ||
173 | key->target_type == cur->key.target_type && | ||
174 | key->target_class == cur->key.target_class && | ||
175 | (specified & cur->datum.specified)) | ||
176 | return cur; | ||
177 | |||
178 | if (key->source_type < cur->key.source_type) | ||
179 | break; | ||
180 | if (key->source_type == cur->key.source_type && | ||
181 | key->target_type < cur->key.target_type) | ||
182 | break; | ||
183 | if (key->source_type == cur->key.source_type && | ||
184 | key->target_type == cur->key.target_type && | ||
185 | key->target_class < cur->key.target_class) | ||
186 | break; | ||
187 | } | ||
188 | return NULL; | ||
189 | } | ||
190 | |||
191 | struct avtab_node* | ||
192 | avtab_search_node_next(struct avtab_node *node, int specified) | ||
193 | { | ||
194 | struct avtab_node *cur; | ||
195 | |||
196 | if (!node) | ||
197 | return NULL; | ||
198 | |||
199 | for (cur = node->next; cur; cur = cur->next) { | ||
200 | if (node->key.source_type == cur->key.source_type && | ||
201 | node->key.target_type == cur->key.target_type && | ||
202 | node->key.target_class == cur->key.target_class && | ||
203 | (specified & cur->datum.specified)) | ||
204 | return cur; | ||
205 | |||
206 | if (node->key.source_type < cur->key.source_type) | ||
207 | break; | ||
208 | if (node->key.source_type == cur->key.source_type && | ||
209 | node->key.target_type < cur->key.target_type) | ||
210 | break; | ||
211 | if (node->key.source_type == cur->key.source_type && | ||
212 | node->key.target_type == cur->key.target_type && | ||
213 | node->key.target_class < cur->key.target_class) | ||
214 | break; | ||
215 | } | ||
216 | return NULL; | ||
217 | } | ||
218 | |||
219 | void avtab_destroy(struct avtab *h) | ||
220 | { | ||
221 | int i; | ||
222 | struct avtab_node *cur, *temp; | ||
223 | |||
224 | if (!h || !h->htable) | ||
225 | return; | ||
226 | |||
227 | for (i = 0; i < AVTAB_SIZE; i++) { | ||
228 | cur = h->htable[i]; | ||
229 | while (cur != NULL) { | ||
230 | temp = cur; | ||
231 | cur = cur->next; | ||
232 | kmem_cache_free(avtab_node_cachep, temp); | ||
233 | } | ||
234 | h->htable[i] = NULL; | ||
235 | } | ||
236 | vfree(h->htable); | ||
237 | h->htable = NULL; | ||
238 | } | ||
239 | |||
240 | |||
241 | int avtab_init(struct avtab *h) | ||
242 | { | ||
243 | int i; | ||
244 | |||
245 | h->htable = vmalloc(sizeof(*(h->htable)) * AVTAB_SIZE); | ||
246 | if (!h->htable) | ||
247 | return -ENOMEM; | ||
248 | for (i = 0; i < AVTAB_SIZE; i++) | ||
249 | h->htable[i] = NULL; | ||
250 | h->nel = 0; | ||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | void avtab_hash_eval(struct avtab *h, char *tag) | ||
255 | { | ||
256 | int i, chain_len, slots_used, max_chain_len; | ||
257 | struct avtab_node *cur; | ||
258 | |||
259 | slots_used = 0; | ||
260 | max_chain_len = 0; | ||
261 | for (i = 0; i < AVTAB_SIZE; i++) { | ||
262 | cur = h->htable[i]; | ||
263 | if (cur) { | ||
264 | slots_used++; | ||
265 | chain_len = 0; | ||
266 | while (cur) { | ||
267 | chain_len++; | ||
268 | cur = cur->next; | ||
269 | } | ||
270 | |||
271 | if (chain_len > max_chain_len) | ||
272 | max_chain_len = chain_len; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | printk(KERN_INFO "%s: %d entries and %d/%d buckets used, longest " | ||
277 | "chain length %d\n", tag, h->nel, slots_used, AVTAB_SIZE, | ||
278 | max_chain_len); | ||
279 | } | ||
280 | |||
281 | int avtab_read_item(void *fp, struct avtab_datum *avdatum, struct avtab_key *avkey) | ||
282 | { | ||
283 | u32 buf[7]; | ||
284 | u32 items, items2; | ||
285 | int rc; | ||
286 | |||
287 | memset(avkey, 0, sizeof(struct avtab_key)); | ||
288 | memset(avdatum, 0, sizeof(struct avtab_datum)); | ||
289 | |||
290 | rc = next_entry(buf, fp, sizeof(u32)); | ||
291 | if (rc < 0) { | ||
292 | printk(KERN_ERR "security: avtab: truncated entry\n"); | ||
293 | goto bad; | ||
294 | } | ||
295 | items2 = le32_to_cpu(buf[0]); | ||
296 | if (items2 > ARRAY_SIZE(buf)) { | ||
297 | printk(KERN_ERR "security: avtab: entry overflow\n"); | ||
298 | goto bad; | ||
299 | } | ||
300 | rc = next_entry(buf, fp, sizeof(u32)*items2); | ||
301 | if (rc < 0) { | ||
302 | printk(KERN_ERR "security: avtab: truncated entry\n"); | ||
303 | goto bad; | ||
304 | } | ||
305 | items = 0; | ||
306 | avkey->source_type = le32_to_cpu(buf[items++]); | ||
307 | avkey->target_type = le32_to_cpu(buf[items++]); | ||
308 | avkey->target_class = le32_to_cpu(buf[items++]); | ||
309 | avdatum->specified = le32_to_cpu(buf[items++]); | ||
310 | if (!(avdatum->specified & (AVTAB_AV | AVTAB_TYPE))) { | ||
311 | printk(KERN_ERR "security: avtab: null entry\n"); | ||
312 | goto bad; | ||
313 | } | ||
314 | if ((avdatum->specified & AVTAB_AV) && | ||
315 | (avdatum->specified & AVTAB_TYPE)) { | ||
316 | printk(KERN_ERR "security: avtab: entry has both access vectors and types\n"); | ||
317 | goto bad; | ||
318 | } | ||
319 | if (avdatum->specified & AVTAB_AV) { | ||
320 | if (avdatum->specified & AVTAB_ALLOWED) | ||
321 | avtab_allowed(avdatum) = le32_to_cpu(buf[items++]); | ||
322 | if (avdatum->specified & AVTAB_AUDITDENY) | ||
323 | avtab_auditdeny(avdatum) = le32_to_cpu(buf[items++]); | ||
324 | if (avdatum->specified & AVTAB_AUDITALLOW) | ||
325 | avtab_auditallow(avdatum) = le32_to_cpu(buf[items++]); | ||
326 | } else { | ||
327 | if (avdatum->specified & AVTAB_TRANSITION) | ||
328 | avtab_transition(avdatum) = le32_to_cpu(buf[items++]); | ||
329 | if (avdatum->specified & AVTAB_CHANGE) | ||
330 | avtab_change(avdatum) = le32_to_cpu(buf[items++]); | ||
331 | if (avdatum->specified & AVTAB_MEMBER) | ||
332 | avtab_member(avdatum) = le32_to_cpu(buf[items++]); | ||
333 | } | ||
334 | if (items != items2) { | ||
335 | printk(KERN_ERR "security: avtab: entry only had %d items, expected %d\n", | ||
336 | items2, items); | ||
337 | goto bad; | ||
338 | } | ||
339 | |||
340 | return 0; | ||
341 | bad: | ||
342 | return -1; | ||
343 | } | ||
344 | |||
345 | int avtab_read(struct avtab *a, void *fp, u32 config) | ||
346 | { | ||
347 | int rc; | ||
348 | struct avtab_key avkey; | ||
349 | struct avtab_datum avdatum; | ||
350 | u32 buf[1]; | ||
351 | u32 nel, i; | ||
352 | |||
353 | |||
354 | rc = next_entry(buf, fp, sizeof(u32)); | ||
355 | if (rc < 0) { | ||
356 | printk(KERN_ERR "security: avtab: truncated table\n"); | ||
357 | goto bad; | ||
358 | } | ||
359 | nel = le32_to_cpu(buf[0]); | ||
360 | if (!nel) { | ||
361 | printk(KERN_ERR "security: avtab: table is empty\n"); | ||
362 | rc = -EINVAL; | ||
363 | goto bad; | ||
364 | } | ||
365 | for (i = 0; i < nel; i++) { | ||
366 | if (avtab_read_item(fp, &avdatum, &avkey)) { | ||
367 | rc = -EINVAL; | ||
368 | goto bad; | ||
369 | } | ||
370 | rc = avtab_insert(a, &avkey, &avdatum); | ||
371 | if (rc) { | ||
372 | if (rc == -ENOMEM) | ||
373 | printk(KERN_ERR "security: avtab: out of memory\n"); | ||
374 | if (rc == -EEXIST) | ||
375 | printk(KERN_ERR "security: avtab: duplicate entry\n"); | ||
376 | goto bad; | ||
377 | } | ||
378 | } | ||
379 | |||
380 | rc = 0; | ||
381 | out: | ||
382 | return rc; | ||
383 | |||
384 | bad: | ||
385 | avtab_destroy(a); | ||
386 | goto out; | ||
387 | } | ||
388 | |||
389 | void avtab_cache_init(void) | ||
390 | { | ||
391 | avtab_node_cachep = kmem_cache_create("avtab_node", | ||
392 | sizeof(struct avtab_node), | ||
393 | 0, SLAB_PANIC, NULL, NULL); | ||
394 | } | ||
395 | |||
396 | void avtab_cache_destroy(void) | ||
397 | { | ||
398 | kmem_cache_destroy (avtab_node_cachep); | ||
399 | } | ||
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h new file mode 100644 index 000000000000..519d4f6dc655 --- /dev/null +++ b/security/selinux/ss/avtab.h | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * An access vector table (avtab) is a hash table | ||
3 | * of access vectors and transition types indexed | ||
4 | * by a type pair and a class. An access vector | ||
5 | * table is used to represent the type enforcement | ||
6 | * tables. | ||
7 | * | ||
8 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
9 | */ | ||
10 | |||
11 | /* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> | ||
12 | * | ||
13 | * Added conditional policy language extensions | ||
14 | * | ||
15 | * Copyright (C) 2003 Tresys Technology, LLC | ||
16 | * This program is free software; you can redistribute it and/or modify | ||
17 | * it under the terms of the GNU General Public License as published by | ||
18 | * the Free Software Foundation, version 2. | ||
19 | */ | ||
20 | #ifndef _SS_AVTAB_H_ | ||
21 | #define _SS_AVTAB_H_ | ||
22 | |||
23 | struct avtab_key { | ||
24 | u32 source_type; /* source type */ | ||
25 | u32 target_type; /* target type */ | ||
26 | u32 target_class; /* target object class */ | ||
27 | }; | ||
28 | |||
29 | struct avtab_datum { | ||
30 | #define AVTAB_ALLOWED 1 | ||
31 | #define AVTAB_AUDITALLOW 2 | ||
32 | #define AVTAB_AUDITDENY 4 | ||
33 | #define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY) | ||
34 | #define AVTAB_TRANSITION 16 | ||
35 | #define AVTAB_MEMBER 32 | ||
36 | #define AVTAB_CHANGE 64 | ||
37 | #define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE) | ||
38 | #define AVTAB_ENABLED 0x80000000 /* reserved for used in cond_avtab */ | ||
39 | u32 specified; /* what fields are specified */ | ||
40 | u32 data[3]; /* access vectors or types */ | ||
41 | #define avtab_allowed(x) (x)->data[0] | ||
42 | #define avtab_auditdeny(x) (x)->data[1] | ||
43 | #define avtab_auditallow(x) (x)->data[2] | ||
44 | #define avtab_transition(x) (x)->data[0] | ||
45 | #define avtab_change(x) (x)->data[1] | ||
46 | #define avtab_member(x) (x)->data[2] | ||
47 | }; | ||
48 | |||
49 | struct avtab_node { | ||
50 | struct avtab_key key; | ||
51 | struct avtab_datum datum; | ||
52 | struct avtab_node *next; | ||
53 | }; | ||
54 | |||
55 | struct avtab { | ||
56 | struct avtab_node **htable; | ||
57 | u32 nel; /* number of elements */ | ||
58 | }; | ||
59 | |||
60 | int avtab_init(struct avtab *); | ||
61 | struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k, int specified); | ||
62 | void avtab_destroy(struct avtab *h); | ||
63 | void avtab_hash_eval(struct avtab *h, char *tag); | ||
64 | |||
65 | int avtab_read_item(void *fp, struct avtab_datum *avdatum, struct avtab_key *avkey); | ||
66 | int avtab_read(struct avtab *a, void *fp, u32 config); | ||
67 | |||
68 | struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, | ||
69 | struct avtab_datum *datum); | ||
70 | |||
71 | struct avtab_node *avtab_search_node(struct avtab *h, struct avtab_key *key, int specified); | ||
72 | |||
73 | struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified); | ||
74 | |||
75 | void avtab_cache_init(void); | ||
76 | void avtab_cache_destroy(void); | ||
77 | |||
78 | #define AVTAB_HASH_BITS 15 | ||
79 | #define AVTAB_HASH_BUCKETS (1 << AVTAB_HASH_BITS) | ||
80 | #define AVTAB_HASH_MASK (AVTAB_HASH_BUCKETS-1) | ||
81 | |||
82 | #define AVTAB_SIZE AVTAB_HASH_BUCKETS | ||
83 | |||
84 | #endif /* _SS_AVTAB_H_ */ | ||
85 | |||
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c new file mode 100644 index 000000000000..b53441184aca --- /dev/null +++ b/security/selinux/ss/conditional.c | |||
@@ -0,0 +1,489 @@ | |||
1 | /* Authors: Karl MacMillan <kmacmillan@tresys.com> | ||
2 | * Frank Mayer <mayerf@tresys.com> | ||
3 | * | ||
4 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation, version 2. | ||
8 | */ | ||
9 | |||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/errno.h> | ||
12 | #include <linux/string.h> | ||
13 | #include <linux/spinlock.h> | ||
14 | #include <asm/semaphore.h> | ||
15 | #include <linux/slab.h> | ||
16 | |||
17 | #include "security.h" | ||
18 | #include "conditional.h" | ||
19 | |||
20 | /* | ||
21 | * cond_evaluate_expr evaluates a conditional expr | ||
22 | * in reverse polish notation. It returns true (1), false (0), | ||
23 | * or undefined (-1). Undefined occurs when the expression | ||
24 | * exceeds the stack depth of COND_EXPR_MAXDEPTH. | ||
25 | */ | ||
26 | static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr) | ||
27 | { | ||
28 | |||
29 | struct cond_expr *cur; | ||
30 | int s[COND_EXPR_MAXDEPTH]; | ||
31 | int sp = -1; | ||
32 | |||
33 | for (cur = expr; cur != NULL; cur = cur->next) { | ||
34 | switch (cur->expr_type) { | ||
35 | case COND_BOOL: | ||
36 | if (sp == (COND_EXPR_MAXDEPTH - 1)) | ||
37 | return -1; | ||
38 | sp++; | ||
39 | s[sp] = p->bool_val_to_struct[cur->bool - 1]->state; | ||
40 | break; | ||
41 | case COND_NOT: | ||
42 | if (sp < 0) | ||
43 | return -1; | ||
44 | s[sp] = !s[sp]; | ||
45 | break; | ||
46 | case COND_OR: | ||
47 | if (sp < 1) | ||
48 | return -1; | ||
49 | sp--; | ||
50 | s[sp] |= s[sp + 1]; | ||
51 | break; | ||
52 | case COND_AND: | ||
53 | if (sp < 1) | ||
54 | return -1; | ||
55 | sp--; | ||
56 | s[sp] &= s[sp + 1]; | ||
57 | break; | ||
58 | case COND_XOR: | ||
59 | if (sp < 1) | ||
60 | return -1; | ||
61 | sp--; | ||
62 | s[sp] ^= s[sp + 1]; | ||
63 | break; | ||
64 | case COND_EQ: | ||
65 | if (sp < 1) | ||
66 | return -1; | ||
67 | sp--; | ||
68 | s[sp] = (s[sp] == s[sp + 1]); | ||
69 | break; | ||
70 | case COND_NEQ: | ||
71 | if (sp < 1) | ||
72 | return -1; | ||
73 | sp--; | ||
74 | s[sp] = (s[sp] != s[sp + 1]); | ||
75 | break; | ||
76 | default: | ||
77 | return -1; | ||
78 | } | ||
79 | } | ||
80 | return s[0]; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * evaluate_cond_node evaluates the conditional stored in | ||
85 | * a struct cond_node and if the result is different than the | ||
86 | * current state of the node it sets the rules in the true/false | ||
87 | * list appropriately. If the result of the expression is undefined | ||
88 | * all of the rules are disabled for safety. | ||
89 | */ | ||
90 | int evaluate_cond_node(struct policydb *p, struct cond_node *node) | ||
91 | { | ||
92 | int new_state; | ||
93 | struct cond_av_list* cur; | ||
94 | |||
95 | new_state = cond_evaluate_expr(p, node->expr); | ||
96 | if (new_state != node->cur_state) { | ||
97 | node->cur_state = new_state; | ||
98 | if (new_state == -1) | ||
99 | printk(KERN_ERR "security: expression result was undefined - disabling all rules.\n"); | ||
100 | /* turn the rules on or off */ | ||
101 | for (cur = node->true_list; cur != NULL; cur = cur->next) { | ||
102 | if (new_state <= 0) { | ||
103 | cur->node->datum.specified &= ~AVTAB_ENABLED; | ||
104 | } else { | ||
105 | cur->node->datum.specified |= AVTAB_ENABLED; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | for (cur = node->false_list; cur != NULL; cur = cur->next) { | ||
110 | /* -1 or 1 */ | ||
111 | if (new_state) { | ||
112 | cur->node->datum.specified &= ~AVTAB_ENABLED; | ||
113 | } else { | ||
114 | cur->node->datum.specified |= AVTAB_ENABLED; | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | int cond_policydb_init(struct policydb *p) | ||
122 | { | ||
123 | p->bool_val_to_struct = NULL; | ||
124 | p->cond_list = NULL; | ||
125 | if (avtab_init(&p->te_cond_avtab)) | ||
126 | return -1; | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static void cond_av_list_destroy(struct cond_av_list *list) | ||
132 | { | ||
133 | struct cond_av_list *cur, *next; | ||
134 | for (cur = list; cur != NULL; cur = next) { | ||
135 | next = cur->next; | ||
136 | /* the avtab_ptr_t node is destroy by the avtab */ | ||
137 | kfree(cur); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | static void cond_node_destroy(struct cond_node *node) | ||
142 | { | ||
143 | struct cond_expr *cur_expr, *next_expr; | ||
144 | |||
145 | for (cur_expr = node->expr; cur_expr != NULL; cur_expr = next_expr) { | ||
146 | next_expr = cur_expr->next; | ||
147 | kfree(cur_expr); | ||
148 | } | ||
149 | cond_av_list_destroy(node->true_list); | ||
150 | cond_av_list_destroy(node->false_list); | ||
151 | kfree(node); | ||
152 | } | ||
153 | |||
154 | static void cond_list_destroy(struct cond_node *list) | ||
155 | { | ||
156 | struct cond_node *next, *cur; | ||
157 | |||
158 | if (list == NULL) | ||
159 | return; | ||
160 | |||
161 | for (cur = list; cur != NULL; cur = next) { | ||
162 | next = cur->next; | ||
163 | cond_node_destroy(cur); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | void cond_policydb_destroy(struct policydb *p) | ||
168 | { | ||
169 | if (p->bool_val_to_struct != NULL) | ||
170 | kfree(p->bool_val_to_struct); | ||
171 | avtab_destroy(&p->te_cond_avtab); | ||
172 | cond_list_destroy(p->cond_list); | ||
173 | } | ||
174 | |||
175 | int cond_init_bool_indexes(struct policydb *p) | ||
176 | { | ||
177 | if (p->bool_val_to_struct) | ||
178 | kfree(p->bool_val_to_struct); | ||
179 | p->bool_val_to_struct = (struct cond_bool_datum**) | ||
180 | kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum*), GFP_KERNEL); | ||
181 | if (!p->bool_val_to_struct) | ||
182 | return -1; | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | int cond_destroy_bool(void *key, void *datum, void *p) | ||
187 | { | ||
188 | if (key) | ||
189 | kfree(key); | ||
190 | kfree(datum); | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | int cond_index_bool(void *key, void *datum, void *datap) | ||
195 | { | ||
196 | struct policydb *p; | ||
197 | struct cond_bool_datum *booldatum; | ||
198 | |||
199 | booldatum = datum; | ||
200 | p = datap; | ||
201 | |||
202 | if (!booldatum->value || booldatum->value > p->p_bools.nprim) | ||
203 | return -EINVAL; | ||
204 | |||
205 | p->p_bool_val_to_name[booldatum->value - 1] = key; | ||
206 | p->bool_val_to_struct[booldatum->value -1] = booldatum; | ||
207 | |||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | static int bool_isvalid(struct cond_bool_datum *b) | ||
212 | { | ||
213 | if (!(b->state == 0 || b->state == 1)) | ||
214 | return 0; | ||
215 | return 1; | ||
216 | } | ||
217 | |||
218 | int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp) | ||
219 | { | ||
220 | char *key = NULL; | ||
221 | struct cond_bool_datum *booldatum; | ||
222 | u32 buf[3], len; | ||
223 | int rc; | ||
224 | |||
225 | booldatum = kmalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); | ||
226 | if (!booldatum) | ||
227 | return -1; | ||
228 | memset(booldatum, 0, sizeof(struct cond_bool_datum)); | ||
229 | |||
230 | rc = next_entry(buf, fp, sizeof buf); | ||
231 | if (rc < 0) | ||
232 | goto err; | ||
233 | |||
234 | booldatum->value = le32_to_cpu(buf[0]); | ||
235 | booldatum->state = le32_to_cpu(buf[1]); | ||
236 | |||
237 | if (!bool_isvalid(booldatum)) | ||
238 | goto err; | ||
239 | |||
240 | len = le32_to_cpu(buf[2]); | ||
241 | |||
242 | key = kmalloc(len + 1, GFP_KERNEL); | ||
243 | if (!key) | ||
244 | goto err; | ||
245 | rc = next_entry(key, fp, len); | ||
246 | if (rc < 0) | ||
247 | goto err; | ||
248 | key[len] = 0; | ||
249 | if (hashtab_insert(h, key, booldatum)) | ||
250 | goto err; | ||
251 | |||
252 | return 0; | ||
253 | err: | ||
254 | cond_destroy_bool(key, booldatum, NULL); | ||
255 | return -1; | ||
256 | } | ||
257 | |||
258 | static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, | ||
259 | struct cond_av_list *other) | ||
260 | { | ||
261 | struct cond_av_list *list, *last = NULL, *cur; | ||
262 | struct avtab_key key; | ||
263 | struct avtab_datum datum; | ||
264 | struct avtab_node *node_ptr; | ||
265 | int rc; | ||
266 | u32 buf[1], i, len; | ||
267 | u8 found; | ||
268 | |||
269 | *ret_list = NULL; | ||
270 | |||
271 | len = 0; | ||
272 | rc = next_entry(buf, fp, sizeof buf); | ||
273 | if (rc < 0) | ||
274 | return -1; | ||
275 | |||
276 | len = le32_to_cpu(buf[0]); | ||
277 | if (len == 0) { | ||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | for (i = 0; i < len; i++) { | ||
282 | if (avtab_read_item(fp, &datum, &key)) | ||
283 | goto err; | ||
284 | |||
285 | /* | ||
286 | * For type rules we have to make certain there aren't any | ||
287 | * conflicting rules by searching the te_avtab and the | ||
288 | * cond_te_avtab. | ||
289 | */ | ||
290 | if (datum.specified & AVTAB_TYPE) { | ||
291 | if (avtab_search(&p->te_avtab, &key, AVTAB_TYPE)) { | ||
292 | printk("security: type rule already exists outside of a conditional."); | ||
293 | goto err; | ||
294 | } | ||
295 | /* | ||
296 | * If we are reading the false list other will be a pointer to | ||
297 | * the true list. We can have duplicate entries if there is only | ||
298 | * 1 other entry and it is in our true list. | ||
299 | * | ||
300 | * If we are reading the true list (other == NULL) there shouldn't | ||
301 | * be any other entries. | ||
302 | */ | ||
303 | if (other) { | ||
304 | node_ptr = avtab_search_node(&p->te_cond_avtab, &key, AVTAB_TYPE); | ||
305 | if (node_ptr) { | ||
306 | if (avtab_search_node_next(node_ptr, AVTAB_TYPE)) { | ||
307 | printk("security: too many conflicting type rules."); | ||
308 | goto err; | ||
309 | } | ||
310 | found = 0; | ||
311 | for (cur = other; cur != NULL; cur = cur->next) { | ||
312 | if (cur->node == node_ptr) { | ||
313 | found = 1; | ||
314 | break; | ||
315 | } | ||
316 | } | ||
317 | if (!found) { | ||
318 | printk("security: conflicting type rules."); | ||
319 | goto err; | ||
320 | } | ||
321 | } | ||
322 | } else { | ||
323 | if (avtab_search(&p->te_cond_avtab, &key, AVTAB_TYPE)) { | ||
324 | printk("security: conflicting type rules when adding type rule for true."); | ||
325 | goto err; | ||
326 | } | ||
327 | } | ||
328 | } | ||
329 | node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, &key, &datum); | ||
330 | if (!node_ptr) { | ||
331 | printk("security: could not insert rule."); | ||
332 | goto err; | ||
333 | } | ||
334 | |||
335 | list = kmalloc(sizeof(struct cond_av_list), GFP_KERNEL); | ||
336 | if (!list) | ||
337 | goto err; | ||
338 | memset(list, 0, sizeof(struct cond_av_list)); | ||
339 | |||
340 | list->node = node_ptr; | ||
341 | if (i == 0) | ||
342 | *ret_list = list; | ||
343 | else | ||
344 | last->next = list; | ||
345 | last = list; | ||
346 | |||
347 | } | ||
348 | |||
349 | return 0; | ||
350 | err: | ||
351 | cond_av_list_destroy(*ret_list); | ||
352 | *ret_list = NULL; | ||
353 | return -1; | ||
354 | } | ||
355 | |||
356 | static int expr_isvalid(struct policydb *p, struct cond_expr *expr) | ||
357 | { | ||
358 | if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) { | ||
359 | printk("security: conditional expressions uses unknown operator.\n"); | ||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | if (expr->bool > p->p_bools.nprim) { | ||
364 | printk("security: conditional expressions uses unknown bool.\n"); | ||
365 | return 0; | ||
366 | } | ||
367 | return 1; | ||
368 | } | ||
369 | |||
370 | static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) | ||
371 | { | ||
372 | u32 buf[2], len, i; | ||
373 | int rc; | ||
374 | struct cond_expr *expr = NULL, *last = NULL; | ||
375 | |||
376 | rc = next_entry(buf, fp, sizeof(u32)); | ||
377 | if (rc < 0) | ||
378 | return -1; | ||
379 | |||
380 | node->cur_state = le32_to_cpu(buf[0]); | ||
381 | |||
382 | len = 0; | ||
383 | rc = next_entry(buf, fp, sizeof(u32)); | ||
384 | if (rc < 0) | ||
385 | return -1; | ||
386 | |||
387 | /* expr */ | ||
388 | len = le32_to_cpu(buf[0]); | ||
389 | |||
390 | for (i = 0; i < len; i++ ) { | ||
391 | rc = next_entry(buf, fp, sizeof(u32) * 2); | ||
392 | if (rc < 0) | ||
393 | goto err; | ||
394 | |||
395 | expr = kmalloc(sizeof(struct cond_expr), GFP_KERNEL); | ||
396 | if (!expr) { | ||
397 | goto err; | ||
398 | } | ||
399 | memset(expr, 0, sizeof(struct cond_expr)); | ||
400 | |||
401 | expr->expr_type = le32_to_cpu(buf[0]); | ||
402 | expr->bool = le32_to_cpu(buf[1]); | ||
403 | |||
404 | if (!expr_isvalid(p, expr)) { | ||
405 | kfree(expr); | ||
406 | goto err; | ||
407 | } | ||
408 | |||
409 | if (i == 0) { | ||
410 | node->expr = expr; | ||
411 | } else { | ||
412 | last->next = expr; | ||
413 | } | ||
414 | last = expr; | ||
415 | } | ||
416 | |||
417 | if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0) | ||
418 | goto err; | ||
419 | if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0) | ||
420 | goto err; | ||
421 | return 0; | ||
422 | err: | ||
423 | cond_node_destroy(node); | ||
424 | return -1; | ||
425 | } | ||
426 | |||
427 | int cond_read_list(struct policydb *p, void *fp) | ||
428 | { | ||
429 | struct cond_node *node, *last = NULL; | ||
430 | u32 buf[1], i, len; | ||
431 | int rc; | ||
432 | |||
433 | rc = next_entry(buf, fp, sizeof buf); | ||
434 | if (rc < 0) | ||
435 | return -1; | ||
436 | |||
437 | len = le32_to_cpu(buf[0]); | ||
438 | |||
439 | for (i = 0; i < len; i++) { | ||
440 | node = kmalloc(sizeof(struct cond_node), GFP_KERNEL); | ||
441 | if (!node) | ||
442 | goto err; | ||
443 | memset(node, 0, sizeof(struct cond_node)); | ||
444 | |||
445 | if (cond_read_node(p, node, fp) != 0) | ||
446 | goto err; | ||
447 | |||
448 | if (i == 0) { | ||
449 | p->cond_list = node; | ||
450 | } else { | ||
451 | last->next = node; | ||
452 | } | ||
453 | last = node; | ||
454 | } | ||
455 | return 0; | ||
456 | err: | ||
457 | cond_list_destroy(p->cond_list); | ||
458 | return -1; | ||
459 | } | ||
460 | |||
461 | /* Determine whether additional permissions are granted by the conditional | ||
462 | * av table, and if so, add them to the result | ||
463 | */ | ||
464 | void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd) | ||
465 | { | ||
466 | struct avtab_node *node; | ||
467 | |||
468 | if(!ctab || !key || !avd) | ||
469 | return; | ||
470 | |||
471 | for(node = avtab_search_node(ctab, key, AVTAB_AV); node != NULL; | ||
472 | node = avtab_search_node_next(node, AVTAB_AV)) { | ||
473 | if ( (__u32) (AVTAB_ALLOWED|AVTAB_ENABLED) == | ||
474 | (node->datum.specified & (AVTAB_ALLOWED|AVTAB_ENABLED))) | ||
475 | avd->allowed |= avtab_allowed(&node->datum); | ||
476 | if ( (__u32) (AVTAB_AUDITDENY|AVTAB_ENABLED) == | ||
477 | (node->datum.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED))) | ||
478 | /* Since a '0' in an auditdeny mask represents a | ||
479 | * permission we do NOT want to audit (dontaudit), we use | ||
480 | * the '&' operand to ensure that all '0's in the mask | ||
481 | * are retained (much unlike the allow and auditallow cases). | ||
482 | */ | ||
483 | avd->auditdeny &= avtab_auditdeny(&node->datum); | ||
484 | if ( (__u32) (AVTAB_AUDITALLOW|AVTAB_ENABLED) == | ||
485 | (node->datum.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED))) | ||
486 | avd->auditallow |= avtab_auditallow(&node->datum); | ||
487 | } | ||
488 | return; | ||
489 | } | ||
diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h new file mode 100644 index 000000000000..f3a1fc6e5d66 --- /dev/null +++ b/security/selinux/ss/conditional.h | |||
@@ -0,0 +1,77 @@ | |||
1 | /* Authors: Karl MacMillan <kmacmillan@tresys.com> | ||
2 | * Frank Mayer <mayerf@tresys.com> | ||
3 | * | ||
4 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation, version 2. | ||
8 | */ | ||
9 | |||
10 | #ifndef _CONDITIONAL_H_ | ||
11 | #define _CONDITIONAL_H_ | ||
12 | |||
13 | #include "avtab.h" | ||
14 | #include "symtab.h" | ||
15 | #include "policydb.h" | ||
16 | |||
17 | #define COND_EXPR_MAXDEPTH 10 | ||
18 | |||
19 | /* | ||
20 | * A conditional expression is a list of operators and operands | ||
21 | * in reverse polish notation. | ||
22 | */ | ||
23 | struct cond_expr { | ||
24 | #define COND_BOOL 1 /* plain bool */ | ||
25 | #define COND_NOT 2 /* !bool */ | ||
26 | #define COND_OR 3 /* bool || bool */ | ||
27 | #define COND_AND 4 /* bool && bool */ | ||
28 | #define COND_XOR 5 /* bool ^ bool */ | ||
29 | #define COND_EQ 6 /* bool == bool */ | ||
30 | #define COND_NEQ 7 /* bool != bool */ | ||
31 | #define COND_LAST 8 | ||
32 | __u32 expr_type; | ||
33 | __u32 bool; | ||
34 | struct cond_expr *next; | ||
35 | }; | ||
36 | |||
37 | /* | ||
38 | * Each cond_node contains a list of rules to be enabled/disabled | ||
39 | * depending on the current value of the conditional expression. This | ||
40 | * struct is for that list. | ||
41 | */ | ||
42 | struct cond_av_list { | ||
43 | struct avtab_node *node; | ||
44 | struct cond_av_list *next; | ||
45 | }; | ||
46 | |||
47 | /* | ||
48 | * A cond node represents a conditional block in a policy. It | ||
49 | * contains a conditional expression, the current state of the expression, | ||
50 | * two lists of rules to enable/disable depending on the value of the | ||
51 | * expression (the true list corresponds to if and the false list corresponds | ||
52 | * to else).. | ||
53 | */ | ||
54 | struct cond_node { | ||
55 | int cur_state; | ||
56 | struct cond_expr *expr; | ||
57 | struct cond_av_list *true_list; | ||
58 | struct cond_av_list *false_list; | ||
59 | struct cond_node *next; | ||
60 | }; | ||
61 | |||
62 | int cond_policydb_init(struct policydb* p); | ||
63 | void cond_policydb_destroy(struct policydb* p); | ||
64 | |||
65 | int cond_init_bool_indexes(struct policydb* p); | ||
66 | int cond_destroy_bool(void *key, void *datum, void *p); | ||
67 | |||
68 | int cond_index_bool(void *key, void *datum, void *datap); | ||
69 | |||
70 | int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp); | ||
71 | int cond_read_list(struct policydb *p, void *fp); | ||
72 | |||
73 | void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd); | ||
74 | |||
75 | int evaluate_cond_node(struct policydb *p, struct cond_node *node); | ||
76 | |||
77 | #endif /* _CONDITIONAL_H_ */ | ||
diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h new file mode 100644 index 000000000000..149dda731fd3 --- /dev/null +++ b/security/selinux/ss/constraint.h | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * A constraint is a condition that must be satisfied in | ||
3 | * order for one or more permissions to be granted. | ||
4 | * Constraints are used to impose additional restrictions | ||
5 | * beyond the type-based rules in `te' or the role-based | ||
6 | * transition rules in `rbac'. Constraints are typically | ||
7 | * used to prevent a process from transitioning to a new user | ||
8 | * identity or role unless it is in a privileged type. | ||
9 | * Constraints are likewise typically used to prevent a | ||
10 | * process from labeling an object with a different user | ||
11 | * identity. | ||
12 | * | ||
13 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
14 | */ | ||
15 | #ifndef _SS_CONSTRAINT_H_ | ||
16 | #define _SS_CONSTRAINT_H_ | ||
17 | |||
18 | #include "ebitmap.h" | ||
19 | |||
20 | #define CEXPR_MAXDEPTH 5 | ||
21 | |||
22 | struct constraint_expr { | ||
23 | #define CEXPR_NOT 1 /* not expr */ | ||
24 | #define CEXPR_AND 2 /* expr and expr */ | ||
25 | #define CEXPR_OR 3 /* expr or expr */ | ||
26 | #define CEXPR_ATTR 4 /* attr op attr */ | ||
27 | #define CEXPR_NAMES 5 /* attr op names */ | ||
28 | u32 expr_type; /* expression type */ | ||
29 | |||
30 | #define CEXPR_USER 1 /* user */ | ||
31 | #define CEXPR_ROLE 2 /* role */ | ||
32 | #define CEXPR_TYPE 4 /* type */ | ||
33 | #define CEXPR_TARGET 8 /* target if set, source otherwise */ | ||
34 | #define CEXPR_XTARGET 16 /* special 3rd target for validatetrans rule */ | ||
35 | #define CEXPR_L1L2 32 /* low level 1 vs. low level 2 */ | ||
36 | #define CEXPR_L1H2 64 /* low level 1 vs. high level 2 */ | ||
37 | #define CEXPR_H1L2 128 /* high level 1 vs. low level 2 */ | ||
38 | #define CEXPR_H1H2 256 /* high level 1 vs. high level 2 */ | ||
39 | #define CEXPR_L1H1 512 /* low level 1 vs. high level 1 */ | ||
40 | #define CEXPR_L2H2 1024 /* low level 2 vs. high level 2 */ | ||
41 | u32 attr; /* attribute */ | ||
42 | |||
43 | #define CEXPR_EQ 1 /* == or eq */ | ||
44 | #define CEXPR_NEQ 2 /* != */ | ||
45 | #define CEXPR_DOM 3 /* dom */ | ||
46 | #define CEXPR_DOMBY 4 /* domby */ | ||
47 | #define CEXPR_INCOMP 5 /* incomp */ | ||
48 | u32 op; /* operator */ | ||
49 | |||
50 | struct ebitmap names; /* names */ | ||
51 | |||
52 | struct constraint_expr *next; /* next expression */ | ||
53 | }; | ||
54 | |||
55 | struct constraint_node { | ||
56 | u32 permissions; /* constrained permissions */ | ||
57 | struct constraint_expr *expr; /* constraint on permissions */ | ||
58 | struct constraint_node *next; /* next constraint */ | ||
59 | }; | ||
60 | |||
61 | #endif /* _SS_CONSTRAINT_H_ */ | ||
diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h new file mode 100644 index 000000000000..0562bacb7b99 --- /dev/null +++ b/security/selinux/ss/context.h | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * A security context is a set of security attributes | ||
3 | * associated with each subject and object controlled | ||
4 | * by the security policy. Security contexts are | ||
5 | * externally represented as variable-length strings | ||
6 | * that can be interpreted by a user or application | ||
7 | * with an understanding of the security policy. | ||
8 | * Internally, the security server uses a simple | ||
9 | * structure. This structure is private to the | ||
10 | * security server and can be changed without affecting | ||
11 | * clients of the security server. | ||
12 | * | ||
13 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
14 | */ | ||
15 | #ifndef _SS_CONTEXT_H_ | ||
16 | #define _SS_CONTEXT_H_ | ||
17 | |||
18 | #include "ebitmap.h" | ||
19 | #include "mls_types.h" | ||
20 | #include "security.h" | ||
21 | |||
22 | /* | ||
23 | * A security context consists of an authenticated user | ||
24 | * identity, a role, a type and a MLS range. | ||
25 | */ | ||
26 | struct context { | ||
27 | u32 user; | ||
28 | u32 role; | ||
29 | u32 type; | ||
30 | struct mls_range range; | ||
31 | }; | ||
32 | |||
33 | static inline void mls_context_init(struct context *c) | ||
34 | { | ||
35 | memset(&c->range, 0, sizeof(c->range)); | ||
36 | } | ||
37 | |||
38 | static inline int mls_context_cpy(struct context *dst, struct context *src) | ||
39 | { | ||
40 | int rc; | ||
41 | |||
42 | if (!selinux_mls_enabled) | ||
43 | return 0; | ||
44 | |||
45 | dst->range.level[0].sens = src->range.level[0].sens; | ||
46 | rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat); | ||
47 | if (rc) | ||
48 | goto out; | ||
49 | |||
50 | dst->range.level[1].sens = src->range.level[1].sens; | ||
51 | rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[1].cat); | ||
52 | if (rc) | ||
53 | ebitmap_destroy(&dst->range.level[0].cat); | ||
54 | out: | ||
55 | return rc; | ||
56 | } | ||
57 | |||
58 | static inline int mls_context_cmp(struct context *c1, struct context *c2) | ||
59 | { | ||
60 | if (!selinux_mls_enabled) | ||
61 | return 1; | ||
62 | |||
63 | return ((c1->range.level[0].sens == c2->range.level[0].sens) && | ||
64 | ebitmap_cmp(&c1->range.level[0].cat,&c2->range.level[0].cat) && | ||
65 | (c1->range.level[1].sens == c2->range.level[1].sens) && | ||
66 | ebitmap_cmp(&c1->range.level[1].cat,&c2->range.level[1].cat)); | ||
67 | } | ||
68 | |||
69 | static inline void mls_context_destroy(struct context *c) | ||
70 | { | ||
71 | if (!selinux_mls_enabled) | ||
72 | return; | ||
73 | |||
74 | ebitmap_destroy(&c->range.level[0].cat); | ||
75 | ebitmap_destroy(&c->range.level[1].cat); | ||
76 | mls_context_init(c); | ||
77 | } | ||
78 | |||
79 | static inline void context_init(struct context *c) | ||
80 | { | ||
81 | memset(c, 0, sizeof(*c)); | ||
82 | } | ||
83 | |||
84 | static inline int context_cpy(struct context *dst, struct context *src) | ||
85 | { | ||
86 | dst->user = src->user; | ||
87 | dst->role = src->role; | ||
88 | dst->type = src->type; | ||
89 | return mls_context_cpy(dst, src); | ||
90 | } | ||
91 | |||
92 | static inline void context_destroy(struct context *c) | ||
93 | { | ||
94 | c->user = c->role = c->type = 0; | ||
95 | mls_context_destroy(c); | ||
96 | } | ||
97 | |||
98 | static inline int context_cmp(struct context *c1, struct context *c2) | ||
99 | { | ||
100 | return ((c1->user == c2->user) && | ||
101 | (c1->role == c2->role) && | ||
102 | (c1->type == c2->type) && | ||
103 | mls_context_cmp(c1, c2)); | ||
104 | } | ||
105 | |||
106 | #endif /* _SS_CONTEXT_H_ */ | ||
107 | |||
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c new file mode 100644 index 000000000000..d8ce9cc0b9f1 --- /dev/null +++ b/security/selinux/ss/ebitmap.c | |||
@@ -0,0 +1,293 @@ | |||
1 | /* | ||
2 | * Implementation of the extensible bitmap type. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/slab.h> | ||
8 | #include <linux/errno.h> | ||
9 | #include "ebitmap.h" | ||
10 | #include "policydb.h" | ||
11 | |||
12 | int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2) | ||
13 | { | ||
14 | struct ebitmap_node *n1, *n2; | ||
15 | |||
16 | if (e1->highbit != e2->highbit) | ||
17 | return 0; | ||
18 | |||
19 | n1 = e1->node; | ||
20 | n2 = e2->node; | ||
21 | while (n1 && n2 && | ||
22 | (n1->startbit == n2->startbit) && | ||
23 | (n1->map == n2->map)) { | ||
24 | n1 = n1->next; | ||
25 | n2 = n2->next; | ||
26 | } | ||
27 | |||
28 | if (n1 || n2) | ||
29 | return 0; | ||
30 | |||
31 | return 1; | ||
32 | } | ||
33 | |||
34 | int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) | ||
35 | { | ||
36 | struct ebitmap_node *n, *new, *prev; | ||
37 | |||
38 | ebitmap_init(dst); | ||
39 | n = src->node; | ||
40 | prev = NULL; | ||
41 | while (n) { | ||
42 | new = kmalloc(sizeof(*new), GFP_ATOMIC); | ||
43 | if (!new) { | ||
44 | ebitmap_destroy(dst); | ||
45 | return -ENOMEM; | ||
46 | } | ||
47 | memset(new, 0, sizeof(*new)); | ||
48 | new->startbit = n->startbit; | ||
49 | new->map = n->map; | ||
50 | new->next = NULL; | ||
51 | if (prev) | ||
52 | prev->next = new; | ||
53 | else | ||
54 | dst->node = new; | ||
55 | prev = new; | ||
56 | n = n->next; | ||
57 | } | ||
58 | |||
59 | dst->highbit = src->highbit; | ||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2) | ||
64 | { | ||
65 | struct ebitmap_node *n1, *n2; | ||
66 | |||
67 | if (e1->highbit < e2->highbit) | ||
68 | return 0; | ||
69 | |||
70 | n1 = e1->node; | ||
71 | n2 = e2->node; | ||
72 | while (n1 && n2 && (n1->startbit <= n2->startbit)) { | ||
73 | if (n1->startbit < n2->startbit) { | ||
74 | n1 = n1->next; | ||
75 | continue; | ||
76 | } | ||
77 | if ((n1->map & n2->map) != n2->map) | ||
78 | return 0; | ||
79 | |||
80 | n1 = n1->next; | ||
81 | n2 = n2->next; | ||
82 | } | ||
83 | |||
84 | if (n2) | ||
85 | return 0; | ||
86 | |||
87 | return 1; | ||
88 | } | ||
89 | |||
90 | int ebitmap_get_bit(struct ebitmap *e, unsigned long bit) | ||
91 | { | ||
92 | struct ebitmap_node *n; | ||
93 | |||
94 | if (e->highbit < bit) | ||
95 | return 0; | ||
96 | |||
97 | n = e->node; | ||
98 | while (n && (n->startbit <= bit)) { | ||
99 | if ((n->startbit + MAPSIZE) > bit) { | ||
100 | if (n->map & (MAPBIT << (bit - n->startbit))) | ||
101 | return 1; | ||
102 | else | ||
103 | return 0; | ||
104 | } | ||
105 | n = n->next; | ||
106 | } | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value) | ||
112 | { | ||
113 | struct ebitmap_node *n, *prev, *new; | ||
114 | |||
115 | prev = NULL; | ||
116 | n = e->node; | ||
117 | while (n && n->startbit <= bit) { | ||
118 | if ((n->startbit + MAPSIZE) > bit) { | ||
119 | if (value) { | ||
120 | n->map |= (MAPBIT << (bit - n->startbit)); | ||
121 | } else { | ||
122 | n->map &= ~(MAPBIT << (bit - n->startbit)); | ||
123 | if (!n->map) { | ||
124 | /* drop this node from the bitmap */ | ||
125 | |||
126 | if (!n->next) { | ||
127 | /* | ||
128 | * this was the highest map | ||
129 | * within the bitmap | ||
130 | */ | ||
131 | if (prev) | ||
132 | e->highbit = prev->startbit + MAPSIZE; | ||
133 | else | ||
134 | e->highbit = 0; | ||
135 | } | ||
136 | if (prev) | ||
137 | prev->next = n->next; | ||
138 | else | ||
139 | e->node = n->next; | ||
140 | |||
141 | kfree(n); | ||
142 | } | ||
143 | } | ||
144 | return 0; | ||
145 | } | ||
146 | prev = n; | ||
147 | n = n->next; | ||
148 | } | ||
149 | |||
150 | if (!value) | ||
151 | return 0; | ||
152 | |||
153 | new = kmalloc(sizeof(*new), GFP_ATOMIC); | ||
154 | if (!new) | ||
155 | return -ENOMEM; | ||
156 | memset(new, 0, sizeof(*new)); | ||
157 | |||
158 | new->startbit = bit & ~(MAPSIZE - 1); | ||
159 | new->map = (MAPBIT << (bit - new->startbit)); | ||
160 | |||
161 | if (!n) | ||
162 | /* this node will be the highest map within the bitmap */ | ||
163 | e->highbit = new->startbit + MAPSIZE; | ||
164 | |||
165 | if (prev) { | ||
166 | new->next = prev->next; | ||
167 | prev->next = new; | ||
168 | } else { | ||
169 | new->next = e->node; | ||
170 | e->node = new; | ||
171 | } | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | void ebitmap_destroy(struct ebitmap *e) | ||
177 | { | ||
178 | struct ebitmap_node *n, *temp; | ||
179 | |||
180 | if (!e) | ||
181 | return; | ||
182 | |||
183 | n = e->node; | ||
184 | while (n) { | ||
185 | temp = n; | ||
186 | n = n->next; | ||
187 | kfree(temp); | ||
188 | } | ||
189 | |||
190 | e->highbit = 0; | ||
191 | e->node = NULL; | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | int ebitmap_read(struct ebitmap *e, void *fp) | ||
196 | { | ||
197 | int rc; | ||
198 | struct ebitmap_node *n, *l; | ||
199 | u32 buf[3], mapsize, count, i; | ||
200 | u64 map; | ||
201 | |||
202 | ebitmap_init(e); | ||
203 | |||
204 | rc = next_entry(buf, fp, sizeof buf); | ||
205 | if (rc < 0) | ||
206 | goto out; | ||
207 | |||
208 | mapsize = le32_to_cpu(buf[0]); | ||
209 | e->highbit = le32_to_cpu(buf[1]); | ||
210 | count = le32_to_cpu(buf[2]); | ||
211 | |||
212 | if (mapsize != MAPSIZE) { | ||
213 | printk(KERN_ERR "security: ebitmap: map size %u does not " | ||
214 | "match my size %Zd (high bit was %d)\n", mapsize, | ||
215 | MAPSIZE, e->highbit); | ||
216 | goto bad; | ||
217 | } | ||
218 | if (!e->highbit) { | ||
219 | e->node = NULL; | ||
220 | goto ok; | ||
221 | } | ||
222 | if (e->highbit & (MAPSIZE - 1)) { | ||
223 | printk(KERN_ERR "security: ebitmap: high bit (%d) is not a " | ||
224 | "multiple of the map size (%Zd)\n", e->highbit, MAPSIZE); | ||
225 | goto bad; | ||
226 | } | ||
227 | l = NULL; | ||
228 | for (i = 0; i < count; i++) { | ||
229 | rc = next_entry(buf, fp, sizeof(u32)); | ||
230 | if (rc < 0) { | ||
231 | printk(KERN_ERR "security: ebitmap: truncated map\n"); | ||
232 | goto bad; | ||
233 | } | ||
234 | n = kmalloc(sizeof(*n), GFP_KERNEL); | ||
235 | if (!n) { | ||
236 | printk(KERN_ERR "security: ebitmap: out of memory\n"); | ||
237 | rc = -ENOMEM; | ||
238 | goto bad; | ||
239 | } | ||
240 | memset(n, 0, sizeof(*n)); | ||
241 | |||
242 | n->startbit = le32_to_cpu(buf[0]); | ||
243 | |||
244 | if (n->startbit & (MAPSIZE - 1)) { | ||
245 | printk(KERN_ERR "security: ebitmap start bit (%d) is " | ||
246 | "not a multiple of the map size (%Zd)\n", | ||
247 | n->startbit, MAPSIZE); | ||
248 | goto bad_free; | ||
249 | } | ||
250 | if (n->startbit > (e->highbit - MAPSIZE)) { | ||
251 | printk(KERN_ERR "security: ebitmap start bit (%d) is " | ||
252 | "beyond the end of the bitmap (%Zd)\n", | ||
253 | n->startbit, (e->highbit - MAPSIZE)); | ||
254 | goto bad_free; | ||
255 | } | ||
256 | rc = next_entry(&map, fp, sizeof(u64)); | ||
257 | if (rc < 0) { | ||
258 | printk(KERN_ERR "security: ebitmap: truncated map\n"); | ||
259 | goto bad_free; | ||
260 | } | ||
261 | n->map = le64_to_cpu(map); | ||
262 | |||
263 | if (!n->map) { | ||
264 | printk(KERN_ERR "security: ebitmap: null map in " | ||
265 | "ebitmap (startbit %d)\n", n->startbit); | ||
266 | goto bad_free; | ||
267 | } | ||
268 | if (l) { | ||
269 | if (n->startbit <= l->startbit) { | ||
270 | printk(KERN_ERR "security: ebitmap: start " | ||
271 | "bit %d comes after start bit %d\n", | ||
272 | n->startbit, l->startbit); | ||
273 | goto bad_free; | ||
274 | } | ||
275 | l->next = n; | ||
276 | } else | ||
277 | e->node = n; | ||
278 | |||
279 | l = n; | ||
280 | } | ||
281 | |||
282 | ok: | ||
283 | rc = 0; | ||
284 | out: | ||
285 | return rc; | ||
286 | bad_free: | ||
287 | kfree(n); | ||
288 | bad: | ||
289 | if (!rc) | ||
290 | rc = -EINVAL; | ||
291 | ebitmap_destroy(e); | ||
292 | goto out; | ||
293 | } | ||
diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h new file mode 100644 index 000000000000..471370233fd9 --- /dev/null +++ b/security/selinux/ss/ebitmap.h | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * An extensible bitmap is a bitmap that supports an | ||
3 | * arbitrary number of bits. Extensible bitmaps are | ||
4 | * used to represent sets of values, such as types, | ||
5 | * roles, categories, and classes. | ||
6 | * | ||
7 | * Each extensible bitmap is implemented as a linked | ||
8 | * list of bitmap nodes, where each bitmap node has | ||
9 | * an explicitly specified starting bit position within | ||
10 | * the total bitmap. | ||
11 | * | ||
12 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
13 | */ | ||
14 | #ifndef _SS_EBITMAP_H_ | ||
15 | #define _SS_EBITMAP_H_ | ||
16 | |||
17 | #define MAPTYPE u64 /* portion of bitmap in each node */ | ||
18 | #define MAPSIZE (sizeof(MAPTYPE) * 8) /* number of bits in node bitmap */ | ||
19 | #define MAPBIT 1ULL /* a bit in the node bitmap */ | ||
20 | |||
21 | struct ebitmap_node { | ||
22 | u32 startbit; /* starting position in the total bitmap */ | ||
23 | MAPTYPE map; /* this node's portion of the bitmap */ | ||
24 | struct ebitmap_node *next; | ||
25 | }; | ||
26 | |||
27 | struct ebitmap { | ||
28 | struct ebitmap_node *node; /* first node in the bitmap */ | ||
29 | u32 highbit; /* highest position in the total bitmap */ | ||
30 | }; | ||
31 | |||
32 | #define ebitmap_length(e) ((e)->highbit) | ||
33 | #define ebitmap_startbit(e) ((e)->node ? (e)->node->startbit : 0) | ||
34 | |||
35 | static inline void ebitmap_init(struct ebitmap *e) | ||
36 | { | ||
37 | memset(e, 0, sizeof(*e)); | ||
38 | } | ||
39 | |||
40 | int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2); | ||
41 | int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src); | ||
42 | int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2); | ||
43 | int ebitmap_get_bit(struct ebitmap *e, unsigned long bit); | ||
44 | int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value); | ||
45 | void ebitmap_destroy(struct ebitmap *e); | ||
46 | int ebitmap_read(struct ebitmap *e, void *fp); | ||
47 | |||
48 | #endif /* _SS_EBITMAP_H_ */ | ||
diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c new file mode 100644 index 000000000000..26661fcc00ce --- /dev/null +++ b/security/selinux/ss/hashtab.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /* | ||
2 | * Implementation of the hash table type. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/slab.h> | ||
8 | #include <linux/errno.h> | ||
9 | #include "hashtab.h" | ||
10 | |||
11 | struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), | ||
12 | int (*keycmp)(struct hashtab *h, void *key1, void *key2), | ||
13 | u32 size) | ||
14 | { | ||
15 | struct hashtab *p; | ||
16 | u32 i; | ||
17 | |||
18 | p = kmalloc(sizeof(*p), GFP_KERNEL); | ||
19 | if (p == NULL) | ||
20 | return p; | ||
21 | |||
22 | memset(p, 0, sizeof(*p)); | ||
23 | p->size = size; | ||
24 | p->nel = 0; | ||
25 | p->hash_value = hash_value; | ||
26 | p->keycmp = keycmp; | ||
27 | p->htable = kmalloc(sizeof(*(p->htable)) * size, GFP_KERNEL); | ||
28 | if (p->htable == NULL) { | ||
29 | kfree(p); | ||
30 | return NULL; | ||
31 | } | ||
32 | |||
33 | for (i = 0; i < size; i++) | ||
34 | p->htable[i] = NULL; | ||
35 | |||
36 | return p; | ||
37 | } | ||
38 | |||
39 | int hashtab_insert(struct hashtab *h, void *key, void *datum) | ||
40 | { | ||
41 | u32 hvalue; | ||
42 | struct hashtab_node *prev, *cur, *newnode; | ||
43 | |||
44 | if (!h || h->nel == HASHTAB_MAX_NODES) | ||
45 | return -EINVAL; | ||
46 | |||
47 | hvalue = h->hash_value(h, key); | ||
48 | prev = NULL; | ||
49 | cur = h->htable[hvalue]; | ||
50 | while (cur && h->keycmp(h, key, cur->key) > 0) { | ||
51 | prev = cur; | ||
52 | cur = cur->next; | ||
53 | } | ||
54 | |||
55 | if (cur && (h->keycmp(h, key, cur->key) == 0)) | ||
56 | return -EEXIST; | ||
57 | |||
58 | newnode = kmalloc(sizeof(*newnode), GFP_KERNEL); | ||
59 | if (newnode == NULL) | ||
60 | return -ENOMEM; | ||
61 | memset(newnode, 0, sizeof(*newnode)); | ||
62 | newnode->key = key; | ||
63 | newnode->datum = datum; | ||
64 | if (prev) { | ||
65 | newnode->next = prev->next; | ||
66 | prev->next = newnode; | ||
67 | } else { | ||
68 | newnode->next = h->htable[hvalue]; | ||
69 | h->htable[hvalue] = newnode; | ||
70 | } | ||
71 | |||
72 | h->nel++; | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | void *hashtab_search(struct hashtab *h, void *key) | ||
77 | { | ||
78 | u32 hvalue; | ||
79 | struct hashtab_node *cur; | ||
80 | |||
81 | if (!h) | ||
82 | return NULL; | ||
83 | |||
84 | hvalue = h->hash_value(h, key); | ||
85 | cur = h->htable[hvalue]; | ||
86 | while (cur != NULL && h->keycmp(h, key, cur->key) > 0) | ||
87 | cur = cur->next; | ||
88 | |||
89 | if (cur == NULL || (h->keycmp(h, key, cur->key) != 0)) | ||
90 | return NULL; | ||
91 | |||
92 | return cur->datum; | ||
93 | } | ||
94 | |||
95 | void hashtab_destroy(struct hashtab *h) | ||
96 | { | ||
97 | u32 i; | ||
98 | struct hashtab_node *cur, *temp; | ||
99 | |||
100 | if (!h) | ||
101 | return; | ||
102 | |||
103 | for (i = 0; i < h->size; i++) { | ||
104 | cur = h->htable[i]; | ||
105 | while (cur != NULL) { | ||
106 | temp = cur; | ||
107 | cur = cur->next; | ||
108 | kfree(temp); | ||
109 | } | ||
110 | h->htable[i] = NULL; | ||
111 | } | ||
112 | |||
113 | kfree(h->htable); | ||
114 | h->htable = NULL; | ||
115 | |||
116 | kfree(h); | ||
117 | } | ||
118 | |||
119 | int hashtab_map(struct hashtab *h, | ||
120 | int (*apply)(void *k, void *d, void *args), | ||
121 | void *args) | ||
122 | { | ||
123 | u32 i; | ||
124 | int ret; | ||
125 | struct hashtab_node *cur; | ||
126 | |||
127 | if (!h) | ||
128 | return 0; | ||
129 | |||
130 | for (i = 0; i < h->size; i++) { | ||
131 | cur = h->htable[i]; | ||
132 | while (cur != NULL) { | ||
133 | ret = apply(cur->key, cur->datum, args); | ||
134 | if (ret) | ||
135 | return ret; | ||
136 | cur = cur->next; | ||
137 | } | ||
138 | } | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | |||
143 | void hashtab_stat(struct hashtab *h, struct hashtab_info *info) | ||
144 | { | ||
145 | u32 i, chain_len, slots_used, max_chain_len; | ||
146 | struct hashtab_node *cur; | ||
147 | |||
148 | slots_used = 0; | ||
149 | max_chain_len = 0; | ||
150 | for (slots_used = max_chain_len = i = 0; i < h->size; i++) { | ||
151 | cur = h->htable[i]; | ||
152 | if (cur) { | ||
153 | slots_used++; | ||
154 | chain_len = 0; | ||
155 | while (cur) { | ||
156 | chain_len++; | ||
157 | cur = cur->next; | ||
158 | } | ||
159 | |||
160 | if (chain_len > max_chain_len) | ||
161 | max_chain_len = chain_len; | ||
162 | } | ||
163 | } | ||
164 | |||
165 | info->slots_used = slots_used; | ||
166 | info->max_chain_len = max_chain_len; | ||
167 | } | ||
diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h new file mode 100644 index 000000000000..4cc85816a718 --- /dev/null +++ b/security/selinux/ss/hashtab.h | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * A hash table (hashtab) maintains associations between | ||
3 | * key values and datum values. The type of the key values | ||
4 | * and the type of the datum values is arbitrary. The | ||
5 | * functions for hash computation and key comparison are | ||
6 | * provided by the creator of the table. | ||
7 | * | ||
8 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
9 | */ | ||
10 | #ifndef _SS_HASHTAB_H_ | ||
11 | #define _SS_HASHTAB_H_ | ||
12 | |||
13 | #define HASHTAB_MAX_NODES 0xffffffff | ||
14 | |||
15 | struct hashtab_node { | ||
16 | void *key; | ||
17 | void *datum; | ||
18 | struct hashtab_node *next; | ||
19 | }; | ||
20 | |||
21 | struct hashtab { | ||
22 | struct hashtab_node **htable; /* hash table */ | ||
23 | u32 size; /* number of slots in hash table */ | ||
24 | u32 nel; /* number of elements in hash table */ | ||
25 | u32 (*hash_value)(struct hashtab *h, void *key); | ||
26 | /* hash function */ | ||
27 | int (*keycmp)(struct hashtab *h, void *key1, void *key2); | ||
28 | /* key comparison function */ | ||
29 | }; | ||
30 | |||
31 | struct hashtab_info { | ||
32 | u32 slots_used; | ||
33 | u32 max_chain_len; | ||
34 | }; | ||
35 | |||
36 | /* | ||
37 | * Creates a new hash table with the specified characteristics. | ||
38 | * | ||
39 | * Returns NULL if insufficent space is available or | ||
40 | * the new hash table otherwise. | ||
41 | */ | ||
42 | struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, void *key), | ||
43 | int (*keycmp)(struct hashtab *h, void *key1, void *key2), | ||
44 | u32 size); | ||
45 | |||
46 | /* | ||
47 | * Inserts the specified (key, datum) pair into the specified hash table. | ||
48 | * | ||
49 | * Returns -ENOMEM on memory allocation error, | ||
50 | * -EEXIST if there is already an entry with the same key, | ||
51 | * -EINVAL for general errors or | ||
52 | * 0 otherwise. | ||
53 | */ | ||
54 | int hashtab_insert(struct hashtab *h, void *k, void *d); | ||
55 | |||
56 | /* | ||
57 | * Searches for the entry with the specified key in the hash table. | ||
58 | * | ||
59 | * Returns NULL if no entry has the specified key or | ||
60 | * the datum of the entry otherwise. | ||
61 | */ | ||
62 | void *hashtab_search(struct hashtab *h, void *k); | ||
63 | |||
64 | /* | ||
65 | * Destroys the specified hash table. | ||
66 | */ | ||
67 | void hashtab_destroy(struct hashtab *h); | ||
68 | |||
69 | /* | ||
70 | * Applies the specified apply function to (key,datum,args) | ||
71 | * for each entry in the specified hash table. | ||
72 | * | ||
73 | * The order in which the function is applied to the entries | ||
74 | * is dependent upon the internal structure of the hash table. | ||
75 | * | ||
76 | * If apply returns a non-zero status, then hashtab_map will cease | ||
77 | * iterating through the hash table and will propagate the error | ||
78 | * return to its caller. | ||
79 | */ | ||
80 | int hashtab_map(struct hashtab *h, | ||
81 | int (*apply)(void *k, void *d, void *args), | ||
82 | void *args); | ||
83 | |||
84 | /* Fill info with some hash table statistics */ | ||
85 | void hashtab_stat(struct hashtab *h, struct hashtab_info *info); | ||
86 | |||
87 | #endif /* _SS_HASHTAB_H */ | ||
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c new file mode 100644 index 000000000000..756036bcc243 --- /dev/null +++ b/security/selinux/ss/mls.c | |||
@@ -0,0 +1,527 @@ | |||
1 | /* | ||
2 | * Implementation of the multi-level security (MLS) policy. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | /* | ||
7 | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> | ||
8 | * | ||
9 | * Support for enhanced MLS infrastructure. | ||
10 | * | ||
11 | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/string.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include "mls.h" | ||
19 | #include "policydb.h" | ||
20 | #include "services.h" | ||
21 | |||
22 | /* | ||
23 | * Return the length in bytes for the MLS fields of the | ||
24 | * security context string representation of `context'. | ||
25 | */ | ||
26 | int mls_compute_context_len(struct context * context) | ||
27 | { | ||
28 | int i, l, len, range; | ||
29 | |||
30 | if (!selinux_mls_enabled) | ||
31 | return 0; | ||
32 | |||
33 | len = 1; /* for the beginning ":" */ | ||
34 | for (l = 0; l < 2; l++) { | ||
35 | range = 0; | ||
36 | len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); | ||
37 | |||
38 | for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) { | ||
39 | if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) { | ||
40 | if (range) { | ||
41 | range++; | ||
42 | continue; | ||
43 | } | ||
44 | |||
45 | len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1; | ||
46 | range++; | ||
47 | } else { | ||
48 | if (range > 1) | ||
49 | len += strlen(policydb.p_cat_val_to_name[i - 2]) + 1; | ||
50 | range = 0; | ||
51 | } | ||
52 | } | ||
53 | /* Handle case where last category is the end of range */ | ||
54 | if (range > 1) | ||
55 | len += strlen(policydb.p_cat_val_to_name[i - 2]) + 1; | ||
56 | |||
57 | if (l == 0) { | ||
58 | if (mls_level_eq(&context->range.level[0], | ||
59 | &context->range.level[1])) | ||
60 | break; | ||
61 | else | ||
62 | len++; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | return len; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * Write the security context string representation of | ||
71 | * the MLS fields of `context' into the string `*scontext'. | ||
72 | * Update `*scontext' to point to the end of the MLS fields. | ||
73 | */ | ||
74 | void mls_sid_to_context(struct context *context, | ||
75 | char **scontext) | ||
76 | { | ||
77 | char *scontextp; | ||
78 | int i, l, range, wrote_sep; | ||
79 | |||
80 | if (!selinux_mls_enabled) | ||
81 | return; | ||
82 | |||
83 | scontextp = *scontext; | ||
84 | |||
85 | *scontextp = ':'; | ||
86 | scontextp++; | ||
87 | |||
88 | for (l = 0; l < 2; l++) { | ||
89 | range = 0; | ||
90 | wrote_sep = 0; | ||
91 | strcpy(scontextp, | ||
92 | policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); | ||
93 | scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]); | ||
94 | |||
95 | /* categories */ | ||
96 | for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) { | ||
97 | if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) { | ||
98 | if (range) { | ||
99 | range++; | ||
100 | continue; | ||
101 | } | ||
102 | |||
103 | if (!wrote_sep) { | ||
104 | *scontextp++ = ':'; | ||
105 | wrote_sep = 1; | ||
106 | } else | ||
107 | *scontextp++ = ','; | ||
108 | strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]); | ||
109 | scontextp += strlen(policydb.p_cat_val_to_name[i - 1]); | ||
110 | range++; | ||
111 | } else { | ||
112 | if (range > 1) { | ||
113 | if (range > 2) | ||
114 | *scontextp++ = '.'; | ||
115 | else | ||
116 | *scontextp++ = ','; | ||
117 | |||
118 | strcpy(scontextp, policydb.p_cat_val_to_name[i - 2]); | ||
119 | scontextp += strlen(policydb.p_cat_val_to_name[i - 2]); | ||
120 | } | ||
121 | range = 0; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | /* Handle case where last category is the end of range */ | ||
126 | if (range > 1) { | ||
127 | if (range > 2) | ||
128 | *scontextp++ = '.'; | ||
129 | else | ||
130 | *scontextp++ = ','; | ||
131 | |||
132 | strcpy(scontextp, policydb.p_cat_val_to_name[i - 2]); | ||
133 | scontextp += strlen(policydb.p_cat_val_to_name[i - 2]); | ||
134 | } | ||
135 | |||
136 | if (l == 0) { | ||
137 | if (mls_level_eq(&context->range.level[0], | ||
138 | &context->range.level[1])) | ||
139 | break; | ||
140 | else { | ||
141 | *scontextp = '-'; | ||
142 | scontextp++; | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | |||
147 | *scontext = scontextp; | ||
148 | return; | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Return 1 if the MLS fields in the security context | ||
153 | * structure `c' are valid. Return 0 otherwise. | ||
154 | */ | ||
155 | int mls_context_isvalid(struct policydb *p, struct context *c) | ||
156 | { | ||
157 | struct level_datum *levdatum; | ||
158 | struct user_datum *usrdatum; | ||
159 | int i, l; | ||
160 | |||
161 | if (!selinux_mls_enabled) | ||
162 | return 1; | ||
163 | |||
164 | /* | ||
165 | * MLS range validity checks: high must dominate low, low level must | ||
166 | * be valid (category set <-> sensitivity check), and high level must | ||
167 | * be valid (category set <-> sensitivity check) | ||
168 | */ | ||
169 | if (!mls_level_dom(&c->range.level[1], &c->range.level[0])) | ||
170 | /* High does not dominate low. */ | ||
171 | return 0; | ||
172 | |||
173 | for (l = 0; l < 2; l++) { | ||
174 | if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim) | ||
175 | return 0; | ||
176 | levdatum = hashtab_search(p->p_levels.table, | ||
177 | p->p_sens_val_to_name[c->range.level[l].sens - 1]); | ||
178 | if (!levdatum) | ||
179 | return 0; | ||
180 | |||
181 | for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) { | ||
182 | if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) { | ||
183 | if (i > p->p_cats.nprim) | ||
184 | return 0; | ||
185 | if (!ebitmap_get_bit(&levdatum->level->cat, i - 1)) | ||
186 | /* | ||
187 | * Category may not be associated with | ||
188 | * sensitivity in low level. | ||
189 | */ | ||
190 | return 0; | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | |||
195 | if (c->role == OBJECT_R_VAL) | ||
196 | return 1; | ||
197 | |||
198 | /* | ||
199 | * User must be authorized for the MLS range. | ||
200 | */ | ||
201 | if (!c->user || c->user > p->p_users.nprim) | ||
202 | return 0; | ||
203 | usrdatum = p->user_val_to_struct[c->user - 1]; | ||
204 | if (!mls_range_contains(usrdatum->range, c->range)) | ||
205 | return 0; /* user may not be associated with range */ | ||
206 | |||
207 | return 1; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Set the MLS fields in the security context structure | ||
212 | * `context' based on the string representation in | ||
213 | * the string `*scontext'. Update `*scontext' to | ||
214 | * point to the end of the string representation of | ||
215 | * the MLS fields. | ||
216 | * | ||
217 | * This function modifies the string in place, inserting | ||
218 | * NULL characters to terminate the MLS fields. | ||
219 | */ | ||
220 | int mls_context_to_sid(char oldc, | ||
221 | char **scontext, | ||
222 | struct context *context) | ||
223 | { | ||
224 | |||
225 | char delim; | ||
226 | char *scontextp, *p, *rngptr; | ||
227 | struct level_datum *levdatum; | ||
228 | struct cat_datum *catdatum, *rngdatum; | ||
229 | int l, rc = -EINVAL; | ||
230 | |||
231 | if (!selinux_mls_enabled) | ||
232 | return 0; | ||
233 | |||
234 | /* No MLS component to the security context. */ | ||
235 | if (!oldc) | ||
236 | goto out; | ||
237 | |||
238 | /* Extract low sensitivity. */ | ||
239 | scontextp = p = *scontext; | ||
240 | while (*p && *p != ':' && *p != '-') | ||
241 | p++; | ||
242 | |||
243 | delim = *p; | ||
244 | if (delim != 0) | ||
245 | *p++ = 0; | ||
246 | |||
247 | for (l = 0; l < 2; l++) { | ||
248 | levdatum = hashtab_search(policydb.p_levels.table, scontextp); | ||
249 | if (!levdatum) { | ||
250 | rc = -EINVAL; | ||
251 | goto out; | ||
252 | } | ||
253 | |||
254 | context->range.level[l].sens = levdatum->level->sens; | ||
255 | |||
256 | if (delim == ':') { | ||
257 | /* Extract category set. */ | ||
258 | while (1) { | ||
259 | scontextp = p; | ||
260 | while (*p && *p != ',' && *p != '-') | ||
261 | p++; | ||
262 | delim = *p; | ||
263 | if (delim != 0) | ||
264 | *p++ = 0; | ||
265 | |||
266 | /* Separate into range if exists */ | ||
267 | if ((rngptr = strchr(scontextp, '.')) != NULL) { | ||
268 | /* Remove '.' */ | ||
269 | *rngptr++ = 0; | ||
270 | } | ||
271 | |||
272 | catdatum = hashtab_search(policydb.p_cats.table, | ||
273 | scontextp); | ||
274 | if (!catdatum) { | ||
275 | rc = -EINVAL; | ||
276 | goto out; | ||
277 | } | ||
278 | |||
279 | rc = ebitmap_set_bit(&context->range.level[l].cat, | ||
280 | catdatum->value - 1, 1); | ||
281 | if (rc) | ||
282 | goto out; | ||
283 | |||
284 | /* If range, set all categories in range */ | ||
285 | if (rngptr) { | ||
286 | int i; | ||
287 | |||
288 | rngdatum = hashtab_search(policydb.p_cats.table, rngptr); | ||
289 | if (!rngdatum) { | ||
290 | rc = -EINVAL; | ||
291 | goto out; | ||
292 | } | ||
293 | |||
294 | if (catdatum->value >= rngdatum->value) { | ||
295 | rc = -EINVAL; | ||
296 | goto out; | ||
297 | } | ||
298 | |||
299 | for (i = catdatum->value; i < rngdatum->value; i++) { | ||
300 | rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1); | ||
301 | if (rc) | ||
302 | goto out; | ||
303 | } | ||
304 | } | ||
305 | |||
306 | if (delim != ',') | ||
307 | break; | ||
308 | } | ||
309 | } | ||
310 | if (delim == '-') { | ||
311 | /* Extract high sensitivity. */ | ||
312 | scontextp = p; | ||
313 | while (*p && *p != ':') | ||
314 | p++; | ||
315 | |||
316 | delim = *p; | ||
317 | if (delim != 0) | ||
318 | *p++ = 0; | ||
319 | } else | ||
320 | break; | ||
321 | } | ||
322 | |||
323 | if (l == 0) { | ||
324 | context->range.level[1].sens = context->range.level[0].sens; | ||
325 | rc = ebitmap_cpy(&context->range.level[1].cat, | ||
326 | &context->range.level[0].cat); | ||
327 | if (rc) | ||
328 | goto out; | ||
329 | } | ||
330 | *scontext = ++p; | ||
331 | rc = 0; | ||
332 | out: | ||
333 | return rc; | ||
334 | } | ||
335 | |||
336 | /* | ||
337 | * Copies the MLS range from `src' into `dst'. | ||
338 | */ | ||
339 | static inline int mls_copy_context(struct context *dst, | ||
340 | struct context *src) | ||
341 | { | ||
342 | int l, rc = 0; | ||
343 | |||
344 | /* Copy the MLS range from the source context */ | ||
345 | for (l = 0; l < 2; l++) { | ||
346 | dst->range.level[l].sens = src->range.level[l].sens; | ||
347 | rc = ebitmap_cpy(&dst->range.level[l].cat, | ||
348 | &src->range.level[l].cat); | ||
349 | if (rc) | ||
350 | break; | ||
351 | } | ||
352 | |||
353 | return rc; | ||
354 | } | ||
355 | |||
356 | /* | ||
357 | * Copies the effective MLS range from `src' into `dst'. | ||
358 | */ | ||
359 | static inline int mls_scopy_context(struct context *dst, | ||
360 | struct context *src) | ||
361 | { | ||
362 | int l, rc = 0; | ||
363 | |||
364 | /* Copy the MLS range from the source context */ | ||
365 | for (l = 0; l < 2; l++) { | ||
366 | dst->range.level[l].sens = src->range.level[0].sens; | ||
367 | rc = ebitmap_cpy(&dst->range.level[l].cat, | ||
368 | &src->range.level[0].cat); | ||
369 | if (rc) | ||
370 | break; | ||
371 | } | ||
372 | |||
373 | return rc; | ||
374 | } | ||
375 | |||
376 | /* | ||
377 | * Copies the MLS range `range' into `context'. | ||
378 | */ | ||
379 | static inline int mls_range_set(struct context *context, | ||
380 | struct mls_range *range) | ||
381 | { | ||
382 | int l, rc = 0; | ||
383 | |||
384 | /* Copy the MLS range into the context */ | ||
385 | for (l = 0; l < 2; l++) { | ||
386 | context->range.level[l].sens = range->level[l].sens; | ||
387 | rc = ebitmap_cpy(&context->range.level[l].cat, | ||
388 | &range->level[l].cat); | ||
389 | if (rc) | ||
390 | break; | ||
391 | } | ||
392 | |||
393 | return rc; | ||
394 | } | ||
395 | |||
396 | int mls_setup_user_range(struct context *fromcon, struct user_datum *user, | ||
397 | struct context *usercon) | ||
398 | { | ||
399 | if (selinux_mls_enabled) { | ||
400 | struct mls_level *fromcon_sen = &(fromcon->range.level[0]); | ||
401 | struct mls_level *fromcon_clr = &(fromcon->range.level[1]); | ||
402 | struct mls_level *user_low = &(user->range.level[0]); | ||
403 | struct mls_level *user_clr = &(user->range.level[1]); | ||
404 | struct mls_level *user_def = &(user->dfltlevel); | ||
405 | struct mls_level *usercon_sen = &(usercon->range.level[0]); | ||
406 | struct mls_level *usercon_clr = &(usercon->range.level[1]); | ||
407 | |||
408 | /* Honor the user's default level if we can */ | ||
409 | if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) { | ||
410 | *usercon_sen = *user_def; | ||
411 | } else if (mls_level_between(fromcon_sen, user_def, user_clr)) { | ||
412 | *usercon_sen = *fromcon_sen; | ||
413 | } else if (mls_level_between(fromcon_clr, user_low, user_def)) { | ||
414 | *usercon_sen = *user_low; | ||
415 | } else | ||
416 | return -EINVAL; | ||
417 | |||
418 | /* Lower the clearance of available contexts | ||
419 | if the clearance of "fromcon" is lower than | ||
420 | that of the user's default clearance (but | ||
421 | only if the "fromcon" clearance dominates | ||
422 | the user's computed sensitivity level) */ | ||
423 | if (mls_level_dom(user_clr, fromcon_clr)) { | ||
424 | *usercon_clr = *fromcon_clr; | ||
425 | } else if (mls_level_dom(fromcon_clr, user_clr)) { | ||
426 | *usercon_clr = *user_clr; | ||
427 | } else | ||
428 | return -EINVAL; | ||
429 | } | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | /* | ||
435 | * Convert the MLS fields in the security context | ||
436 | * structure `c' from the values specified in the | ||
437 | * policy `oldp' to the values specified in the policy `newp'. | ||
438 | */ | ||
439 | int mls_convert_context(struct policydb *oldp, | ||
440 | struct policydb *newp, | ||
441 | struct context *c) | ||
442 | { | ||
443 | struct level_datum *levdatum; | ||
444 | struct cat_datum *catdatum; | ||
445 | struct ebitmap bitmap; | ||
446 | int l, i; | ||
447 | |||
448 | if (!selinux_mls_enabled) | ||
449 | return 0; | ||
450 | |||
451 | for (l = 0; l < 2; l++) { | ||
452 | levdatum = hashtab_search(newp->p_levels.table, | ||
453 | oldp->p_sens_val_to_name[c->range.level[l].sens - 1]); | ||
454 | |||
455 | if (!levdatum) | ||
456 | return -EINVAL; | ||
457 | c->range.level[l].sens = levdatum->level->sens; | ||
458 | |||
459 | ebitmap_init(&bitmap); | ||
460 | for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) { | ||
461 | if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) { | ||
462 | int rc; | ||
463 | |||
464 | catdatum = hashtab_search(newp->p_cats.table, | ||
465 | oldp->p_cat_val_to_name[i - 1]); | ||
466 | if (!catdatum) | ||
467 | return -EINVAL; | ||
468 | rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1); | ||
469 | if (rc) | ||
470 | return rc; | ||
471 | } | ||
472 | } | ||
473 | ebitmap_destroy(&c->range.level[l].cat); | ||
474 | c->range.level[l].cat = bitmap; | ||
475 | } | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | int mls_compute_sid(struct context *scontext, | ||
481 | struct context *tcontext, | ||
482 | u16 tclass, | ||
483 | u32 specified, | ||
484 | struct context *newcontext) | ||
485 | { | ||
486 | if (!selinux_mls_enabled) | ||
487 | return 0; | ||
488 | |||
489 | switch (specified) { | ||
490 | case AVTAB_TRANSITION: | ||
491 | if (tclass == SECCLASS_PROCESS) { | ||
492 | struct range_trans *rangetr; | ||
493 | /* Look for a range transition rule. */ | ||
494 | for (rangetr = policydb.range_tr; rangetr; | ||
495 | rangetr = rangetr->next) { | ||
496 | if (rangetr->dom == scontext->type && | ||
497 | rangetr->type == tcontext->type) { | ||
498 | /* Set the range from the rule */ | ||
499 | return mls_range_set(newcontext, | ||
500 | &rangetr->range); | ||
501 | } | ||
502 | } | ||
503 | } | ||
504 | /* Fallthrough */ | ||
505 | case AVTAB_CHANGE: | ||
506 | if (tclass == SECCLASS_PROCESS) | ||
507 | /* Use the process MLS attributes. */ | ||
508 | return mls_copy_context(newcontext, scontext); | ||
509 | else | ||
510 | /* Use the process effective MLS attributes. */ | ||
511 | return mls_scopy_context(newcontext, scontext); | ||
512 | case AVTAB_MEMBER: | ||
513 | /* Only polyinstantiate the MLS attributes if | ||
514 | the type is being polyinstantiated */ | ||
515 | if (newcontext->type != tcontext->type) { | ||
516 | /* Use the process effective MLS attributes. */ | ||
517 | return mls_scopy_context(newcontext, scontext); | ||
518 | } else { | ||
519 | /* Use the related object MLS attributes. */ | ||
520 | return mls_copy_context(newcontext, tcontext); | ||
521 | } | ||
522 | default: | ||
523 | return -EINVAL; | ||
524 | } | ||
525 | return -EINVAL; | ||
526 | } | ||
527 | |||
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h new file mode 100644 index 000000000000..0d37beaa85e2 --- /dev/null +++ b/security/selinux/ss/mls.h | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * Multi-level security (MLS) policy operations. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | /* | ||
7 | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> | ||
8 | * | ||
9 | * Support for enhanced MLS infrastructure. | ||
10 | * | ||
11 | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. | ||
12 | */ | ||
13 | |||
14 | #ifndef _SS_MLS_H_ | ||
15 | #define _SS_MLS_H_ | ||
16 | |||
17 | #include "context.h" | ||
18 | #include "policydb.h" | ||
19 | |||
20 | int mls_compute_context_len(struct context *context); | ||
21 | void mls_sid_to_context(struct context *context, char **scontext); | ||
22 | int mls_context_isvalid(struct policydb *p, struct context *c); | ||
23 | |||
24 | int mls_context_to_sid(char oldc, | ||
25 | char **scontext, | ||
26 | struct context *context); | ||
27 | |||
28 | int mls_convert_context(struct policydb *oldp, | ||
29 | struct policydb *newp, | ||
30 | struct context *context); | ||
31 | |||
32 | int mls_compute_sid(struct context *scontext, | ||
33 | struct context *tcontext, | ||
34 | u16 tclass, | ||
35 | u32 specified, | ||
36 | struct context *newcontext); | ||
37 | |||
38 | int mls_setup_user_range(struct context *fromcon, struct user_datum *user, | ||
39 | struct context *usercon); | ||
40 | |||
41 | #endif /* _SS_MLS_H */ | ||
42 | |||
diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h new file mode 100644 index 000000000000..0c692d58d489 --- /dev/null +++ b/security/selinux/ss/mls_types.h | |||
@@ -0,0 +1,56 @@ | |||
1 | /* | ||
2 | * Type definitions for the multi-level security (MLS) policy. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | /* | ||
7 | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> | ||
8 | * | ||
9 | * Support for enhanced MLS infrastructure. | ||
10 | * | ||
11 | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. | ||
12 | */ | ||
13 | |||
14 | #ifndef _SS_MLS_TYPES_H_ | ||
15 | #define _SS_MLS_TYPES_H_ | ||
16 | |||
17 | #include "security.h" | ||
18 | |||
19 | struct mls_level { | ||
20 | u32 sens; /* sensitivity */ | ||
21 | struct ebitmap cat; /* category set */ | ||
22 | }; | ||
23 | |||
24 | struct mls_range { | ||
25 | struct mls_level level[2]; /* low == level[0], high == level[1] */ | ||
26 | }; | ||
27 | |||
28 | static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2) | ||
29 | { | ||
30 | if (!selinux_mls_enabled) | ||
31 | return 1; | ||
32 | |||
33 | return ((l1->sens == l2->sens) && | ||
34 | ebitmap_cmp(&l1->cat, &l2->cat)); | ||
35 | } | ||
36 | |||
37 | static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2) | ||
38 | { | ||
39 | if (!selinux_mls_enabled) | ||
40 | return 1; | ||
41 | |||
42 | return ((l1->sens >= l2->sens) && | ||
43 | ebitmap_contains(&l1->cat, &l2->cat)); | ||
44 | } | ||
45 | |||
46 | #define mls_level_incomp(l1, l2) \ | ||
47 | (!mls_level_dom((l1), (l2)) && !mls_level_dom((l2), (l1))) | ||
48 | |||
49 | #define mls_level_between(l1, l2, l3) \ | ||
50 | (mls_level_dom((l1), (l2)) && mls_level_dom((l3), (l1))) | ||
51 | |||
52 | #define mls_range_contains(r1, r2) \ | ||
53 | (mls_level_dom(&(r2).level[0], &(r1).level[0]) && \ | ||
54 | mls_level_dom(&(r1).level[1], &(r2).level[1])) | ||
55 | |||
56 | #endif /* _SS_MLS_TYPES_H_ */ | ||
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c new file mode 100644 index 000000000000..14190efbf333 --- /dev/null +++ b/security/selinux/ss/policydb.c | |||
@@ -0,0 +1,1843 @@ | |||
1 | /* | ||
2 | * Implementation of the policy database. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> | ||
9 | * | ||
10 | * Support for enhanced MLS infrastructure. | ||
11 | * | ||
12 | * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> | ||
13 | * | ||
14 | * Added conditional policy language extensions | ||
15 | * | ||
16 | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. | ||
17 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | ||
18 | * This program is free software; you can redistribute it and/or modify | ||
19 | * it under the terms of the GNU General Public License as published by | ||
20 | * the Free Software Foundation, version 2. | ||
21 | */ | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/string.h> | ||
26 | #include <linux/errno.h> | ||
27 | #include "security.h" | ||
28 | |||
29 | #include "policydb.h" | ||
30 | #include "conditional.h" | ||
31 | #include "mls.h" | ||
32 | |||
33 | #define _DEBUG_HASHES | ||
34 | |||
35 | #ifdef DEBUG_HASHES | ||
36 | static char *symtab_name[SYM_NUM] = { | ||
37 | "common prefixes", | ||
38 | "classes", | ||
39 | "roles", | ||
40 | "types", | ||
41 | "users", | ||
42 | "bools", | ||
43 | "levels", | ||
44 | "categories", | ||
45 | }; | ||
46 | #endif | ||
47 | |||
48 | int selinux_mls_enabled = 0; | ||
49 | |||
50 | static unsigned int symtab_sizes[SYM_NUM] = { | ||
51 | 2, | ||
52 | 32, | ||
53 | 16, | ||
54 | 512, | ||
55 | 128, | ||
56 | 16, | ||
57 | 16, | ||
58 | 16, | ||
59 | }; | ||
60 | |||
61 | struct policydb_compat_info { | ||
62 | int version; | ||
63 | int sym_num; | ||
64 | int ocon_num; | ||
65 | }; | ||
66 | |||
67 | /* These need to be updated if SYM_NUM or OCON_NUM changes */ | ||
68 | static struct policydb_compat_info policydb_compat[] = { | ||
69 | { | ||
70 | .version = POLICYDB_VERSION_BASE, | ||
71 | .sym_num = SYM_NUM - 3, | ||
72 | .ocon_num = OCON_NUM - 1, | ||
73 | }, | ||
74 | { | ||
75 | .version = POLICYDB_VERSION_BOOL, | ||
76 | .sym_num = SYM_NUM - 2, | ||
77 | .ocon_num = OCON_NUM - 1, | ||
78 | }, | ||
79 | { | ||
80 | .version = POLICYDB_VERSION_IPV6, | ||
81 | .sym_num = SYM_NUM - 2, | ||
82 | .ocon_num = OCON_NUM, | ||
83 | }, | ||
84 | { | ||
85 | .version = POLICYDB_VERSION_NLCLASS, | ||
86 | .sym_num = SYM_NUM - 2, | ||
87 | .ocon_num = OCON_NUM, | ||
88 | }, | ||
89 | { | ||
90 | .version = POLICYDB_VERSION_MLS, | ||
91 | .sym_num = SYM_NUM, | ||
92 | .ocon_num = OCON_NUM, | ||
93 | }, | ||
94 | }; | ||
95 | |||
96 | static struct policydb_compat_info *policydb_lookup_compat(int version) | ||
97 | { | ||
98 | int i; | ||
99 | struct policydb_compat_info *info = NULL; | ||
100 | |||
101 | for (i = 0; i < sizeof(policydb_compat)/sizeof(*info); i++) { | ||
102 | if (policydb_compat[i].version == version) { | ||
103 | info = &policydb_compat[i]; | ||
104 | break; | ||
105 | } | ||
106 | } | ||
107 | return info; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * Initialize the role table. | ||
112 | */ | ||
113 | static int roles_init(struct policydb *p) | ||
114 | { | ||
115 | char *key = NULL; | ||
116 | int rc; | ||
117 | struct role_datum *role; | ||
118 | |||
119 | role = kmalloc(sizeof(*role), GFP_KERNEL); | ||
120 | if (!role) { | ||
121 | rc = -ENOMEM; | ||
122 | goto out; | ||
123 | } | ||
124 | memset(role, 0, sizeof(*role)); | ||
125 | role->value = ++p->p_roles.nprim; | ||
126 | if (role->value != OBJECT_R_VAL) { | ||
127 | rc = -EINVAL; | ||
128 | goto out_free_role; | ||
129 | } | ||
130 | key = kmalloc(strlen(OBJECT_R)+1,GFP_KERNEL); | ||
131 | if (!key) { | ||
132 | rc = -ENOMEM; | ||
133 | goto out_free_role; | ||
134 | } | ||
135 | strcpy(key, OBJECT_R); | ||
136 | rc = hashtab_insert(p->p_roles.table, key, role); | ||
137 | if (rc) | ||
138 | goto out_free_key; | ||
139 | out: | ||
140 | return rc; | ||
141 | |||
142 | out_free_key: | ||
143 | kfree(key); | ||
144 | out_free_role: | ||
145 | kfree(role); | ||
146 | goto out; | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * Initialize a policy database structure. | ||
151 | */ | ||
152 | static int policydb_init(struct policydb *p) | ||
153 | { | ||
154 | int i, rc; | ||
155 | |||
156 | memset(p, 0, sizeof(*p)); | ||
157 | |||
158 | for (i = 0; i < SYM_NUM; i++) { | ||
159 | rc = symtab_init(&p->symtab[i], symtab_sizes[i]); | ||
160 | if (rc) | ||
161 | goto out_free_symtab; | ||
162 | } | ||
163 | |||
164 | rc = avtab_init(&p->te_avtab); | ||
165 | if (rc) | ||
166 | goto out_free_symtab; | ||
167 | |||
168 | rc = roles_init(p); | ||
169 | if (rc) | ||
170 | goto out_free_avtab; | ||
171 | |||
172 | rc = cond_policydb_init(p); | ||
173 | if (rc) | ||
174 | goto out_free_avtab; | ||
175 | |||
176 | out: | ||
177 | return rc; | ||
178 | |||
179 | out_free_avtab: | ||
180 | avtab_destroy(&p->te_avtab); | ||
181 | |||
182 | out_free_symtab: | ||
183 | for (i = 0; i < SYM_NUM; i++) | ||
184 | hashtab_destroy(p->symtab[i].table); | ||
185 | goto out; | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * The following *_index functions are used to | ||
190 | * define the val_to_name and val_to_struct arrays | ||
191 | * in a policy database structure. The val_to_name | ||
192 | * arrays are used when converting security context | ||
193 | * structures into string representations. The | ||
194 | * val_to_struct arrays are used when the attributes | ||
195 | * of a class, role, or user are needed. | ||
196 | */ | ||
197 | |||
198 | static int common_index(void *key, void *datum, void *datap) | ||
199 | { | ||
200 | struct policydb *p; | ||
201 | struct common_datum *comdatum; | ||
202 | |||
203 | comdatum = datum; | ||
204 | p = datap; | ||
205 | if (!comdatum->value || comdatum->value > p->p_commons.nprim) | ||
206 | return -EINVAL; | ||
207 | p->p_common_val_to_name[comdatum->value - 1] = key; | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | static int class_index(void *key, void *datum, void *datap) | ||
212 | { | ||
213 | struct policydb *p; | ||
214 | struct class_datum *cladatum; | ||
215 | |||
216 | cladatum = datum; | ||
217 | p = datap; | ||
218 | if (!cladatum->value || cladatum->value > p->p_classes.nprim) | ||
219 | return -EINVAL; | ||
220 | p->p_class_val_to_name[cladatum->value - 1] = key; | ||
221 | p->class_val_to_struct[cladatum->value - 1] = cladatum; | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static int role_index(void *key, void *datum, void *datap) | ||
226 | { | ||
227 | struct policydb *p; | ||
228 | struct role_datum *role; | ||
229 | |||
230 | role = datum; | ||
231 | p = datap; | ||
232 | if (!role->value || role->value > p->p_roles.nprim) | ||
233 | return -EINVAL; | ||
234 | p->p_role_val_to_name[role->value - 1] = key; | ||
235 | p->role_val_to_struct[role->value - 1] = role; | ||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static int type_index(void *key, void *datum, void *datap) | ||
240 | { | ||
241 | struct policydb *p; | ||
242 | struct type_datum *typdatum; | ||
243 | |||
244 | typdatum = datum; | ||
245 | p = datap; | ||
246 | |||
247 | if (typdatum->primary) { | ||
248 | if (!typdatum->value || typdatum->value > p->p_types.nprim) | ||
249 | return -EINVAL; | ||
250 | p->p_type_val_to_name[typdatum->value - 1] = key; | ||
251 | } | ||
252 | |||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static int user_index(void *key, void *datum, void *datap) | ||
257 | { | ||
258 | struct policydb *p; | ||
259 | struct user_datum *usrdatum; | ||
260 | |||
261 | usrdatum = datum; | ||
262 | p = datap; | ||
263 | if (!usrdatum->value || usrdatum->value > p->p_users.nprim) | ||
264 | return -EINVAL; | ||
265 | p->p_user_val_to_name[usrdatum->value - 1] = key; | ||
266 | p->user_val_to_struct[usrdatum->value - 1] = usrdatum; | ||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | static int sens_index(void *key, void *datum, void *datap) | ||
271 | { | ||
272 | struct policydb *p; | ||
273 | struct level_datum *levdatum; | ||
274 | |||
275 | levdatum = datum; | ||
276 | p = datap; | ||
277 | |||
278 | if (!levdatum->isalias) { | ||
279 | if (!levdatum->level->sens || | ||
280 | levdatum->level->sens > p->p_levels.nprim) | ||
281 | return -EINVAL; | ||
282 | p->p_sens_val_to_name[levdatum->level->sens - 1] = key; | ||
283 | } | ||
284 | |||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static int cat_index(void *key, void *datum, void *datap) | ||
289 | { | ||
290 | struct policydb *p; | ||
291 | struct cat_datum *catdatum; | ||
292 | |||
293 | catdatum = datum; | ||
294 | p = datap; | ||
295 | |||
296 | if (!catdatum->isalias) { | ||
297 | if (!catdatum->value || catdatum->value > p->p_cats.nprim) | ||
298 | return -EINVAL; | ||
299 | p->p_cat_val_to_name[catdatum->value - 1] = key; | ||
300 | } | ||
301 | |||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) = | ||
306 | { | ||
307 | common_index, | ||
308 | class_index, | ||
309 | role_index, | ||
310 | type_index, | ||
311 | user_index, | ||
312 | cond_index_bool, | ||
313 | sens_index, | ||
314 | cat_index, | ||
315 | }; | ||
316 | |||
317 | /* | ||
318 | * Define the common val_to_name array and the class | ||
319 | * val_to_name and val_to_struct arrays in a policy | ||
320 | * database structure. | ||
321 | * | ||
322 | * Caller must clean up upon failure. | ||
323 | */ | ||
324 | static int policydb_index_classes(struct policydb *p) | ||
325 | { | ||
326 | int rc; | ||
327 | |||
328 | p->p_common_val_to_name = | ||
329 | kmalloc(p->p_commons.nprim * sizeof(char *), GFP_KERNEL); | ||
330 | if (!p->p_common_val_to_name) { | ||
331 | rc = -ENOMEM; | ||
332 | goto out; | ||
333 | } | ||
334 | |||
335 | rc = hashtab_map(p->p_commons.table, common_index, p); | ||
336 | if (rc) | ||
337 | goto out; | ||
338 | |||
339 | p->class_val_to_struct = | ||
340 | kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)), GFP_KERNEL); | ||
341 | if (!p->class_val_to_struct) { | ||
342 | rc = -ENOMEM; | ||
343 | goto out; | ||
344 | } | ||
345 | |||
346 | p->p_class_val_to_name = | ||
347 | kmalloc(p->p_classes.nprim * sizeof(char *), GFP_KERNEL); | ||
348 | if (!p->p_class_val_to_name) { | ||
349 | rc = -ENOMEM; | ||
350 | goto out; | ||
351 | } | ||
352 | |||
353 | rc = hashtab_map(p->p_classes.table, class_index, p); | ||
354 | out: | ||
355 | return rc; | ||
356 | } | ||
357 | |||
358 | #ifdef DEBUG_HASHES | ||
359 | static void symtab_hash_eval(struct symtab *s) | ||
360 | { | ||
361 | int i; | ||
362 | |||
363 | for (i = 0; i < SYM_NUM; i++) { | ||
364 | struct hashtab *h = s[i].table; | ||
365 | struct hashtab_info info; | ||
366 | |||
367 | hashtab_stat(h, &info); | ||
368 | printk(KERN_INFO "%s: %d entries and %d/%d buckets used, " | ||
369 | "longest chain length %d\n", symtab_name[i], h->nel, | ||
370 | info.slots_used, h->size, info.max_chain_len); | ||
371 | } | ||
372 | } | ||
373 | #endif | ||
374 | |||
375 | /* | ||
376 | * Define the other val_to_name and val_to_struct arrays | ||
377 | * in a policy database structure. | ||
378 | * | ||
379 | * Caller must clean up on failure. | ||
380 | */ | ||
381 | static int policydb_index_others(struct policydb *p) | ||
382 | { | ||
383 | int i, rc = 0; | ||
384 | |||
385 | printk(KERN_INFO "security: %d users, %d roles, %d types, %d bools", | ||
386 | p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim, p->p_bools.nprim); | ||
387 | if (selinux_mls_enabled) | ||
388 | printk(", %d sens, %d cats", p->p_levels.nprim, | ||
389 | p->p_cats.nprim); | ||
390 | printk("\n"); | ||
391 | |||
392 | printk(KERN_INFO "security: %d classes, %d rules\n", | ||
393 | p->p_classes.nprim, p->te_avtab.nel); | ||
394 | |||
395 | #ifdef DEBUG_HASHES | ||
396 | avtab_hash_eval(&p->te_avtab, "rules"); | ||
397 | symtab_hash_eval(p->symtab); | ||
398 | #endif | ||
399 | |||
400 | p->role_val_to_struct = | ||
401 | kmalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)), | ||
402 | GFP_KERNEL); | ||
403 | if (!p->role_val_to_struct) { | ||
404 | rc = -ENOMEM; | ||
405 | goto out; | ||
406 | } | ||
407 | |||
408 | p->user_val_to_struct = | ||
409 | kmalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)), | ||
410 | GFP_KERNEL); | ||
411 | if (!p->user_val_to_struct) { | ||
412 | rc = -ENOMEM; | ||
413 | goto out; | ||
414 | } | ||
415 | |||
416 | if (cond_init_bool_indexes(p)) { | ||
417 | rc = -ENOMEM; | ||
418 | goto out; | ||
419 | } | ||
420 | |||
421 | for (i = SYM_ROLES; i < SYM_NUM; i++) { | ||
422 | p->sym_val_to_name[i] = | ||
423 | kmalloc(p->symtab[i].nprim * sizeof(char *), GFP_KERNEL); | ||
424 | if (!p->sym_val_to_name[i]) { | ||
425 | rc = -ENOMEM; | ||
426 | goto out; | ||
427 | } | ||
428 | rc = hashtab_map(p->symtab[i].table, index_f[i], p); | ||
429 | if (rc) | ||
430 | goto out; | ||
431 | } | ||
432 | |||
433 | out: | ||
434 | return rc; | ||
435 | } | ||
436 | |||
437 | /* | ||
438 | * The following *_destroy functions are used to | ||
439 | * free any memory allocated for each kind of | ||
440 | * symbol data in the policy database. | ||
441 | */ | ||
442 | |||
443 | static int perm_destroy(void *key, void *datum, void *p) | ||
444 | { | ||
445 | kfree(key); | ||
446 | kfree(datum); | ||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | static int common_destroy(void *key, void *datum, void *p) | ||
451 | { | ||
452 | struct common_datum *comdatum; | ||
453 | |||
454 | kfree(key); | ||
455 | comdatum = datum; | ||
456 | hashtab_map(comdatum->permissions.table, perm_destroy, NULL); | ||
457 | hashtab_destroy(comdatum->permissions.table); | ||
458 | kfree(datum); | ||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | static int class_destroy(void *key, void *datum, void *p) | ||
463 | { | ||
464 | struct class_datum *cladatum; | ||
465 | struct constraint_node *constraint, *ctemp; | ||
466 | struct constraint_expr *e, *etmp; | ||
467 | |||
468 | kfree(key); | ||
469 | cladatum = datum; | ||
470 | hashtab_map(cladatum->permissions.table, perm_destroy, NULL); | ||
471 | hashtab_destroy(cladatum->permissions.table); | ||
472 | constraint = cladatum->constraints; | ||
473 | while (constraint) { | ||
474 | e = constraint->expr; | ||
475 | while (e) { | ||
476 | ebitmap_destroy(&e->names); | ||
477 | etmp = e; | ||
478 | e = e->next; | ||
479 | kfree(etmp); | ||
480 | } | ||
481 | ctemp = constraint; | ||
482 | constraint = constraint->next; | ||
483 | kfree(ctemp); | ||
484 | } | ||
485 | |||
486 | constraint = cladatum->validatetrans; | ||
487 | while (constraint) { | ||
488 | e = constraint->expr; | ||
489 | while (e) { | ||
490 | ebitmap_destroy(&e->names); | ||
491 | etmp = e; | ||
492 | e = e->next; | ||
493 | kfree(etmp); | ||
494 | } | ||
495 | ctemp = constraint; | ||
496 | constraint = constraint->next; | ||
497 | kfree(ctemp); | ||
498 | } | ||
499 | |||
500 | kfree(cladatum->comkey); | ||
501 | kfree(datum); | ||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | static int role_destroy(void *key, void *datum, void *p) | ||
506 | { | ||
507 | struct role_datum *role; | ||
508 | |||
509 | kfree(key); | ||
510 | role = datum; | ||
511 | ebitmap_destroy(&role->dominates); | ||
512 | ebitmap_destroy(&role->types); | ||
513 | kfree(datum); | ||
514 | return 0; | ||
515 | } | ||
516 | |||
517 | static int type_destroy(void *key, void *datum, void *p) | ||
518 | { | ||
519 | kfree(key); | ||
520 | kfree(datum); | ||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | static int user_destroy(void *key, void *datum, void *p) | ||
525 | { | ||
526 | struct user_datum *usrdatum; | ||
527 | |||
528 | kfree(key); | ||
529 | usrdatum = datum; | ||
530 | ebitmap_destroy(&usrdatum->roles); | ||
531 | ebitmap_destroy(&usrdatum->range.level[0].cat); | ||
532 | ebitmap_destroy(&usrdatum->range.level[1].cat); | ||
533 | ebitmap_destroy(&usrdatum->dfltlevel.cat); | ||
534 | kfree(datum); | ||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | static int sens_destroy(void *key, void *datum, void *p) | ||
539 | { | ||
540 | struct level_datum *levdatum; | ||
541 | |||
542 | kfree(key); | ||
543 | levdatum = datum; | ||
544 | ebitmap_destroy(&levdatum->level->cat); | ||
545 | kfree(levdatum->level); | ||
546 | kfree(datum); | ||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static int cat_destroy(void *key, void *datum, void *p) | ||
551 | { | ||
552 | kfree(key); | ||
553 | kfree(datum); | ||
554 | return 0; | ||
555 | } | ||
556 | |||
557 | static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) = | ||
558 | { | ||
559 | common_destroy, | ||
560 | class_destroy, | ||
561 | role_destroy, | ||
562 | type_destroy, | ||
563 | user_destroy, | ||
564 | cond_destroy_bool, | ||
565 | sens_destroy, | ||
566 | cat_destroy, | ||
567 | }; | ||
568 | |||
569 | static void ocontext_destroy(struct ocontext *c, int i) | ||
570 | { | ||
571 | context_destroy(&c->context[0]); | ||
572 | context_destroy(&c->context[1]); | ||
573 | if (i == OCON_ISID || i == OCON_FS || | ||
574 | i == OCON_NETIF || i == OCON_FSUSE) | ||
575 | kfree(c->u.name); | ||
576 | kfree(c); | ||
577 | } | ||
578 | |||
579 | /* | ||
580 | * Free any memory allocated by a policy database structure. | ||
581 | */ | ||
582 | void policydb_destroy(struct policydb *p) | ||
583 | { | ||
584 | struct ocontext *c, *ctmp; | ||
585 | struct genfs *g, *gtmp; | ||
586 | int i; | ||
587 | |||
588 | for (i = 0; i < SYM_NUM; i++) { | ||
589 | hashtab_map(p->symtab[i].table, destroy_f[i], NULL); | ||
590 | hashtab_destroy(p->symtab[i].table); | ||
591 | } | ||
592 | |||
593 | for (i = 0; i < SYM_NUM; i++) { | ||
594 | if (p->sym_val_to_name[i]) | ||
595 | kfree(p->sym_val_to_name[i]); | ||
596 | } | ||
597 | |||
598 | if (p->class_val_to_struct) | ||
599 | kfree(p->class_val_to_struct); | ||
600 | if (p->role_val_to_struct) | ||
601 | kfree(p->role_val_to_struct); | ||
602 | if (p->user_val_to_struct) | ||
603 | kfree(p->user_val_to_struct); | ||
604 | |||
605 | avtab_destroy(&p->te_avtab); | ||
606 | |||
607 | for (i = 0; i < OCON_NUM; i++) { | ||
608 | c = p->ocontexts[i]; | ||
609 | while (c) { | ||
610 | ctmp = c; | ||
611 | c = c->next; | ||
612 | ocontext_destroy(ctmp,i); | ||
613 | } | ||
614 | } | ||
615 | |||
616 | g = p->genfs; | ||
617 | while (g) { | ||
618 | kfree(g->fstype); | ||
619 | c = g->head; | ||
620 | while (c) { | ||
621 | ctmp = c; | ||
622 | c = c->next; | ||
623 | ocontext_destroy(ctmp,OCON_FSUSE); | ||
624 | } | ||
625 | gtmp = g; | ||
626 | g = g->next; | ||
627 | kfree(gtmp); | ||
628 | } | ||
629 | |||
630 | cond_policydb_destroy(p); | ||
631 | |||
632 | return; | ||
633 | } | ||
634 | |||
635 | /* | ||
636 | * Load the initial SIDs specified in a policy database | ||
637 | * structure into a SID table. | ||
638 | */ | ||
639 | int policydb_load_isids(struct policydb *p, struct sidtab *s) | ||
640 | { | ||
641 | struct ocontext *head, *c; | ||
642 | int rc; | ||
643 | |||
644 | rc = sidtab_init(s); | ||
645 | if (rc) { | ||
646 | printk(KERN_ERR "security: out of memory on SID table init\n"); | ||
647 | goto out; | ||
648 | } | ||
649 | |||
650 | head = p->ocontexts[OCON_ISID]; | ||
651 | for (c = head; c; c = c->next) { | ||
652 | if (!c->context[0].user) { | ||
653 | printk(KERN_ERR "security: SID %s was never " | ||
654 | "defined.\n", c->u.name); | ||
655 | rc = -EINVAL; | ||
656 | goto out; | ||
657 | } | ||
658 | if (sidtab_insert(s, c->sid[0], &c->context[0])) { | ||
659 | printk(KERN_ERR "security: unable to load initial " | ||
660 | "SID %s.\n", c->u.name); | ||
661 | rc = -EINVAL; | ||
662 | goto out; | ||
663 | } | ||
664 | } | ||
665 | out: | ||
666 | return rc; | ||
667 | } | ||
668 | |||
669 | /* | ||
670 | * Return 1 if the fields in the security context | ||
671 | * structure `c' are valid. Return 0 otherwise. | ||
672 | */ | ||
673 | int policydb_context_isvalid(struct policydb *p, struct context *c) | ||
674 | { | ||
675 | struct role_datum *role; | ||
676 | struct user_datum *usrdatum; | ||
677 | |||
678 | if (!c->role || c->role > p->p_roles.nprim) | ||
679 | return 0; | ||
680 | |||
681 | if (!c->user || c->user > p->p_users.nprim) | ||
682 | return 0; | ||
683 | |||
684 | if (!c->type || c->type > p->p_types.nprim) | ||
685 | return 0; | ||
686 | |||
687 | if (c->role != OBJECT_R_VAL) { | ||
688 | /* | ||
689 | * Role must be authorized for the type. | ||
690 | */ | ||
691 | role = p->role_val_to_struct[c->role - 1]; | ||
692 | if (!ebitmap_get_bit(&role->types, | ||
693 | c->type - 1)) | ||
694 | /* role may not be associated with type */ | ||
695 | return 0; | ||
696 | |||
697 | /* | ||
698 | * User must be authorized for the role. | ||
699 | */ | ||
700 | usrdatum = p->user_val_to_struct[c->user - 1]; | ||
701 | if (!usrdatum) | ||
702 | return 0; | ||
703 | |||
704 | if (!ebitmap_get_bit(&usrdatum->roles, | ||
705 | c->role - 1)) | ||
706 | /* user may not be associated with role */ | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | if (!mls_context_isvalid(p, c)) | ||
711 | return 0; | ||
712 | |||
713 | return 1; | ||
714 | } | ||
715 | |||
716 | /* | ||
717 | * Read a MLS range structure from a policydb binary | ||
718 | * representation file. | ||
719 | */ | ||
720 | static int mls_read_range_helper(struct mls_range *r, void *fp) | ||
721 | { | ||
722 | u32 buf[2], items; | ||
723 | int rc; | ||
724 | |||
725 | rc = next_entry(buf, fp, sizeof(u32)); | ||
726 | if (rc < 0) | ||
727 | goto out; | ||
728 | |||
729 | items = le32_to_cpu(buf[0]); | ||
730 | if (items > ARRAY_SIZE(buf)) { | ||
731 | printk(KERN_ERR "security: mls: range overflow\n"); | ||
732 | rc = -EINVAL; | ||
733 | goto out; | ||
734 | } | ||
735 | rc = next_entry(buf, fp, sizeof(u32) * items); | ||
736 | if (rc < 0) { | ||
737 | printk(KERN_ERR "security: mls: truncated range\n"); | ||
738 | goto out; | ||
739 | } | ||
740 | r->level[0].sens = le32_to_cpu(buf[0]); | ||
741 | if (items > 1) | ||
742 | r->level[1].sens = le32_to_cpu(buf[1]); | ||
743 | else | ||
744 | r->level[1].sens = r->level[0].sens; | ||
745 | |||
746 | rc = ebitmap_read(&r->level[0].cat, fp); | ||
747 | if (rc) { | ||
748 | printk(KERN_ERR "security: mls: error reading low " | ||
749 | "categories\n"); | ||
750 | goto out; | ||
751 | } | ||
752 | if (items > 1) { | ||
753 | rc = ebitmap_read(&r->level[1].cat, fp); | ||
754 | if (rc) { | ||
755 | printk(KERN_ERR "security: mls: error reading high " | ||
756 | "categories\n"); | ||
757 | goto bad_high; | ||
758 | } | ||
759 | } else { | ||
760 | rc = ebitmap_cpy(&r->level[1].cat, &r->level[0].cat); | ||
761 | if (rc) { | ||
762 | printk(KERN_ERR "security: mls: out of memory\n"); | ||
763 | goto bad_high; | ||
764 | } | ||
765 | } | ||
766 | |||
767 | rc = 0; | ||
768 | out: | ||
769 | return rc; | ||
770 | bad_high: | ||
771 | ebitmap_destroy(&r->level[0].cat); | ||
772 | goto out; | ||
773 | } | ||
774 | |||
775 | /* | ||
776 | * Read and validate a security context structure | ||
777 | * from a policydb binary representation file. | ||
778 | */ | ||
779 | static int context_read_and_validate(struct context *c, | ||
780 | struct policydb *p, | ||
781 | void *fp) | ||
782 | { | ||
783 | u32 buf[3]; | ||
784 | int rc; | ||
785 | |||
786 | rc = next_entry(buf, fp, sizeof buf); | ||
787 | if (rc < 0) { | ||
788 | printk(KERN_ERR "security: context truncated\n"); | ||
789 | goto out; | ||
790 | } | ||
791 | c->user = le32_to_cpu(buf[0]); | ||
792 | c->role = le32_to_cpu(buf[1]); | ||
793 | c->type = le32_to_cpu(buf[2]); | ||
794 | if (p->policyvers >= POLICYDB_VERSION_MLS) { | ||
795 | if (mls_read_range_helper(&c->range, fp)) { | ||
796 | printk(KERN_ERR "security: error reading MLS range of " | ||
797 | "context\n"); | ||
798 | rc = -EINVAL; | ||
799 | goto out; | ||
800 | } | ||
801 | } | ||
802 | |||
803 | if (!policydb_context_isvalid(p, c)) { | ||
804 | printk(KERN_ERR "security: invalid security context\n"); | ||
805 | context_destroy(c); | ||
806 | rc = -EINVAL; | ||
807 | } | ||
808 | out: | ||
809 | return rc; | ||
810 | } | ||
811 | |||
812 | /* | ||
813 | * The following *_read functions are used to | ||
814 | * read the symbol data from a policy database | ||
815 | * binary representation file. | ||
816 | */ | ||
817 | |||
818 | static int perm_read(struct policydb *p, struct hashtab *h, void *fp) | ||
819 | { | ||
820 | char *key = NULL; | ||
821 | struct perm_datum *perdatum; | ||
822 | int rc; | ||
823 | u32 buf[2], len; | ||
824 | |||
825 | perdatum = kmalloc(sizeof(*perdatum), GFP_KERNEL); | ||
826 | if (!perdatum) { | ||
827 | rc = -ENOMEM; | ||
828 | goto out; | ||
829 | } | ||
830 | memset(perdatum, 0, sizeof(*perdatum)); | ||
831 | |||
832 | rc = next_entry(buf, fp, sizeof buf); | ||
833 | if (rc < 0) | ||
834 | goto bad; | ||
835 | |||
836 | len = le32_to_cpu(buf[0]); | ||
837 | perdatum->value = le32_to_cpu(buf[1]); | ||
838 | |||
839 | key = kmalloc(len + 1,GFP_KERNEL); | ||
840 | if (!key) { | ||
841 | rc = -ENOMEM; | ||
842 | goto bad; | ||
843 | } | ||
844 | rc = next_entry(key, fp, len); | ||
845 | if (rc < 0) | ||
846 | goto bad; | ||
847 | key[len] = 0; | ||
848 | |||
849 | rc = hashtab_insert(h, key, perdatum); | ||
850 | if (rc) | ||
851 | goto bad; | ||
852 | out: | ||
853 | return rc; | ||
854 | bad: | ||
855 | perm_destroy(key, perdatum, NULL); | ||
856 | goto out; | ||
857 | } | ||
858 | |||
859 | static int common_read(struct policydb *p, struct hashtab *h, void *fp) | ||
860 | { | ||
861 | char *key = NULL; | ||
862 | struct common_datum *comdatum; | ||
863 | u32 buf[4], len, nel; | ||
864 | int i, rc; | ||
865 | |||
866 | comdatum = kmalloc(sizeof(*comdatum), GFP_KERNEL); | ||
867 | if (!comdatum) { | ||
868 | rc = -ENOMEM; | ||
869 | goto out; | ||
870 | } | ||
871 | memset(comdatum, 0, sizeof(*comdatum)); | ||
872 | |||
873 | rc = next_entry(buf, fp, sizeof buf); | ||
874 | if (rc < 0) | ||
875 | goto bad; | ||
876 | |||
877 | len = le32_to_cpu(buf[0]); | ||
878 | comdatum->value = le32_to_cpu(buf[1]); | ||
879 | |||
880 | rc = symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE); | ||
881 | if (rc) | ||
882 | goto bad; | ||
883 | comdatum->permissions.nprim = le32_to_cpu(buf[2]); | ||
884 | nel = le32_to_cpu(buf[3]); | ||
885 | |||
886 | key = kmalloc(len + 1,GFP_KERNEL); | ||
887 | if (!key) { | ||
888 | rc = -ENOMEM; | ||
889 | goto bad; | ||
890 | } | ||
891 | rc = next_entry(key, fp, len); | ||
892 | if (rc < 0) | ||
893 | goto bad; | ||
894 | key[len] = 0; | ||
895 | |||
896 | for (i = 0; i < nel; i++) { | ||
897 | rc = perm_read(p, comdatum->permissions.table, fp); | ||
898 | if (rc) | ||
899 | goto bad; | ||
900 | } | ||
901 | |||
902 | rc = hashtab_insert(h, key, comdatum); | ||
903 | if (rc) | ||
904 | goto bad; | ||
905 | out: | ||
906 | return rc; | ||
907 | bad: | ||
908 | common_destroy(key, comdatum, NULL); | ||
909 | goto out; | ||
910 | } | ||
911 | |||
912 | static int read_cons_helper(struct constraint_node **nodep, int ncons, | ||
913 | int allowxtarget, void *fp) | ||
914 | { | ||
915 | struct constraint_node *c, *lc; | ||
916 | struct constraint_expr *e, *le; | ||
917 | u32 buf[3], nexpr; | ||
918 | int rc, i, j, depth; | ||
919 | |||
920 | lc = NULL; | ||
921 | for (i = 0; i < ncons; i++) { | ||
922 | c = kmalloc(sizeof(*c), GFP_KERNEL); | ||
923 | if (!c) | ||
924 | return -ENOMEM; | ||
925 | memset(c, 0, sizeof(*c)); | ||
926 | |||
927 | if (lc) { | ||
928 | lc->next = c; | ||
929 | } else { | ||
930 | *nodep = c; | ||
931 | } | ||
932 | |||
933 | rc = next_entry(buf, fp, (sizeof(u32) * 2)); | ||
934 | if (rc < 0) | ||
935 | return rc; | ||
936 | c->permissions = le32_to_cpu(buf[0]); | ||
937 | nexpr = le32_to_cpu(buf[1]); | ||
938 | le = NULL; | ||
939 | depth = -1; | ||
940 | for (j = 0; j < nexpr; j++) { | ||
941 | e = kmalloc(sizeof(*e), GFP_KERNEL); | ||
942 | if (!e) | ||
943 | return -ENOMEM; | ||
944 | memset(e, 0, sizeof(*e)); | ||
945 | |||
946 | if (le) { | ||
947 | le->next = e; | ||
948 | } else { | ||
949 | c->expr = e; | ||
950 | } | ||
951 | |||
952 | rc = next_entry(buf, fp, (sizeof(u32) * 3)); | ||
953 | if (rc < 0) | ||
954 | return rc; | ||
955 | e->expr_type = le32_to_cpu(buf[0]); | ||
956 | e->attr = le32_to_cpu(buf[1]); | ||
957 | e->op = le32_to_cpu(buf[2]); | ||
958 | |||
959 | switch (e->expr_type) { | ||
960 | case CEXPR_NOT: | ||
961 | if (depth < 0) | ||
962 | return -EINVAL; | ||
963 | break; | ||
964 | case CEXPR_AND: | ||
965 | case CEXPR_OR: | ||
966 | if (depth < 1) | ||
967 | return -EINVAL; | ||
968 | depth--; | ||
969 | break; | ||
970 | case CEXPR_ATTR: | ||
971 | if (depth == (CEXPR_MAXDEPTH - 1)) | ||
972 | return -EINVAL; | ||
973 | depth++; | ||
974 | break; | ||
975 | case CEXPR_NAMES: | ||
976 | if (!allowxtarget && (e->attr & CEXPR_XTARGET)) | ||
977 | return -EINVAL; | ||
978 | if (depth == (CEXPR_MAXDEPTH - 1)) | ||
979 | return -EINVAL; | ||
980 | depth++; | ||
981 | if (ebitmap_read(&e->names, fp)) | ||
982 | return -EINVAL; | ||
983 | break; | ||
984 | default: | ||
985 | return -EINVAL; | ||
986 | } | ||
987 | le = e; | ||
988 | } | ||
989 | if (depth != 0) | ||
990 | return -EINVAL; | ||
991 | lc = c; | ||
992 | } | ||
993 | |||
994 | return 0; | ||
995 | } | ||
996 | |||
997 | static int class_read(struct policydb *p, struct hashtab *h, void *fp) | ||
998 | { | ||
999 | char *key = NULL; | ||
1000 | struct class_datum *cladatum; | ||
1001 | u32 buf[6], len, len2, ncons, nel; | ||
1002 | int i, rc; | ||
1003 | |||
1004 | cladatum = kmalloc(sizeof(*cladatum), GFP_KERNEL); | ||
1005 | if (!cladatum) { | ||
1006 | rc = -ENOMEM; | ||
1007 | goto out; | ||
1008 | } | ||
1009 | memset(cladatum, 0, sizeof(*cladatum)); | ||
1010 | |||
1011 | rc = next_entry(buf, fp, sizeof(u32)*6); | ||
1012 | if (rc < 0) | ||
1013 | goto bad; | ||
1014 | |||
1015 | len = le32_to_cpu(buf[0]); | ||
1016 | len2 = le32_to_cpu(buf[1]); | ||
1017 | cladatum->value = le32_to_cpu(buf[2]); | ||
1018 | |||
1019 | rc = symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE); | ||
1020 | if (rc) | ||
1021 | goto bad; | ||
1022 | cladatum->permissions.nprim = le32_to_cpu(buf[3]); | ||
1023 | nel = le32_to_cpu(buf[4]); | ||
1024 | |||
1025 | ncons = le32_to_cpu(buf[5]); | ||
1026 | |||
1027 | key = kmalloc(len + 1,GFP_KERNEL); | ||
1028 | if (!key) { | ||
1029 | rc = -ENOMEM; | ||
1030 | goto bad; | ||
1031 | } | ||
1032 | rc = next_entry(key, fp, len); | ||
1033 | if (rc < 0) | ||
1034 | goto bad; | ||
1035 | key[len] = 0; | ||
1036 | |||
1037 | if (len2) { | ||
1038 | cladatum->comkey = kmalloc(len2 + 1,GFP_KERNEL); | ||
1039 | if (!cladatum->comkey) { | ||
1040 | rc = -ENOMEM; | ||
1041 | goto bad; | ||
1042 | } | ||
1043 | rc = next_entry(cladatum->comkey, fp, len2); | ||
1044 | if (rc < 0) | ||
1045 | goto bad; | ||
1046 | cladatum->comkey[len2] = 0; | ||
1047 | |||
1048 | cladatum->comdatum = hashtab_search(p->p_commons.table, | ||
1049 | cladatum->comkey); | ||
1050 | if (!cladatum->comdatum) { | ||
1051 | printk(KERN_ERR "security: unknown common %s\n", | ||
1052 | cladatum->comkey); | ||
1053 | rc = -EINVAL; | ||
1054 | goto bad; | ||
1055 | } | ||
1056 | } | ||
1057 | for (i = 0; i < nel; i++) { | ||
1058 | rc = perm_read(p, cladatum->permissions.table, fp); | ||
1059 | if (rc) | ||
1060 | goto bad; | ||
1061 | } | ||
1062 | |||
1063 | rc = read_cons_helper(&cladatum->constraints, ncons, 0, fp); | ||
1064 | if (rc) | ||
1065 | goto bad; | ||
1066 | |||
1067 | if (p->policyvers >= POLICYDB_VERSION_VALIDATETRANS) { | ||
1068 | /* grab the validatetrans rules */ | ||
1069 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1070 | if (rc < 0) | ||
1071 | goto bad; | ||
1072 | ncons = le32_to_cpu(buf[0]); | ||
1073 | rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp); | ||
1074 | if (rc) | ||
1075 | goto bad; | ||
1076 | } | ||
1077 | |||
1078 | rc = hashtab_insert(h, key, cladatum); | ||
1079 | if (rc) | ||
1080 | goto bad; | ||
1081 | |||
1082 | rc = 0; | ||
1083 | out: | ||
1084 | return rc; | ||
1085 | bad: | ||
1086 | class_destroy(key, cladatum, NULL); | ||
1087 | goto out; | ||
1088 | } | ||
1089 | |||
1090 | static int role_read(struct policydb *p, struct hashtab *h, void *fp) | ||
1091 | { | ||
1092 | char *key = NULL; | ||
1093 | struct role_datum *role; | ||
1094 | int rc; | ||
1095 | u32 buf[2], len; | ||
1096 | |||
1097 | role = kmalloc(sizeof(*role), GFP_KERNEL); | ||
1098 | if (!role) { | ||
1099 | rc = -ENOMEM; | ||
1100 | goto out; | ||
1101 | } | ||
1102 | memset(role, 0, sizeof(*role)); | ||
1103 | |||
1104 | rc = next_entry(buf, fp, sizeof buf); | ||
1105 | if (rc < 0) | ||
1106 | goto bad; | ||
1107 | |||
1108 | len = le32_to_cpu(buf[0]); | ||
1109 | role->value = le32_to_cpu(buf[1]); | ||
1110 | |||
1111 | key = kmalloc(len + 1,GFP_KERNEL); | ||
1112 | if (!key) { | ||
1113 | rc = -ENOMEM; | ||
1114 | goto bad; | ||
1115 | } | ||
1116 | rc = next_entry(key, fp, len); | ||
1117 | if (rc < 0) | ||
1118 | goto bad; | ||
1119 | key[len] = 0; | ||
1120 | |||
1121 | rc = ebitmap_read(&role->dominates, fp); | ||
1122 | if (rc) | ||
1123 | goto bad; | ||
1124 | |||
1125 | rc = ebitmap_read(&role->types, fp); | ||
1126 | if (rc) | ||
1127 | goto bad; | ||
1128 | |||
1129 | if (strcmp(key, OBJECT_R) == 0) { | ||
1130 | if (role->value != OBJECT_R_VAL) { | ||
1131 | printk(KERN_ERR "Role %s has wrong value %d\n", | ||
1132 | OBJECT_R, role->value); | ||
1133 | rc = -EINVAL; | ||
1134 | goto bad; | ||
1135 | } | ||
1136 | rc = 0; | ||
1137 | goto bad; | ||
1138 | } | ||
1139 | |||
1140 | rc = hashtab_insert(h, key, role); | ||
1141 | if (rc) | ||
1142 | goto bad; | ||
1143 | out: | ||
1144 | return rc; | ||
1145 | bad: | ||
1146 | role_destroy(key, role, NULL); | ||
1147 | goto out; | ||
1148 | } | ||
1149 | |||
1150 | static int type_read(struct policydb *p, struct hashtab *h, void *fp) | ||
1151 | { | ||
1152 | char *key = NULL; | ||
1153 | struct type_datum *typdatum; | ||
1154 | int rc; | ||
1155 | u32 buf[3], len; | ||
1156 | |||
1157 | typdatum = kmalloc(sizeof(*typdatum),GFP_KERNEL); | ||
1158 | if (!typdatum) { | ||
1159 | rc = -ENOMEM; | ||
1160 | return rc; | ||
1161 | } | ||
1162 | memset(typdatum, 0, sizeof(*typdatum)); | ||
1163 | |||
1164 | rc = next_entry(buf, fp, sizeof buf); | ||
1165 | if (rc < 0) | ||
1166 | goto bad; | ||
1167 | |||
1168 | len = le32_to_cpu(buf[0]); | ||
1169 | typdatum->value = le32_to_cpu(buf[1]); | ||
1170 | typdatum->primary = le32_to_cpu(buf[2]); | ||
1171 | |||
1172 | key = kmalloc(len + 1,GFP_KERNEL); | ||
1173 | if (!key) { | ||
1174 | rc = -ENOMEM; | ||
1175 | goto bad; | ||
1176 | } | ||
1177 | rc = next_entry(key, fp, len); | ||
1178 | if (rc < 0) | ||
1179 | goto bad; | ||
1180 | key[len] = 0; | ||
1181 | |||
1182 | rc = hashtab_insert(h, key, typdatum); | ||
1183 | if (rc) | ||
1184 | goto bad; | ||
1185 | out: | ||
1186 | return rc; | ||
1187 | bad: | ||
1188 | type_destroy(key, typdatum, NULL); | ||
1189 | goto out; | ||
1190 | } | ||
1191 | |||
1192 | |||
1193 | /* | ||
1194 | * Read a MLS level structure from a policydb binary | ||
1195 | * representation file. | ||
1196 | */ | ||
1197 | static int mls_read_level(struct mls_level *lp, void *fp) | ||
1198 | { | ||
1199 | u32 buf[1]; | ||
1200 | int rc; | ||
1201 | |||
1202 | memset(lp, 0, sizeof(*lp)); | ||
1203 | |||
1204 | rc = next_entry(buf, fp, sizeof buf); | ||
1205 | if (rc < 0) { | ||
1206 | printk(KERN_ERR "security: mls: truncated level\n"); | ||
1207 | goto bad; | ||
1208 | } | ||
1209 | lp->sens = le32_to_cpu(buf[0]); | ||
1210 | |||
1211 | if (ebitmap_read(&lp->cat, fp)) { | ||
1212 | printk(KERN_ERR "security: mls: error reading level " | ||
1213 | "categories\n"); | ||
1214 | goto bad; | ||
1215 | } | ||
1216 | return 0; | ||
1217 | |||
1218 | bad: | ||
1219 | return -EINVAL; | ||
1220 | } | ||
1221 | |||
1222 | static int user_read(struct policydb *p, struct hashtab *h, void *fp) | ||
1223 | { | ||
1224 | char *key = NULL; | ||
1225 | struct user_datum *usrdatum; | ||
1226 | int rc; | ||
1227 | u32 buf[2], len; | ||
1228 | |||
1229 | usrdatum = kmalloc(sizeof(*usrdatum), GFP_KERNEL); | ||
1230 | if (!usrdatum) { | ||
1231 | rc = -ENOMEM; | ||
1232 | goto out; | ||
1233 | } | ||
1234 | memset(usrdatum, 0, sizeof(*usrdatum)); | ||
1235 | |||
1236 | rc = next_entry(buf, fp, sizeof buf); | ||
1237 | if (rc < 0) | ||
1238 | goto bad; | ||
1239 | |||
1240 | len = le32_to_cpu(buf[0]); | ||
1241 | usrdatum->value = le32_to_cpu(buf[1]); | ||
1242 | |||
1243 | key = kmalloc(len + 1,GFP_KERNEL); | ||
1244 | if (!key) { | ||
1245 | rc = -ENOMEM; | ||
1246 | goto bad; | ||
1247 | } | ||
1248 | rc = next_entry(key, fp, len); | ||
1249 | if (rc < 0) | ||
1250 | goto bad; | ||
1251 | key[len] = 0; | ||
1252 | |||
1253 | rc = ebitmap_read(&usrdatum->roles, fp); | ||
1254 | if (rc) | ||
1255 | goto bad; | ||
1256 | |||
1257 | if (p->policyvers >= POLICYDB_VERSION_MLS) { | ||
1258 | rc = mls_read_range_helper(&usrdatum->range, fp); | ||
1259 | if (rc) | ||
1260 | goto bad; | ||
1261 | rc = mls_read_level(&usrdatum->dfltlevel, fp); | ||
1262 | if (rc) | ||
1263 | goto bad; | ||
1264 | } | ||
1265 | |||
1266 | rc = hashtab_insert(h, key, usrdatum); | ||
1267 | if (rc) | ||
1268 | goto bad; | ||
1269 | out: | ||
1270 | return rc; | ||
1271 | bad: | ||
1272 | user_destroy(key, usrdatum, NULL); | ||
1273 | goto out; | ||
1274 | } | ||
1275 | |||
1276 | static int sens_read(struct policydb *p, struct hashtab *h, void *fp) | ||
1277 | { | ||
1278 | char *key = NULL; | ||
1279 | struct level_datum *levdatum; | ||
1280 | int rc; | ||
1281 | u32 buf[2], len; | ||
1282 | |||
1283 | levdatum = kmalloc(sizeof(*levdatum), GFP_ATOMIC); | ||
1284 | if (!levdatum) { | ||
1285 | rc = -ENOMEM; | ||
1286 | goto out; | ||
1287 | } | ||
1288 | memset(levdatum, 0, sizeof(*levdatum)); | ||
1289 | |||
1290 | rc = next_entry(buf, fp, sizeof buf); | ||
1291 | if (rc < 0) | ||
1292 | goto bad; | ||
1293 | |||
1294 | len = le32_to_cpu(buf[0]); | ||
1295 | levdatum->isalias = le32_to_cpu(buf[1]); | ||
1296 | |||
1297 | key = kmalloc(len + 1,GFP_ATOMIC); | ||
1298 | if (!key) { | ||
1299 | rc = -ENOMEM; | ||
1300 | goto bad; | ||
1301 | } | ||
1302 | rc = next_entry(key, fp, len); | ||
1303 | if (rc < 0) | ||
1304 | goto bad; | ||
1305 | key[len] = 0; | ||
1306 | |||
1307 | levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC); | ||
1308 | if (!levdatum->level) { | ||
1309 | rc = -ENOMEM; | ||
1310 | goto bad; | ||
1311 | } | ||
1312 | if (mls_read_level(levdatum->level, fp)) { | ||
1313 | rc = -EINVAL; | ||
1314 | goto bad; | ||
1315 | } | ||
1316 | |||
1317 | rc = hashtab_insert(h, key, levdatum); | ||
1318 | if (rc) | ||
1319 | goto bad; | ||
1320 | out: | ||
1321 | return rc; | ||
1322 | bad: | ||
1323 | sens_destroy(key, levdatum, NULL); | ||
1324 | goto out; | ||
1325 | } | ||
1326 | |||
1327 | static int cat_read(struct policydb *p, struct hashtab *h, void *fp) | ||
1328 | { | ||
1329 | char *key = NULL; | ||
1330 | struct cat_datum *catdatum; | ||
1331 | int rc; | ||
1332 | u32 buf[3], len; | ||
1333 | |||
1334 | catdatum = kmalloc(sizeof(*catdatum), GFP_ATOMIC); | ||
1335 | if (!catdatum) { | ||
1336 | rc = -ENOMEM; | ||
1337 | goto out; | ||
1338 | } | ||
1339 | memset(catdatum, 0, sizeof(*catdatum)); | ||
1340 | |||
1341 | rc = next_entry(buf, fp, sizeof buf); | ||
1342 | if (rc < 0) | ||
1343 | goto bad; | ||
1344 | |||
1345 | len = le32_to_cpu(buf[0]); | ||
1346 | catdatum->value = le32_to_cpu(buf[1]); | ||
1347 | catdatum->isalias = le32_to_cpu(buf[2]); | ||
1348 | |||
1349 | key = kmalloc(len + 1,GFP_ATOMIC); | ||
1350 | if (!key) { | ||
1351 | rc = -ENOMEM; | ||
1352 | goto bad; | ||
1353 | } | ||
1354 | rc = next_entry(key, fp, len); | ||
1355 | if (rc < 0) | ||
1356 | goto bad; | ||
1357 | key[len] = 0; | ||
1358 | |||
1359 | rc = hashtab_insert(h, key, catdatum); | ||
1360 | if (rc) | ||
1361 | goto bad; | ||
1362 | out: | ||
1363 | return rc; | ||
1364 | |||
1365 | bad: | ||
1366 | cat_destroy(key, catdatum, NULL); | ||
1367 | goto out; | ||
1368 | } | ||
1369 | |||
1370 | static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) = | ||
1371 | { | ||
1372 | common_read, | ||
1373 | class_read, | ||
1374 | role_read, | ||
1375 | type_read, | ||
1376 | user_read, | ||
1377 | cond_read_bool, | ||
1378 | sens_read, | ||
1379 | cat_read, | ||
1380 | }; | ||
1381 | |||
1382 | extern int ss_initialized; | ||
1383 | |||
1384 | /* | ||
1385 | * Read the configuration data from a policy database binary | ||
1386 | * representation file into a policy database structure. | ||
1387 | */ | ||
1388 | int policydb_read(struct policydb *p, void *fp) | ||
1389 | { | ||
1390 | struct role_allow *ra, *lra; | ||
1391 | struct role_trans *tr, *ltr; | ||
1392 | struct ocontext *l, *c, *newc; | ||
1393 | struct genfs *genfs_p, *genfs, *newgenfs; | ||
1394 | int i, j, rc; | ||
1395 | u32 buf[8], len, len2, config, nprim, nel, nel2; | ||
1396 | char *policydb_str; | ||
1397 | struct policydb_compat_info *info; | ||
1398 | struct range_trans *rt, *lrt; | ||
1399 | |||
1400 | config = 0; | ||
1401 | |||
1402 | rc = policydb_init(p); | ||
1403 | if (rc) | ||
1404 | goto out; | ||
1405 | |||
1406 | /* Read the magic number and string length. */ | ||
1407 | rc = next_entry(buf, fp, sizeof(u32)* 2); | ||
1408 | if (rc < 0) | ||
1409 | goto bad; | ||
1410 | |||
1411 | for (i = 0; i < 2; i++) | ||
1412 | buf[i] = le32_to_cpu(buf[i]); | ||
1413 | |||
1414 | if (buf[0] != POLICYDB_MAGIC) { | ||
1415 | printk(KERN_ERR "security: policydb magic number 0x%x does " | ||
1416 | "not match expected magic number 0x%x\n", | ||
1417 | buf[0], POLICYDB_MAGIC); | ||
1418 | goto bad; | ||
1419 | } | ||
1420 | |||
1421 | len = buf[1]; | ||
1422 | if (len != strlen(POLICYDB_STRING)) { | ||
1423 | printk(KERN_ERR "security: policydb string length %d does not " | ||
1424 | "match expected length %Zu\n", | ||
1425 | len, strlen(POLICYDB_STRING)); | ||
1426 | goto bad; | ||
1427 | } | ||
1428 | policydb_str = kmalloc(len + 1,GFP_KERNEL); | ||
1429 | if (!policydb_str) { | ||
1430 | printk(KERN_ERR "security: unable to allocate memory for policydb " | ||
1431 | "string of length %d\n", len); | ||
1432 | rc = -ENOMEM; | ||
1433 | goto bad; | ||
1434 | } | ||
1435 | rc = next_entry(policydb_str, fp, len); | ||
1436 | if (rc < 0) { | ||
1437 | printk(KERN_ERR "security: truncated policydb string identifier\n"); | ||
1438 | kfree(policydb_str); | ||
1439 | goto bad; | ||
1440 | } | ||
1441 | policydb_str[len] = 0; | ||
1442 | if (strcmp(policydb_str, POLICYDB_STRING)) { | ||
1443 | printk(KERN_ERR "security: policydb string %s does not match " | ||
1444 | "my string %s\n", policydb_str, POLICYDB_STRING); | ||
1445 | kfree(policydb_str); | ||
1446 | goto bad; | ||
1447 | } | ||
1448 | /* Done with policydb_str. */ | ||
1449 | kfree(policydb_str); | ||
1450 | policydb_str = NULL; | ||
1451 | |||
1452 | /* Read the version, config, and table sizes. */ | ||
1453 | rc = next_entry(buf, fp, sizeof(u32)*4); | ||
1454 | if (rc < 0) | ||
1455 | goto bad; | ||
1456 | for (i = 0; i < 4; i++) | ||
1457 | buf[i] = le32_to_cpu(buf[i]); | ||
1458 | |||
1459 | p->policyvers = buf[0]; | ||
1460 | if (p->policyvers < POLICYDB_VERSION_MIN || | ||
1461 | p->policyvers > POLICYDB_VERSION_MAX) { | ||
1462 | printk(KERN_ERR "security: policydb version %d does not match " | ||
1463 | "my version range %d-%d\n", | ||
1464 | buf[0], POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX); | ||
1465 | goto bad; | ||
1466 | } | ||
1467 | |||
1468 | if ((buf[1] & POLICYDB_CONFIG_MLS)) { | ||
1469 | if (ss_initialized && !selinux_mls_enabled) { | ||
1470 | printk(KERN_ERR "Cannot switch between non-MLS and MLS " | ||
1471 | "policies\n"); | ||
1472 | goto bad; | ||
1473 | } | ||
1474 | selinux_mls_enabled = 1; | ||
1475 | config |= POLICYDB_CONFIG_MLS; | ||
1476 | |||
1477 | if (p->policyvers < POLICYDB_VERSION_MLS) { | ||
1478 | printk(KERN_ERR "security policydb version %d (MLS) " | ||
1479 | "not backwards compatible\n", p->policyvers); | ||
1480 | goto bad; | ||
1481 | } | ||
1482 | } else { | ||
1483 | if (ss_initialized && selinux_mls_enabled) { | ||
1484 | printk(KERN_ERR "Cannot switch between MLS and non-MLS " | ||
1485 | "policies\n"); | ||
1486 | goto bad; | ||
1487 | } | ||
1488 | } | ||
1489 | |||
1490 | info = policydb_lookup_compat(p->policyvers); | ||
1491 | if (!info) { | ||
1492 | printk(KERN_ERR "security: unable to find policy compat info " | ||
1493 | "for version %d\n", p->policyvers); | ||
1494 | goto bad; | ||
1495 | } | ||
1496 | |||
1497 | if (buf[2] != info->sym_num || buf[3] != info->ocon_num) { | ||
1498 | printk(KERN_ERR "security: policydb table sizes (%d,%d) do " | ||
1499 | "not match mine (%d,%d)\n", buf[2], buf[3], | ||
1500 | info->sym_num, info->ocon_num); | ||
1501 | goto bad; | ||
1502 | } | ||
1503 | |||
1504 | for (i = 0; i < info->sym_num; i++) { | ||
1505 | rc = next_entry(buf, fp, sizeof(u32)*2); | ||
1506 | if (rc < 0) | ||
1507 | goto bad; | ||
1508 | nprim = le32_to_cpu(buf[0]); | ||
1509 | nel = le32_to_cpu(buf[1]); | ||
1510 | for (j = 0; j < nel; j++) { | ||
1511 | rc = read_f[i](p, p->symtab[i].table, fp); | ||
1512 | if (rc) | ||
1513 | goto bad; | ||
1514 | } | ||
1515 | |||
1516 | p->symtab[i].nprim = nprim; | ||
1517 | } | ||
1518 | |||
1519 | rc = avtab_read(&p->te_avtab, fp, config); | ||
1520 | if (rc) | ||
1521 | goto bad; | ||
1522 | |||
1523 | if (p->policyvers >= POLICYDB_VERSION_BOOL) { | ||
1524 | rc = cond_read_list(p, fp); | ||
1525 | if (rc) | ||
1526 | goto bad; | ||
1527 | } | ||
1528 | |||
1529 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1530 | if (rc < 0) | ||
1531 | goto bad; | ||
1532 | nel = le32_to_cpu(buf[0]); | ||
1533 | ltr = NULL; | ||
1534 | for (i = 0; i < nel; i++) { | ||
1535 | tr = kmalloc(sizeof(*tr), GFP_KERNEL); | ||
1536 | if (!tr) { | ||
1537 | rc = -ENOMEM; | ||
1538 | goto bad; | ||
1539 | } | ||
1540 | memset(tr, 0, sizeof(*tr)); | ||
1541 | if (ltr) { | ||
1542 | ltr->next = tr; | ||
1543 | } else { | ||
1544 | p->role_tr = tr; | ||
1545 | } | ||
1546 | rc = next_entry(buf, fp, sizeof(u32)*3); | ||
1547 | if (rc < 0) | ||
1548 | goto bad; | ||
1549 | tr->role = le32_to_cpu(buf[0]); | ||
1550 | tr->type = le32_to_cpu(buf[1]); | ||
1551 | tr->new_role = le32_to_cpu(buf[2]); | ||
1552 | ltr = tr; | ||
1553 | } | ||
1554 | |||
1555 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1556 | if (rc < 0) | ||
1557 | goto bad; | ||
1558 | nel = le32_to_cpu(buf[0]); | ||
1559 | lra = NULL; | ||
1560 | for (i = 0; i < nel; i++) { | ||
1561 | ra = kmalloc(sizeof(*ra), GFP_KERNEL); | ||
1562 | if (!ra) { | ||
1563 | rc = -ENOMEM; | ||
1564 | goto bad; | ||
1565 | } | ||
1566 | memset(ra, 0, sizeof(*ra)); | ||
1567 | if (lra) { | ||
1568 | lra->next = ra; | ||
1569 | } else { | ||
1570 | p->role_allow = ra; | ||
1571 | } | ||
1572 | rc = next_entry(buf, fp, sizeof(u32)*2); | ||
1573 | if (rc < 0) | ||
1574 | goto bad; | ||
1575 | ra->role = le32_to_cpu(buf[0]); | ||
1576 | ra->new_role = le32_to_cpu(buf[1]); | ||
1577 | lra = ra; | ||
1578 | } | ||
1579 | |||
1580 | rc = policydb_index_classes(p); | ||
1581 | if (rc) | ||
1582 | goto bad; | ||
1583 | |||
1584 | rc = policydb_index_others(p); | ||
1585 | if (rc) | ||
1586 | goto bad; | ||
1587 | |||
1588 | for (i = 0; i < info->ocon_num; i++) { | ||
1589 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1590 | if (rc < 0) | ||
1591 | goto bad; | ||
1592 | nel = le32_to_cpu(buf[0]); | ||
1593 | l = NULL; | ||
1594 | for (j = 0; j < nel; j++) { | ||
1595 | c = kmalloc(sizeof(*c), GFP_KERNEL); | ||
1596 | if (!c) { | ||
1597 | rc = -ENOMEM; | ||
1598 | goto bad; | ||
1599 | } | ||
1600 | memset(c, 0, sizeof(*c)); | ||
1601 | if (l) { | ||
1602 | l->next = c; | ||
1603 | } else { | ||
1604 | p->ocontexts[i] = c; | ||
1605 | } | ||
1606 | l = c; | ||
1607 | rc = -EINVAL; | ||
1608 | switch (i) { | ||
1609 | case OCON_ISID: | ||
1610 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1611 | if (rc < 0) | ||
1612 | goto bad; | ||
1613 | c->sid[0] = le32_to_cpu(buf[0]); | ||
1614 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1615 | if (rc) | ||
1616 | goto bad; | ||
1617 | break; | ||
1618 | case OCON_FS: | ||
1619 | case OCON_NETIF: | ||
1620 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1621 | if (rc < 0) | ||
1622 | goto bad; | ||
1623 | len = le32_to_cpu(buf[0]); | ||
1624 | c->u.name = kmalloc(len + 1,GFP_KERNEL); | ||
1625 | if (!c->u.name) { | ||
1626 | rc = -ENOMEM; | ||
1627 | goto bad; | ||
1628 | } | ||
1629 | rc = next_entry(c->u.name, fp, len); | ||
1630 | if (rc < 0) | ||
1631 | goto bad; | ||
1632 | c->u.name[len] = 0; | ||
1633 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1634 | if (rc) | ||
1635 | goto bad; | ||
1636 | rc = context_read_and_validate(&c->context[1], p, fp); | ||
1637 | if (rc) | ||
1638 | goto bad; | ||
1639 | break; | ||
1640 | case OCON_PORT: | ||
1641 | rc = next_entry(buf, fp, sizeof(u32)*3); | ||
1642 | if (rc < 0) | ||
1643 | goto bad; | ||
1644 | c->u.port.protocol = le32_to_cpu(buf[0]); | ||
1645 | c->u.port.low_port = le32_to_cpu(buf[1]); | ||
1646 | c->u.port.high_port = le32_to_cpu(buf[2]); | ||
1647 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1648 | if (rc) | ||
1649 | goto bad; | ||
1650 | break; | ||
1651 | case OCON_NODE: | ||
1652 | rc = next_entry(buf, fp, sizeof(u32)* 2); | ||
1653 | if (rc < 0) | ||
1654 | goto bad; | ||
1655 | c->u.node.addr = le32_to_cpu(buf[0]); | ||
1656 | c->u.node.mask = le32_to_cpu(buf[1]); | ||
1657 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1658 | if (rc) | ||
1659 | goto bad; | ||
1660 | break; | ||
1661 | case OCON_FSUSE: | ||
1662 | rc = next_entry(buf, fp, sizeof(u32)*2); | ||
1663 | if (rc < 0) | ||
1664 | goto bad; | ||
1665 | c->v.behavior = le32_to_cpu(buf[0]); | ||
1666 | if (c->v.behavior > SECURITY_FS_USE_NONE) | ||
1667 | goto bad; | ||
1668 | len = le32_to_cpu(buf[1]); | ||
1669 | c->u.name = kmalloc(len + 1,GFP_KERNEL); | ||
1670 | if (!c->u.name) { | ||
1671 | rc = -ENOMEM; | ||
1672 | goto bad; | ||
1673 | } | ||
1674 | rc = next_entry(c->u.name, fp, len); | ||
1675 | if (rc < 0) | ||
1676 | goto bad; | ||
1677 | c->u.name[len] = 0; | ||
1678 | rc = context_read_and_validate(&c->context[0], p, fp); | ||
1679 | if (rc) | ||
1680 | goto bad; | ||
1681 | break; | ||
1682 | case OCON_NODE6: { | ||
1683 | int k; | ||
1684 | |||
1685 | rc = next_entry(buf, fp, sizeof(u32) * 8); | ||
1686 | if (rc < 0) | ||
1687 | goto bad; | ||
1688 | for (k = 0; k < 4; k++) | ||
1689 | c->u.node6.addr[k] = le32_to_cpu(buf[k]); | ||
1690 | for (k = 0; k < 4; k++) | ||
1691 | c->u.node6.mask[k] = le32_to_cpu(buf[k+4]); | ||
1692 | if (context_read_and_validate(&c->context[0], p, fp)) | ||
1693 | goto bad; | ||
1694 | break; | ||
1695 | } | ||
1696 | } | ||
1697 | } | ||
1698 | } | ||
1699 | |||
1700 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1701 | if (rc < 0) | ||
1702 | goto bad; | ||
1703 | nel = le32_to_cpu(buf[0]); | ||
1704 | genfs_p = NULL; | ||
1705 | rc = -EINVAL; | ||
1706 | for (i = 0; i < nel; i++) { | ||
1707 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1708 | if (rc < 0) | ||
1709 | goto bad; | ||
1710 | len = le32_to_cpu(buf[0]); | ||
1711 | newgenfs = kmalloc(sizeof(*newgenfs), GFP_KERNEL); | ||
1712 | if (!newgenfs) { | ||
1713 | rc = -ENOMEM; | ||
1714 | goto bad; | ||
1715 | } | ||
1716 | memset(newgenfs, 0, sizeof(*newgenfs)); | ||
1717 | |||
1718 | newgenfs->fstype = kmalloc(len + 1,GFP_KERNEL); | ||
1719 | if (!newgenfs->fstype) { | ||
1720 | rc = -ENOMEM; | ||
1721 | kfree(newgenfs); | ||
1722 | goto bad; | ||
1723 | } | ||
1724 | rc = next_entry(newgenfs->fstype, fp, len); | ||
1725 | if (rc < 0) { | ||
1726 | kfree(newgenfs->fstype); | ||
1727 | kfree(newgenfs); | ||
1728 | goto bad; | ||
1729 | } | ||
1730 | newgenfs->fstype[len] = 0; | ||
1731 | for (genfs_p = NULL, genfs = p->genfs; genfs; | ||
1732 | genfs_p = genfs, genfs = genfs->next) { | ||
1733 | if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { | ||
1734 | printk(KERN_ERR "security: dup genfs " | ||
1735 | "fstype %s\n", newgenfs->fstype); | ||
1736 | kfree(newgenfs->fstype); | ||
1737 | kfree(newgenfs); | ||
1738 | goto bad; | ||
1739 | } | ||
1740 | if (strcmp(newgenfs->fstype, genfs->fstype) < 0) | ||
1741 | break; | ||
1742 | } | ||
1743 | newgenfs->next = genfs; | ||
1744 | if (genfs_p) | ||
1745 | genfs_p->next = newgenfs; | ||
1746 | else | ||
1747 | p->genfs = newgenfs; | ||
1748 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1749 | if (rc < 0) | ||
1750 | goto bad; | ||
1751 | nel2 = le32_to_cpu(buf[0]); | ||
1752 | for (j = 0; j < nel2; j++) { | ||
1753 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1754 | if (rc < 0) | ||
1755 | goto bad; | ||
1756 | len = le32_to_cpu(buf[0]); | ||
1757 | |||
1758 | newc = kmalloc(sizeof(*newc), GFP_KERNEL); | ||
1759 | if (!newc) { | ||
1760 | rc = -ENOMEM; | ||
1761 | goto bad; | ||
1762 | } | ||
1763 | memset(newc, 0, sizeof(*newc)); | ||
1764 | |||
1765 | newc->u.name = kmalloc(len + 1,GFP_KERNEL); | ||
1766 | if (!newc->u.name) { | ||
1767 | rc = -ENOMEM; | ||
1768 | goto bad_newc; | ||
1769 | } | ||
1770 | rc = next_entry(newc->u.name, fp, len); | ||
1771 | if (rc < 0) | ||
1772 | goto bad_newc; | ||
1773 | newc->u.name[len] = 0; | ||
1774 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1775 | if (rc < 0) | ||
1776 | goto bad_newc; | ||
1777 | newc->v.sclass = le32_to_cpu(buf[0]); | ||
1778 | if (context_read_and_validate(&newc->context[0], p, fp)) | ||
1779 | goto bad_newc; | ||
1780 | for (l = NULL, c = newgenfs->head; c; | ||
1781 | l = c, c = c->next) { | ||
1782 | if (!strcmp(newc->u.name, c->u.name) && | ||
1783 | (!c->v.sclass || !newc->v.sclass || | ||
1784 | newc->v.sclass == c->v.sclass)) { | ||
1785 | printk(KERN_ERR "security: dup genfs " | ||
1786 | "entry (%s,%s)\n", | ||
1787 | newgenfs->fstype, c->u.name); | ||
1788 | goto bad_newc; | ||
1789 | } | ||
1790 | len = strlen(newc->u.name); | ||
1791 | len2 = strlen(c->u.name); | ||
1792 | if (len > len2) | ||
1793 | break; | ||
1794 | } | ||
1795 | |||
1796 | newc->next = c; | ||
1797 | if (l) | ||
1798 | l->next = newc; | ||
1799 | else | ||
1800 | newgenfs->head = newc; | ||
1801 | } | ||
1802 | } | ||
1803 | |||
1804 | if (p->policyvers >= POLICYDB_VERSION_MLS) { | ||
1805 | rc = next_entry(buf, fp, sizeof(u32)); | ||
1806 | if (rc < 0) | ||
1807 | goto bad; | ||
1808 | nel = le32_to_cpu(buf[0]); | ||
1809 | lrt = NULL; | ||
1810 | for (i = 0; i < nel; i++) { | ||
1811 | rt = kmalloc(sizeof(*rt), GFP_KERNEL); | ||
1812 | if (!rt) { | ||
1813 | rc = -ENOMEM; | ||
1814 | goto bad; | ||
1815 | } | ||
1816 | memset(rt, 0, sizeof(*rt)); | ||
1817 | if (lrt) | ||
1818 | lrt->next = rt; | ||
1819 | else | ||
1820 | p->range_tr = rt; | ||
1821 | rc = next_entry(buf, fp, (sizeof(u32) * 2)); | ||
1822 | if (rc < 0) | ||
1823 | goto bad; | ||
1824 | rt->dom = le32_to_cpu(buf[0]); | ||
1825 | rt->type = le32_to_cpu(buf[1]); | ||
1826 | rc = mls_read_range_helper(&rt->range, fp); | ||
1827 | if (rc) | ||
1828 | goto bad; | ||
1829 | lrt = rt; | ||
1830 | } | ||
1831 | } | ||
1832 | |||
1833 | rc = 0; | ||
1834 | out: | ||
1835 | return rc; | ||
1836 | bad_newc: | ||
1837 | ocontext_destroy(newc,OCON_FSUSE); | ||
1838 | bad: | ||
1839 | if (!rc) | ||
1840 | rc = -EINVAL; | ||
1841 | policydb_destroy(p); | ||
1842 | goto out; | ||
1843 | } | ||
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h new file mode 100644 index 000000000000..2470e2a1a1c3 --- /dev/null +++ b/security/selinux/ss/policydb.h | |||
@@ -0,0 +1,275 @@ | |||
1 | /* | ||
2 | * A policy database (policydb) specifies the | ||
3 | * configuration data for the security policy. | ||
4 | * | ||
5 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
6 | */ | ||
7 | |||
8 | /* | ||
9 | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> | ||
10 | * | ||
11 | * Support for enhanced MLS infrastructure. | ||
12 | * | ||
13 | * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> | ||
14 | * | ||
15 | * Added conditional policy language extensions | ||
16 | * | ||
17 | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. | ||
18 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | ||
19 | * This program is free software; you can redistribute it and/or modify | ||
20 | * it under the terms of the GNU General Public License as published by | ||
21 | * the Free Software Foundation, version 2. | ||
22 | */ | ||
23 | |||
24 | #ifndef _SS_POLICYDB_H_ | ||
25 | #define _SS_POLICYDB_H_ | ||
26 | |||
27 | #include "symtab.h" | ||
28 | #include "avtab.h" | ||
29 | #include "sidtab.h" | ||
30 | #include "context.h" | ||
31 | #include "constraint.h" | ||
32 | |||
33 | /* | ||
34 | * A datum type is defined for each kind of symbol | ||
35 | * in the configuration data: individual permissions, | ||
36 | * common prefixes for access vectors, classes, | ||
37 | * users, roles, types, sensitivities, categories, etc. | ||
38 | */ | ||
39 | |||
40 | /* Permission attributes */ | ||
41 | struct perm_datum { | ||
42 | u32 value; /* permission bit + 1 */ | ||
43 | }; | ||
44 | |||
45 | /* Attributes of a common prefix for access vectors */ | ||
46 | struct common_datum { | ||
47 | u32 value; /* internal common value */ | ||
48 | struct symtab permissions; /* common permissions */ | ||
49 | }; | ||
50 | |||
51 | /* Class attributes */ | ||
52 | struct class_datum { | ||
53 | u32 value; /* class value */ | ||
54 | char *comkey; /* common name */ | ||
55 | struct common_datum *comdatum; /* common datum */ | ||
56 | struct symtab permissions; /* class-specific permission symbol table */ | ||
57 | struct constraint_node *constraints; /* constraints on class permissions */ | ||
58 | struct constraint_node *validatetrans; /* special transition rules */ | ||
59 | }; | ||
60 | |||
61 | /* Role attributes */ | ||
62 | struct role_datum { | ||
63 | u32 value; /* internal role value */ | ||
64 | struct ebitmap dominates; /* set of roles dominated by this role */ | ||
65 | struct ebitmap types; /* set of authorized types for role */ | ||
66 | }; | ||
67 | |||
68 | struct role_trans { | ||
69 | u32 role; /* current role */ | ||
70 | u32 type; /* program executable type */ | ||
71 | u32 new_role; /* new role */ | ||
72 | struct role_trans *next; | ||
73 | }; | ||
74 | |||
75 | struct role_allow { | ||
76 | u32 role; /* current role */ | ||
77 | u32 new_role; /* new role */ | ||
78 | struct role_allow *next; | ||
79 | }; | ||
80 | |||
81 | /* Type attributes */ | ||
82 | struct type_datum { | ||
83 | u32 value; /* internal type value */ | ||
84 | unsigned char primary; /* primary name? */ | ||
85 | }; | ||
86 | |||
87 | /* User attributes */ | ||
88 | struct user_datum { | ||
89 | u32 value; /* internal user value */ | ||
90 | struct ebitmap roles; /* set of authorized roles for user */ | ||
91 | struct mls_range range; /* MLS range (min - max) for user */ | ||
92 | struct mls_level dfltlevel; /* default login MLS level for user */ | ||
93 | }; | ||
94 | |||
95 | |||
96 | /* Sensitivity attributes */ | ||
97 | struct level_datum { | ||
98 | struct mls_level *level; /* sensitivity and associated categories */ | ||
99 | unsigned char isalias; /* is this sensitivity an alias for another? */ | ||
100 | }; | ||
101 | |||
102 | /* Category attributes */ | ||
103 | struct cat_datum { | ||
104 | u32 value; /* internal category bit + 1 */ | ||
105 | unsigned char isalias; /* is this category an alias for another? */ | ||
106 | }; | ||
107 | |||
108 | struct range_trans { | ||
109 | u32 dom; /* current process domain */ | ||
110 | u32 type; /* program executable type */ | ||
111 | struct mls_range range; /* new range */ | ||
112 | struct range_trans *next; | ||
113 | }; | ||
114 | |||
115 | /* Boolean data type */ | ||
116 | struct cond_bool_datum { | ||
117 | __u32 value; /* internal type value */ | ||
118 | int state; | ||
119 | }; | ||
120 | |||
121 | struct cond_node; | ||
122 | |||
123 | /* | ||
124 | * The configuration data includes security contexts for | ||
125 | * initial SIDs, unlabeled file systems, TCP and UDP port numbers, | ||
126 | * network interfaces, and nodes. This structure stores the | ||
127 | * relevant data for one such entry. Entries of the same kind | ||
128 | * (e.g. all initial SIDs) are linked together into a list. | ||
129 | */ | ||
130 | struct ocontext { | ||
131 | union { | ||
132 | char *name; /* name of initial SID, fs, netif, fstype, path */ | ||
133 | struct { | ||
134 | u8 protocol; | ||
135 | u16 low_port; | ||
136 | u16 high_port; | ||
137 | } port; /* TCP or UDP port information */ | ||
138 | struct { | ||
139 | u32 addr; | ||
140 | u32 mask; | ||
141 | } node; /* node information */ | ||
142 | struct { | ||
143 | u32 addr[4]; | ||
144 | u32 mask[4]; | ||
145 | } node6; /* IPv6 node information */ | ||
146 | } u; | ||
147 | union { | ||
148 | u32 sclass; /* security class for genfs */ | ||
149 | u32 behavior; /* labeling behavior for fs_use */ | ||
150 | } v; | ||
151 | struct context context[2]; /* security context(s) */ | ||
152 | u32 sid[2]; /* SID(s) */ | ||
153 | struct ocontext *next; | ||
154 | }; | ||
155 | |||
156 | struct genfs { | ||
157 | char *fstype; | ||
158 | struct ocontext *head; | ||
159 | struct genfs *next; | ||
160 | }; | ||
161 | |||
162 | /* symbol table array indices */ | ||
163 | #define SYM_COMMONS 0 | ||
164 | #define SYM_CLASSES 1 | ||
165 | #define SYM_ROLES 2 | ||
166 | #define SYM_TYPES 3 | ||
167 | #define SYM_USERS 4 | ||
168 | #define SYM_BOOLS 5 | ||
169 | #define SYM_LEVELS 6 | ||
170 | #define SYM_CATS 7 | ||
171 | #define SYM_NUM 8 | ||
172 | |||
173 | /* object context array indices */ | ||
174 | #define OCON_ISID 0 /* initial SIDs */ | ||
175 | #define OCON_FS 1 /* unlabeled file systems */ | ||
176 | #define OCON_PORT 2 /* TCP and UDP port numbers */ | ||
177 | #define OCON_NETIF 3 /* network interfaces */ | ||
178 | #define OCON_NODE 4 /* nodes */ | ||
179 | #define OCON_FSUSE 5 /* fs_use */ | ||
180 | #define OCON_NODE6 6 /* IPv6 nodes */ | ||
181 | #define OCON_NUM 7 | ||
182 | |||
183 | /* The policy database */ | ||
184 | struct policydb { | ||
185 | /* symbol tables */ | ||
186 | struct symtab symtab[SYM_NUM]; | ||
187 | #define p_commons symtab[SYM_COMMONS] | ||
188 | #define p_classes symtab[SYM_CLASSES] | ||
189 | #define p_roles symtab[SYM_ROLES] | ||
190 | #define p_types symtab[SYM_TYPES] | ||
191 | #define p_users symtab[SYM_USERS] | ||
192 | #define p_bools symtab[SYM_BOOLS] | ||
193 | #define p_levels symtab[SYM_LEVELS] | ||
194 | #define p_cats symtab[SYM_CATS] | ||
195 | |||
196 | /* symbol names indexed by (value - 1) */ | ||
197 | char **sym_val_to_name[SYM_NUM]; | ||
198 | #define p_common_val_to_name sym_val_to_name[SYM_COMMONS] | ||
199 | #define p_class_val_to_name sym_val_to_name[SYM_CLASSES] | ||
200 | #define p_role_val_to_name sym_val_to_name[SYM_ROLES] | ||
201 | #define p_type_val_to_name sym_val_to_name[SYM_TYPES] | ||
202 | #define p_user_val_to_name sym_val_to_name[SYM_USERS] | ||
203 | #define p_bool_val_to_name sym_val_to_name[SYM_BOOLS] | ||
204 | #define p_sens_val_to_name sym_val_to_name[SYM_LEVELS] | ||
205 | #define p_cat_val_to_name sym_val_to_name[SYM_CATS] | ||
206 | |||
207 | /* class, role, and user attributes indexed by (value - 1) */ | ||
208 | struct class_datum **class_val_to_struct; | ||
209 | struct role_datum **role_val_to_struct; | ||
210 | struct user_datum **user_val_to_struct; | ||
211 | |||
212 | /* type enforcement access vectors and transitions */ | ||
213 | struct avtab te_avtab; | ||
214 | |||
215 | /* role transitions */ | ||
216 | struct role_trans *role_tr; | ||
217 | |||
218 | /* bools indexed by (value - 1) */ | ||
219 | struct cond_bool_datum **bool_val_to_struct; | ||
220 | /* type enforcement conditional access vectors and transitions */ | ||
221 | struct avtab te_cond_avtab; | ||
222 | /* linked list indexing te_cond_avtab by conditional */ | ||
223 | struct cond_node* cond_list; | ||
224 | |||
225 | /* role allows */ | ||
226 | struct role_allow *role_allow; | ||
227 | |||
228 | /* security contexts of initial SIDs, unlabeled file systems, | ||
229 | TCP or UDP port numbers, network interfaces and nodes */ | ||
230 | struct ocontext *ocontexts[OCON_NUM]; | ||
231 | |||
232 | /* security contexts for files in filesystems that cannot support | ||
233 | a persistent label mapping or use another | ||
234 | fixed labeling behavior. */ | ||
235 | struct genfs *genfs; | ||
236 | |||
237 | /* range transitions */ | ||
238 | struct range_trans *range_tr; | ||
239 | |||
240 | unsigned int policyvers; | ||
241 | }; | ||
242 | |||
243 | extern void policydb_destroy(struct policydb *p); | ||
244 | extern int policydb_load_isids(struct policydb *p, struct sidtab *s); | ||
245 | extern int policydb_context_isvalid(struct policydb *p, struct context *c); | ||
246 | extern int policydb_read(struct policydb *p, void *fp); | ||
247 | |||
248 | #define PERM_SYMTAB_SIZE 32 | ||
249 | |||
250 | #define POLICYDB_CONFIG_MLS 1 | ||
251 | |||
252 | #define OBJECT_R "object_r" | ||
253 | #define OBJECT_R_VAL 1 | ||
254 | |||
255 | #define POLICYDB_MAGIC SELINUX_MAGIC | ||
256 | #define POLICYDB_STRING "SE Linux" | ||
257 | |||
258 | struct policy_file { | ||
259 | char *data; | ||
260 | size_t len; | ||
261 | }; | ||
262 | |||
263 | static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes) | ||
264 | { | ||
265 | if (bytes > fp->len) | ||
266 | return -EINVAL; | ||
267 | |||
268 | memcpy(buf, fp->data, bytes); | ||
269 | fp->data += bytes; | ||
270 | fp->len -= bytes; | ||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | #endif /* _SS_POLICYDB_H_ */ | ||
275 | |||
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c new file mode 100644 index 000000000000..5a820cf88c9c --- /dev/null +++ b/security/selinux/ss/services.c | |||
@@ -0,0 +1,1777 @@ | |||
1 | /* | ||
2 | * Implementation of the security services. | ||
3 | * | ||
4 | * Authors : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | * James Morris <jmorris@redhat.com> | ||
6 | * | ||
7 | * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> | ||
8 | * | ||
9 | * Support for enhanced MLS infrastructure. | ||
10 | * | ||
11 | * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> | ||
12 | * | ||
13 | * Added conditional policy language extensions | ||
14 | * | ||
15 | * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. | ||
16 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC | ||
17 | * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com> | ||
18 | * This program is free software; you can redistribute it and/or modify | ||
19 | * it under the terms of the GNU General Public License as published by | ||
20 | * the Free Software Foundation, version 2. | ||
21 | */ | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/string.h> | ||
25 | #include <linux/spinlock.h> | ||
26 | #include <linux/errno.h> | ||
27 | #include <linux/in.h> | ||
28 | #include <linux/sched.h> | ||
29 | #include <linux/audit.h> | ||
30 | #include <asm/semaphore.h> | ||
31 | #include "flask.h" | ||
32 | #include "avc.h" | ||
33 | #include "avc_ss.h" | ||
34 | #include "security.h" | ||
35 | #include "context.h" | ||
36 | #include "policydb.h" | ||
37 | #include "sidtab.h" | ||
38 | #include "services.h" | ||
39 | #include "conditional.h" | ||
40 | #include "mls.h" | ||
41 | |||
42 | extern void selnl_notify_policyload(u32 seqno); | ||
43 | unsigned int policydb_loaded_version; | ||
44 | |||
45 | static DEFINE_RWLOCK(policy_rwlock); | ||
46 | #define POLICY_RDLOCK read_lock(&policy_rwlock) | ||
47 | #define POLICY_WRLOCK write_lock_irq(&policy_rwlock) | ||
48 | #define POLICY_RDUNLOCK read_unlock(&policy_rwlock) | ||
49 | #define POLICY_WRUNLOCK write_unlock_irq(&policy_rwlock) | ||
50 | |||
51 | static DECLARE_MUTEX(load_sem); | ||
52 | #define LOAD_LOCK down(&load_sem) | ||
53 | #define LOAD_UNLOCK up(&load_sem) | ||
54 | |||
55 | static struct sidtab sidtab; | ||
56 | struct policydb policydb; | ||
57 | int ss_initialized = 0; | ||
58 | |||
59 | /* | ||
60 | * The largest sequence number that has been used when | ||
61 | * providing an access decision to the access vector cache. | ||
62 | * The sequence number only changes when a policy change | ||
63 | * occurs. | ||
64 | */ | ||
65 | static u32 latest_granting = 0; | ||
66 | |||
67 | /* Forward declaration. */ | ||
68 | static int context_struct_to_string(struct context *context, char **scontext, | ||
69 | u32 *scontext_len); | ||
70 | |||
71 | /* | ||
72 | * Return the boolean value of a constraint expression | ||
73 | * when it is applied to the specified source and target | ||
74 | * security contexts. | ||
75 | * | ||
76 | * xcontext is a special beast... It is used by the validatetrans rules | ||
77 | * only. For these rules, scontext is the context before the transition, | ||
78 | * tcontext is the context after the transition, and xcontext is the context | ||
79 | * of the process performing the transition. All other callers of | ||
80 | * constraint_expr_eval should pass in NULL for xcontext. | ||
81 | */ | ||
82 | static int constraint_expr_eval(struct context *scontext, | ||
83 | struct context *tcontext, | ||
84 | struct context *xcontext, | ||
85 | struct constraint_expr *cexpr) | ||
86 | { | ||
87 | u32 val1, val2; | ||
88 | struct context *c; | ||
89 | struct role_datum *r1, *r2; | ||
90 | struct mls_level *l1, *l2; | ||
91 | struct constraint_expr *e; | ||
92 | int s[CEXPR_MAXDEPTH]; | ||
93 | int sp = -1; | ||
94 | |||
95 | for (e = cexpr; e; e = e->next) { | ||
96 | switch (e->expr_type) { | ||
97 | case CEXPR_NOT: | ||
98 | BUG_ON(sp < 0); | ||
99 | s[sp] = !s[sp]; | ||
100 | break; | ||
101 | case CEXPR_AND: | ||
102 | BUG_ON(sp < 1); | ||
103 | sp--; | ||
104 | s[sp] &= s[sp+1]; | ||
105 | break; | ||
106 | case CEXPR_OR: | ||
107 | BUG_ON(sp < 1); | ||
108 | sp--; | ||
109 | s[sp] |= s[sp+1]; | ||
110 | break; | ||
111 | case CEXPR_ATTR: | ||
112 | if (sp == (CEXPR_MAXDEPTH-1)) | ||
113 | return 0; | ||
114 | switch (e->attr) { | ||
115 | case CEXPR_USER: | ||
116 | val1 = scontext->user; | ||
117 | val2 = tcontext->user; | ||
118 | break; | ||
119 | case CEXPR_TYPE: | ||
120 | val1 = scontext->type; | ||
121 | val2 = tcontext->type; | ||
122 | break; | ||
123 | case CEXPR_ROLE: | ||
124 | val1 = scontext->role; | ||
125 | val2 = tcontext->role; | ||
126 | r1 = policydb.role_val_to_struct[val1 - 1]; | ||
127 | r2 = policydb.role_val_to_struct[val2 - 1]; | ||
128 | switch (e->op) { | ||
129 | case CEXPR_DOM: | ||
130 | s[++sp] = ebitmap_get_bit(&r1->dominates, | ||
131 | val2 - 1); | ||
132 | continue; | ||
133 | case CEXPR_DOMBY: | ||
134 | s[++sp] = ebitmap_get_bit(&r2->dominates, | ||
135 | val1 - 1); | ||
136 | continue; | ||
137 | case CEXPR_INCOMP: | ||
138 | s[++sp] = ( !ebitmap_get_bit(&r1->dominates, | ||
139 | val2 - 1) && | ||
140 | !ebitmap_get_bit(&r2->dominates, | ||
141 | val1 - 1) ); | ||
142 | continue; | ||
143 | default: | ||
144 | break; | ||
145 | } | ||
146 | break; | ||
147 | case CEXPR_L1L2: | ||
148 | l1 = &(scontext->range.level[0]); | ||
149 | l2 = &(tcontext->range.level[0]); | ||
150 | goto mls_ops; | ||
151 | case CEXPR_L1H2: | ||
152 | l1 = &(scontext->range.level[0]); | ||
153 | l2 = &(tcontext->range.level[1]); | ||
154 | goto mls_ops; | ||
155 | case CEXPR_H1L2: | ||
156 | l1 = &(scontext->range.level[1]); | ||
157 | l2 = &(tcontext->range.level[0]); | ||
158 | goto mls_ops; | ||
159 | case CEXPR_H1H2: | ||
160 | l1 = &(scontext->range.level[1]); | ||
161 | l2 = &(tcontext->range.level[1]); | ||
162 | goto mls_ops; | ||
163 | case CEXPR_L1H1: | ||
164 | l1 = &(scontext->range.level[0]); | ||
165 | l2 = &(scontext->range.level[1]); | ||
166 | goto mls_ops; | ||
167 | case CEXPR_L2H2: | ||
168 | l1 = &(tcontext->range.level[0]); | ||
169 | l2 = &(tcontext->range.level[1]); | ||
170 | goto mls_ops; | ||
171 | mls_ops: | ||
172 | switch (e->op) { | ||
173 | case CEXPR_EQ: | ||
174 | s[++sp] = mls_level_eq(l1, l2); | ||
175 | continue; | ||
176 | case CEXPR_NEQ: | ||
177 | s[++sp] = !mls_level_eq(l1, l2); | ||
178 | continue; | ||
179 | case CEXPR_DOM: | ||
180 | s[++sp] = mls_level_dom(l1, l2); | ||
181 | continue; | ||
182 | case CEXPR_DOMBY: | ||
183 | s[++sp] = mls_level_dom(l2, l1); | ||
184 | continue; | ||
185 | case CEXPR_INCOMP: | ||
186 | s[++sp] = mls_level_incomp(l2, l1); | ||
187 | continue; | ||
188 | default: | ||
189 | BUG(); | ||
190 | return 0; | ||
191 | } | ||
192 | break; | ||
193 | default: | ||
194 | BUG(); | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | switch (e->op) { | ||
199 | case CEXPR_EQ: | ||
200 | s[++sp] = (val1 == val2); | ||
201 | break; | ||
202 | case CEXPR_NEQ: | ||
203 | s[++sp] = (val1 != val2); | ||
204 | break; | ||
205 | default: | ||
206 | BUG(); | ||
207 | return 0; | ||
208 | } | ||
209 | break; | ||
210 | case CEXPR_NAMES: | ||
211 | if (sp == (CEXPR_MAXDEPTH-1)) | ||
212 | return 0; | ||
213 | c = scontext; | ||
214 | if (e->attr & CEXPR_TARGET) | ||
215 | c = tcontext; | ||
216 | else if (e->attr & CEXPR_XTARGET) { | ||
217 | c = xcontext; | ||
218 | if (!c) { | ||
219 | BUG(); | ||
220 | return 0; | ||
221 | } | ||
222 | } | ||
223 | if (e->attr & CEXPR_USER) | ||
224 | val1 = c->user; | ||
225 | else if (e->attr & CEXPR_ROLE) | ||
226 | val1 = c->role; | ||
227 | else if (e->attr & CEXPR_TYPE) | ||
228 | val1 = c->type; | ||
229 | else { | ||
230 | BUG(); | ||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | switch (e->op) { | ||
235 | case CEXPR_EQ: | ||
236 | s[++sp] = ebitmap_get_bit(&e->names, val1 - 1); | ||
237 | break; | ||
238 | case CEXPR_NEQ: | ||
239 | s[++sp] = !ebitmap_get_bit(&e->names, val1 - 1); | ||
240 | break; | ||
241 | default: | ||
242 | BUG(); | ||
243 | return 0; | ||
244 | } | ||
245 | break; | ||
246 | default: | ||
247 | BUG(); | ||
248 | return 0; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | BUG_ON(sp != 0); | ||
253 | return s[0]; | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * Compute access vectors based on a context structure pair for | ||
258 | * the permissions in a particular class. | ||
259 | */ | ||
260 | static int context_struct_compute_av(struct context *scontext, | ||
261 | struct context *tcontext, | ||
262 | u16 tclass, | ||
263 | u32 requested, | ||
264 | struct av_decision *avd) | ||
265 | { | ||
266 | struct constraint_node *constraint; | ||
267 | struct role_allow *ra; | ||
268 | struct avtab_key avkey; | ||
269 | struct avtab_datum *avdatum; | ||
270 | struct class_datum *tclass_datum; | ||
271 | |||
272 | /* | ||
273 | * Remap extended Netlink classes for old policy versions. | ||
274 | * Do this here rather than socket_type_to_security_class() | ||
275 | * in case a newer policy version is loaded, allowing sockets | ||
276 | * to remain in the correct class. | ||
277 | */ | ||
278 | if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS) | ||
279 | if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET && | ||
280 | tclass <= SECCLASS_NETLINK_DNRT_SOCKET) | ||
281 | tclass = SECCLASS_NETLINK_SOCKET; | ||
282 | |||
283 | if (!tclass || tclass > policydb.p_classes.nprim) { | ||
284 | printk(KERN_ERR "security_compute_av: unrecognized class %d\n", | ||
285 | tclass); | ||
286 | return -EINVAL; | ||
287 | } | ||
288 | tclass_datum = policydb.class_val_to_struct[tclass - 1]; | ||
289 | |||
290 | /* | ||
291 | * Initialize the access vectors to the default values. | ||
292 | */ | ||
293 | avd->allowed = 0; | ||
294 | avd->decided = 0xffffffff; | ||
295 | avd->auditallow = 0; | ||
296 | avd->auditdeny = 0xffffffff; | ||
297 | avd->seqno = latest_granting; | ||
298 | |||
299 | /* | ||
300 | * If a specific type enforcement rule was defined for | ||
301 | * this permission check, then use it. | ||
302 | */ | ||
303 | avkey.source_type = scontext->type; | ||
304 | avkey.target_type = tcontext->type; | ||
305 | avkey.target_class = tclass; | ||
306 | avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_AV); | ||
307 | if (avdatum) { | ||
308 | if (avdatum->specified & AVTAB_ALLOWED) | ||
309 | avd->allowed = avtab_allowed(avdatum); | ||
310 | if (avdatum->specified & AVTAB_AUDITDENY) | ||
311 | avd->auditdeny = avtab_auditdeny(avdatum); | ||
312 | if (avdatum->specified & AVTAB_AUDITALLOW) | ||
313 | avd->auditallow = avtab_auditallow(avdatum); | ||
314 | } | ||
315 | |||
316 | /* Check conditional av table for additional permissions */ | ||
317 | cond_compute_av(&policydb.te_cond_avtab, &avkey, avd); | ||
318 | |||
319 | /* | ||
320 | * Remove any permissions prohibited by a constraint (this includes | ||
321 | * the MLS policy). | ||
322 | */ | ||
323 | constraint = tclass_datum->constraints; | ||
324 | while (constraint) { | ||
325 | if ((constraint->permissions & (avd->allowed)) && | ||
326 | !constraint_expr_eval(scontext, tcontext, NULL, | ||
327 | constraint->expr)) { | ||
328 | avd->allowed = (avd->allowed) & ~(constraint->permissions); | ||
329 | } | ||
330 | constraint = constraint->next; | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * If checking process transition permission and the | ||
335 | * role is changing, then check the (current_role, new_role) | ||
336 | * pair. | ||
337 | */ | ||
338 | if (tclass == SECCLASS_PROCESS && | ||
339 | (avd->allowed & (PROCESS__TRANSITION | PROCESS__DYNTRANSITION)) && | ||
340 | scontext->role != tcontext->role) { | ||
341 | for (ra = policydb.role_allow; ra; ra = ra->next) { | ||
342 | if (scontext->role == ra->role && | ||
343 | tcontext->role == ra->new_role) | ||
344 | break; | ||
345 | } | ||
346 | if (!ra) | ||
347 | avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION | | ||
348 | PROCESS__DYNTRANSITION); | ||
349 | } | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static int security_validtrans_handle_fail(struct context *ocontext, | ||
355 | struct context *ncontext, | ||
356 | struct context *tcontext, | ||
357 | u16 tclass) | ||
358 | { | ||
359 | char *o = NULL, *n = NULL, *t = NULL; | ||
360 | u32 olen, nlen, tlen; | ||
361 | |||
362 | if (context_struct_to_string(ocontext, &o, &olen) < 0) | ||
363 | goto out; | ||
364 | if (context_struct_to_string(ncontext, &n, &nlen) < 0) | ||
365 | goto out; | ||
366 | if (context_struct_to_string(tcontext, &t, &tlen) < 0) | ||
367 | goto out; | ||
368 | audit_log(current->audit_context, | ||
369 | "security_validate_transition: denied for" | ||
370 | " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s", | ||
371 | o, n, t, policydb.p_class_val_to_name[tclass-1]); | ||
372 | out: | ||
373 | kfree(o); | ||
374 | kfree(n); | ||
375 | kfree(t); | ||
376 | |||
377 | if (!selinux_enforcing) | ||
378 | return 0; | ||
379 | return -EPERM; | ||
380 | } | ||
381 | |||
382 | int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, | ||
383 | u16 tclass) | ||
384 | { | ||
385 | struct context *ocontext; | ||
386 | struct context *ncontext; | ||
387 | struct context *tcontext; | ||
388 | struct class_datum *tclass_datum; | ||
389 | struct constraint_node *constraint; | ||
390 | int rc = 0; | ||
391 | |||
392 | if (!ss_initialized) | ||
393 | return 0; | ||
394 | |||
395 | POLICY_RDLOCK; | ||
396 | |||
397 | /* | ||
398 | * Remap extended Netlink classes for old policy versions. | ||
399 | * Do this here rather than socket_type_to_security_class() | ||
400 | * in case a newer policy version is loaded, allowing sockets | ||
401 | * to remain in the correct class. | ||
402 | */ | ||
403 | if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS) | ||
404 | if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET && | ||
405 | tclass <= SECCLASS_NETLINK_DNRT_SOCKET) | ||
406 | tclass = SECCLASS_NETLINK_SOCKET; | ||
407 | |||
408 | if (!tclass || tclass > policydb.p_classes.nprim) { | ||
409 | printk(KERN_ERR "security_validate_transition: " | ||
410 | "unrecognized class %d\n", tclass); | ||
411 | rc = -EINVAL; | ||
412 | goto out; | ||
413 | } | ||
414 | tclass_datum = policydb.class_val_to_struct[tclass - 1]; | ||
415 | |||
416 | ocontext = sidtab_search(&sidtab, oldsid); | ||
417 | if (!ocontext) { | ||
418 | printk(KERN_ERR "security_validate_transition: " | ||
419 | " unrecognized SID %d\n", oldsid); | ||
420 | rc = -EINVAL; | ||
421 | goto out; | ||
422 | } | ||
423 | |||
424 | ncontext = sidtab_search(&sidtab, newsid); | ||
425 | if (!ncontext) { | ||
426 | printk(KERN_ERR "security_validate_transition: " | ||
427 | " unrecognized SID %d\n", newsid); | ||
428 | rc = -EINVAL; | ||
429 | goto out; | ||
430 | } | ||
431 | |||
432 | tcontext = sidtab_search(&sidtab, tasksid); | ||
433 | if (!tcontext) { | ||
434 | printk(KERN_ERR "security_validate_transition: " | ||
435 | " unrecognized SID %d\n", tasksid); | ||
436 | rc = -EINVAL; | ||
437 | goto out; | ||
438 | } | ||
439 | |||
440 | constraint = tclass_datum->validatetrans; | ||
441 | while (constraint) { | ||
442 | if (!constraint_expr_eval(ocontext, ncontext, tcontext, | ||
443 | constraint->expr)) { | ||
444 | rc = security_validtrans_handle_fail(ocontext, ncontext, | ||
445 | tcontext, tclass); | ||
446 | goto out; | ||
447 | } | ||
448 | constraint = constraint->next; | ||
449 | } | ||
450 | |||
451 | out: | ||
452 | POLICY_RDUNLOCK; | ||
453 | return rc; | ||
454 | } | ||
455 | |||
456 | /** | ||
457 | * security_compute_av - Compute access vector decisions. | ||
458 | * @ssid: source security identifier | ||
459 | * @tsid: target security identifier | ||
460 | * @tclass: target security class | ||
461 | * @requested: requested permissions | ||
462 | * @avd: access vector decisions | ||
463 | * | ||
464 | * Compute a set of access vector decisions based on the | ||
465 | * SID pair (@ssid, @tsid) for the permissions in @tclass. | ||
466 | * Return -%EINVAL if any of the parameters are invalid or %0 | ||
467 | * if the access vector decisions were computed successfully. | ||
468 | */ | ||
469 | int security_compute_av(u32 ssid, | ||
470 | u32 tsid, | ||
471 | u16 tclass, | ||
472 | u32 requested, | ||
473 | struct av_decision *avd) | ||
474 | { | ||
475 | struct context *scontext = NULL, *tcontext = NULL; | ||
476 | int rc = 0; | ||
477 | |||
478 | if (!ss_initialized) { | ||
479 | avd->allowed = requested; | ||
480 | avd->decided = requested; | ||
481 | avd->auditallow = 0; | ||
482 | avd->auditdeny = 0xffffffff; | ||
483 | avd->seqno = latest_granting; | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | POLICY_RDLOCK; | ||
488 | |||
489 | scontext = sidtab_search(&sidtab, ssid); | ||
490 | if (!scontext) { | ||
491 | printk(KERN_ERR "security_compute_av: unrecognized SID %d\n", | ||
492 | ssid); | ||
493 | rc = -EINVAL; | ||
494 | goto out; | ||
495 | } | ||
496 | tcontext = sidtab_search(&sidtab, tsid); | ||
497 | if (!tcontext) { | ||
498 | printk(KERN_ERR "security_compute_av: unrecognized SID %d\n", | ||
499 | tsid); | ||
500 | rc = -EINVAL; | ||
501 | goto out; | ||
502 | } | ||
503 | |||
504 | rc = context_struct_compute_av(scontext, tcontext, tclass, | ||
505 | requested, avd); | ||
506 | out: | ||
507 | POLICY_RDUNLOCK; | ||
508 | return rc; | ||
509 | } | ||
510 | |||
511 | /* | ||
512 | * Write the security context string representation of | ||
513 | * the context structure `context' into a dynamically | ||
514 | * allocated string of the correct size. Set `*scontext' | ||
515 | * to point to this string and set `*scontext_len' to | ||
516 | * the length of the string. | ||
517 | */ | ||
518 | static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len) | ||
519 | { | ||
520 | char *scontextp; | ||
521 | |||
522 | *scontext = NULL; | ||
523 | *scontext_len = 0; | ||
524 | |||
525 | /* Compute the size of the context. */ | ||
526 | *scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1; | ||
527 | *scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1; | ||
528 | *scontext_len += strlen(policydb.p_type_val_to_name[context->type - 1]) + 1; | ||
529 | *scontext_len += mls_compute_context_len(context); | ||
530 | |||
531 | /* Allocate space for the context; caller must free this space. */ | ||
532 | scontextp = kmalloc(*scontext_len, GFP_ATOMIC); | ||
533 | if (!scontextp) { | ||
534 | return -ENOMEM; | ||
535 | } | ||
536 | *scontext = scontextp; | ||
537 | |||
538 | /* | ||
539 | * Copy the user name, role name and type name into the context. | ||
540 | */ | ||
541 | sprintf(scontextp, "%s:%s:%s", | ||
542 | policydb.p_user_val_to_name[context->user - 1], | ||
543 | policydb.p_role_val_to_name[context->role - 1], | ||
544 | policydb.p_type_val_to_name[context->type - 1]); | ||
545 | scontextp += strlen(policydb.p_user_val_to_name[context->user - 1]) + | ||
546 | 1 + strlen(policydb.p_role_val_to_name[context->role - 1]) + | ||
547 | 1 + strlen(policydb.p_type_val_to_name[context->type - 1]); | ||
548 | |||
549 | mls_sid_to_context(context, &scontextp); | ||
550 | |||
551 | *scontextp = 0; | ||
552 | |||
553 | return 0; | ||
554 | } | ||
555 | |||
556 | #include "initial_sid_to_string.h" | ||
557 | |||
558 | /** | ||
559 | * security_sid_to_context - Obtain a context for a given SID. | ||
560 | * @sid: security identifier, SID | ||
561 | * @scontext: security context | ||
562 | * @scontext_len: length in bytes | ||
563 | * | ||
564 | * Write the string representation of the context associated with @sid | ||
565 | * into a dynamically allocated string of the correct size. Set @scontext | ||
566 | * to point to this string and set @scontext_len to the length of the string. | ||
567 | */ | ||
568 | int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len) | ||
569 | { | ||
570 | struct context *context; | ||
571 | int rc = 0; | ||
572 | |||
573 | if (!ss_initialized) { | ||
574 | if (sid <= SECINITSID_NUM) { | ||
575 | char *scontextp; | ||
576 | |||
577 | *scontext_len = strlen(initial_sid_to_string[sid]) + 1; | ||
578 | scontextp = kmalloc(*scontext_len,GFP_ATOMIC); | ||
579 | strcpy(scontextp, initial_sid_to_string[sid]); | ||
580 | *scontext = scontextp; | ||
581 | goto out; | ||
582 | } | ||
583 | printk(KERN_ERR "security_sid_to_context: called before initial " | ||
584 | "load_policy on unknown SID %d\n", sid); | ||
585 | rc = -EINVAL; | ||
586 | goto out; | ||
587 | } | ||
588 | POLICY_RDLOCK; | ||
589 | context = sidtab_search(&sidtab, sid); | ||
590 | if (!context) { | ||
591 | printk(KERN_ERR "security_sid_to_context: unrecognized SID " | ||
592 | "%d\n", sid); | ||
593 | rc = -EINVAL; | ||
594 | goto out_unlock; | ||
595 | } | ||
596 | rc = context_struct_to_string(context, scontext, scontext_len); | ||
597 | out_unlock: | ||
598 | POLICY_RDUNLOCK; | ||
599 | out: | ||
600 | return rc; | ||
601 | |||
602 | } | ||
603 | |||
604 | /** | ||
605 | * security_context_to_sid - Obtain a SID for a given security context. | ||
606 | * @scontext: security context | ||
607 | * @scontext_len: length in bytes | ||
608 | * @sid: security identifier, SID | ||
609 | * | ||
610 | * Obtains a SID associated with the security context that | ||
611 | * has the string representation specified by @scontext. | ||
612 | * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient | ||
613 | * memory is available, or 0 on success. | ||
614 | */ | ||
615 | int security_context_to_sid(char *scontext, u32 scontext_len, u32 *sid) | ||
616 | { | ||
617 | char *scontext2; | ||
618 | struct context context; | ||
619 | struct role_datum *role; | ||
620 | struct type_datum *typdatum; | ||
621 | struct user_datum *usrdatum; | ||
622 | char *scontextp, *p, oldc; | ||
623 | int rc = 0; | ||
624 | |||
625 | if (!ss_initialized) { | ||
626 | int i; | ||
627 | |||
628 | for (i = 1; i < SECINITSID_NUM; i++) { | ||
629 | if (!strcmp(initial_sid_to_string[i], scontext)) { | ||
630 | *sid = i; | ||
631 | goto out; | ||
632 | } | ||
633 | } | ||
634 | *sid = SECINITSID_KERNEL; | ||
635 | goto out; | ||
636 | } | ||
637 | *sid = SECSID_NULL; | ||
638 | |||
639 | /* Copy the string so that we can modify the copy as we parse it. | ||
640 | The string should already by null terminated, but we append a | ||
641 | null suffix to the copy to avoid problems with the existing | ||
642 | attr package, which doesn't view the null terminator as part | ||
643 | of the attribute value. */ | ||
644 | scontext2 = kmalloc(scontext_len+1,GFP_KERNEL); | ||
645 | if (!scontext2) { | ||
646 | rc = -ENOMEM; | ||
647 | goto out; | ||
648 | } | ||
649 | memcpy(scontext2, scontext, scontext_len); | ||
650 | scontext2[scontext_len] = 0; | ||
651 | |||
652 | context_init(&context); | ||
653 | *sid = SECSID_NULL; | ||
654 | |||
655 | POLICY_RDLOCK; | ||
656 | |||
657 | /* Parse the security context. */ | ||
658 | |||
659 | rc = -EINVAL; | ||
660 | scontextp = (char *) scontext2; | ||
661 | |||
662 | /* Extract the user. */ | ||
663 | p = scontextp; | ||
664 | while (*p && *p != ':') | ||
665 | p++; | ||
666 | |||
667 | if (*p == 0) | ||
668 | goto out_unlock; | ||
669 | |||
670 | *p++ = 0; | ||
671 | |||
672 | usrdatum = hashtab_search(policydb.p_users.table, scontextp); | ||
673 | if (!usrdatum) | ||
674 | goto out_unlock; | ||
675 | |||
676 | context.user = usrdatum->value; | ||
677 | |||
678 | /* Extract role. */ | ||
679 | scontextp = p; | ||
680 | while (*p && *p != ':') | ||
681 | p++; | ||
682 | |||
683 | if (*p == 0) | ||
684 | goto out_unlock; | ||
685 | |||
686 | *p++ = 0; | ||
687 | |||
688 | role = hashtab_search(policydb.p_roles.table, scontextp); | ||
689 | if (!role) | ||
690 | goto out_unlock; | ||
691 | context.role = role->value; | ||
692 | |||
693 | /* Extract type. */ | ||
694 | scontextp = p; | ||
695 | while (*p && *p != ':') | ||
696 | p++; | ||
697 | oldc = *p; | ||
698 | *p++ = 0; | ||
699 | |||
700 | typdatum = hashtab_search(policydb.p_types.table, scontextp); | ||
701 | if (!typdatum) | ||
702 | goto out_unlock; | ||
703 | |||
704 | context.type = typdatum->value; | ||
705 | |||
706 | rc = mls_context_to_sid(oldc, &p, &context); | ||
707 | if (rc) | ||
708 | goto out_unlock; | ||
709 | |||
710 | if ((p - scontext2) < scontext_len) { | ||
711 | rc = -EINVAL; | ||
712 | goto out_unlock; | ||
713 | } | ||
714 | |||
715 | /* Check the validity of the new context. */ | ||
716 | if (!policydb_context_isvalid(&policydb, &context)) { | ||
717 | rc = -EINVAL; | ||
718 | goto out_unlock; | ||
719 | } | ||
720 | /* Obtain the new sid. */ | ||
721 | rc = sidtab_context_to_sid(&sidtab, &context, sid); | ||
722 | out_unlock: | ||
723 | POLICY_RDUNLOCK; | ||
724 | context_destroy(&context); | ||
725 | kfree(scontext2); | ||
726 | out: | ||
727 | return rc; | ||
728 | } | ||
729 | |||
730 | static int compute_sid_handle_invalid_context( | ||
731 | struct context *scontext, | ||
732 | struct context *tcontext, | ||
733 | u16 tclass, | ||
734 | struct context *newcontext) | ||
735 | { | ||
736 | char *s = NULL, *t = NULL, *n = NULL; | ||
737 | u32 slen, tlen, nlen; | ||
738 | |||
739 | if (context_struct_to_string(scontext, &s, &slen) < 0) | ||
740 | goto out; | ||
741 | if (context_struct_to_string(tcontext, &t, &tlen) < 0) | ||
742 | goto out; | ||
743 | if (context_struct_to_string(newcontext, &n, &nlen) < 0) | ||
744 | goto out; | ||
745 | audit_log(current->audit_context, | ||
746 | "security_compute_sid: invalid context %s" | ||
747 | " for scontext=%s" | ||
748 | " tcontext=%s" | ||
749 | " tclass=%s", | ||
750 | n, s, t, policydb.p_class_val_to_name[tclass-1]); | ||
751 | out: | ||
752 | kfree(s); | ||
753 | kfree(t); | ||
754 | kfree(n); | ||
755 | if (!selinux_enforcing) | ||
756 | return 0; | ||
757 | return -EACCES; | ||
758 | } | ||
759 | |||
760 | static int security_compute_sid(u32 ssid, | ||
761 | u32 tsid, | ||
762 | u16 tclass, | ||
763 | u32 specified, | ||
764 | u32 *out_sid) | ||
765 | { | ||
766 | struct context *scontext = NULL, *tcontext = NULL, newcontext; | ||
767 | struct role_trans *roletr = NULL; | ||
768 | struct avtab_key avkey; | ||
769 | struct avtab_datum *avdatum; | ||
770 | struct avtab_node *node; | ||
771 | unsigned int type_change = 0; | ||
772 | int rc = 0; | ||
773 | |||
774 | if (!ss_initialized) { | ||
775 | switch (tclass) { | ||
776 | case SECCLASS_PROCESS: | ||
777 | *out_sid = ssid; | ||
778 | break; | ||
779 | default: | ||
780 | *out_sid = tsid; | ||
781 | break; | ||
782 | } | ||
783 | goto out; | ||
784 | } | ||
785 | |||
786 | POLICY_RDLOCK; | ||
787 | |||
788 | scontext = sidtab_search(&sidtab, ssid); | ||
789 | if (!scontext) { | ||
790 | printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n", | ||
791 | ssid); | ||
792 | rc = -EINVAL; | ||
793 | goto out_unlock; | ||
794 | } | ||
795 | tcontext = sidtab_search(&sidtab, tsid); | ||
796 | if (!tcontext) { | ||
797 | printk(KERN_ERR "security_compute_sid: unrecognized SID %d\n", | ||
798 | tsid); | ||
799 | rc = -EINVAL; | ||
800 | goto out_unlock; | ||
801 | } | ||
802 | |||
803 | context_init(&newcontext); | ||
804 | |||
805 | /* Set the user identity. */ | ||
806 | switch (specified) { | ||
807 | case AVTAB_TRANSITION: | ||
808 | case AVTAB_CHANGE: | ||
809 | /* Use the process user identity. */ | ||
810 | newcontext.user = scontext->user; | ||
811 | break; | ||
812 | case AVTAB_MEMBER: | ||
813 | /* Use the related object owner. */ | ||
814 | newcontext.user = tcontext->user; | ||
815 | break; | ||
816 | } | ||
817 | |||
818 | /* Set the role and type to default values. */ | ||
819 | switch (tclass) { | ||
820 | case SECCLASS_PROCESS: | ||
821 | /* Use the current role and type of process. */ | ||
822 | newcontext.role = scontext->role; | ||
823 | newcontext.type = scontext->type; | ||
824 | break; | ||
825 | default: | ||
826 | /* Use the well-defined object role. */ | ||
827 | newcontext.role = OBJECT_R_VAL; | ||
828 | /* Use the type of the related object. */ | ||
829 | newcontext.type = tcontext->type; | ||
830 | } | ||
831 | |||
832 | /* Look for a type transition/member/change rule. */ | ||
833 | avkey.source_type = scontext->type; | ||
834 | avkey.target_type = tcontext->type; | ||
835 | avkey.target_class = tclass; | ||
836 | avdatum = avtab_search(&policydb.te_avtab, &avkey, AVTAB_TYPE); | ||
837 | |||
838 | /* If no permanent rule, also check for enabled conditional rules */ | ||
839 | if(!avdatum) { | ||
840 | node = avtab_search_node(&policydb.te_cond_avtab, &avkey, specified); | ||
841 | for (; node != NULL; node = avtab_search_node_next(node, specified)) { | ||
842 | if (node->datum.specified & AVTAB_ENABLED) { | ||
843 | avdatum = &node->datum; | ||
844 | break; | ||
845 | } | ||
846 | } | ||
847 | } | ||
848 | |||
849 | type_change = (avdatum && (avdatum->specified & specified)); | ||
850 | if (type_change) { | ||
851 | /* Use the type from the type transition/member/change rule. */ | ||
852 | switch (specified) { | ||
853 | case AVTAB_TRANSITION: | ||
854 | newcontext.type = avtab_transition(avdatum); | ||
855 | break; | ||
856 | case AVTAB_MEMBER: | ||
857 | newcontext.type = avtab_member(avdatum); | ||
858 | break; | ||
859 | case AVTAB_CHANGE: | ||
860 | newcontext.type = avtab_change(avdatum); | ||
861 | break; | ||
862 | } | ||
863 | } | ||
864 | |||
865 | /* Check for class-specific changes. */ | ||
866 | switch (tclass) { | ||
867 | case SECCLASS_PROCESS: | ||
868 | if (specified & AVTAB_TRANSITION) { | ||
869 | /* Look for a role transition rule. */ | ||
870 | for (roletr = policydb.role_tr; roletr; | ||
871 | roletr = roletr->next) { | ||
872 | if (roletr->role == scontext->role && | ||
873 | roletr->type == tcontext->type) { | ||
874 | /* Use the role transition rule. */ | ||
875 | newcontext.role = roletr->new_role; | ||
876 | break; | ||
877 | } | ||
878 | } | ||
879 | } | ||
880 | break; | ||
881 | default: | ||
882 | break; | ||
883 | } | ||
884 | |||
885 | /* Set the MLS attributes. | ||
886 | This is done last because it may allocate memory. */ | ||
887 | rc = mls_compute_sid(scontext, tcontext, tclass, specified, &newcontext); | ||
888 | if (rc) | ||
889 | goto out_unlock; | ||
890 | |||
891 | /* Check the validity of the context. */ | ||
892 | if (!policydb_context_isvalid(&policydb, &newcontext)) { | ||
893 | rc = compute_sid_handle_invalid_context(scontext, | ||
894 | tcontext, | ||
895 | tclass, | ||
896 | &newcontext); | ||
897 | if (rc) | ||
898 | goto out_unlock; | ||
899 | } | ||
900 | /* Obtain the sid for the context. */ | ||
901 | rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid); | ||
902 | out_unlock: | ||
903 | POLICY_RDUNLOCK; | ||
904 | context_destroy(&newcontext); | ||
905 | out: | ||
906 | return rc; | ||
907 | } | ||
908 | |||
909 | /** | ||
910 | * security_transition_sid - Compute the SID for a new subject/object. | ||
911 | * @ssid: source security identifier | ||
912 | * @tsid: target security identifier | ||
913 | * @tclass: target security class | ||
914 | * @out_sid: security identifier for new subject/object | ||
915 | * | ||
916 | * Compute a SID to use for labeling a new subject or object in the | ||
917 | * class @tclass based on a SID pair (@ssid, @tsid). | ||
918 | * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM | ||
919 | * if insufficient memory is available, or %0 if the new SID was | ||
920 | * computed successfully. | ||
921 | */ | ||
922 | int security_transition_sid(u32 ssid, | ||
923 | u32 tsid, | ||
924 | u16 tclass, | ||
925 | u32 *out_sid) | ||
926 | { | ||
927 | return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, out_sid); | ||
928 | } | ||
929 | |||
930 | /** | ||
931 | * security_member_sid - Compute the SID for member selection. | ||
932 | * @ssid: source security identifier | ||
933 | * @tsid: target security identifier | ||
934 | * @tclass: target security class | ||
935 | * @out_sid: security identifier for selected member | ||
936 | * | ||
937 | * Compute a SID to use when selecting a member of a polyinstantiated | ||
938 | * object of class @tclass based on a SID pair (@ssid, @tsid). | ||
939 | * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM | ||
940 | * if insufficient memory is available, or %0 if the SID was | ||
941 | * computed successfully. | ||
942 | */ | ||
943 | int security_member_sid(u32 ssid, | ||
944 | u32 tsid, | ||
945 | u16 tclass, | ||
946 | u32 *out_sid) | ||
947 | { | ||
948 | return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid); | ||
949 | } | ||
950 | |||
951 | /** | ||
952 | * security_change_sid - Compute the SID for object relabeling. | ||
953 | * @ssid: source security identifier | ||
954 | * @tsid: target security identifier | ||
955 | * @tclass: target security class | ||
956 | * @out_sid: security identifier for selected member | ||
957 | * | ||
958 | * Compute a SID to use for relabeling an object of class @tclass | ||
959 | * based on a SID pair (@ssid, @tsid). | ||
960 | * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM | ||
961 | * if insufficient memory is available, or %0 if the SID was | ||
962 | * computed successfully. | ||
963 | */ | ||
964 | int security_change_sid(u32 ssid, | ||
965 | u32 tsid, | ||
966 | u16 tclass, | ||
967 | u32 *out_sid) | ||
968 | { | ||
969 | return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid); | ||
970 | } | ||
971 | |||
972 | /* | ||
973 | * Verify that each permission that is defined under the | ||
974 | * existing policy is still defined with the same value | ||
975 | * in the new policy. | ||
976 | */ | ||
977 | static int validate_perm(void *key, void *datum, void *p) | ||
978 | { | ||
979 | struct hashtab *h; | ||
980 | struct perm_datum *perdatum, *perdatum2; | ||
981 | int rc = 0; | ||
982 | |||
983 | |||
984 | h = p; | ||
985 | perdatum = datum; | ||
986 | |||
987 | perdatum2 = hashtab_search(h, key); | ||
988 | if (!perdatum2) { | ||
989 | printk(KERN_ERR "security: permission %s disappeared", | ||
990 | (char *)key); | ||
991 | rc = -ENOENT; | ||
992 | goto out; | ||
993 | } | ||
994 | if (perdatum->value != perdatum2->value) { | ||
995 | printk(KERN_ERR "security: the value of permission %s changed", | ||
996 | (char *)key); | ||
997 | rc = -EINVAL; | ||
998 | } | ||
999 | out: | ||
1000 | return rc; | ||
1001 | } | ||
1002 | |||
1003 | /* | ||
1004 | * Verify that each class that is defined under the | ||
1005 | * existing policy is still defined with the same | ||
1006 | * attributes in the new policy. | ||
1007 | */ | ||
1008 | static int validate_class(void *key, void *datum, void *p) | ||
1009 | { | ||
1010 | struct policydb *newp; | ||
1011 | struct class_datum *cladatum, *cladatum2; | ||
1012 | int rc; | ||
1013 | |||
1014 | newp = p; | ||
1015 | cladatum = datum; | ||
1016 | |||
1017 | cladatum2 = hashtab_search(newp->p_classes.table, key); | ||
1018 | if (!cladatum2) { | ||
1019 | printk(KERN_ERR "security: class %s disappeared\n", | ||
1020 | (char *)key); | ||
1021 | rc = -ENOENT; | ||
1022 | goto out; | ||
1023 | } | ||
1024 | if (cladatum->value != cladatum2->value) { | ||
1025 | printk(KERN_ERR "security: the value of class %s changed\n", | ||
1026 | (char *)key); | ||
1027 | rc = -EINVAL; | ||
1028 | goto out; | ||
1029 | } | ||
1030 | if ((cladatum->comdatum && !cladatum2->comdatum) || | ||
1031 | (!cladatum->comdatum && cladatum2->comdatum)) { | ||
1032 | printk(KERN_ERR "security: the inherits clause for the access " | ||
1033 | "vector definition for class %s changed\n", (char *)key); | ||
1034 | rc = -EINVAL; | ||
1035 | goto out; | ||
1036 | } | ||
1037 | if (cladatum->comdatum) { | ||
1038 | rc = hashtab_map(cladatum->comdatum->permissions.table, validate_perm, | ||
1039 | cladatum2->comdatum->permissions.table); | ||
1040 | if (rc) { | ||
1041 | printk(" in the access vector definition for class " | ||
1042 | "%s\n", (char *)key); | ||
1043 | goto out; | ||
1044 | } | ||
1045 | } | ||
1046 | rc = hashtab_map(cladatum->permissions.table, validate_perm, | ||
1047 | cladatum2->permissions.table); | ||
1048 | if (rc) | ||
1049 | printk(" in access vector definition for class %s\n", | ||
1050 | (char *)key); | ||
1051 | out: | ||
1052 | return rc; | ||
1053 | } | ||
1054 | |||
1055 | /* Clone the SID into the new SID table. */ | ||
1056 | static int clone_sid(u32 sid, | ||
1057 | struct context *context, | ||
1058 | void *arg) | ||
1059 | { | ||
1060 | struct sidtab *s = arg; | ||
1061 | |||
1062 | return sidtab_insert(s, sid, context); | ||
1063 | } | ||
1064 | |||
1065 | static inline int convert_context_handle_invalid_context(struct context *context) | ||
1066 | { | ||
1067 | int rc = 0; | ||
1068 | |||
1069 | if (selinux_enforcing) { | ||
1070 | rc = -EINVAL; | ||
1071 | } else { | ||
1072 | char *s; | ||
1073 | u32 len; | ||
1074 | |||
1075 | context_struct_to_string(context, &s, &len); | ||
1076 | printk(KERN_ERR "security: context %s is invalid\n", s); | ||
1077 | kfree(s); | ||
1078 | } | ||
1079 | return rc; | ||
1080 | } | ||
1081 | |||
1082 | struct convert_context_args { | ||
1083 | struct policydb *oldp; | ||
1084 | struct policydb *newp; | ||
1085 | }; | ||
1086 | |||
1087 | /* | ||
1088 | * Convert the values in the security context | ||
1089 | * structure `c' from the values specified | ||
1090 | * in the policy `p->oldp' to the values specified | ||
1091 | * in the policy `p->newp'. Verify that the | ||
1092 | * context is valid under the new policy. | ||
1093 | */ | ||
1094 | static int convert_context(u32 key, | ||
1095 | struct context *c, | ||
1096 | void *p) | ||
1097 | { | ||
1098 | struct convert_context_args *args; | ||
1099 | struct context oldc; | ||
1100 | struct role_datum *role; | ||
1101 | struct type_datum *typdatum; | ||
1102 | struct user_datum *usrdatum; | ||
1103 | char *s; | ||
1104 | u32 len; | ||
1105 | int rc; | ||
1106 | |||
1107 | args = p; | ||
1108 | |||
1109 | rc = context_cpy(&oldc, c); | ||
1110 | if (rc) | ||
1111 | goto out; | ||
1112 | |||
1113 | rc = -EINVAL; | ||
1114 | |||
1115 | /* Convert the user. */ | ||
1116 | usrdatum = hashtab_search(args->newp->p_users.table, | ||
1117 | args->oldp->p_user_val_to_name[c->user - 1]); | ||
1118 | if (!usrdatum) { | ||
1119 | goto bad; | ||
1120 | } | ||
1121 | c->user = usrdatum->value; | ||
1122 | |||
1123 | /* Convert the role. */ | ||
1124 | role = hashtab_search(args->newp->p_roles.table, | ||
1125 | args->oldp->p_role_val_to_name[c->role - 1]); | ||
1126 | if (!role) { | ||
1127 | goto bad; | ||
1128 | } | ||
1129 | c->role = role->value; | ||
1130 | |||
1131 | /* Convert the type. */ | ||
1132 | typdatum = hashtab_search(args->newp->p_types.table, | ||
1133 | args->oldp->p_type_val_to_name[c->type - 1]); | ||
1134 | if (!typdatum) { | ||
1135 | goto bad; | ||
1136 | } | ||
1137 | c->type = typdatum->value; | ||
1138 | |||
1139 | rc = mls_convert_context(args->oldp, args->newp, c); | ||
1140 | if (rc) | ||
1141 | goto bad; | ||
1142 | |||
1143 | /* Check the validity of the new context. */ | ||
1144 | if (!policydb_context_isvalid(args->newp, c)) { | ||
1145 | rc = convert_context_handle_invalid_context(&oldc); | ||
1146 | if (rc) | ||
1147 | goto bad; | ||
1148 | } | ||
1149 | |||
1150 | context_destroy(&oldc); | ||
1151 | out: | ||
1152 | return rc; | ||
1153 | bad: | ||
1154 | context_struct_to_string(&oldc, &s, &len); | ||
1155 | context_destroy(&oldc); | ||
1156 | printk(KERN_ERR "security: invalidating context %s\n", s); | ||
1157 | kfree(s); | ||
1158 | goto out; | ||
1159 | } | ||
1160 | |||
1161 | extern void selinux_complete_init(void); | ||
1162 | |||
1163 | /** | ||
1164 | * security_load_policy - Load a security policy configuration. | ||
1165 | * @data: binary policy data | ||
1166 | * @len: length of data in bytes | ||
1167 | * | ||
1168 | * Load a new set of security policy configuration data, | ||
1169 | * validate it and convert the SID table as necessary. | ||
1170 | * This function will flush the access vector cache after | ||
1171 | * loading the new policy. | ||
1172 | */ | ||
1173 | int security_load_policy(void *data, size_t len) | ||
1174 | { | ||
1175 | struct policydb oldpolicydb, newpolicydb; | ||
1176 | struct sidtab oldsidtab, newsidtab; | ||
1177 | struct convert_context_args args; | ||
1178 | u32 seqno; | ||
1179 | int rc = 0; | ||
1180 | struct policy_file file = { data, len }, *fp = &file; | ||
1181 | |||
1182 | LOAD_LOCK; | ||
1183 | |||
1184 | if (!ss_initialized) { | ||
1185 | avtab_cache_init(); | ||
1186 | if (policydb_read(&policydb, fp)) { | ||
1187 | LOAD_UNLOCK; | ||
1188 | avtab_cache_destroy(); | ||
1189 | return -EINVAL; | ||
1190 | } | ||
1191 | if (policydb_load_isids(&policydb, &sidtab)) { | ||
1192 | LOAD_UNLOCK; | ||
1193 | policydb_destroy(&policydb); | ||
1194 | avtab_cache_destroy(); | ||
1195 | return -EINVAL; | ||
1196 | } | ||
1197 | policydb_loaded_version = policydb.policyvers; | ||
1198 | ss_initialized = 1; | ||
1199 | |||
1200 | LOAD_UNLOCK; | ||
1201 | selinux_complete_init(); | ||
1202 | return 0; | ||
1203 | } | ||
1204 | |||
1205 | #if 0 | ||
1206 | sidtab_hash_eval(&sidtab, "sids"); | ||
1207 | #endif | ||
1208 | |||
1209 | if (policydb_read(&newpolicydb, fp)) { | ||
1210 | LOAD_UNLOCK; | ||
1211 | return -EINVAL; | ||
1212 | } | ||
1213 | |||
1214 | sidtab_init(&newsidtab); | ||
1215 | |||
1216 | /* Verify that the existing classes did not change. */ | ||
1217 | if (hashtab_map(policydb.p_classes.table, validate_class, &newpolicydb)) { | ||
1218 | printk(KERN_ERR "security: the definition of an existing " | ||
1219 | "class changed\n"); | ||
1220 | rc = -EINVAL; | ||
1221 | goto err; | ||
1222 | } | ||
1223 | |||
1224 | /* Clone the SID table. */ | ||
1225 | sidtab_shutdown(&sidtab); | ||
1226 | if (sidtab_map(&sidtab, clone_sid, &newsidtab)) { | ||
1227 | rc = -ENOMEM; | ||
1228 | goto err; | ||
1229 | } | ||
1230 | |||
1231 | /* Convert the internal representations of contexts | ||
1232 | in the new SID table and remove invalid SIDs. */ | ||
1233 | args.oldp = &policydb; | ||
1234 | args.newp = &newpolicydb; | ||
1235 | sidtab_map_remove_on_error(&newsidtab, convert_context, &args); | ||
1236 | |||
1237 | /* Save the old policydb and SID table to free later. */ | ||
1238 | memcpy(&oldpolicydb, &policydb, sizeof policydb); | ||
1239 | sidtab_set(&oldsidtab, &sidtab); | ||
1240 | |||
1241 | /* Install the new policydb and SID table. */ | ||
1242 | POLICY_WRLOCK; | ||
1243 | memcpy(&policydb, &newpolicydb, sizeof policydb); | ||
1244 | sidtab_set(&sidtab, &newsidtab); | ||
1245 | seqno = ++latest_granting; | ||
1246 | policydb_loaded_version = policydb.policyvers; | ||
1247 | POLICY_WRUNLOCK; | ||
1248 | LOAD_UNLOCK; | ||
1249 | |||
1250 | /* Free the old policydb and SID table. */ | ||
1251 | policydb_destroy(&oldpolicydb); | ||
1252 | sidtab_destroy(&oldsidtab); | ||
1253 | |||
1254 | avc_ss_reset(seqno); | ||
1255 | selnl_notify_policyload(seqno); | ||
1256 | |||
1257 | return 0; | ||
1258 | |||
1259 | err: | ||
1260 | LOAD_UNLOCK; | ||
1261 | sidtab_destroy(&newsidtab); | ||
1262 | policydb_destroy(&newpolicydb); | ||
1263 | return rc; | ||
1264 | |||
1265 | } | ||
1266 | |||
1267 | /** | ||
1268 | * security_port_sid - Obtain the SID for a port. | ||
1269 | * @domain: communication domain aka address family | ||
1270 | * @type: socket type | ||
1271 | * @protocol: protocol number | ||
1272 | * @port: port number | ||
1273 | * @out_sid: security identifier | ||
1274 | */ | ||
1275 | int security_port_sid(u16 domain, | ||
1276 | u16 type, | ||
1277 | u8 protocol, | ||
1278 | u16 port, | ||
1279 | u32 *out_sid) | ||
1280 | { | ||
1281 | struct ocontext *c; | ||
1282 | int rc = 0; | ||
1283 | |||
1284 | POLICY_RDLOCK; | ||
1285 | |||
1286 | c = policydb.ocontexts[OCON_PORT]; | ||
1287 | while (c) { | ||
1288 | if (c->u.port.protocol == protocol && | ||
1289 | c->u.port.low_port <= port && | ||
1290 | c->u.port.high_port >= port) | ||
1291 | break; | ||
1292 | c = c->next; | ||
1293 | } | ||
1294 | |||
1295 | if (c) { | ||
1296 | if (!c->sid[0]) { | ||
1297 | rc = sidtab_context_to_sid(&sidtab, | ||
1298 | &c->context[0], | ||
1299 | &c->sid[0]); | ||
1300 | if (rc) | ||
1301 | goto out; | ||
1302 | } | ||
1303 | *out_sid = c->sid[0]; | ||
1304 | } else { | ||
1305 | *out_sid = SECINITSID_PORT; | ||
1306 | } | ||
1307 | |||
1308 | out: | ||
1309 | POLICY_RDUNLOCK; | ||
1310 | return rc; | ||
1311 | } | ||
1312 | |||
1313 | /** | ||
1314 | * security_netif_sid - Obtain the SID for a network interface. | ||
1315 | * @name: interface name | ||
1316 | * @if_sid: interface SID | ||
1317 | * @msg_sid: default SID for received packets | ||
1318 | */ | ||
1319 | int security_netif_sid(char *name, | ||
1320 | u32 *if_sid, | ||
1321 | u32 *msg_sid) | ||
1322 | { | ||
1323 | int rc = 0; | ||
1324 | struct ocontext *c; | ||
1325 | |||
1326 | POLICY_RDLOCK; | ||
1327 | |||
1328 | c = policydb.ocontexts[OCON_NETIF]; | ||
1329 | while (c) { | ||
1330 | if (strcmp(name, c->u.name) == 0) | ||
1331 | break; | ||
1332 | c = c->next; | ||
1333 | } | ||
1334 | |||
1335 | if (c) { | ||
1336 | if (!c->sid[0] || !c->sid[1]) { | ||
1337 | rc = sidtab_context_to_sid(&sidtab, | ||
1338 | &c->context[0], | ||
1339 | &c->sid[0]); | ||
1340 | if (rc) | ||
1341 | goto out; | ||
1342 | rc = sidtab_context_to_sid(&sidtab, | ||
1343 | &c->context[1], | ||
1344 | &c->sid[1]); | ||
1345 | if (rc) | ||
1346 | goto out; | ||
1347 | } | ||
1348 | *if_sid = c->sid[0]; | ||
1349 | *msg_sid = c->sid[1]; | ||
1350 | } else { | ||
1351 | *if_sid = SECINITSID_NETIF; | ||
1352 | *msg_sid = SECINITSID_NETMSG; | ||
1353 | } | ||
1354 | |||
1355 | out: | ||
1356 | POLICY_RDUNLOCK; | ||
1357 | return rc; | ||
1358 | } | ||
1359 | |||
1360 | static int match_ipv6_addrmask(u32 *input, u32 *addr, u32 *mask) | ||
1361 | { | ||
1362 | int i, fail = 0; | ||
1363 | |||
1364 | for(i = 0; i < 4; i++) | ||
1365 | if(addr[i] != (input[i] & mask[i])) { | ||
1366 | fail = 1; | ||
1367 | break; | ||
1368 | } | ||
1369 | |||
1370 | return !fail; | ||
1371 | } | ||
1372 | |||
1373 | /** | ||
1374 | * security_node_sid - Obtain the SID for a node (host). | ||
1375 | * @domain: communication domain aka address family | ||
1376 | * @addrp: address | ||
1377 | * @addrlen: address length in bytes | ||
1378 | * @out_sid: security identifier | ||
1379 | */ | ||
1380 | int security_node_sid(u16 domain, | ||
1381 | void *addrp, | ||
1382 | u32 addrlen, | ||
1383 | u32 *out_sid) | ||
1384 | { | ||
1385 | int rc = 0; | ||
1386 | struct ocontext *c; | ||
1387 | |||
1388 | POLICY_RDLOCK; | ||
1389 | |||
1390 | switch (domain) { | ||
1391 | case AF_INET: { | ||
1392 | u32 addr; | ||
1393 | |||
1394 | if (addrlen != sizeof(u32)) { | ||
1395 | rc = -EINVAL; | ||
1396 | goto out; | ||
1397 | } | ||
1398 | |||
1399 | addr = *((u32 *)addrp); | ||
1400 | |||
1401 | c = policydb.ocontexts[OCON_NODE]; | ||
1402 | while (c) { | ||
1403 | if (c->u.node.addr == (addr & c->u.node.mask)) | ||
1404 | break; | ||
1405 | c = c->next; | ||
1406 | } | ||
1407 | break; | ||
1408 | } | ||
1409 | |||
1410 | case AF_INET6: | ||
1411 | if (addrlen != sizeof(u64) * 2) { | ||
1412 | rc = -EINVAL; | ||
1413 | goto out; | ||
1414 | } | ||
1415 | c = policydb.ocontexts[OCON_NODE6]; | ||
1416 | while (c) { | ||
1417 | if (match_ipv6_addrmask(addrp, c->u.node6.addr, | ||
1418 | c->u.node6.mask)) | ||
1419 | break; | ||
1420 | c = c->next; | ||
1421 | } | ||
1422 | break; | ||
1423 | |||
1424 | default: | ||
1425 | *out_sid = SECINITSID_NODE; | ||
1426 | goto out; | ||
1427 | } | ||
1428 | |||
1429 | if (c) { | ||
1430 | if (!c->sid[0]) { | ||
1431 | rc = sidtab_context_to_sid(&sidtab, | ||
1432 | &c->context[0], | ||
1433 | &c->sid[0]); | ||
1434 | if (rc) | ||
1435 | goto out; | ||
1436 | } | ||
1437 | *out_sid = c->sid[0]; | ||
1438 | } else { | ||
1439 | *out_sid = SECINITSID_NODE; | ||
1440 | } | ||
1441 | |||
1442 | out: | ||
1443 | POLICY_RDUNLOCK; | ||
1444 | return rc; | ||
1445 | } | ||
1446 | |||
1447 | #define SIDS_NEL 25 | ||
1448 | |||
1449 | /** | ||
1450 | * security_get_user_sids - Obtain reachable SIDs for a user. | ||
1451 | * @fromsid: starting SID | ||
1452 | * @username: username | ||
1453 | * @sids: array of reachable SIDs for user | ||
1454 | * @nel: number of elements in @sids | ||
1455 | * | ||
1456 | * Generate the set of SIDs for legal security contexts | ||
1457 | * for a given user that can be reached by @fromsid. | ||
1458 | * Set *@sids to point to a dynamically allocated | ||
1459 | * array containing the set of SIDs. Set *@nel to the | ||
1460 | * number of elements in the array. | ||
1461 | */ | ||
1462 | |||
1463 | int security_get_user_sids(u32 fromsid, | ||
1464 | char *username, | ||
1465 | u32 **sids, | ||
1466 | u32 *nel) | ||
1467 | { | ||
1468 | struct context *fromcon, usercon; | ||
1469 | u32 *mysids, *mysids2, sid; | ||
1470 | u32 mynel = 0, maxnel = SIDS_NEL; | ||
1471 | struct user_datum *user; | ||
1472 | struct role_datum *role; | ||
1473 | struct av_decision avd; | ||
1474 | int rc = 0, i, j; | ||
1475 | |||
1476 | if (!ss_initialized) { | ||
1477 | *sids = NULL; | ||
1478 | *nel = 0; | ||
1479 | goto out; | ||
1480 | } | ||
1481 | |||
1482 | POLICY_RDLOCK; | ||
1483 | |||
1484 | fromcon = sidtab_search(&sidtab, fromsid); | ||
1485 | if (!fromcon) { | ||
1486 | rc = -EINVAL; | ||
1487 | goto out_unlock; | ||
1488 | } | ||
1489 | |||
1490 | user = hashtab_search(policydb.p_users.table, username); | ||
1491 | if (!user) { | ||
1492 | rc = -EINVAL; | ||
1493 | goto out_unlock; | ||
1494 | } | ||
1495 | usercon.user = user->value; | ||
1496 | |||
1497 | mysids = kmalloc(maxnel*sizeof(*mysids), GFP_ATOMIC); | ||
1498 | if (!mysids) { | ||
1499 | rc = -ENOMEM; | ||
1500 | goto out_unlock; | ||
1501 | } | ||
1502 | memset(mysids, 0, maxnel*sizeof(*mysids)); | ||
1503 | |||
1504 | for (i = ebitmap_startbit(&user->roles); i < ebitmap_length(&user->roles); i++) { | ||
1505 | if (!ebitmap_get_bit(&user->roles, i)) | ||
1506 | continue; | ||
1507 | role = policydb.role_val_to_struct[i]; | ||
1508 | usercon.role = i+1; | ||
1509 | for (j = ebitmap_startbit(&role->types); j < ebitmap_length(&role->types); j++) { | ||
1510 | if (!ebitmap_get_bit(&role->types, j)) | ||
1511 | continue; | ||
1512 | usercon.type = j+1; | ||
1513 | |||
1514 | if (mls_setup_user_range(fromcon, user, &usercon)) | ||
1515 | continue; | ||
1516 | |||
1517 | rc = context_struct_compute_av(fromcon, &usercon, | ||
1518 | SECCLASS_PROCESS, | ||
1519 | PROCESS__TRANSITION, | ||
1520 | &avd); | ||
1521 | if (rc || !(avd.allowed & PROCESS__TRANSITION)) | ||
1522 | continue; | ||
1523 | rc = sidtab_context_to_sid(&sidtab, &usercon, &sid); | ||
1524 | if (rc) { | ||
1525 | kfree(mysids); | ||
1526 | goto out_unlock; | ||
1527 | } | ||
1528 | if (mynel < maxnel) { | ||
1529 | mysids[mynel++] = sid; | ||
1530 | } else { | ||
1531 | maxnel += SIDS_NEL; | ||
1532 | mysids2 = kmalloc(maxnel*sizeof(*mysids2), GFP_ATOMIC); | ||
1533 | if (!mysids2) { | ||
1534 | rc = -ENOMEM; | ||
1535 | kfree(mysids); | ||
1536 | goto out_unlock; | ||
1537 | } | ||
1538 | memset(mysids2, 0, maxnel*sizeof(*mysids2)); | ||
1539 | memcpy(mysids2, mysids, mynel * sizeof(*mysids2)); | ||
1540 | kfree(mysids); | ||
1541 | mysids = mysids2; | ||
1542 | mysids[mynel++] = sid; | ||
1543 | } | ||
1544 | } | ||
1545 | } | ||
1546 | |||
1547 | *sids = mysids; | ||
1548 | *nel = mynel; | ||
1549 | |||
1550 | out_unlock: | ||
1551 | POLICY_RDUNLOCK; | ||
1552 | out: | ||
1553 | return rc; | ||
1554 | } | ||
1555 | |||
1556 | /** | ||
1557 | * security_genfs_sid - Obtain a SID for a file in a filesystem | ||
1558 | * @fstype: filesystem type | ||
1559 | * @path: path from root of mount | ||
1560 | * @sclass: file security class | ||
1561 | * @sid: SID for path | ||
1562 | * | ||
1563 | * Obtain a SID to use for a file in a filesystem that | ||
1564 | * cannot support xattr or use a fixed labeling behavior like | ||
1565 | * transition SIDs or task SIDs. | ||
1566 | */ | ||
1567 | int security_genfs_sid(const char *fstype, | ||
1568 | char *path, | ||
1569 | u16 sclass, | ||
1570 | u32 *sid) | ||
1571 | { | ||
1572 | int len; | ||
1573 | struct genfs *genfs; | ||
1574 | struct ocontext *c; | ||
1575 | int rc = 0, cmp = 0; | ||
1576 | |||
1577 | POLICY_RDLOCK; | ||
1578 | |||
1579 | for (genfs = policydb.genfs; genfs; genfs = genfs->next) { | ||
1580 | cmp = strcmp(fstype, genfs->fstype); | ||
1581 | if (cmp <= 0) | ||
1582 | break; | ||
1583 | } | ||
1584 | |||
1585 | if (!genfs || cmp) { | ||
1586 | *sid = SECINITSID_UNLABELED; | ||
1587 | rc = -ENOENT; | ||
1588 | goto out; | ||
1589 | } | ||
1590 | |||
1591 | for (c = genfs->head; c; c = c->next) { | ||
1592 | len = strlen(c->u.name); | ||
1593 | if ((!c->v.sclass || sclass == c->v.sclass) && | ||
1594 | (strncmp(c->u.name, path, len) == 0)) | ||
1595 | break; | ||
1596 | } | ||
1597 | |||
1598 | if (!c) { | ||
1599 | *sid = SECINITSID_UNLABELED; | ||
1600 | rc = -ENOENT; | ||
1601 | goto out; | ||
1602 | } | ||
1603 | |||
1604 | if (!c->sid[0]) { | ||
1605 | rc = sidtab_context_to_sid(&sidtab, | ||
1606 | &c->context[0], | ||
1607 | &c->sid[0]); | ||
1608 | if (rc) | ||
1609 | goto out; | ||
1610 | } | ||
1611 | |||
1612 | *sid = c->sid[0]; | ||
1613 | out: | ||
1614 | POLICY_RDUNLOCK; | ||
1615 | return rc; | ||
1616 | } | ||
1617 | |||
1618 | /** | ||
1619 | * security_fs_use - Determine how to handle labeling for a filesystem. | ||
1620 | * @fstype: filesystem type | ||
1621 | * @behavior: labeling behavior | ||
1622 | * @sid: SID for filesystem (superblock) | ||
1623 | */ | ||
1624 | int security_fs_use( | ||
1625 | const char *fstype, | ||
1626 | unsigned int *behavior, | ||
1627 | u32 *sid) | ||
1628 | { | ||
1629 | int rc = 0; | ||
1630 | struct ocontext *c; | ||
1631 | |||
1632 | POLICY_RDLOCK; | ||
1633 | |||
1634 | c = policydb.ocontexts[OCON_FSUSE]; | ||
1635 | while (c) { | ||
1636 | if (strcmp(fstype, c->u.name) == 0) | ||
1637 | break; | ||
1638 | c = c->next; | ||
1639 | } | ||
1640 | |||
1641 | if (c) { | ||
1642 | *behavior = c->v.behavior; | ||
1643 | if (!c->sid[0]) { | ||
1644 | rc = sidtab_context_to_sid(&sidtab, | ||
1645 | &c->context[0], | ||
1646 | &c->sid[0]); | ||
1647 | if (rc) | ||
1648 | goto out; | ||
1649 | } | ||
1650 | *sid = c->sid[0]; | ||
1651 | } else { | ||
1652 | rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid); | ||
1653 | if (rc) { | ||
1654 | *behavior = SECURITY_FS_USE_NONE; | ||
1655 | rc = 0; | ||
1656 | } else { | ||
1657 | *behavior = SECURITY_FS_USE_GENFS; | ||
1658 | } | ||
1659 | } | ||
1660 | |||
1661 | out: | ||
1662 | POLICY_RDUNLOCK; | ||
1663 | return rc; | ||
1664 | } | ||
1665 | |||
1666 | int security_get_bools(int *len, char ***names, int **values) | ||
1667 | { | ||
1668 | int i, rc = -ENOMEM; | ||
1669 | |||
1670 | POLICY_RDLOCK; | ||
1671 | *names = NULL; | ||
1672 | *values = NULL; | ||
1673 | |||
1674 | *len = policydb.p_bools.nprim; | ||
1675 | if (!*len) { | ||
1676 | rc = 0; | ||
1677 | goto out; | ||
1678 | } | ||
1679 | |||
1680 | *names = (char**)kmalloc(sizeof(char*) * *len, GFP_ATOMIC); | ||
1681 | if (!*names) | ||
1682 | goto err; | ||
1683 | memset(*names, 0, sizeof(char*) * *len); | ||
1684 | |||
1685 | *values = (int*)kmalloc(sizeof(int) * *len, GFP_ATOMIC); | ||
1686 | if (!*values) | ||
1687 | goto err; | ||
1688 | |||
1689 | for (i = 0; i < *len; i++) { | ||
1690 | size_t name_len; | ||
1691 | (*values)[i] = policydb.bool_val_to_struct[i]->state; | ||
1692 | name_len = strlen(policydb.p_bool_val_to_name[i]) + 1; | ||
1693 | (*names)[i] = (char*)kmalloc(sizeof(char) * name_len, GFP_ATOMIC); | ||
1694 | if (!(*names)[i]) | ||
1695 | goto err; | ||
1696 | strncpy((*names)[i], policydb.p_bool_val_to_name[i], name_len); | ||
1697 | (*names)[i][name_len - 1] = 0; | ||
1698 | } | ||
1699 | rc = 0; | ||
1700 | out: | ||
1701 | POLICY_RDUNLOCK; | ||
1702 | return rc; | ||
1703 | err: | ||
1704 | if (*names) { | ||
1705 | for (i = 0; i < *len; i++) | ||
1706 | if ((*names)[i]) | ||
1707 | kfree((*names)[i]); | ||
1708 | } | ||
1709 | if (*values) | ||
1710 | kfree(*values); | ||
1711 | goto out; | ||
1712 | } | ||
1713 | |||
1714 | |||
1715 | int security_set_bools(int len, int *values) | ||
1716 | { | ||
1717 | int i, rc = 0; | ||
1718 | int lenp, seqno = 0; | ||
1719 | struct cond_node *cur; | ||
1720 | |||
1721 | POLICY_WRLOCK; | ||
1722 | |||
1723 | lenp = policydb.p_bools.nprim; | ||
1724 | if (len != lenp) { | ||
1725 | rc = -EFAULT; | ||
1726 | goto out; | ||
1727 | } | ||
1728 | |||
1729 | printk(KERN_INFO "security: committed booleans { "); | ||
1730 | for (i = 0; i < len; i++) { | ||
1731 | if (values[i]) { | ||
1732 | policydb.bool_val_to_struct[i]->state = 1; | ||
1733 | } else { | ||
1734 | policydb.bool_val_to_struct[i]->state = 0; | ||
1735 | } | ||
1736 | if (i != 0) | ||
1737 | printk(", "); | ||
1738 | printk("%s:%d", policydb.p_bool_val_to_name[i], | ||
1739 | policydb.bool_val_to_struct[i]->state); | ||
1740 | } | ||
1741 | printk(" }\n"); | ||
1742 | |||
1743 | for (cur = policydb.cond_list; cur != NULL; cur = cur->next) { | ||
1744 | rc = evaluate_cond_node(&policydb, cur); | ||
1745 | if (rc) | ||
1746 | goto out; | ||
1747 | } | ||
1748 | |||
1749 | seqno = ++latest_granting; | ||
1750 | |||
1751 | out: | ||
1752 | POLICY_WRUNLOCK; | ||
1753 | if (!rc) { | ||
1754 | avc_ss_reset(seqno); | ||
1755 | selnl_notify_policyload(seqno); | ||
1756 | } | ||
1757 | return rc; | ||
1758 | } | ||
1759 | |||
1760 | int security_get_bool_value(int bool) | ||
1761 | { | ||
1762 | int rc = 0; | ||
1763 | int len; | ||
1764 | |||
1765 | POLICY_RDLOCK; | ||
1766 | |||
1767 | len = policydb.p_bools.nprim; | ||
1768 | if (bool >= len) { | ||
1769 | rc = -EFAULT; | ||
1770 | goto out; | ||
1771 | } | ||
1772 | |||
1773 | rc = policydb.bool_val_to_struct[bool]->state; | ||
1774 | out: | ||
1775 | POLICY_RDUNLOCK; | ||
1776 | return rc; | ||
1777 | } | ||
diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h new file mode 100644 index 000000000000..e8d907e903cd --- /dev/null +++ b/security/selinux/ss/services.h | |||
@@ -0,0 +1,15 @@ | |||
1 | /* | ||
2 | * Implementation of the security services. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | #ifndef _SS_SERVICES_H_ | ||
7 | #define _SS_SERVICES_H_ | ||
8 | |||
9 | #include "policydb.h" | ||
10 | #include "sidtab.h" | ||
11 | |||
12 | extern struct policydb policydb; | ||
13 | |||
14 | #endif /* _SS_SERVICES_H_ */ | ||
15 | |||
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c new file mode 100644 index 000000000000..871c33bd0741 --- /dev/null +++ b/security/selinux/ss/sidtab.c | |||
@@ -0,0 +1,305 @@ | |||
1 | /* | ||
2 | * Implementation of the SID table type. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/slab.h> | ||
8 | #include <linux/spinlock.h> | ||
9 | #include <linux/errno.h> | ||
10 | #include <linux/sched.h> | ||
11 | #include "flask.h" | ||
12 | #include "security.h" | ||
13 | #include "sidtab.h" | ||
14 | |||
15 | #define SIDTAB_HASH(sid) \ | ||
16 | (sid & SIDTAB_HASH_MASK) | ||
17 | |||
18 | #define INIT_SIDTAB_LOCK(s) spin_lock_init(&s->lock) | ||
19 | #define SIDTAB_LOCK(s, x) spin_lock_irqsave(&s->lock, x) | ||
20 | #define SIDTAB_UNLOCK(s, x) spin_unlock_irqrestore(&s->lock, x) | ||
21 | |||
22 | int sidtab_init(struct sidtab *s) | ||
23 | { | ||
24 | int i; | ||
25 | |||
26 | s->htable = kmalloc(sizeof(*(s->htable)) * SIDTAB_SIZE, GFP_ATOMIC); | ||
27 | if (!s->htable) | ||
28 | return -ENOMEM; | ||
29 | for (i = 0; i < SIDTAB_SIZE; i++) | ||
30 | s->htable[i] = NULL; | ||
31 | s->nel = 0; | ||
32 | s->next_sid = 1; | ||
33 | s->shutdown = 0; | ||
34 | INIT_SIDTAB_LOCK(s); | ||
35 | return 0; | ||
36 | } | ||
37 | |||
38 | int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) | ||
39 | { | ||
40 | int hvalue, rc = 0; | ||
41 | struct sidtab_node *prev, *cur, *newnode; | ||
42 | |||
43 | if (!s) { | ||
44 | rc = -ENOMEM; | ||
45 | goto out; | ||
46 | } | ||
47 | |||
48 | hvalue = SIDTAB_HASH(sid); | ||
49 | prev = NULL; | ||
50 | cur = s->htable[hvalue]; | ||
51 | while (cur != NULL && sid > cur->sid) { | ||
52 | prev = cur; | ||
53 | cur = cur->next; | ||
54 | } | ||
55 | |||
56 | if (cur && sid == cur->sid) { | ||
57 | rc = -EEXIST; | ||
58 | goto out; | ||
59 | } | ||
60 | |||
61 | newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC); | ||
62 | if (newnode == NULL) { | ||
63 | rc = -ENOMEM; | ||
64 | goto out; | ||
65 | } | ||
66 | newnode->sid = sid; | ||
67 | if (context_cpy(&newnode->context, context)) { | ||
68 | kfree(newnode); | ||
69 | rc = -ENOMEM; | ||
70 | goto out; | ||
71 | } | ||
72 | |||
73 | if (prev) { | ||
74 | newnode->next = prev->next; | ||
75 | wmb(); | ||
76 | prev->next = newnode; | ||
77 | } else { | ||
78 | newnode->next = s->htable[hvalue]; | ||
79 | wmb(); | ||
80 | s->htable[hvalue] = newnode; | ||
81 | } | ||
82 | |||
83 | s->nel++; | ||
84 | if (sid >= s->next_sid) | ||
85 | s->next_sid = sid + 1; | ||
86 | out: | ||
87 | return rc; | ||
88 | } | ||
89 | |||
90 | struct context *sidtab_search(struct sidtab *s, u32 sid) | ||
91 | { | ||
92 | int hvalue; | ||
93 | struct sidtab_node *cur; | ||
94 | |||
95 | if (!s) | ||
96 | return NULL; | ||
97 | |||
98 | hvalue = SIDTAB_HASH(sid); | ||
99 | cur = s->htable[hvalue]; | ||
100 | while (cur != NULL && sid > cur->sid) | ||
101 | cur = cur->next; | ||
102 | |||
103 | if (cur == NULL || sid != cur->sid) { | ||
104 | /* Remap invalid SIDs to the unlabeled SID. */ | ||
105 | sid = SECINITSID_UNLABELED; | ||
106 | hvalue = SIDTAB_HASH(sid); | ||
107 | cur = s->htable[hvalue]; | ||
108 | while (cur != NULL && sid > cur->sid) | ||
109 | cur = cur->next; | ||
110 | if (!cur || sid != cur->sid) | ||
111 | return NULL; | ||
112 | } | ||
113 | |||
114 | return &cur->context; | ||
115 | } | ||
116 | |||
117 | int sidtab_map(struct sidtab *s, | ||
118 | int (*apply) (u32 sid, | ||
119 | struct context *context, | ||
120 | void *args), | ||
121 | void *args) | ||
122 | { | ||
123 | int i, rc = 0; | ||
124 | struct sidtab_node *cur; | ||
125 | |||
126 | if (!s) | ||
127 | goto out; | ||
128 | |||
129 | for (i = 0; i < SIDTAB_SIZE; i++) { | ||
130 | cur = s->htable[i]; | ||
131 | while (cur != NULL) { | ||
132 | rc = apply(cur->sid, &cur->context, args); | ||
133 | if (rc) | ||
134 | goto out; | ||
135 | cur = cur->next; | ||
136 | } | ||
137 | } | ||
138 | out: | ||
139 | return rc; | ||
140 | } | ||
141 | |||
142 | void sidtab_map_remove_on_error(struct sidtab *s, | ||
143 | int (*apply) (u32 sid, | ||
144 | struct context *context, | ||
145 | void *args), | ||
146 | void *args) | ||
147 | { | ||
148 | int i, ret; | ||
149 | struct sidtab_node *last, *cur, *temp; | ||
150 | |||
151 | if (!s) | ||
152 | return; | ||
153 | |||
154 | for (i = 0; i < SIDTAB_SIZE; i++) { | ||
155 | last = NULL; | ||
156 | cur = s->htable[i]; | ||
157 | while (cur != NULL) { | ||
158 | ret = apply(cur->sid, &cur->context, args); | ||
159 | if (ret) { | ||
160 | if (last) { | ||
161 | last->next = cur->next; | ||
162 | } else { | ||
163 | s->htable[i] = cur->next; | ||
164 | } | ||
165 | |||
166 | temp = cur; | ||
167 | cur = cur->next; | ||
168 | context_destroy(&temp->context); | ||
169 | kfree(temp); | ||
170 | s->nel--; | ||
171 | } else { | ||
172 | last = cur; | ||
173 | cur = cur->next; | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | |||
178 | return; | ||
179 | } | ||
180 | |||
181 | static inline u32 sidtab_search_context(struct sidtab *s, | ||
182 | struct context *context) | ||
183 | { | ||
184 | int i; | ||
185 | struct sidtab_node *cur; | ||
186 | |||
187 | for (i = 0; i < SIDTAB_SIZE; i++) { | ||
188 | cur = s->htable[i]; | ||
189 | while (cur != NULL) { | ||
190 | if (context_cmp(&cur->context, context)) | ||
191 | return cur->sid; | ||
192 | cur = cur->next; | ||
193 | } | ||
194 | } | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | int sidtab_context_to_sid(struct sidtab *s, | ||
199 | struct context *context, | ||
200 | u32 *out_sid) | ||
201 | { | ||
202 | u32 sid; | ||
203 | int ret = 0; | ||
204 | unsigned long flags; | ||
205 | |||
206 | *out_sid = SECSID_NULL; | ||
207 | |||
208 | sid = sidtab_search_context(s, context); | ||
209 | if (!sid) { | ||
210 | SIDTAB_LOCK(s, flags); | ||
211 | /* Rescan now that we hold the lock. */ | ||
212 | sid = sidtab_search_context(s, context); | ||
213 | if (sid) | ||
214 | goto unlock_out; | ||
215 | /* No SID exists for the context. Allocate a new one. */ | ||
216 | if (s->next_sid == UINT_MAX || s->shutdown) { | ||
217 | ret = -ENOMEM; | ||
218 | goto unlock_out; | ||
219 | } | ||
220 | sid = s->next_sid++; | ||
221 | ret = sidtab_insert(s, sid, context); | ||
222 | if (ret) | ||
223 | s->next_sid--; | ||
224 | unlock_out: | ||
225 | SIDTAB_UNLOCK(s, flags); | ||
226 | } | ||
227 | |||
228 | if (ret) | ||
229 | return ret; | ||
230 | |||
231 | *out_sid = sid; | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | void sidtab_hash_eval(struct sidtab *h, char *tag) | ||
236 | { | ||
237 | int i, chain_len, slots_used, max_chain_len; | ||
238 | struct sidtab_node *cur; | ||
239 | |||
240 | slots_used = 0; | ||
241 | max_chain_len = 0; | ||
242 | for (i = 0; i < SIDTAB_SIZE; i++) { | ||
243 | cur = h->htable[i]; | ||
244 | if (cur) { | ||
245 | slots_used++; | ||
246 | chain_len = 0; | ||
247 | while (cur) { | ||
248 | chain_len++; | ||
249 | cur = cur->next; | ||
250 | } | ||
251 | |||
252 | if (chain_len > max_chain_len) | ||
253 | max_chain_len = chain_len; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | printk(KERN_INFO "%s: %d entries and %d/%d buckets used, longest " | ||
258 | "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE, | ||
259 | max_chain_len); | ||
260 | } | ||
261 | |||
262 | void sidtab_destroy(struct sidtab *s) | ||
263 | { | ||
264 | int i; | ||
265 | struct sidtab_node *cur, *temp; | ||
266 | |||
267 | if (!s) | ||
268 | return; | ||
269 | |||
270 | for (i = 0; i < SIDTAB_SIZE; i++) { | ||
271 | cur = s->htable[i]; | ||
272 | while (cur != NULL) { | ||
273 | temp = cur; | ||
274 | cur = cur->next; | ||
275 | context_destroy(&temp->context); | ||
276 | kfree(temp); | ||
277 | } | ||
278 | s->htable[i] = NULL; | ||
279 | } | ||
280 | kfree(s->htable); | ||
281 | s->htable = NULL; | ||
282 | s->nel = 0; | ||
283 | s->next_sid = 1; | ||
284 | } | ||
285 | |||
286 | void sidtab_set(struct sidtab *dst, struct sidtab *src) | ||
287 | { | ||
288 | unsigned long flags; | ||
289 | |||
290 | SIDTAB_LOCK(src, flags); | ||
291 | dst->htable = src->htable; | ||
292 | dst->nel = src->nel; | ||
293 | dst->next_sid = src->next_sid; | ||
294 | dst->shutdown = 0; | ||
295 | SIDTAB_UNLOCK(src, flags); | ||
296 | } | ||
297 | |||
298 | void sidtab_shutdown(struct sidtab *s) | ||
299 | { | ||
300 | unsigned long flags; | ||
301 | |||
302 | SIDTAB_LOCK(s, flags); | ||
303 | s->shutdown = 1; | ||
304 | SIDTAB_UNLOCK(s, flags); | ||
305 | } | ||
diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h new file mode 100644 index 000000000000..2fe9dfa3eb3a --- /dev/null +++ b/security/selinux/ss/sidtab.h | |||
@@ -0,0 +1,59 @@ | |||
1 | /* | ||
2 | * A security identifier table (sidtab) is a hash table | ||
3 | * of security context structures indexed by SID value. | ||
4 | * | ||
5 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
6 | */ | ||
7 | #ifndef _SS_SIDTAB_H_ | ||
8 | #define _SS_SIDTAB_H_ | ||
9 | |||
10 | #include "context.h" | ||
11 | |||
12 | struct sidtab_node { | ||
13 | u32 sid; /* security identifier */ | ||
14 | struct context context; /* security context structure */ | ||
15 | struct sidtab_node *next; | ||
16 | }; | ||
17 | |||
18 | #define SIDTAB_HASH_BITS 7 | ||
19 | #define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS) | ||
20 | #define SIDTAB_HASH_MASK (SIDTAB_HASH_BUCKETS-1) | ||
21 | |||
22 | #define SIDTAB_SIZE SIDTAB_HASH_BUCKETS | ||
23 | |||
24 | struct sidtab { | ||
25 | struct sidtab_node **htable; | ||
26 | unsigned int nel; /* number of elements */ | ||
27 | unsigned int next_sid; /* next SID to allocate */ | ||
28 | unsigned char shutdown; | ||
29 | spinlock_t lock; | ||
30 | }; | ||
31 | |||
32 | int sidtab_init(struct sidtab *s); | ||
33 | int sidtab_insert(struct sidtab *s, u32 sid, struct context *context); | ||
34 | struct context *sidtab_search(struct sidtab *s, u32 sid); | ||
35 | |||
36 | int sidtab_map(struct sidtab *s, | ||
37 | int (*apply) (u32 sid, | ||
38 | struct context *context, | ||
39 | void *args), | ||
40 | void *args); | ||
41 | |||
42 | void sidtab_map_remove_on_error(struct sidtab *s, | ||
43 | int (*apply) (u32 sid, | ||
44 | struct context *context, | ||
45 | void *args), | ||
46 | void *args); | ||
47 | |||
48 | int sidtab_context_to_sid(struct sidtab *s, | ||
49 | struct context *context, | ||
50 | u32 *sid); | ||
51 | |||
52 | void sidtab_hash_eval(struct sidtab *h, char *tag); | ||
53 | void sidtab_destroy(struct sidtab *s); | ||
54 | void sidtab_set(struct sidtab *dst, struct sidtab *src); | ||
55 | void sidtab_shutdown(struct sidtab *s); | ||
56 | |||
57 | #endif /* _SS_SIDTAB_H_ */ | ||
58 | |||
59 | |||
diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c new file mode 100644 index 000000000000..24a10d36d3b6 --- /dev/null +++ b/security/selinux/ss/symtab.c | |||
@@ -0,0 +1,44 @@ | |||
1 | /* | ||
2 | * Implementation of the symbol table type. | ||
3 | * | ||
4 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
5 | */ | ||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/slab.h> | ||
8 | #include <linux/string.h> | ||
9 | #include <linux/errno.h> | ||
10 | #include "symtab.h" | ||
11 | |||
12 | static unsigned int symhash(struct hashtab *h, void *key) | ||
13 | { | ||
14 | char *p, *keyp; | ||
15 | unsigned int size; | ||
16 | unsigned int val; | ||
17 | |||
18 | val = 0; | ||
19 | keyp = key; | ||
20 | size = strlen(keyp); | ||
21 | for (p = keyp; (p - keyp) < size; p++) | ||
22 | val = (val << 4 | (val >> (8*sizeof(unsigned int)-4))) ^ (*p); | ||
23 | return val & (h->size - 1); | ||
24 | } | ||
25 | |||
26 | static int symcmp(struct hashtab *h, void *key1, void *key2) | ||
27 | { | ||
28 | char *keyp1, *keyp2; | ||
29 | |||
30 | keyp1 = key1; | ||
31 | keyp2 = key2; | ||
32 | return strcmp(keyp1, keyp2); | ||
33 | } | ||
34 | |||
35 | |||
36 | int symtab_init(struct symtab *s, unsigned int size) | ||
37 | { | ||
38 | s->table = hashtab_create(symhash, symcmp, size); | ||
39 | if (!s->table) | ||
40 | return -1; | ||
41 | s->nprim = 0; | ||
42 | return 0; | ||
43 | } | ||
44 | |||
diff --git a/security/selinux/ss/symtab.h b/security/selinux/ss/symtab.h new file mode 100644 index 000000000000..ca422b42fbc0 --- /dev/null +++ b/security/selinux/ss/symtab.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * A symbol table (symtab) maintains associations between symbol | ||
3 | * strings and datum values. The type of the datum values | ||
4 | * is arbitrary. The symbol table type is implemented | ||
5 | * using the hash table type (hashtab). | ||
6 | * | ||
7 | * Author : Stephen Smalley, <sds@epoch.ncsc.mil> | ||
8 | */ | ||
9 | #ifndef _SS_SYMTAB_H_ | ||
10 | #define _SS_SYMTAB_H_ | ||
11 | |||
12 | #include "hashtab.h" | ||
13 | |||
14 | struct symtab { | ||
15 | struct hashtab *table; /* hash table (keyed on a string) */ | ||
16 | u32 nprim; /* number of primary names in table */ | ||
17 | }; | ||
18 | |||
19 | int symtab_init(struct symtab *s, unsigned int size); | ||
20 | |||
21 | #endif /* _SS_SYMTAB_H_ */ | ||
22 | |||
23 | |||