diff options
author | Armin Kuster <akuster808@gmail.com> | 2015-10-25 14:53:39 -0700 |
---|---|---|
committer | Armin Kuster <akuster808@gmail.com> | 2015-10-30 11:56:30 -0700 |
commit | b0c1edfe23f9ed18884e506a6653a23e504705db (patch) | |
tree | 640920aa1f482a2da14dfbdcf1a4b93f42cf6806 | |
parent | 36feac0d9d318200208da19543acfdca5fe61573 (diff) | |
download | meta-security-b0c1edfe23f9ed18884e506a6653a23e504705db.tar.gz |
ccs-patch: Add ccs kernel patches
add 4.1 kernel support for css
Signed-off-by: Armin Kuster <akuster808@gmail.com>
-rw-r--r-- | recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.1.diff | 968 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.2.diff | 681 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto.4.1.patch | 1029 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto_security.patch | 17920 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-yocto-4.1/tomoyo.cfg | 16 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-yocto-4.1/tomoyo.scc | 4 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-yocto_4.1.bbappend | 9 |
7 files changed, 20627 insertions, 0 deletions
diff --git a/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.1.diff b/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.1.diff new file mode 100644 index 0000000..9ad49fd --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.1.diff | |||
@@ -0,0 +1,968 @@ | |||
1 | This is TOMOYO Linux patch for kernel 4.1.8. | ||
2 | |||
3 | Source code for this patch is https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.1.8.tar.xz | ||
4 | --- | ||
5 | fs/exec.c | 2 | ||
6 | fs/open.c | 2 | ||
7 | fs/proc/version.c | 7 ++ | ||
8 | include/linux/init_task.h | 9 +++ | ||
9 | include/linux/sched.h | 6 ++ | ||
10 | include/linux/security.h | 62 +++++++++++++++---------- | ||
11 | include/net/ip.h | 4 + | ||
12 | kernel/fork.c | 5 ++ | ||
13 | kernel/kexec.c | 3 + | ||
14 | kernel/module.c | 5 ++ | ||
15 | kernel/ptrace.c | 10 ++++ | ||
16 | kernel/reboot.c | 3 + | ||
17 | kernel/sched/core.c | 2 | ||
18 | kernel/signal.c | 10 ++++ | ||
19 | kernel/sys.c | 8 +++ | ||
20 | kernel/time/ntp.c | 8 +++ | ||
21 | net/ipv4/raw.c | 4 + | ||
22 | net/ipv4/udp.c | 4 + | ||
23 | net/ipv6/raw.c | 4 + | ||
24 | net/ipv6/udp.c | 4 + | ||
25 | net/socket.c | 4 + | ||
26 | net/unix/af_unix.c | 4 + | ||
27 | security/Kconfig | 2 | ||
28 | security/Makefile | 3 + | ||
29 | security/security.c | 110 ++++++++++++++++++++++++++++++++++++++++------ | ||
30 | 25 files changed, 248 insertions(+), 37 deletions(-) | ||
31 | |||
32 | --- linux-4.1.8.orig/fs/exec.c | ||
33 | +++ linux-4.1.8/fs/exec.c | ||
34 | @@ -1461,7 +1461,7 @@ static int exec_binprm(struct linux_binp | ||
35 | old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent)); | ||
36 | rcu_read_unlock(); | ||
37 | |||
38 | - ret = search_binary_handler(bprm); | ||
39 | + ret = ccs_search_binary_handler(bprm); | ||
40 | if (ret >= 0) { | ||
41 | audit_bprm(bprm); | ||
42 | trace_sched_process_exec(current, old_pid, bprm); | ||
43 | --- linux-4.1.8.orig/fs/open.c | ||
44 | +++ linux-4.1.8/fs/open.c | ||
45 | @@ -1106,6 +1106,8 @@ EXPORT_SYMBOL(sys_close); | ||
46 | */ | ||
47 | SYSCALL_DEFINE0(vhangup) | ||
48 | { | ||
49 | + if (!ccs_capable(CCS_SYS_VHANGUP)) | ||
50 | + return -EPERM; | ||
51 | if (capable(CAP_SYS_TTY_CONFIG)) { | ||
52 | tty_vhangup_self(); | ||
53 | return 0; | ||
54 | --- linux-4.1.8.orig/fs/proc/version.c | ||
55 | +++ linux-4.1.8/fs/proc/version.c | ||
56 | @@ -32,3 +32,10 @@ static int __init proc_version_init(void | ||
57 | return 0; | ||
58 | } | ||
59 | fs_initcall(proc_version_init); | ||
60 | + | ||
61 | +static int __init ccs_show_version(void) | ||
62 | +{ | ||
63 | + printk(KERN_INFO "Hook version: 4.1.8 2015/09/26\n"); | ||
64 | + return 0; | ||
65 | +} | ||
66 | +fs_initcall(ccs_show_version); | ||
67 | --- linux-4.1.8.orig/include/linux/init_task.h | ||
68 | +++ linux-4.1.8/include/linux/init_task.h | ||
69 | @@ -182,6 +182,14 @@ extern struct task_group root_task_group | ||
70 | # define INIT_KASAN(tsk) | ||
71 | #endif | ||
72 | |||
73 | +#if defined(CONFIG_CCSECURITY) && !defined(CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY) | ||
74 | +#define INIT_CCSECURITY \ | ||
75 | + .ccs_domain_info = NULL, \ | ||
76 | + .ccs_flags = 0, | ||
77 | +#else | ||
78 | +#define INIT_CCSECURITY | ||
79 | +#endif | ||
80 | + | ||
81 | /* | ||
82 | * INIT_TASK is used to set up the first task table, touch at | ||
83 | * your own risk!. Base=0, limit=0x1fffff (=2MB) | ||
84 | @@ -258,6 +266,7 @@ extern struct task_group root_task_group | ||
85 | INIT_VTIME(tsk) \ | ||
86 | INIT_NUMA_BALANCING(tsk) \ | ||
87 | INIT_KASAN(tsk) \ | ||
88 | + INIT_CCSECURITY \ | ||
89 | } | ||
90 | |||
91 | |||
92 | --- linux-4.1.8.orig/include/linux/sched.h | ||
93 | +++ linux-4.1.8/include/linux/sched.h | ||
94 | @@ -6,6 +6,8 @@ | ||
95 | #include <linux/sched/prio.h> | ||
96 | |||
97 | |||
98 | +struct ccs_domain_info; | ||
99 | + | ||
100 | struct sched_param { | ||
101 | int sched_priority; | ||
102 | }; | ||
103 | @@ -1724,6 +1726,10 @@ struct task_struct { | ||
104 | #ifdef CONFIG_DEBUG_ATOMIC_SLEEP | ||
105 | unsigned long task_state_change; | ||
106 | #endif | ||
107 | +#if defined(CONFIG_CCSECURITY) && !defined(CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY) | ||
108 | + struct ccs_domain_info *ccs_domain_info; | ||
109 | + u32 ccs_flags; | ||
110 | +#endif | ||
111 | }; | ||
112 | |||
113 | /* Future-safe accessor for struct task_struct's cpus_allowed. */ | ||
114 | --- linux-4.1.8.orig/include/linux/security.h | ||
115 | +++ linux-4.1.8/include/linux/security.h | ||
116 | @@ -53,6 +53,7 @@ struct msg_queue; | ||
117 | struct xattr; | ||
118 | struct xfrm_sec_ctx; | ||
119 | struct mm_struct; | ||
120 | +#include <linux/ccsecurity.h> | ||
121 | |||
122 | /* Maximum number of letters for an LSM name string */ | ||
123 | #define SECURITY_NAME_MAX 10 | ||
124 | @@ -2042,7 +2043,10 @@ static inline int security_syslog(int ty | ||
125 | static inline int security_settime(const struct timespec *ts, | ||
126 | const struct timezone *tz) | ||
127 | { | ||
128 | - return cap_settime(ts, tz); | ||
129 | + int error = cap_settime(ts, tz); | ||
130 | + if (!error) | ||
131 | + error = ccs_settime(ts, tz); | ||
132 | + return error; | ||
133 | } | ||
134 | |||
135 | static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) | ||
136 | @@ -2111,18 +2115,18 @@ static inline int security_sb_mount(cons | ||
137 | const char *type, unsigned long flags, | ||
138 | void *data) | ||
139 | { | ||
140 | - return 0; | ||
141 | + return ccs_sb_mount(dev_name, path, type, flags, data); | ||
142 | } | ||
143 | |||
144 | static inline int security_sb_umount(struct vfsmount *mnt, int flags) | ||
145 | { | ||
146 | - return 0; | ||
147 | + return ccs_sb_umount(mnt, flags); | ||
148 | } | ||
149 | |||
150 | static inline int security_sb_pivotroot(struct path *old_path, | ||
151 | struct path *new_path) | ||
152 | { | ||
153 | - return 0; | ||
154 | + return ccs_sb_pivotroot(old_path, new_path); | ||
155 | } | ||
156 | |||
157 | static inline int security_sb_set_mnt_opts(struct super_block *sb, | ||
158 | @@ -2260,7 +2264,7 @@ static inline int security_inode_setattr | ||
159 | |||
160 | static inline int security_inode_getattr(const struct path *path) | ||
161 | { | ||
162 | - return 0; | ||
163 | + return ccs_inode_getattr(path); | ||
164 | } | ||
165 | |||
166 | static inline int security_inode_setxattr(struct dentry *dentry, | ||
167 | @@ -2336,7 +2340,7 @@ static inline void security_file_free(st | ||
168 | static inline int security_file_ioctl(struct file *file, unsigned int cmd, | ||
169 | unsigned long arg) | ||
170 | { | ||
171 | - return 0; | ||
172 | + return ccs_file_ioctl(file, cmd, arg); | ||
173 | } | ||
174 | |||
175 | static inline int security_mmap_file(struct file *file, unsigned long prot, | ||
176 | @@ -2365,7 +2369,7 @@ static inline int security_file_lock(str | ||
177 | static inline int security_file_fcntl(struct file *file, unsigned int cmd, | ||
178 | unsigned long arg) | ||
179 | { | ||
180 | - return 0; | ||
181 | + return ccs_file_fcntl(file, cmd, arg); | ||
182 | } | ||
183 | |||
184 | static inline void security_file_set_fowner(struct file *file) | ||
185 | @@ -2388,7 +2392,7 @@ static inline int security_file_receive( | ||
186 | static inline int security_file_open(struct file *file, | ||
187 | const struct cred *cred) | ||
188 | { | ||
189 | - return 0; | ||
190 | + return ccs_file_open(file, cred); | ||
191 | } | ||
192 | |||
193 | static inline int security_task_create(unsigned long clone_flags) | ||
194 | @@ -2750,7 +2754,7 @@ static inline int security_unix_may_send | ||
195 | static inline int security_socket_create(int family, int type, | ||
196 | int protocol, int kern) | ||
197 | { | ||
198 | - return 0; | ||
199 | + return ccs_socket_create(family, type, protocol, kern); | ||
200 | } | ||
201 | |||
202 | static inline int security_socket_post_create(struct socket *sock, | ||
203 | @@ -2765,19 +2769,19 @@ static inline int security_socket_bind(s | ||
204 | struct sockaddr *address, | ||
205 | int addrlen) | ||
206 | { | ||
207 | - return 0; | ||
208 | + return ccs_socket_bind(sock, address, addrlen); | ||
209 | } | ||
210 | |||
211 | static inline int security_socket_connect(struct socket *sock, | ||
212 | struct sockaddr *address, | ||
213 | int addrlen) | ||
214 | { | ||
215 | - return 0; | ||
216 | + return ccs_socket_connect(sock, address, addrlen); | ||
217 | } | ||
218 | |||
219 | static inline int security_socket_listen(struct socket *sock, int backlog) | ||
220 | { | ||
221 | - return 0; | ||
222 | + return ccs_socket_listen(sock, backlog); | ||
223 | } | ||
224 | |||
225 | static inline int security_socket_accept(struct socket *sock, | ||
226 | @@ -2789,7 +2793,7 @@ static inline int security_socket_accept | ||
227 | static inline int security_socket_sendmsg(struct socket *sock, | ||
228 | struct msghdr *msg, int size) | ||
229 | { | ||
230 | - return 0; | ||
231 | + return ccs_socket_sendmsg(sock, msg, size); | ||
232 | } | ||
233 | |||
234 | static inline int security_socket_recvmsg(struct socket *sock, | ||
235 | @@ -3031,42 +3035,42 @@ int security_path_chroot(struct path *pa | ||
236 | #else /* CONFIG_SECURITY_PATH */ | ||
237 | static inline int security_path_unlink(struct path *dir, struct dentry *dentry) | ||
238 | { | ||
239 | - return 0; | ||
240 | + return ccs_path_unlink(dir, dentry); | ||
241 | } | ||
242 | |||
243 | static inline int security_path_mkdir(struct path *dir, struct dentry *dentry, | ||
244 | umode_t mode) | ||
245 | { | ||
246 | - return 0; | ||
247 | + return ccs_path_mkdir(dir, dentry, mode); | ||
248 | } | ||
249 | |||
250 | static inline int security_path_rmdir(struct path *dir, struct dentry *dentry) | ||
251 | { | ||
252 | - return 0; | ||
253 | + return ccs_path_rmdir(dir, dentry); | ||
254 | } | ||
255 | |||
256 | static inline int security_path_mknod(struct path *dir, struct dentry *dentry, | ||
257 | umode_t mode, unsigned int dev) | ||
258 | { | ||
259 | - return 0; | ||
260 | + return ccs_path_mknod(dir, dentry, mode, dev); | ||
261 | } | ||
262 | |||
263 | static inline int security_path_truncate(struct path *path) | ||
264 | { | ||
265 | - return 0; | ||
266 | + return ccs_path_truncate(path); | ||
267 | } | ||
268 | |||
269 | static inline int security_path_symlink(struct path *dir, struct dentry *dentry, | ||
270 | const char *old_name) | ||
271 | { | ||
272 | - return 0; | ||
273 | + return ccs_path_symlink(dir, dentry, old_name); | ||
274 | } | ||
275 | |||
276 | static inline int security_path_link(struct dentry *old_dentry, | ||
277 | struct path *new_dir, | ||
278 | struct dentry *new_dentry) | ||
279 | { | ||
280 | - return 0; | ||
281 | + return ccs_path_link(old_dentry, new_dir, new_dentry); | ||
282 | } | ||
283 | |||
284 | static inline int security_path_rename(struct path *old_dir, | ||
285 | @@ -3075,22 +3079,32 @@ static inline int security_path_rename(s | ||
286 | struct dentry *new_dentry, | ||
287 | unsigned int flags) | ||
288 | { | ||
289 | - return 0; | ||
290 | + /* | ||
291 | + * Not using RENAME_EXCHANGE here in order to avoid KABI breakage | ||
292 | + * by doing "#include <uapi/linux/fs.h>" . | ||
293 | + */ | ||
294 | + if (flags & (1 << 1)) { | ||
295 | + int err = ccs_path_rename(new_dir, new_dentry, old_dir, | ||
296 | + old_dentry); | ||
297 | + if (err) | ||
298 | + return err; | ||
299 | + } | ||
300 | + return ccs_path_rename(old_dir, old_dentry, new_dir, new_dentry); | ||
301 | } | ||
302 | |||
303 | static inline int security_path_chmod(struct path *path, umode_t mode) | ||
304 | { | ||
305 | - return 0; | ||
306 | + return ccs_path_chmod(path, mode); | ||
307 | } | ||
308 | |||
309 | static inline int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) | ||
310 | { | ||
311 | - return 0; | ||
312 | + return ccs_path_chown(path, uid, gid); | ||
313 | } | ||
314 | |||
315 | static inline int security_path_chroot(struct path *path) | ||
316 | { | ||
317 | - return 0; | ||
318 | + return ccs_path_chroot(path); | ||
319 | } | ||
320 | #endif /* CONFIG_SECURITY_PATH */ | ||
321 | |||
322 | --- linux-4.1.8.orig/include/net/ip.h | ||
323 | +++ linux-4.1.8/include/net/ip.h | ||
324 | @@ -216,6 +216,8 @@ void inet_get_local_port_range(struct ne | ||
325 | #ifdef CONFIG_SYSCTL | ||
326 | static inline int inet_is_local_reserved_port(struct net *net, int port) | ||
327 | { | ||
328 | + if (ccs_lport_reserved(port)) | ||
329 | + return 1; | ||
330 | if (!net->ipv4.sysctl_local_reserved_ports) | ||
331 | return 0; | ||
332 | return test_bit(port, net->ipv4.sysctl_local_reserved_ports); | ||
333 | @@ -229,6 +231,8 @@ static inline bool sysctl_dev_name_is_al | ||
334 | #else | ||
335 | static inline int inet_is_local_reserved_port(struct net *net, int port) | ||
336 | { | ||
337 | + if (ccs_lport_reserved(port)) | ||
338 | + return 1; | ||
339 | return 0; | ||
340 | } | ||
341 | #endif | ||
342 | --- linux-4.1.8.orig/kernel/fork.c | ||
343 | +++ linux-4.1.8/kernel/fork.c | ||
344 | @@ -257,6 +257,7 @@ void __put_task_struct(struct task_struc | ||
345 | delayacct_tsk_free(tsk); | ||
346 | put_signal_struct(tsk->signal); | ||
347 | |||
348 | + ccs_free_task_security(tsk); | ||
349 | if (!profile_handoff_task(tsk)) | ||
350 | free_task(tsk); | ||
351 | } | ||
352 | @@ -1423,6 +1424,9 @@ static struct task_struct *copy_process( | ||
353 | goto bad_fork_cleanup_perf; | ||
354 | /* copy all the process information */ | ||
355 | shm_init_task(p); | ||
356 | + retval = ccs_alloc_task_security(p); | ||
357 | + if (retval) | ||
358 | + goto bad_fork_cleanup_audit; | ||
359 | retval = copy_semundo(clone_flags, p); | ||
360 | if (retval) | ||
361 | goto bad_fork_cleanup_audit; | ||
362 | @@ -1627,6 +1631,7 @@ bad_fork_cleanup_semundo: | ||
363 | exit_sem(p); | ||
364 | bad_fork_cleanup_audit: | ||
365 | audit_free(p); | ||
366 | + ccs_free_task_security(p); | ||
367 | bad_fork_cleanup_perf: | ||
368 | perf_event_free_task(p); | ||
369 | bad_fork_cleanup_policy: | ||
370 | --- linux-4.1.8.orig/kernel/kexec.c | ||
371 | +++ linux-4.1.8/kernel/kexec.c | ||
372 | @@ -41,6 +41,7 @@ | ||
373 | #include <asm/uaccess.h> | ||
374 | #include <asm/io.h> | ||
375 | #include <asm/sections.h> | ||
376 | +#include <linux/ccsecurity.h> | ||
377 | |||
378 | #include <crypto/hash.h> | ||
379 | #include <crypto/sha.h> | ||
380 | @@ -1245,6 +1246,8 @@ SYSCALL_DEFINE4(kexec_load, unsigned lon | ||
381 | /* We only trust the superuser with rebooting the system. */ | ||
382 | if (!capable(CAP_SYS_BOOT) || kexec_load_disabled) | ||
383 | return -EPERM; | ||
384 | + if (!ccs_capable(CCS_SYS_KEXEC_LOAD)) | ||
385 | + return -EPERM; | ||
386 | |||
387 | /* | ||
388 | * Verify we have a legal set of flags | ||
389 | --- linux-4.1.8.orig/kernel/module.c | ||
390 | +++ linux-4.1.8/kernel/module.c | ||
391 | @@ -61,6 +61,7 @@ | ||
392 | #include <linux/bsearch.h> | ||
393 | #include <uapi/linux/module.h> | ||
394 | #include "module-internal.h" | ||
395 | +#include <linux/ccsecurity.h> | ||
396 | |||
397 | #define CREATE_TRACE_POINTS | ||
398 | #include <trace/events/module.h> | ||
399 | @@ -799,6 +800,8 @@ SYSCALL_DEFINE2(delete_module, const cha | ||
400 | |||
401 | if (!capable(CAP_SYS_MODULE) || modules_disabled) | ||
402 | return -EPERM; | ||
403 | + if (!ccs_capable(CCS_USE_KERNEL_MODULE)) | ||
404 | + return -EPERM; | ||
405 | |||
406 | if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0) | ||
407 | return -EFAULT; | ||
408 | @@ -3155,6 +3158,8 @@ static int may_init_module(void) | ||
409 | { | ||
410 | if (!capable(CAP_SYS_MODULE) || modules_disabled) | ||
411 | return -EPERM; | ||
412 | + if (!ccs_capable(CCS_USE_KERNEL_MODULE)) | ||
413 | + return -EPERM; | ||
414 | |||
415 | return 0; | ||
416 | } | ||
417 | --- linux-4.1.8.orig/kernel/ptrace.c | ||
418 | +++ linux-4.1.8/kernel/ptrace.c | ||
419 | @@ -1034,6 +1034,11 @@ SYSCALL_DEFINE4(ptrace, long, request, l | ||
420 | { | ||
421 | struct task_struct *child; | ||
422 | long ret; | ||
423 | + { | ||
424 | + const int rc = ccs_ptrace_permission(request, pid); | ||
425 | + if (rc) | ||
426 | + return rc; | ||
427 | + } | ||
428 | |||
429 | if (request == PTRACE_TRACEME) { | ||
430 | ret = ptrace_traceme(); | ||
431 | @@ -1180,6 +1185,11 @@ COMPAT_SYSCALL_DEFINE4(ptrace, compat_lo | ||
432 | { | ||
433 | struct task_struct *child; | ||
434 | long ret; | ||
435 | + { | ||
436 | + const int rc = ccs_ptrace_permission(request, pid); | ||
437 | + if (rc) | ||
438 | + return rc; | ||
439 | + } | ||
440 | |||
441 | if (request == PTRACE_TRACEME) { | ||
442 | ret = ptrace_traceme(); | ||
443 | --- linux-4.1.8.orig/kernel/reboot.c | ||
444 | +++ linux-4.1.8/kernel/reboot.c | ||
445 | @@ -16,6 +16,7 @@ | ||
446 | #include <linux/syscalls.h> | ||
447 | #include <linux/syscore_ops.h> | ||
448 | #include <linux/uaccess.h> | ||
449 | +#include <linux/ccsecurity.h> | ||
450 | |||
451 | /* | ||
452 | * this indicates whether you can reboot with ctrl-alt-del: the default is yes | ||
453 | @@ -295,6 +296,8 @@ SYSCALL_DEFINE4(reboot, int, magic1, int | ||
454 | magic2 != LINUX_REBOOT_MAGIC2B && | ||
455 | magic2 != LINUX_REBOOT_MAGIC2C)) | ||
456 | return -EINVAL; | ||
457 | + if (!ccs_capable(CCS_SYS_REBOOT)) | ||
458 | + return -EPERM; | ||
459 | |||
460 | /* | ||
461 | * If pid namespaces are enabled and the current task is in a child | ||
462 | --- linux-4.1.8.orig/kernel/sched/core.c | ||
463 | +++ linux-4.1.8/kernel/sched/core.c | ||
464 | @@ -3145,6 +3145,8 @@ int can_nice(const struct task_struct *p | ||
465 | SYSCALL_DEFINE1(nice, int, increment) | ||
466 | { | ||
467 | long nice, retval; | ||
468 | + if (!ccs_capable(CCS_SYS_NICE)) | ||
469 | + return -EPERM; | ||
470 | |||
471 | /* | ||
472 | * Setpriority might change our priority at the same moment. | ||
473 | --- linux-4.1.8.orig/kernel/signal.c | ||
474 | +++ linux-4.1.8/kernel/signal.c | ||
475 | @@ -2901,6 +2901,8 @@ SYSCALL_DEFINE4(rt_sigtimedwait, const s | ||
476 | SYSCALL_DEFINE2(kill, pid_t, pid, int, sig) | ||
477 | { | ||
478 | struct siginfo info; | ||
479 | + if (ccs_kill_permission(pid, sig)) | ||
480 | + return -EPERM; | ||
481 | |||
482 | info.si_signo = sig; | ||
483 | info.si_errno = 0; | ||
484 | @@ -2969,6 +2971,8 @@ SYSCALL_DEFINE3(tgkill, pid_t, tgid, pid | ||
485 | /* This is only valid for single tasks */ | ||
486 | if (pid <= 0 || tgid <= 0) | ||
487 | return -EINVAL; | ||
488 | + if (ccs_tgkill_permission(tgid, pid, sig)) | ||
489 | + return -EPERM; | ||
490 | |||
491 | return do_tkill(tgid, pid, sig); | ||
492 | } | ||
493 | @@ -2985,6 +2989,8 @@ SYSCALL_DEFINE2(tkill, pid_t, pid, int, | ||
494 | /* This is only valid for single tasks */ | ||
495 | if (pid <= 0) | ||
496 | return -EINVAL; | ||
497 | + if (ccs_tkill_permission(pid, sig)) | ||
498 | + return -EPERM; | ||
499 | |||
500 | return do_tkill(0, pid, sig); | ||
501 | } | ||
502 | @@ -2999,6 +3005,8 @@ static int do_rt_sigqueueinfo(pid_t pid, | ||
503 | return -EPERM; | ||
504 | |||
505 | info->si_signo = sig; | ||
506 | + if (ccs_sigqueue_permission(pid, sig)) | ||
507 | + return -EPERM; | ||
508 | |||
509 | /* POSIX.1b doesn't mention process groups. */ | ||
510 | return kill_proc_info(sig, info, pid); | ||
511 | @@ -3047,6 +3055,8 @@ static int do_rt_tgsigqueueinfo(pid_t tg | ||
512 | return -EPERM; | ||
513 | |||
514 | info->si_signo = sig; | ||
515 | + if (ccs_tgsigqueue_permission(tgid, pid, sig)) | ||
516 | + return -EPERM; | ||
517 | |||
518 | return do_send_specific(tgid, pid, sig, info); | ||
519 | } | ||
520 | --- linux-4.1.8.orig/kernel/sys.c | ||
521 | +++ linux-4.1.8/kernel/sys.c | ||
522 | @@ -183,6 +183,10 @@ SYSCALL_DEFINE3(setpriority, int, which, | ||
523 | |||
524 | if (which > PRIO_USER || which < PRIO_PROCESS) | ||
525 | goto out; | ||
526 | + if (!ccs_capable(CCS_SYS_NICE)) { | ||
527 | + error = -EPERM; | ||
528 | + goto out; | ||
529 | + } | ||
530 | |||
531 | /* normalize: avoid signed division (rounding problems) */ | ||
532 | error = -ESRCH; | ||
533 | @@ -1222,6 +1226,8 @@ SYSCALL_DEFINE2(sethostname, char __user | ||
534 | |||
535 | if (len < 0 || len > __NEW_UTS_LEN) | ||
536 | return -EINVAL; | ||
537 | + if (!ccs_capable(CCS_SYS_SETHOSTNAME)) | ||
538 | + return -EPERM; | ||
539 | down_write(&uts_sem); | ||
540 | errno = -EFAULT; | ||
541 | if (!copy_from_user(tmp, name, len)) { | ||
542 | @@ -1272,6 +1278,8 @@ SYSCALL_DEFINE2(setdomainname, char __us | ||
543 | return -EPERM; | ||
544 | if (len < 0 || len > __NEW_UTS_LEN) | ||
545 | return -EINVAL; | ||
546 | + if (!ccs_capable(CCS_SYS_SETHOSTNAME)) | ||
547 | + return -EPERM; | ||
548 | |||
549 | down_write(&uts_sem); | ||
550 | errno = -EFAULT; | ||
551 | --- linux-4.1.8.orig/kernel/time/ntp.c | ||
552 | +++ linux-4.1.8/kernel/time/ntp.c | ||
553 | @@ -16,6 +16,7 @@ | ||
554 | #include <linux/mm.h> | ||
555 | #include <linux/module.h> | ||
556 | #include <linux/rtc.h> | ||
557 | +#include <linux/ccsecurity.h> | ||
558 | |||
559 | #include "ntp_internal.h" | ||
560 | |||
561 | @@ -626,10 +627,15 @@ int ntp_validate_timex(struct timex *txc | ||
562 | if (!(txc->modes & ADJ_OFFSET_READONLY) && | ||
563 | !capable(CAP_SYS_TIME)) | ||
564 | return -EPERM; | ||
565 | + if (!(txc->modes & ADJ_OFFSET_READONLY) && | ||
566 | + !ccs_capable(CCS_SYS_SETTIME)) | ||
567 | + return -EPERM; | ||
568 | } else { | ||
569 | /* In order to modify anything, you gotta be super-user! */ | ||
570 | if (txc->modes && !capable(CAP_SYS_TIME)) | ||
571 | return -EPERM; | ||
572 | + if (txc->modes && !ccs_capable(CCS_SYS_SETTIME)) | ||
573 | + return -EPERM; | ||
574 | /* | ||
575 | * if the quartz is off by more than 10% then | ||
576 | * something is VERY wrong! | ||
577 | @@ -642,6 +648,8 @@ int ntp_validate_timex(struct timex *txc | ||
578 | |||
579 | if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME))) | ||
580 | return -EPERM; | ||
581 | + if ((txc->modes & ADJ_SETOFFSET) && !ccs_capable(CCS_SYS_SETTIME)) | ||
582 | + return -EPERM; | ||
583 | |||
584 | /* | ||
585 | * Check for potential multiplication overflows that can | ||
586 | --- linux-4.1.8.orig/net/ipv4/raw.c | ||
587 | +++ linux-4.1.8/net/ipv4/raw.c | ||
588 | @@ -727,6 +727,10 @@ static int raw_recvmsg(struct sock *sk, | ||
589 | skb = skb_recv_datagram(sk, flags, noblock, &err); | ||
590 | if (!skb) | ||
591 | goto out; | ||
592 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
593 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
594 | + goto out; | ||
595 | + } | ||
596 | |||
597 | copied = skb->len; | ||
598 | if (len < copied) { | ||
599 | --- linux-4.1.8.orig/net/ipv4/udp.c | ||
600 | +++ linux-4.1.8/net/ipv4/udp.c | ||
601 | @@ -1272,6 +1272,10 @@ try_again: | ||
602 | &peeked, &off, &err); | ||
603 | if (!skb) | ||
604 | goto out; | ||
605 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
606 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
607 | + goto out; | ||
608 | + } | ||
609 | |||
610 | ulen = skb->len - sizeof(struct udphdr); | ||
611 | copied = len; | ||
612 | --- linux-4.1.8.orig/net/ipv6/raw.c | ||
613 | +++ linux-4.1.8/net/ipv6/raw.c | ||
614 | @@ -477,6 +477,10 @@ static int rawv6_recvmsg(struct sock *sk | ||
615 | skb = skb_recv_datagram(sk, flags, noblock, &err); | ||
616 | if (!skb) | ||
617 | goto out; | ||
618 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
619 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
620 | + goto out; | ||
621 | + } | ||
622 | |||
623 | copied = skb->len; | ||
624 | if (copied > len) { | ||
625 | --- linux-4.1.8.orig/net/ipv6/udp.c | ||
626 | +++ linux-4.1.8/net/ipv6/udp.c | ||
627 | @@ -413,6 +413,10 @@ try_again: | ||
628 | &peeked, &off, &err); | ||
629 | if (!skb) | ||
630 | goto out; | ||
631 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
632 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
633 | + goto out; | ||
634 | + } | ||
635 | |||
636 | ulen = skb->len - sizeof(struct udphdr); | ||
637 | copied = len; | ||
638 | --- linux-4.1.8.orig/net/socket.c | ||
639 | +++ linux-4.1.8/net/socket.c | ||
640 | @@ -1485,6 +1485,10 @@ SYSCALL_DEFINE4(accept4, int, fd, struct | ||
641 | if (err < 0) | ||
642 | goto out_fd; | ||
643 | |||
644 | + if (ccs_socket_post_accept_permission(sock, newsock)) { | ||
645 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
646 | + goto out_fd; | ||
647 | + } | ||
648 | if (upeer_sockaddr) { | ||
649 | if (newsock->ops->getname(newsock, (struct sockaddr *)&address, | ||
650 | &len, 2) < 0) { | ||
651 | --- linux-4.1.8.orig/net/unix/af_unix.c | ||
652 | +++ linux-4.1.8/net/unix/af_unix.c | ||
653 | @@ -1800,6 +1800,10 @@ static int unix_dgram_recvmsg(struct soc | ||
654 | wake_up_interruptible_sync_poll(&u->peer_wait, | ||
655 | POLLOUT | POLLWRNORM | POLLWRBAND); | ||
656 | |||
657 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
658 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
659 | + goto out_unlock; | ||
660 | + } | ||
661 | if (msg->msg_name) | ||
662 | unix_copy_addr(msg, skb->sk); | ||
663 | |||
664 | --- linux-4.1.8.orig/security/Kconfig | ||
665 | +++ linux-4.1.8/security/Kconfig | ||
666 | @@ -168,5 +168,7 @@ config DEFAULT_SECURITY | ||
667 | default "yama" if DEFAULT_SECURITY_YAMA | ||
668 | default "" if DEFAULT_SECURITY_DAC | ||
669 | |||
670 | +source security/ccsecurity/Kconfig | ||
671 | + | ||
672 | endmenu | ||
673 | |||
674 | --- linux-4.1.8.orig/security/Makefile | ||
675 | +++ linux-4.1.8/security/Makefile | ||
676 | @@ -27,3 +27,6 @@ obj-$(CONFIG_CGROUP_DEVICE) += device_c | ||
677 | # Object integrity file lists | ||
678 | subdir-$(CONFIG_INTEGRITY) += integrity | ||
679 | obj-$(CONFIG_INTEGRITY) += integrity/ | ||
680 | + | ||
681 | +subdir-$(CONFIG_CCSECURITY) += ccsecurity | ||
682 | +obj-$(CONFIG_CCSECURITY) += ccsecurity/ | ||
683 | --- linux-4.1.8.orig/security/security.c | ||
684 | +++ linux-4.1.8/security/security.c | ||
685 | @@ -226,7 +226,10 @@ int security_syslog(int type) | ||
686 | |||
687 | int security_settime(const struct timespec *ts, const struct timezone *tz) | ||
688 | { | ||
689 | - return security_ops->settime(ts, tz); | ||
690 | + int error = security_ops->settime(ts, tz); | ||
691 | + if (!error) | ||
692 | + error = ccs_settime(ts, tz); | ||
693 | + return error; | ||
694 | } | ||
695 | |||
696 | int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) | ||
697 | @@ -303,17 +306,26 @@ int security_sb_statfs(struct dentry *de | ||
698 | int security_sb_mount(const char *dev_name, struct path *path, | ||
699 | const char *type, unsigned long flags, void *data) | ||
700 | { | ||
701 | - return security_ops->sb_mount(dev_name, path, type, flags, data); | ||
702 | + int error = security_ops->sb_mount(dev_name, path, type, flags, data); | ||
703 | + if (!error) | ||
704 | + error = ccs_sb_mount(dev_name, path, type, flags, data); | ||
705 | + return error; | ||
706 | } | ||
707 | |||
708 | int security_sb_umount(struct vfsmount *mnt, int flags) | ||
709 | { | ||
710 | - return security_ops->sb_umount(mnt, flags); | ||
711 | + int error = security_ops->sb_umount(mnt, flags); | ||
712 | + if (!error) | ||
713 | + error = ccs_sb_umount(mnt, flags); | ||
714 | + return error; | ||
715 | } | ||
716 | |||
717 | int security_sb_pivotroot(struct path *old_path, struct path *new_path) | ||
718 | { | ||
719 | - return security_ops->sb_pivotroot(old_path, new_path); | ||
720 | + int error = security_ops->sb_pivotroot(old_path, new_path); | ||
721 | + if (!error) | ||
722 | + error = ccs_sb_pivotroot(old_path, new_path); | ||
723 | + return error; | ||
724 | } | ||
725 | |||
726 | int security_sb_set_mnt_opts(struct super_block *sb, | ||
727 | @@ -410,31 +422,47 @@ EXPORT_SYMBOL(security_old_inode_init_se | ||
728 | int security_path_mknod(struct path *dir, struct dentry *dentry, umode_t mode, | ||
729 | unsigned int dev) | ||
730 | { | ||
731 | + int error; | ||
732 | if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) | ||
733 | return 0; | ||
734 | + error = ccs_path_mknod(dir, dentry, mode, dev); | ||
735 | + if (error) | ||
736 | + return error; | ||
737 | return security_ops->path_mknod(dir, dentry, mode, dev); | ||
738 | } | ||
739 | EXPORT_SYMBOL(security_path_mknod); | ||
740 | |||
741 | int security_path_mkdir(struct path *dir, struct dentry *dentry, umode_t mode) | ||
742 | { | ||
743 | + int error; | ||
744 | if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) | ||
745 | return 0; | ||
746 | + error = ccs_path_mkdir(dir, dentry, mode); | ||
747 | + if (error) | ||
748 | + return error; | ||
749 | return security_ops->path_mkdir(dir, dentry, mode); | ||
750 | } | ||
751 | EXPORT_SYMBOL(security_path_mkdir); | ||
752 | |||
753 | int security_path_rmdir(struct path *dir, struct dentry *dentry) | ||
754 | { | ||
755 | + int error; | ||
756 | if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) | ||
757 | return 0; | ||
758 | + error = ccs_path_rmdir(dir, dentry); | ||
759 | + if (error) | ||
760 | + return error; | ||
761 | return security_ops->path_rmdir(dir, dentry); | ||
762 | } | ||
763 | |||
764 | int security_path_unlink(struct path *dir, struct dentry *dentry) | ||
765 | { | ||
766 | + int error; | ||
767 | if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) | ||
768 | return 0; | ||
769 | + error = ccs_path_unlink(dir, dentry); | ||
770 | + if (error) | ||
771 | + return error; | ||
772 | return security_ops->path_unlink(dir, dentry); | ||
773 | } | ||
774 | EXPORT_SYMBOL(security_path_unlink); | ||
775 | @@ -442,16 +470,24 @@ EXPORT_SYMBOL(security_path_unlink); | ||
776 | int security_path_symlink(struct path *dir, struct dentry *dentry, | ||
777 | const char *old_name) | ||
778 | { | ||
779 | + int error; | ||
780 | if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) | ||
781 | return 0; | ||
782 | + error = ccs_path_symlink(dir, dentry, old_name); | ||
783 | + if (error) | ||
784 | + return error; | ||
785 | return security_ops->path_symlink(dir, dentry, old_name); | ||
786 | } | ||
787 | |||
788 | int security_path_link(struct dentry *old_dentry, struct path *new_dir, | ||
789 | struct dentry *new_dentry) | ||
790 | { | ||
791 | + int error; | ||
792 | if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)))) | ||
793 | return 0; | ||
794 | + error = ccs_path_link(old_dentry, new_dir, new_dentry); | ||
795 | + if (error) | ||
796 | + return error; | ||
797 | return security_ops->path_link(old_dentry, new_dir, new_dentry); | ||
798 | } | ||
799 | |||
800 | @@ -459,6 +495,7 @@ int security_path_rename(struct path *ol | ||
801 | struct path *new_dir, struct dentry *new_dentry, | ||
802 | unsigned int flags) | ||
803 | { | ||
804 | + int error; | ||
805 | if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) || | ||
806 | (d_is_positive(new_dentry) && IS_PRIVATE(d_backing_inode(new_dentry))))) | ||
807 | return 0; | ||
808 | @@ -468,8 +505,15 @@ int security_path_rename(struct path *ol | ||
809 | old_dir, old_dentry); | ||
810 | if (err) | ||
811 | return err; | ||
812 | + err = ccs_path_rename(new_dir, new_dentry, old_dir, | ||
813 | + old_dentry); | ||
814 | + if (err) | ||
815 | + return err; | ||
816 | } | ||
817 | |||
818 | + error = ccs_path_rename(old_dir, old_dentry, new_dir, new_dentry); | ||
819 | + if (error) | ||
820 | + return error; | ||
821 | return security_ops->path_rename(old_dir, old_dentry, new_dir, | ||
822 | new_dentry); | ||
823 | } | ||
824 | @@ -477,27 +521,42 @@ EXPORT_SYMBOL(security_path_rename); | ||
825 | |||
826 | int security_path_truncate(struct path *path) | ||
827 | { | ||
828 | + int error; | ||
829 | if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) | ||
830 | return 0; | ||
831 | + error = ccs_path_truncate(path); | ||
832 | + if (error) | ||
833 | + return error; | ||
834 | return security_ops->path_truncate(path); | ||
835 | } | ||
836 | |||
837 | int security_path_chmod(struct path *path, umode_t mode) | ||
838 | { | ||
839 | + int error; | ||
840 | if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) | ||
841 | return 0; | ||
842 | + error = ccs_path_chmod(path, mode); | ||
843 | + if (error) | ||
844 | + return error; | ||
845 | return security_ops->path_chmod(path, mode); | ||
846 | } | ||
847 | |||
848 | int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) | ||
849 | { | ||
850 | + int error; | ||
851 | if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) | ||
852 | return 0; | ||
853 | + error = ccs_path_chown(path, uid, gid); | ||
854 | + if (error) | ||
855 | + return error; | ||
856 | return security_ops->path_chown(path, uid, gid); | ||
857 | } | ||
858 | |||
859 | int security_path_chroot(struct path *path) | ||
860 | { | ||
861 | + int error = ccs_path_chroot(path); | ||
862 | + if (error) | ||
863 | + return error; | ||
864 | return security_ops->path_chroot(path); | ||
865 | } | ||
866 | #endif | ||
867 | @@ -610,9 +669,13 @@ EXPORT_SYMBOL_GPL(security_inode_setattr | ||
868 | |||
869 | int security_inode_getattr(const struct path *path) | ||
870 | { | ||
871 | + int error; | ||
872 | if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) | ||
873 | return 0; | ||
874 | - return security_ops->inode_getattr(path); | ||
875 | + error = security_ops->inode_getattr(path); | ||
876 | + if (!error) | ||
877 | + error = ccs_inode_getattr(path); | ||
878 | + return error; | ||
879 | } | ||
880 | |||
881 | int security_inode_setxattr(struct dentry *dentry, const char *name, | ||
882 | @@ -729,7 +792,10 @@ void security_file_free(struct file *fil | ||
883 | |||
884 | int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
885 | { | ||
886 | - return security_ops->file_ioctl(file, cmd, arg); | ||
887 | + int error = security_ops->file_ioctl(file, cmd, arg); | ||
888 | + if (!error) | ||
889 | + error = ccs_file_ioctl(file, cmd, arg); | ||
890 | + return error; | ||
891 | } | ||
892 | |||
893 | static inline unsigned long mmap_prot(struct file *file, unsigned long prot) | ||
894 | @@ -794,7 +860,10 @@ int security_file_lock(struct file *file | ||
895 | |||
896 | int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg) | ||
897 | { | ||
898 | - return security_ops->file_fcntl(file, cmd, arg); | ||
899 | + int error = security_ops->file_fcntl(file, cmd, arg); | ||
900 | + if (!error) | ||
901 | + error = ccs_file_fcntl(file, cmd, arg); | ||
902 | + return error; | ||
903 | } | ||
904 | |||
905 | void security_file_set_fowner(struct file *file) | ||
906 | @@ -818,6 +887,8 @@ int security_file_open(struct file *file | ||
907 | int ret; | ||
908 | |||
909 | ret = security_ops->file_open(file, cred); | ||
910 | + if (!ret) | ||
911 | + ret = ccs_file_open(file, cred); | ||
912 | if (ret) | ||
913 | return ret; | ||
914 | |||
915 | @@ -1168,7 +1239,10 @@ EXPORT_SYMBOL(security_unix_may_send); | ||
916 | |||
917 | int security_socket_create(int family, int type, int protocol, int kern) | ||
918 | { | ||
919 | - return security_ops->socket_create(family, type, protocol, kern); | ||
920 | + int error = security_ops->socket_create(family, type, protocol, kern); | ||
921 | + if (!error) | ||
922 | + error = ccs_socket_create(family, type, protocol, kern); | ||
923 | + return error; | ||
924 | } | ||
925 | |||
926 | int security_socket_post_create(struct socket *sock, int family, | ||
927 | @@ -1180,17 +1254,26 @@ int security_socket_post_create(struct s | ||
928 | |||
929 | int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) | ||
930 | { | ||
931 | - return security_ops->socket_bind(sock, address, addrlen); | ||
932 | + int error = security_ops->socket_bind(sock, address, addrlen); | ||
933 | + if (!error) | ||
934 | + error = ccs_socket_bind(sock, address, addrlen); | ||
935 | + return error; | ||
936 | } | ||
937 | |||
938 | int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) | ||
939 | { | ||
940 | - return security_ops->socket_connect(sock, address, addrlen); | ||
941 | + int error = security_ops->socket_connect(sock, address, addrlen); | ||
942 | + if (!error) | ||
943 | + error = ccs_socket_connect(sock, address, addrlen); | ||
944 | + return error; | ||
945 | } | ||
946 | |||
947 | int security_socket_listen(struct socket *sock, int backlog) | ||
948 | { | ||
949 | - return security_ops->socket_listen(sock, backlog); | ||
950 | + int error = security_ops->socket_listen(sock, backlog); | ||
951 | + if (!error) | ||
952 | + error = ccs_socket_listen(sock, backlog); | ||
953 | + return error; | ||
954 | } | ||
955 | |||
956 | int security_socket_accept(struct socket *sock, struct socket *newsock) | ||
957 | @@ -1200,7 +1283,10 @@ int security_socket_accept(struct socket | ||
958 | |||
959 | int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) | ||
960 | { | ||
961 | - return security_ops->socket_sendmsg(sock, msg, size); | ||
962 | + int error = security_ops->socket_sendmsg(sock, msg, size); | ||
963 | + if (!error) | ||
964 | + error = ccs_socket_sendmsg(sock, msg, size); | ||
965 | + return error; | ||
966 | } | ||
967 | |||
968 | int security_socket_recvmsg(struct socket *sock, struct msghdr *msg, | ||
diff --git a/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.2.diff b/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.2.diff new file mode 100644 index 0000000..d1b021a --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.2.diff | |||
@@ -0,0 +1,681 @@ | |||
1 | This is TOMOYO Linux patch for kernel 4.2.1. | ||
2 | |||
3 | Source code for this patch is https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.2.1.tar.xz | ||
4 | --- | ||
5 | fs/exec.c | 2 - | ||
6 | fs/open.c | 2 + | ||
7 | fs/proc/version.c | 7 +++++ | ||
8 | include/linux/init_task.h | 9 ++++++ | ||
9 | include/linux/sched.h | 6 ++++ | ||
10 | include/linux/security.h | 62 ++++++++++++++++++++++++++++------------------ | ||
11 | include/net/ip.h | 4 ++ | ||
12 | kernel/fork.c | 5 +++ | ||
13 | kernel/kexec.c | 3 ++ | ||
14 | kernel/module.c | 5 +++ | ||
15 | kernel/ptrace.c | 10 +++++++ | ||
16 | kernel/reboot.c | 3 ++ | ||
17 | kernel/sched/core.c | 2 + | ||
18 | kernel/signal.c | 10 +++++++ | ||
19 | kernel/sys.c | 8 +++++ | ||
20 | kernel/time/ntp.c | 8 +++++ | ||
21 | net/ipv4/raw.c | 4 ++ | ||
22 | net/ipv4/udp.c | 4 ++ | ||
23 | net/ipv6/raw.c | 4 ++ | ||
24 | net/ipv6/udp.c | 4 ++ | ||
25 | net/socket.c | 4 ++ | ||
26 | net/unix/af_unix.c | 4 ++ | ||
27 | security/Kconfig | 2 + | ||
28 | security/Makefile | 3 ++ | ||
29 | 24 files changed, 150 insertions(+), 25 deletions(-) | ||
30 | |||
31 | --- linux-4.2.1.orig/fs/exec.c | ||
32 | +++ linux-4.2.1/fs/exec.c | ||
33 | @@ -1461,7 +1461,7 @@ static int exec_binprm(struct linux_binp | ||
34 | old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent)); | ||
35 | rcu_read_unlock(); | ||
36 | |||
37 | - ret = search_binary_handler(bprm); | ||
38 | + ret = ccs_search_binary_handler(bprm); | ||
39 | if (ret >= 0) { | ||
40 | audit_bprm(bprm); | ||
41 | trace_sched_process_exec(current, old_pid, bprm); | ||
42 | --- linux-4.2.1.orig/fs/open.c | ||
43 | +++ linux-4.2.1/fs/open.c | ||
44 | @@ -1117,6 +1117,8 @@ EXPORT_SYMBOL(sys_close); | ||
45 | */ | ||
46 | SYSCALL_DEFINE0(vhangup) | ||
47 | { | ||
48 | + if (!ccs_capable(CCS_SYS_VHANGUP)) | ||
49 | + return -EPERM; | ||
50 | if (capable(CAP_SYS_TTY_CONFIG)) { | ||
51 | tty_vhangup_self(); | ||
52 | return 0; | ||
53 | --- linux-4.2.1.orig/fs/proc/version.c | ||
54 | +++ linux-4.2.1/fs/proc/version.c | ||
55 | @@ -32,3 +32,10 @@ static int __init proc_version_init(void | ||
56 | return 0; | ||
57 | } | ||
58 | fs_initcall(proc_version_init); | ||
59 | + | ||
60 | +static int __init ccs_show_version(void) | ||
61 | +{ | ||
62 | + printk(KERN_INFO "Hook version: 4.2 2015/09/26\n"); | ||
63 | + return 0; | ||
64 | +} | ||
65 | +fs_initcall(ccs_show_version); | ||
66 | --- linux-4.2.1.orig/include/linux/init_task.h | ||
67 | +++ linux-4.2.1/include/linux/init_task.h | ||
68 | @@ -173,6 +173,14 @@ extern struct task_group root_task_group | ||
69 | # define INIT_KASAN(tsk) | ||
70 | #endif | ||
71 | |||
72 | +#if defined(CONFIG_CCSECURITY) && !defined(CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY) | ||
73 | +#define INIT_CCSECURITY \ | ||
74 | + .ccs_domain_info = NULL, \ | ||
75 | + .ccs_flags = 0, | ||
76 | +#else | ||
77 | +#define INIT_CCSECURITY | ||
78 | +#endif | ||
79 | + | ||
80 | /* | ||
81 | * INIT_TASK is used to set up the first task table, touch at | ||
82 | * your own risk!. Base=0, limit=0x1fffff (=2MB) | ||
83 | @@ -249,6 +257,7 @@ extern struct task_group root_task_group | ||
84 | INIT_VTIME(tsk) \ | ||
85 | INIT_NUMA_BALANCING(tsk) \ | ||
86 | INIT_KASAN(tsk) \ | ||
87 | + INIT_CCSECURITY \ | ||
88 | } | ||
89 | |||
90 | |||
91 | --- linux-4.2.1.orig/include/linux/sched.h | ||
92 | +++ linux-4.2.1/include/linux/sched.h | ||
93 | @@ -6,6 +6,8 @@ | ||
94 | #include <linux/sched/prio.h> | ||
95 | |||
96 | |||
97 | +struct ccs_domain_info; | ||
98 | + | ||
99 | struct sched_param { | ||
100 | int sched_priority; | ||
101 | }; | ||
102 | @@ -1776,6 +1778,10 @@ struct task_struct { | ||
103 | unsigned long task_state_change; | ||
104 | #endif | ||
105 | int pagefault_disabled; | ||
106 | +#if defined(CONFIG_CCSECURITY) && !defined(CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY) | ||
107 | + struct ccs_domain_info *ccs_domain_info; | ||
108 | + u32 ccs_flags; | ||
109 | +#endif | ||
110 | /* CPU-specific state of this task */ | ||
111 | struct thread_struct thread; | ||
112 | /* | ||
113 | --- linux-4.2.1.orig/include/linux/security.h | ||
114 | +++ linux-4.2.1/include/linux/security.h | ||
115 | @@ -53,6 +53,7 @@ struct msg_queue; | ||
116 | struct xattr; | ||
117 | struct xfrm_sec_ctx; | ||
118 | struct mm_struct; | ||
119 | +#include <linux/ccsecurity.h> | ||
120 | |||
121 | /* If capable should audit the security request */ | ||
122 | #define SECURITY_CAP_NOAUDIT 0 | ||
123 | @@ -460,7 +461,10 @@ static inline int security_syslog(int ty | ||
124 | static inline int security_settime(const struct timespec *ts, | ||
125 | const struct timezone *tz) | ||
126 | { | ||
127 | - return cap_settime(ts, tz); | ||
128 | + int error = cap_settime(ts, tz); | ||
129 | + if (!error) | ||
130 | + error = ccs_settime(ts, tz); | ||
131 | + return error; | ||
132 | } | ||
133 | |||
134 | static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) | ||
135 | @@ -529,18 +533,18 @@ static inline int security_sb_mount(cons | ||
136 | const char *type, unsigned long flags, | ||
137 | void *data) | ||
138 | { | ||
139 | - return 0; | ||
140 | + return ccs_sb_mount(dev_name, path, type, flags, data); | ||
141 | } | ||
142 | |||
143 | static inline int security_sb_umount(struct vfsmount *mnt, int flags) | ||
144 | { | ||
145 | - return 0; | ||
146 | + return ccs_sb_umount(mnt, flags); | ||
147 | } | ||
148 | |||
149 | static inline int security_sb_pivotroot(struct path *old_path, | ||
150 | struct path *new_path) | ||
151 | { | ||
152 | - return 0; | ||
153 | + return ccs_sb_pivotroot(old_path, new_path); | ||
154 | } | ||
155 | |||
156 | static inline int security_sb_set_mnt_opts(struct super_block *sb, | ||
157 | @@ -679,7 +683,7 @@ static inline int security_inode_setattr | ||
158 | |||
159 | static inline int security_inode_getattr(const struct path *path) | ||
160 | { | ||
161 | - return 0; | ||
162 | + return ccs_inode_getattr(path); | ||
163 | } | ||
164 | |||
165 | static inline int security_inode_setxattr(struct dentry *dentry, | ||
166 | @@ -755,7 +759,7 @@ static inline void security_file_free(st | ||
167 | static inline int security_file_ioctl(struct file *file, unsigned int cmd, | ||
168 | unsigned long arg) | ||
169 | { | ||
170 | - return 0; | ||
171 | + return ccs_file_ioctl(file, cmd, arg); | ||
172 | } | ||
173 | |||
174 | static inline int security_mmap_file(struct file *file, unsigned long prot, | ||
175 | @@ -784,7 +788,7 @@ static inline int security_file_lock(str | ||
176 | static inline int security_file_fcntl(struct file *file, unsigned int cmd, | ||
177 | unsigned long arg) | ||
178 | { | ||
179 | - return 0; | ||
180 | + return ccs_file_fcntl(file, cmd, arg); | ||
181 | } | ||
182 | |||
183 | static inline void security_file_set_fowner(struct file *file) | ||
184 | @@ -807,7 +811,7 @@ static inline int security_file_receive( | ||
185 | static inline int security_file_open(struct file *file, | ||
186 | const struct cred *cred) | ||
187 | { | ||
188 | - return 0; | ||
189 | + return ccs_file_open(file, cred); | ||
190 | } | ||
191 | |||
192 | static inline int security_task_create(unsigned long clone_flags) | ||
193 | @@ -1169,7 +1173,7 @@ static inline int security_unix_may_send | ||
194 | static inline int security_socket_create(int family, int type, | ||
195 | int protocol, int kern) | ||
196 | { | ||
197 | - return 0; | ||
198 | + return ccs_socket_create(family, type, protocol, kern); | ||
199 | } | ||
200 | |||
201 | static inline int security_socket_post_create(struct socket *sock, | ||
202 | @@ -1184,19 +1188,19 @@ static inline int security_socket_bind(s | ||
203 | struct sockaddr *address, | ||
204 | int addrlen) | ||
205 | { | ||
206 | - return 0; | ||
207 | + return ccs_socket_bind(sock, address, addrlen); | ||
208 | } | ||
209 | |||
210 | static inline int security_socket_connect(struct socket *sock, | ||
211 | struct sockaddr *address, | ||
212 | int addrlen) | ||
213 | { | ||
214 | - return 0; | ||
215 | + return ccs_socket_connect(sock, address, addrlen); | ||
216 | } | ||
217 | |||
218 | static inline int security_socket_listen(struct socket *sock, int backlog) | ||
219 | { | ||
220 | - return 0; | ||
221 | + return ccs_socket_listen(sock, backlog); | ||
222 | } | ||
223 | |||
224 | static inline int security_socket_accept(struct socket *sock, | ||
225 | @@ -1208,7 +1212,7 @@ static inline int security_socket_accept | ||
226 | static inline int security_socket_sendmsg(struct socket *sock, | ||
227 | struct msghdr *msg, int size) | ||
228 | { | ||
229 | - return 0; | ||
230 | + return ccs_socket_sendmsg(sock, msg, size); | ||
231 | } | ||
232 | |||
233 | static inline int security_socket_recvmsg(struct socket *sock, | ||
234 | @@ -1450,42 +1454,42 @@ int security_path_chroot(struct path *pa | ||
235 | #else /* CONFIG_SECURITY_PATH */ | ||
236 | static inline int security_path_unlink(struct path *dir, struct dentry *dentry) | ||
237 | { | ||
238 | - return 0; | ||
239 | + return ccs_path_unlink(dir, dentry); | ||
240 | } | ||
241 | |||
242 | static inline int security_path_mkdir(struct path *dir, struct dentry *dentry, | ||
243 | umode_t mode) | ||
244 | { | ||
245 | - return 0; | ||
246 | + return ccs_path_mkdir(dir, dentry, mode); | ||
247 | } | ||
248 | |||
249 | static inline int security_path_rmdir(struct path *dir, struct dentry *dentry) | ||
250 | { | ||
251 | - return 0; | ||
252 | + return ccs_path_rmdir(dir, dentry); | ||
253 | } | ||
254 | |||
255 | static inline int security_path_mknod(struct path *dir, struct dentry *dentry, | ||
256 | umode_t mode, unsigned int dev) | ||
257 | { | ||
258 | - return 0; | ||
259 | + return ccs_path_mknod(dir, dentry, mode, dev); | ||
260 | } | ||
261 | |||
262 | static inline int security_path_truncate(struct path *path) | ||
263 | { | ||
264 | - return 0; | ||
265 | + return ccs_path_truncate(path); | ||
266 | } | ||
267 | |||
268 | static inline int security_path_symlink(struct path *dir, struct dentry *dentry, | ||
269 | const char *old_name) | ||
270 | { | ||
271 | - return 0; | ||
272 | + return ccs_path_symlink(dir, dentry, old_name); | ||
273 | } | ||
274 | |||
275 | static inline int security_path_link(struct dentry *old_dentry, | ||
276 | struct path *new_dir, | ||
277 | struct dentry *new_dentry) | ||
278 | { | ||
279 | - return 0; | ||
280 | + return ccs_path_link(old_dentry, new_dir, new_dentry); | ||
281 | } | ||
282 | |||
283 | static inline int security_path_rename(struct path *old_dir, | ||
284 | @@ -1494,22 +1498,32 @@ static inline int security_path_rename(s | ||
285 | struct dentry *new_dentry, | ||
286 | unsigned int flags) | ||
287 | { | ||
288 | - return 0; | ||
289 | + /* | ||
290 | + * Not using RENAME_EXCHANGE here in order to avoid KABI breakage | ||
291 | + * by doing "#include <uapi/linux/fs.h>" . | ||
292 | + */ | ||
293 | + if (flags & (1 << 1)) { | ||
294 | + int err = ccs_path_rename(new_dir, new_dentry, old_dir, | ||
295 | + old_dentry); | ||
296 | + if (err) | ||
297 | + return err; | ||
298 | + } | ||
299 | + return ccs_path_rename(old_dir, old_dentry, new_dir, new_dentry); | ||
300 | } | ||
301 | |||
302 | static inline int security_path_chmod(struct path *path, umode_t mode) | ||
303 | { | ||
304 | - return 0; | ||
305 | + return ccs_path_chmod(path, mode); | ||
306 | } | ||
307 | |||
308 | static inline int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) | ||
309 | { | ||
310 | - return 0; | ||
311 | + return ccs_path_chown(path, uid, gid); | ||
312 | } | ||
313 | |||
314 | static inline int security_path_chroot(struct path *path) | ||
315 | { | ||
316 | - return 0; | ||
317 | + return ccs_path_chroot(path); | ||
318 | } | ||
319 | #endif /* CONFIG_SECURITY_PATH */ | ||
320 | |||
321 | --- linux-4.2.1.orig/include/net/ip.h | ||
322 | +++ linux-4.2.1/include/net/ip.h | ||
323 | @@ -217,6 +217,8 @@ void inet_get_local_port_range(struct ne | ||
324 | #ifdef CONFIG_SYSCTL | ||
325 | static inline int inet_is_local_reserved_port(struct net *net, int port) | ||
326 | { | ||
327 | + if (ccs_lport_reserved(port)) | ||
328 | + return 1; | ||
329 | if (!net->ipv4.sysctl_local_reserved_ports) | ||
330 | return 0; | ||
331 | return test_bit(port, net->ipv4.sysctl_local_reserved_ports); | ||
332 | @@ -230,6 +232,8 @@ static inline bool sysctl_dev_name_is_al | ||
333 | #else | ||
334 | static inline int inet_is_local_reserved_port(struct net *net, int port) | ||
335 | { | ||
336 | + if (ccs_lport_reserved(port)) | ||
337 | + return 1; | ||
338 | return 0; | ||
339 | } | ||
340 | #endif | ||
341 | --- linux-4.2.1.orig/kernel/fork.c | ||
342 | +++ linux-4.2.1/kernel/fork.c | ||
343 | @@ -257,6 +257,7 @@ void __put_task_struct(struct task_struc | ||
344 | delayacct_tsk_free(tsk); | ||
345 | put_signal_struct(tsk->signal); | ||
346 | |||
347 | + ccs_free_task_security(tsk); | ||
348 | if (!profile_handoff_task(tsk)) | ||
349 | free_task(tsk); | ||
350 | } | ||
351 | @@ -1425,6 +1426,9 @@ static struct task_struct *copy_process( | ||
352 | goto bad_fork_cleanup_perf; | ||
353 | /* copy all the process information */ | ||
354 | shm_init_task(p); | ||
355 | + retval = ccs_alloc_task_security(p); | ||
356 | + if (retval) | ||
357 | + goto bad_fork_cleanup_audit; | ||
358 | retval = copy_semundo(clone_flags, p); | ||
359 | if (retval) | ||
360 | goto bad_fork_cleanup_audit; | ||
361 | @@ -1629,6 +1633,7 @@ bad_fork_cleanup_semundo: | ||
362 | exit_sem(p); | ||
363 | bad_fork_cleanup_audit: | ||
364 | audit_free(p); | ||
365 | + ccs_free_task_security(p); | ||
366 | bad_fork_cleanup_perf: | ||
367 | perf_event_free_task(p); | ||
368 | bad_fork_cleanup_policy: | ||
369 | --- linux-4.2.1.orig/kernel/kexec.c | ||
370 | +++ linux-4.2.1/kernel/kexec.c | ||
371 | @@ -41,6 +41,7 @@ | ||
372 | #include <asm/uaccess.h> | ||
373 | #include <asm/io.h> | ||
374 | #include <asm/sections.h> | ||
375 | +#include <linux/ccsecurity.h> | ||
376 | |||
377 | #include <crypto/hash.h> | ||
378 | #include <crypto/sha.h> | ||
379 | @@ -1256,6 +1257,8 @@ SYSCALL_DEFINE4(kexec_load, unsigned lon | ||
380 | /* We only trust the superuser with rebooting the system. */ | ||
381 | if (!capable(CAP_SYS_BOOT) || kexec_load_disabled) | ||
382 | return -EPERM; | ||
383 | + if (!ccs_capable(CCS_SYS_KEXEC_LOAD)) | ||
384 | + return -EPERM; | ||
385 | |||
386 | /* | ||
387 | * Verify we have a legal set of flags | ||
388 | --- linux-4.2.1.orig/kernel/module.c | ||
389 | +++ linux-4.2.1/kernel/module.c | ||
390 | @@ -61,6 +61,7 @@ | ||
391 | #include <linux/bsearch.h> | ||
392 | #include <uapi/linux/module.h> | ||
393 | #include "module-internal.h" | ||
394 | +#include <linux/ccsecurity.h> | ||
395 | |||
396 | #define CREATE_TRACE_POINTS | ||
397 | #include <trace/events/module.h> | ||
398 | @@ -956,6 +957,8 @@ SYSCALL_DEFINE2(delete_module, const cha | ||
399 | |||
400 | if (!capable(CAP_SYS_MODULE) || modules_disabled) | ||
401 | return -EPERM; | ||
402 | + if (!ccs_capable(CCS_USE_KERNEL_MODULE)) | ||
403 | + return -EPERM; | ||
404 | |||
405 | if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0) | ||
406 | return -EFAULT; | ||
407 | @@ -3311,6 +3314,8 @@ static int may_init_module(void) | ||
408 | { | ||
409 | if (!capable(CAP_SYS_MODULE) || modules_disabled) | ||
410 | return -EPERM; | ||
411 | + if (!ccs_capable(CCS_USE_KERNEL_MODULE)) | ||
412 | + return -EPERM; | ||
413 | |||
414 | return 0; | ||
415 | } | ||
416 | --- linux-4.2.1.orig/kernel/ptrace.c | ||
417 | +++ linux-4.2.1/kernel/ptrace.c | ||
418 | @@ -1034,6 +1034,11 @@ SYSCALL_DEFINE4(ptrace, long, request, l | ||
419 | { | ||
420 | struct task_struct *child; | ||
421 | long ret; | ||
422 | + { | ||
423 | + const int rc = ccs_ptrace_permission(request, pid); | ||
424 | + if (rc) | ||
425 | + return rc; | ||
426 | + } | ||
427 | |||
428 | if (request == PTRACE_TRACEME) { | ||
429 | ret = ptrace_traceme(); | ||
430 | @@ -1180,6 +1185,11 @@ COMPAT_SYSCALL_DEFINE4(ptrace, compat_lo | ||
431 | { | ||
432 | struct task_struct *child; | ||
433 | long ret; | ||
434 | + { | ||
435 | + const int rc = ccs_ptrace_permission(request, pid); | ||
436 | + if (rc) | ||
437 | + return rc; | ||
438 | + } | ||
439 | |||
440 | if (request == PTRACE_TRACEME) { | ||
441 | ret = ptrace_traceme(); | ||
442 | --- linux-4.2.1.orig/kernel/reboot.c | ||
443 | +++ linux-4.2.1/kernel/reboot.c | ||
444 | @@ -16,6 +16,7 @@ | ||
445 | #include <linux/syscalls.h> | ||
446 | #include <linux/syscore_ops.h> | ||
447 | #include <linux/uaccess.h> | ||
448 | +#include <linux/ccsecurity.h> | ||
449 | |||
450 | /* | ||
451 | * this indicates whether you can reboot with ctrl-alt-del: the default is yes | ||
452 | @@ -295,6 +296,8 @@ SYSCALL_DEFINE4(reboot, int, magic1, int | ||
453 | magic2 != LINUX_REBOOT_MAGIC2B && | ||
454 | magic2 != LINUX_REBOOT_MAGIC2C)) | ||
455 | return -EINVAL; | ||
456 | + if (!ccs_capable(CCS_SYS_REBOOT)) | ||
457 | + return -EPERM; | ||
458 | |||
459 | /* | ||
460 | * If pid namespaces are enabled and the current task is in a child | ||
461 | --- linux-4.2.1.orig/kernel/sched/core.c | ||
462 | +++ linux-4.2.1/kernel/sched/core.c | ||
463 | @@ -3402,6 +3402,8 @@ int can_nice(const struct task_struct *p | ||
464 | SYSCALL_DEFINE1(nice, int, increment) | ||
465 | { | ||
466 | long nice, retval; | ||
467 | + if (!ccs_capable(CCS_SYS_NICE)) | ||
468 | + return -EPERM; | ||
469 | |||
470 | /* | ||
471 | * Setpriority might change our priority at the same moment. | ||
472 | --- linux-4.2.1.orig/kernel/signal.c | ||
473 | +++ linux-4.2.1/kernel/signal.c | ||
474 | @@ -2896,6 +2896,8 @@ SYSCALL_DEFINE4(rt_sigtimedwait, const s | ||
475 | SYSCALL_DEFINE2(kill, pid_t, pid, int, sig) | ||
476 | { | ||
477 | struct siginfo info; | ||
478 | + if (ccs_kill_permission(pid, sig)) | ||
479 | + return -EPERM; | ||
480 | |||
481 | info.si_signo = sig; | ||
482 | info.si_errno = 0; | ||
483 | @@ -2964,6 +2966,8 @@ SYSCALL_DEFINE3(tgkill, pid_t, tgid, pid | ||
484 | /* This is only valid for single tasks */ | ||
485 | if (pid <= 0 || tgid <= 0) | ||
486 | return -EINVAL; | ||
487 | + if (ccs_tgkill_permission(tgid, pid, sig)) | ||
488 | + return -EPERM; | ||
489 | |||
490 | return do_tkill(tgid, pid, sig); | ||
491 | } | ||
492 | @@ -2980,6 +2984,8 @@ SYSCALL_DEFINE2(tkill, pid_t, pid, int, | ||
493 | /* This is only valid for single tasks */ | ||
494 | if (pid <= 0) | ||
495 | return -EINVAL; | ||
496 | + if (ccs_tkill_permission(pid, sig)) | ||
497 | + return -EPERM; | ||
498 | |||
499 | return do_tkill(0, pid, sig); | ||
500 | } | ||
501 | @@ -2994,6 +3000,8 @@ static int do_rt_sigqueueinfo(pid_t pid, | ||
502 | return -EPERM; | ||
503 | |||
504 | info->si_signo = sig; | ||
505 | + if (ccs_sigqueue_permission(pid, sig)) | ||
506 | + return -EPERM; | ||
507 | |||
508 | /* POSIX.1b doesn't mention process groups. */ | ||
509 | return kill_proc_info(sig, info, pid); | ||
510 | @@ -3042,6 +3050,8 @@ static int do_rt_tgsigqueueinfo(pid_t tg | ||
511 | return -EPERM; | ||
512 | |||
513 | info->si_signo = sig; | ||
514 | + if (ccs_tgsigqueue_permission(tgid, pid, sig)) | ||
515 | + return -EPERM; | ||
516 | |||
517 | return do_send_specific(tgid, pid, sig, info); | ||
518 | } | ||
519 | --- linux-4.2.1.orig/kernel/sys.c | ||
520 | +++ linux-4.2.1/kernel/sys.c | ||
521 | @@ -183,6 +183,10 @@ SYSCALL_DEFINE3(setpriority, int, which, | ||
522 | |||
523 | if (which > PRIO_USER || which < PRIO_PROCESS) | ||
524 | goto out; | ||
525 | + if (!ccs_capable(CCS_SYS_NICE)) { | ||
526 | + error = -EPERM; | ||
527 | + goto out; | ||
528 | + } | ||
529 | |||
530 | /* normalize: avoid signed division (rounding problems) */ | ||
531 | error = -ESRCH; | ||
532 | @@ -1222,6 +1226,8 @@ SYSCALL_DEFINE2(sethostname, char __user | ||
533 | |||
534 | if (len < 0 || len > __NEW_UTS_LEN) | ||
535 | return -EINVAL; | ||
536 | + if (!ccs_capable(CCS_SYS_SETHOSTNAME)) | ||
537 | + return -EPERM; | ||
538 | down_write(&uts_sem); | ||
539 | errno = -EFAULT; | ||
540 | if (!copy_from_user(tmp, name, len)) { | ||
541 | @@ -1272,6 +1278,8 @@ SYSCALL_DEFINE2(setdomainname, char __us | ||
542 | return -EPERM; | ||
543 | if (len < 0 || len > __NEW_UTS_LEN) | ||
544 | return -EINVAL; | ||
545 | + if (!ccs_capable(CCS_SYS_SETHOSTNAME)) | ||
546 | + return -EPERM; | ||
547 | |||
548 | down_write(&uts_sem); | ||
549 | errno = -EFAULT; | ||
550 | --- linux-4.2.1.orig/kernel/time/ntp.c | ||
551 | +++ linux-4.2.1/kernel/time/ntp.c | ||
552 | @@ -16,6 +16,7 @@ | ||
553 | #include <linux/mm.h> | ||
554 | #include <linux/module.h> | ||
555 | #include <linux/rtc.h> | ||
556 | +#include <linux/ccsecurity.h> | ||
557 | |||
558 | #include "ntp_internal.h" | ||
559 | |||
560 | @@ -655,10 +656,15 @@ int ntp_validate_timex(struct timex *txc | ||
561 | if (!(txc->modes & ADJ_OFFSET_READONLY) && | ||
562 | !capable(CAP_SYS_TIME)) | ||
563 | return -EPERM; | ||
564 | + if (!(txc->modes & ADJ_OFFSET_READONLY) && | ||
565 | + !ccs_capable(CCS_SYS_SETTIME)) | ||
566 | + return -EPERM; | ||
567 | } else { | ||
568 | /* In order to modify anything, you gotta be super-user! */ | ||
569 | if (txc->modes && !capable(CAP_SYS_TIME)) | ||
570 | return -EPERM; | ||
571 | + if (txc->modes && !ccs_capable(CCS_SYS_SETTIME)) | ||
572 | + return -EPERM; | ||
573 | /* | ||
574 | * if the quartz is off by more than 10% then | ||
575 | * something is VERY wrong! | ||
576 | @@ -671,6 +677,8 @@ int ntp_validate_timex(struct timex *txc | ||
577 | |||
578 | if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME))) | ||
579 | return -EPERM; | ||
580 | + if ((txc->modes & ADJ_SETOFFSET) && !ccs_capable(CCS_SYS_SETTIME)) | ||
581 | + return -EPERM; | ||
582 | |||
583 | /* | ||
584 | * Check for potential multiplication overflows that can | ||
585 | --- linux-4.2.1.orig/net/ipv4/raw.c | ||
586 | +++ linux-4.2.1/net/ipv4/raw.c | ||
587 | @@ -727,6 +727,10 @@ static int raw_recvmsg(struct sock *sk, | ||
588 | skb = skb_recv_datagram(sk, flags, noblock, &err); | ||
589 | if (!skb) | ||
590 | goto out; | ||
591 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
592 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
593 | + goto out; | ||
594 | + } | ||
595 | |||
596 | copied = skb->len; | ||
597 | if (len < copied) { | ||
598 | --- linux-4.2.1.orig/net/ipv4/udp.c | ||
599 | +++ linux-4.2.1/net/ipv4/udp.c | ||
600 | @@ -1272,6 +1272,10 @@ try_again: | ||
601 | &peeked, &off, &err); | ||
602 | if (!skb) | ||
603 | goto out; | ||
604 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
605 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
606 | + goto out; | ||
607 | + } | ||
608 | |||
609 | ulen = skb->len - sizeof(struct udphdr); | ||
610 | copied = len; | ||
611 | --- linux-4.2.1.orig/net/ipv6/raw.c | ||
612 | +++ linux-4.2.1/net/ipv6/raw.c | ||
613 | @@ -477,6 +477,10 @@ static int rawv6_recvmsg(struct sock *sk | ||
614 | skb = skb_recv_datagram(sk, flags, noblock, &err); | ||
615 | if (!skb) | ||
616 | goto out; | ||
617 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
618 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
619 | + goto out; | ||
620 | + } | ||
621 | |||
622 | copied = skb->len; | ||
623 | if (copied > len) { | ||
624 | --- linux-4.2.1.orig/net/ipv6/udp.c | ||
625 | +++ linux-4.2.1/net/ipv6/udp.c | ||
626 | @@ -413,6 +413,10 @@ try_again: | ||
627 | &peeked, &off, &err); | ||
628 | if (!skb) | ||
629 | goto out; | ||
630 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
631 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
632 | + goto out; | ||
633 | + } | ||
634 | |||
635 | ulen = skb->len - sizeof(struct udphdr); | ||
636 | copied = len; | ||
637 | --- linux-4.2.1.orig/net/socket.c | ||
638 | +++ linux-4.2.1/net/socket.c | ||
639 | @@ -1482,6 +1482,10 @@ SYSCALL_DEFINE4(accept4, int, fd, struct | ||
640 | if (err < 0) | ||
641 | goto out_fd; | ||
642 | |||
643 | + if (ccs_socket_post_accept_permission(sock, newsock)) { | ||
644 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
645 | + goto out_fd; | ||
646 | + } | ||
647 | if (upeer_sockaddr) { | ||
648 | if (newsock->ops->getname(newsock, (struct sockaddr *)&address, | ||
649 | &len, 2) < 0) { | ||
650 | --- linux-4.2.1.orig/net/unix/af_unix.c | ||
651 | +++ linux-4.2.1/net/unix/af_unix.c | ||
652 | @@ -1911,6 +1911,10 @@ static int unix_dgram_recvmsg(struct soc | ||
653 | wake_up_interruptible_sync_poll(&u->peer_wait, | ||
654 | POLLOUT | POLLWRNORM | POLLWRBAND); | ||
655 | |||
656 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
657 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
658 | + goto out_unlock; | ||
659 | + } | ||
660 | if (msg->msg_name) | ||
661 | unix_copy_addr(msg, skb->sk); | ||
662 | |||
663 | --- linux-4.2.1.orig/security/Kconfig | ||
664 | +++ linux-4.2.1/security/Kconfig | ||
665 | @@ -168,5 +168,7 @@ config DEFAULT_SECURITY | ||
666 | default "yama" if DEFAULT_SECURITY_YAMA | ||
667 | default "" if DEFAULT_SECURITY_DAC | ||
668 | |||
669 | +source security/ccsecurity/Kconfig | ||
670 | + | ||
671 | endmenu | ||
672 | |||
673 | --- linux-4.2.1.orig/security/Makefile | ||
674 | +++ linux-4.2.1/security/Makefile | ||
675 | @@ -27,3 +27,6 @@ obj-$(CONFIG_CGROUP_DEVICE) += device_c | ||
676 | # Object integrity file lists | ||
677 | subdir-$(CONFIG_INTEGRITY) += integrity | ||
678 | obj-$(CONFIG_INTEGRITY) += integrity/ | ||
679 | + | ||
680 | +subdir-$(CONFIG_CCSECURITY) += ccsecurity | ||
681 | +obj-$(CONFIG_CCSECURITY) += ccsecurity/ | ||
diff --git a/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto.4.1.patch b/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto.4.1.patch new file mode 100644 index 0000000..611d396 --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto.4.1.patch | |||
@@ -0,0 +1,1029 @@ | |||
1 | From 13c3a21c549a87cf7410bd2ff88eeb6cb1ddda8c Mon Sep 17 00:00:00 2001 | ||
2 | From: invalid_git config <unknown@unknown> | ||
3 | Date: Sun, 25 Oct 2015 12:34:02 -0700 | ||
4 | Subject: [PATCH] This is TOMOYO Linux patch for kernel 4.1.8. | ||
5 | |||
6 | Source code for this patch is https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.1.8.tar.xz | ||
7 | --- | ||
8 | fs/exec.c | 2 +- | ||
9 | fs/open.c | 2 + | ||
10 | fs/proc/version.c | 7 +++ | ||
11 | include/linux/init_task.h | 9 ++++ | ||
12 | include/linux/sched.h | 6 +++ | ||
13 | include/linux/security.h | 62 ++++++++++++++++---------- | ||
14 | include/net/ip.h | 4 ++ | ||
15 | kernel/fork.c | 5 +++ | ||
16 | kernel/kexec.c | 3 ++ | ||
17 | kernel/module.c | 5 +++ | ||
18 | kernel/ptrace.c | 10 +++++ | ||
19 | kernel/reboot.c | 3 ++ | ||
20 | kernel/sched/core.c | 2 + | ||
21 | kernel/signal.c | 10 +++++ | ||
22 | kernel/sys.c | 8 ++++ | ||
23 | kernel/time/ntp.c | 8 ++++ | ||
24 | net/ipv4/raw.c | 4 ++ | ||
25 | net/ipv4/udp.c | 4 ++ | ||
26 | net/ipv6/raw.c | 4 ++ | ||
27 | net/ipv6/udp.c | 4 ++ | ||
28 | net/socket.c | 4 ++ | ||
29 | net/unix/af_unix.c | 4 ++ | ||
30 | security/Kconfig | 2 + | ||
31 | security/Makefile | 3 ++ | ||
32 | security/security.c | 110 +++++++++++++++++++++++++++++++++++++++++----- | ||
33 | 25 files changed, 248 insertions(+), 37 deletions(-) | ||
34 | |||
35 | diff --git a/fs/exec.c b/fs/exec.c | ||
36 | index 1977c2a..5c69bcc 100644 | ||
37 | --- a/fs/exec.c | ||
38 | +++ b/fs/exec.c | ||
39 | @@ -1461,7 +1461,7 @@ static int exec_binprm(struct linux_binprm *bprm) | ||
40 | old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent)); | ||
41 | rcu_read_unlock(); | ||
42 | |||
43 | - ret = search_binary_handler(bprm); | ||
44 | + ret = ccs_search_binary_handler(bprm); | ||
45 | if (ret >= 0) { | ||
46 | audit_bprm(bprm); | ||
47 | trace_sched_process_exec(current, old_pid, bprm); | ||
48 | diff --git a/fs/open.c b/fs/open.c | ||
49 | index a94e2e7..6c79f72c 100644 | ||
50 | --- a/fs/open.c | ||
51 | +++ b/fs/open.c | ||
52 | @@ -1108,6 +1108,8 @@ EXPORT_SYMBOL(sys_close); | ||
53 | */ | ||
54 | SYSCALL_DEFINE0(vhangup) | ||
55 | { | ||
56 | + if (!ccs_capable(CCS_SYS_VHANGUP)) | ||
57 | + return -EPERM; | ||
58 | if (capable(CAP_SYS_TTY_CONFIG)) { | ||
59 | tty_vhangup_self(); | ||
60 | return 0; | ||
61 | diff --git a/fs/proc/version.c b/fs/proc/version.c | ||
62 | index d2154eb..a84ba8d 100644 | ||
63 | --- a/fs/proc/version.c | ||
64 | +++ b/fs/proc/version.c | ||
65 | @@ -32,3 +32,10 @@ static int __init proc_version_init(void) | ||
66 | return 0; | ||
67 | } | ||
68 | fs_initcall(proc_version_init); | ||
69 | + | ||
70 | +static int __init ccs_show_version(void) | ||
71 | +{ | ||
72 | + printk(KERN_INFO "Hook version: 4.1.8 2015/09/26\n"); | ||
73 | + return 0; | ||
74 | +} | ||
75 | +fs_initcall(ccs_show_version); | ||
76 | diff --git a/include/linux/init_task.h b/include/linux/init_task.h | ||
77 | index 696d223..c112803 100644 | ||
78 | --- a/include/linux/init_task.h | ||
79 | +++ b/include/linux/init_task.h | ||
80 | @@ -182,6 +182,14 @@ extern struct task_group root_task_group; | ||
81 | # define INIT_KASAN(tsk) | ||
82 | #endif | ||
83 | |||
84 | +#if defined(CONFIG_CCSECURITY) && !defined(CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY) | ||
85 | +#define INIT_CCSECURITY \ | ||
86 | + .ccs_domain_info = NULL, \ | ||
87 | + .ccs_flags = 0, | ||
88 | +#else | ||
89 | +#define INIT_CCSECURITY | ||
90 | +#endif | ||
91 | + | ||
92 | /* | ||
93 | * INIT_TASK is used to set up the first task table, touch at | ||
94 | * your own risk!. Base=0, limit=0x1fffff (=2MB) | ||
95 | @@ -258,6 +266,7 @@ extern struct task_group root_task_group; | ||
96 | INIT_VTIME(tsk) \ | ||
97 | INIT_NUMA_BALANCING(tsk) \ | ||
98 | INIT_KASAN(tsk) \ | ||
99 | + INIT_CCSECURITY \ | ||
100 | } | ||
101 | |||
102 | |||
103 | diff --git a/include/linux/sched.h b/include/linux/sched.h | ||
104 | index 26a2e61..c32a704 100644 | ||
105 | --- a/include/linux/sched.h | ||
106 | +++ b/include/linux/sched.h | ||
107 | @@ -6,6 +6,8 @@ | ||
108 | #include <linux/sched/prio.h> | ||
109 | |||
110 | |||
111 | +struct ccs_domain_info; | ||
112 | + | ||
113 | struct sched_param { | ||
114 | int sched_priority; | ||
115 | }; | ||
116 | @@ -1724,6 +1726,10 @@ struct task_struct { | ||
117 | #ifdef CONFIG_DEBUG_ATOMIC_SLEEP | ||
118 | unsigned long task_state_change; | ||
119 | #endif | ||
120 | +#if defined(CONFIG_CCSECURITY) && !defined(CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY) | ||
121 | + struct ccs_domain_info *ccs_domain_info; | ||
122 | + u32 ccs_flags; | ||
123 | +#endif | ||
124 | }; | ||
125 | |||
126 | /* Future-safe accessor for struct task_struct's cpus_allowed. */ | ||
127 | diff --git a/include/linux/security.h b/include/linux/security.h | ||
128 | index 18264ea..621562b 100644 | ||
129 | --- a/include/linux/security.h | ||
130 | +++ b/include/linux/security.h | ||
131 | @@ -53,6 +53,7 @@ struct msg_queue; | ||
132 | struct xattr; | ||
133 | struct xfrm_sec_ctx; | ||
134 | struct mm_struct; | ||
135 | +#include <linux/ccsecurity.h> | ||
136 | |||
137 | /* Maximum number of letters for an LSM name string */ | ||
138 | #define SECURITY_NAME_MAX 10 | ||
139 | @@ -2042,7 +2043,10 @@ static inline int security_syslog(int type) | ||
140 | static inline int security_settime(const struct timespec *ts, | ||
141 | const struct timezone *tz) | ||
142 | { | ||
143 | - return cap_settime(ts, tz); | ||
144 | + int error = cap_settime(ts, tz); | ||
145 | + if (!error) | ||
146 | + error = ccs_settime(ts, tz); | ||
147 | + return error; | ||
148 | } | ||
149 | |||
150 | static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) | ||
151 | @@ -2111,18 +2115,18 @@ static inline int security_sb_mount(const char *dev_name, struct path *path, | ||
152 | const char *type, unsigned long flags, | ||
153 | void *data) | ||
154 | { | ||
155 | - return 0; | ||
156 | + return ccs_sb_mount(dev_name, path, type, flags, data); | ||
157 | } | ||
158 | |||
159 | static inline int security_sb_umount(struct vfsmount *mnt, int flags) | ||
160 | { | ||
161 | - return 0; | ||
162 | + return ccs_sb_umount(mnt, flags); | ||
163 | } | ||
164 | |||
165 | static inline int security_sb_pivotroot(struct path *old_path, | ||
166 | struct path *new_path) | ||
167 | { | ||
168 | - return 0; | ||
169 | + return ccs_sb_pivotroot(old_path, new_path); | ||
170 | } | ||
171 | |||
172 | static inline int security_sb_set_mnt_opts(struct super_block *sb, | ||
173 | @@ -2260,7 +2264,7 @@ static inline int security_inode_setattr(struct dentry *dentry, | ||
174 | |||
175 | static inline int security_inode_getattr(const struct path *path) | ||
176 | { | ||
177 | - return 0; | ||
178 | + return ccs_inode_getattr(path); | ||
179 | } | ||
180 | |||
181 | static inline int security_inode_setxattr(struct dentry *dentry, | ||
182 | @@ -2336,7 +2340,7 @@ static inline void security_file_free(struct file *file) | ||
183 | static inline int security_file_ioctl(struct file *file, unsigned int cmd, | ||
184 | unsigned long arg) | ||
185 | { | ||
186 | - return 0; | ||
187 | + return ccs_file_ioctl(file, cmd, arg); | ||
188 | } | ||
189 | |||
190 | static inline int security_mmap_file(struct file *file, unsigned long prot, | ||
191 | @@ -2365,7 +2369,7 @@ static inline int security_file_lock(struct file *file, unsigned int cmd) | ||
192 | static inline int security_file_fcntl(struct file *file, unsigned int cmd, | ||
193 | unsigned long arg) | ||
194 | { | ||
195 | - return 0; | ||
196 | + return ccs_file_fcntl(file, cmd, arg); | ||
197 | } | ||
198 | |||
199 | static inline void security_file_set_fowner(struct file *file) | ||
200 | @@ -2388,7 +2392,7 @@ static inline int security_file_receive(struct file *file) | ||
201 | static inline int security_file_open(struct file *file, | ||
202 | const struct cred *cred) | ||
203 | { | ||
204 | - return 0; | ||
205 | + return ccs_file_open(file, cred); | ||
206 | } | ||
207 | |||
208 | static inline int security_task_create(unsigned long clone_flags) | ||
209 | @@ -2750,7 +2754,7 @@ static inline int security_unix_may_send(struct socket *sock, | ||
210 | static inline int security_socket_create(int family, int type, | ||
211 | int protocol, int kern) | ||
212 | { | ||
213 | - return 0; | ||
214 | + return ccs_socket_create(family, type, protocol, kern); | ||
215 | } | ||
216 | |||
217 | static inline int security_socket_post_create(struct socket *sock, | ||
218 | @@ -2765,19 +2769,19 @@ static inline int security_socket_bind(struct socket *sock, | ||
219 | struct sockaddr *address, | ||
220 | int addrlen) | ||
221 | { | ||
222 | - return 0; | ||
223 | + return ccs_socket_bind(sock, address, addrlen); | ||
224 | } | ||
225 | |||
226 | static inline int security_socket_connect(struct socket *sock, | ||
227 | struct sockaddr *address, | ||
228 | int addrlen) | ||
229 | { | ||
230 | - return 0; | ||
231 | + return ccs_socket_connect(sock, address, addrlen); | ||
232 | } | ||
233 | |||
234 | static inline int security_socket_listen(struct socket *sock, int backlog) | ||
235 | { | ||
236 | - return 0; | ||
237 | + return ccs_socket_listen(sock, backlog); | ||
238 | } | ||
239 | |||
240 | static inline int security_socket_accept(struct socket *sock, | ||
241 | @@ -2789,7 +2793,7 @@ static inline int security_socket_accept(struct socket *sock, | ||
242 | static inline int security_socket_sendmsg(struct socket *sock, | ||
243 | struct msghdr *msg, int size) | ||
244 | { | ||
245 | - return 0; | ||
246 | + return ccs_socket_sendmsg(sock, msg, size); | ||
247 | } | ||
248 | |||
249 | static inline int security_socket_recvmsg(struct socket *sock, | ||
250 | @@ -3031,42 +3035,42 @@ int security_path_chroot(struct path *path); | ||
251 | #else /* CONFIG_SECURITY_PATH */ | ||
252 | static inline int security_path_unlink(struct path *dir, struct dentry *dentry) | ||
253 | { | ||
254 | - return 0; | ||
255 | + return ccs_path_unlink(dir, dentry); | ||
256 | } | ||
257 | |||
258 | static inline int security_path_mkdir(struct path *dir, struct dentry *dentry, | ||
259 | umode_t mode) | ||
260 | { | ||
261 | - return 0; | ||
262 | + return ccs_path_mkdir(dir, dentry, mode); | ||
263 | } | ||
264 | |||
265 | static inline int security_path_rmdir(struct path *dir, struct dentry *dentry) | ||
266 | { | ||
267 | - return 0; | ||
268 | + return ccs_path_rmdir(dir, dentry); | ||
269 | } | ||
270 | |||
271 | static inline int security_path_mknod(struct path *dir, struct dentry *dentry, | ||
272 | umode_t mode, unsigned int dev) | ||
273 | { | ||
274 | - return 0; | ||
275 | + return ccs_path_mknod(dir, dentry, mode, dev); | ||
276 | } | ||
277 | |||
278 | static inline int security_path_truncate(struct path *path) | ||
279 | { | ||
280 | - return 0; | ||
281 | + return ccs_path_truncate(path); | ||
282 | } | ||
283 | |||
284 | static inline int security_path_symlink(struct path *dir, struct dentry *dentry, | ||
285 | const char *old_name) | ||
286 | { | ||
287 | - return 0; | ||
288 | + return ccs_path_symlink(dir, dentry, old_name); | ||
289 | } | ||
290 | |||
291 | static inline int security_path_link(struct dentry *old_dentry, | ||
292 | struct path *new_dir, | ||
293 | struct dentry *new_dentry) | ||
294 | { | ||
295 | - return 0; | ||
296 | + return ccs_path_link(old_dentry, new_dir, new_dentry); | ||
297 | } | ||
298 | |||
299 | static inline int security_path_rename(struct path *old_dir, | ||
300 | @@ -3075,22 +3079,32 @@ static inline int security_path_rename(struct path *old_dir, | ||
301 | struct dentry *new_dentry, | ||
302 | unsigned int flags) | ||
303 | { | ||
304 | - return 0; | ||
305 | + /* | ||
306 | + * Not using RENAME_EXCHANGE here in order to avoid KABI breakage | ||
307 | + * by doing "#include <uapi/linux/fs.h>" . | ||
308 | + */ | ||
309 | + if (flags & (1 << 1)) { | ||
310 | + int err = ccs_path_rename(new_dir, new_dentry, old_dir, | ||
311 | + old_dentry); | ||
312 | + if (err) | ||
313 | + return err; | ||
314 | + } | ||
315 | + return ccs_path_rename(old_dir, old_dentry, new_dir, new_dentry); | ||
316 | } | ||
317 | |||
318 | static inline int security_path_chmod(struct path *path, umode_t mode) | ||
319 | { | ||
320 | - return 0; | ||
321 | + return ccs_path_chmod(path, mode); | ||
322 | } | ||
323 | |||
324 | static inline int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) | ||
325 | { | ||
326 | - return 0; | ||
327 | + return ccs_path_chown(path, uid, gid); | ||
328 | } | ||
329 | |||
330 | static inline int security_path_chroot(struct path *path) | ||
331 | { | ||
332 | - return 0; | ||
333 | + return ccs_path_chroot(path); | ||
334 | } | ||
335 | #endif /* CONFIG_SECURITY_PATH */ | ||
336 | |||
337 | diff --git a/include/net/ip.h b/include/net/ip.h | ||
338 | index d14af7e..34eb1cb 100644 | ||
339 | --- a/include/net/ip.h | ||
340 | +++ b/include/net/ip.h | ||
341 | @@ -216,6 +216,8 @@ void inet_get_local_port_range(struct net *net, int *low, int *high); | ||
342 | #ifdef CONFIG_SYSCTL | ||
343 | static inline int inet_is_local_reserved_port(struct net *net, int port) | ||
344 | { | ||
345 | + if (ccs_lport_reserved(port)) | ||
346 | + return 1; | ||
347 | if (!net->ipv4.sysctl_local_reserved_ports) | ||
348 | return 0; | ||
349 | return test_bit(port, net->ipv4.sysctl_local_reserved_ports); | ||
350 | @@ -229,6 +231,8 @@ static inline bool sysctl_dev_name_is_allowed(const char *name) | ||
351 | #else | ||
352 | static inline int inet_is_local_reserved_port(struct net *net, int port) | ||
353 | { | ||
354 | + if (ccs_lport_reserved(port)) | ||
355 | + return 1; | ||
356 | return 0; | ||
357 | } | ||
358 | #endif | ||
359 | diff --git a/kernel/fork.c b/kernel/fork.c | ||
360 | index 7e215ba..9bce902 100644 | ||
361 | --- a/kernel/fork.c | ||
362 | +++ b/kernel/fork.c | ||
363 | @@ -257,6 +257,7 @@ void __put_task_struct(struct task_struct *tsk) | ||
364 | delayacct_tsk_free(tsk); | ||
365 | put_signal_struct(tsk->signal); | ||
366 | |||
367 | + ccs_free_task_security(tsk); | ||
368 | if (!profile_handoff_task(tsk)) | ||
369 | free_task(tsk); | ||
370 | } | ||
371 | @@ -1423,6 +1424,9 @@ static struct task_struct *copy_process(unsigned long clone_flags, | ||
372 | goto bad_fork_cleanup_perf; | ||
373 | /* copy all the process information */ | ||
374 | shm_init_task(p); | ||
375 | + retval = ccs_alloc_task_security(p); | ||
376 | + if (retval) | ||
377 | + goto bad_fork_cleanup_audit; | ||
378 | retval = copy_semundo(clone_flags, p); | ||
379 | if (retval) | ||
380 | goto bad_fork_cleanup_audit; | ||
381 | @@ -1627,6 +1631,7 @@ bad_fork_cleanup_semundo: | ||
382 | exit_sem(p); | ||
383 | bad_fork_cleanup_audit: | ||
384 | audit_free(p); | ||
385 | + ccs_free_task_security(p); | ||
386 | bad_fork_cleanup_perf: | ||
387 | perf_event_free_task(p); | ||
388 | bad_fork_cleanup_policy: | ||
389 | diff --git a/kernel/kexec.c b/kernel/kexec.c | ||
390 | index 7a36fdc..294444e 100644 | ||
391 | --- a/kernel/kexec.c | ||
392 | +++ b/kernel/kexec.c | ||
393 | @@ -41,6 +41,7 @@ | ||
394 | #include <asm/uaccess.h> | ||
395 | #include <asm/io.h> | ||
396 | #include <asm/sections.h> | ||
397 | +#include <linux/ccsecurity.h> | ||
398 | |||
399 | #include <crypto/hash.h> | ||
400 | #include <crypto/sha.h> | ||
401 | @@ -1245,6 +1246,8 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, | ||
402 | /* We only trust the superuser with rebooting the system. */ | ||
403 | if (!capable(CAP_SYS_BOOT) || kexec_load_disabled) | ||
404 | return -EPERM; | ||
405 | + if (!ccs_capable(CCS_SYS_KEXEC_LOAD)) | ||
406 | + return -EPERM; | ||
407 | |||
408 | /* | ||
409 | * Verify we have a legal set of flags | ||
410 | diff --git a/kernel/module.c b/kernel/module.c | ||
411 | index cfc9e84..73fd5f5 100644 | ||
412 | --- a/kernel/module.c | ||
413 | +++ b/kernel/module.c | ||
414 | @@ -61,6 +61,7 @@ | ||
415 | #include <linux/bsearch.h> | ||
416 | #include <uapi/linux/module.h> | ||
417 | #include "module-internal.h" | ||
418 | +#include <linux/ccsecurity.h> | ||
419 | |||
420 | #define CREATE_TRACE_POINTS | ||
421 | #include <trace/events/module.h> | ||
422 | @@ -799,6 +800,8 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user, | ||
423 | |||
424 | if (!capable(CAP_SYS_MODULE) || modules_disabled) | ||
425 | return -EPERM; | ||
426 | + if (!ccs_capable(CCS_USE_KERNEL_MODULE)) | ||
427 | + return -EPERM; | ||
428 | |||
429 | if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0) | ||
430 | return -EFAULT; | ||
431 | @@ -3155,6 +3158,8 @@ static int may_init_module(void) | ||
432 | { | ||
433 | if (!capable(CAP_SYS_MODULE) || modules_disabled) | ||
434 | return -EPERM; | ||
435 | + if (!ccs_capable(CCS_USE_KERNEL_MODULE)) | ||
436 | + return -EPERM; | ||
437 | |||
438 | return 0; | ||
439 | } | ||
440 | diff --git a/kernel/ptrace.c b/kernel/ptrace.c | ||
441 | index c8e0e05..4106a2a 100644 | ||
442 | --- a/kernel/ptrace.c | ||
443 | +++ b/kernel/ptrace.c | ||
444 | @@ -1034,6 +1034,11 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, | ||
445 | { | ||
446 | struct task_struct *child; | ||
447 | long ret; | ||
448 | + { | ||
449 | + const int rc = ccs_ptrace_permission(request, pid); | ||
450 | + if (rc) | ||
451 | + return rc; | ||
452 | + } | ||
453 | |||
454 | if (request == PTRACE_TRACEME) { | ||
455 | ret = ptrace_traceme(); | ||
456 | @@ -1180,6 +1185,11 @@ COMPAT_SYSCALL_DEFINE4(ptrace, compat_long_t, request, compat_long_t, pid, | ||
457 | { | ||
458 | struct task_struct *child; | ||
459 | long ret; | ||
460 | + { | ||
461 | + const int rc = ccs_ptrace_permission(request, pid); | ||
462 | + if (rc) | ||
463 | + return rc; | ||
464 | + } | ||
465 | |||
466 | if (request == PTRACE_TRACEME) { | ||
467 | ret = ptrace_traceme(); | ||
468 | diff --git a/kernel/reboot.c b/kernel/reboot.c | ||
469 | index d20c85d..61ffd97 100644 | ||
470 | --- a/kernel/reboot.c | ||
471 | +++ b/kernel/reboot.c | ||
472 | @@ -16,6 +16,7 @@ | ||
473 | #include <linux/syscalls.h> | ||
474 | #include <linux/syscore_ops.h> | ||
475 | #include <linux/uaccess.h> | ||
476 | +#include <linux/ccsecurity.h> | ||
477 | |||
478 | /* | ||
479 | * this indicates whether you can reboot with ctrl-alt-del: the default is yes | ||
480 | @@ -295,6 +296,8 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, | ||
481 | magic2 != LINUX_REBOOT_MAGIC2B && | ||
482 | magic2 != LINUX_REBOOT_MAGIC2C)) | ||
483 | return -EINVAL; | ||
484 | + if (!ccs_capable(CCS_SYS_REBOOT)) | ||
485 | + return -EPERM; | ||
486 | |||
487 | /* | ||
488 | * If pid namespaces are enabled and the current task is in a child | ||
489 | diff --git a/kernel/sched/core.c b/kernel/sched/core.c | ||
490 | index e691052..c63bbd8 100644 | ||
491 | --- a/kernel/sched/core.c | ||
492 | +++ b/kernel/sched/core.c | ||
493 | @@ -3145,6 +3145,8 @@ int can_nice(const struct task_struct *p, const int nice) | ||
494 | SYSCALL_DEFINE1(nice, int, increment) | ||
495 | { | ||
496 | long nice, retval; | ||
497 | + if (!ccs_capable(CCS_SYS_NICE)) | ||
498 | + return -EPERM; | ||
499 | |||
500 | /* | ||
501 | * Setpriority might change our priority at the same moment. | ||
502 | diff --git a/kernel/signal.c b/kernel/signal.c | ||
503 | index 0206be7..9e01cca 100644 | ||
504 | --- a/kernel/signal.c | ||
505 | +++ b/kernel/signal.c | ||
506 | @@ -2901,6 +2901,8 @@ SYSCALL_DEFINE4(rt_sigtimedwait, const sigset_t __user *, uthese, | ||
507 | SYSCALL_DEFINE2(kill, pid_t, pid, int, sig) | ||
508 | { | ||
509 | struct siginfo info; | ||
510 | + if (ccs_kill_permission(pid, sig)) | ||
511 | + return -EPERM; | ||
512 | |||
513 | info.si_signo = sig; | ||
514 | info.si_errno = 0; | ||
515 | @@ -2969,6 +2971,8 @@ SYSCALL_DEFINE3(tgkill, pid_t, tgid, pid_t, pid, int, sig) | ||
516 | /* This is only valid for single tasks */ | ||
517 | if (pid <= 0 || tgid <= 0) | ||
518 | return -EINVAL; | ||
519 | + if (ccs_tgkill_permission(tgid, pid, sig)) | ||
520 | + return -EPERM; | ||
521 | |||
522 | return do_tkill(tgid, pid, sig); | ||
523 | } | ||
524 | @@ -2985,6 +2989,8 @@ SYSCALL_DEFINE2(tkill, pid_t, pid, int, sig) | ||
525 | /* This is only valid for single tasks */ | ||
526 | if (pid <= 0) | ||
527 | return -EINVAL; | ||
528 | + if (ccs_tkill_permission(pid, sig)) | ||
529 | + return -EPERM; | ||
530 | |||
531 | return do_tkill(0, pid, sig); | ||
532 | } | ||
533 | @@ -2999,6 +3005,8 @@ static int do_rt_sigqueueinfo(pid_t pid, int sig, siginfo_t *info) | ||
534 | return -EPERM; | ||
535 | |||
536 | info->si_signo = sig; | ||
537 | + if (ccs_sigqueue_permission(pid, sig)) | ||
538 | + return -EPERM; | ||
539 | |||
540 | /* POSIX.1b doesn't mention process groups. */ | ||
541 | return kill_proc_info(sig, info, pid); | ||
542 | @@ -3047,6 +3055,8 @@ static int do_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig, siginfo_t *info) | ||
543 | return -EPERM; | ||
544 | |||
545 | info->si_signo = sig; | ||
546 | + if (ccs_tgsigqueue_permission(tgid, pid, sig)) | ||
547 | + return -EPERM; | ||
548 | |||
549 | return do_send_specific(tgid, pid, sig, info); | ||
550 | } | ||
551 | diff --git a/kernel/sys.c b/kernel/sys.c | ||
552 | index a4e372b..77c6970 100644 | ||
553 | --- a/kernel/sys.c | ||
554 | +++ b/kernel/sys.c | ||
555 | @@ -183,6 +183,10 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval) | ||
556 | |||
557 | if (which > PRIO_USER || which < PRIO_PROCESS) | ||
558 | goto out; | ||
559 | + if (!ccs_capable(CCS_SYS_NICE)) { | ||
560 | + error = -EPERM; | ||
561 | + goto out; | ||
562 | + } | ||
563 | |||
564 | /* normalize: avoid signed division (rounding problems) */ | ||
565 | error = -ESRCH; | ||
566 | @@ -1222,6 +1226,8 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len) | ||
567 | |||
568 | if (len < 0 || len > __NEW_UTS_LEN) | ||
569 | return -EINVAL; | ||
570 | + if (!ccs_capable(CCS_SYS_SETHOSTNAME)) | ||
571 | + return -EPERM; | ||
572 | down_write(&uts_sem); | ||
573 | errno = -EFAULT; | ||
574 | if (!copy_from_user(tmp, name, len)) { | ||
575 | @@ -1272,6 +1278,8 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len) | ||
576 | return -EPERM; | ||
577 | if (len < 0 || len > __NEW_UTS_LEN) | ||
578 | return -EINVAL; | ||
579 | + if (!ccs_capable(CCS_SYS_SETHOSTNAME)) | ||
580 | + return -EPERM; | ||
581 | |||
582 | down_write(&uts_sem); | ||
583 | errno = -EFAULT; | ||
584 | diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c | ||
585 | index 7a68100..3c8766f 100644 | ||
586 | --- a/kernel/time/ntp.c | ||
587 | +++ b/kernel/time/ntp.c | ||
588 | @@ -16,6 +16,7 @@ | ||
589 | #include <linux/mm.h> | ||
590 | #include <linux/module.h> | ||
591 | #include <linux/rtc.h> | ||
592 | +#include <linux/ccsecurity.h> | ||
593 | |||
594 | #include "ntp_internal.h" | ||
595 | |||
596 | @@ -626,10 +627,15 @@ int ntp_validate_timex(struct timex *txc) | ||
597 | if (!(txc->modes & ADJ_OFFSET_READONLY) && | ||
598 | !capable(CAP_SYS_TIME)) | ||
599 | return -EPERM; | ||
600 | + if (!(txc->modes & ADJ_OFFSET_READONLY) && | ||
601 | + !ccs_capable(CCS_SYS_SETTIME)) | ||
602 | + return -EPERM; | ||
603 | } else { | ||
604 | /* In order to modify anything, you gotta be super-user! */ | ||
605 | if (txc->modes && !capable(CAP_SYS_TIME)) | ||
606 | return -EPERM; | ||
607 | + if (txc->modes && !ccs_capable(CCS_SYS_SETTIME)) | ||
608 | + return -EPERM; | ||
609 | /* | ||
610 | * if the quartz is off by more than 10% then | ||
611 | * something is VERY wrong! | ||
612 | @@ -642,6 +648,8 @@ int ntp_validate_timex(struct timex *txc) | ||
613 | |||
614 | if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME))) | ||
615 | return -EPERM; | ||
616 | + if ((txc->modes & ADJ_SETOFFSET) && !ccs_capable(CCS_SYS_SETTIME)) | ||
617 | + return -EPERM; | ||
618 | |||
619 | /* | ||
620 | * Check for potential multiplication overflows that can | ||
621 | diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c | ||
622 | index 561cd4b..16e23e5 100644 | ||
623 | --- a/net/ipv4/raw.c | ||
624 | +++ b/net/ipv4/raw.c | ||
625 | @@ -727,6 +727,10 @@ static int raw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, | ||
626 | skb = skb_recv_datagram(sk, flags, noblock, &err); | ||
627 | if (!skb) | ||
628 | goto out; | ||
629 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
630 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
631 | + goto out; | ||
632 | + } | ||
633 | |||
634 | copied = skb->len; | ||
635 | if (len < copied) { | ||
636 | diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c | ||
637 | index 83aa604..0326e29 100644 | ||
638 | --- a/net/ipv4/udp.c | ||
639 | +++ b/net/ipv4/udp.c | ||
640 | @@ -1272,6 +1272,10 @@ try_again: | ||
641 | &peeked, &off, &err); | ||
642 | if (!skb) | ||
643 | goto out; | ||
644 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
645 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
646 | + goto out; | ||
647 | + } | ||
648 | |||
649 | ulen = skb->len - sizeof(struct udphdr); | ||
650 | copied = len; | ||
651 | diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c | ||
652 | index 8072bd4..fbd33d4 100644 | ||
653 | --- a/net/ipv6/raw.c | ||
654 | +++ b/net/ipv6/raw.c | ||
655 | @@ -477,6 +477,10 @@ static int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, | ||
656 | skb = skb_recv_datagram(sk, flags, noblock, &err); | ||
657 | if (!skb) | ||
658 | goto out; | ||
659 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
660 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
661 | + goto out; | ||
662 | + } | ||
663 | |||
664 | copied = skb->len; | ||
665 | if (copied > len) { | ||
666 | diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c | ||
667 | index e51fc3e..5b09dbb 100644 | ||
668 | --- a/net/ipv6/udp.c | ||
669 | +++ b/net/ipv6/udp.c | ||
670 | @@ -413,6 +413,10 @@ try_again: | ||
671 | &peeked, &off, &err); | ||
672 | if (!skb) | ||
673 | goto out; | ||
674 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
675 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
676 | + goto out; | ||
677 | + } | ||
678 | |||
679 | ulen = skb->len - sizeof(struct udphdr); | ||
680 | copied = len; | ||
681 | diff --git a/net/socket.c b/net/socket.c | ||
682 | index 884e329..767338ff 100644 | ||
683 | --- a/net/socket.c | ||
684 | +++ b/net/socket.c | ||
685 | @@ -1485,6 +1485,10 @@ SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr, | ||
686 | if (err < 0) | ||
687 | goto out_fd; | ||
688 | |||
689 | + if (ccs_socket_post_accept_permission(sock, newsock)) { | ||
690 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
691 | + goto out_fd; | ||
692 | + } | ||
693 | if (upeer_sockaddr) { | ||
694 | if (newsock->ops->getname(newsock, (struct sockaddr *)&address, | ||
695 | &len, 2) < 0) { | ||
696 | diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c | ||
697 | index 0643059..ba5d3d0 100644 | ||
698 | --- a/net/unix/af_unix.c | ||
699 | +++ b/net/unix/af_unix.c | ||
700 | @@ -1800,6 +1800,10 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, | ||
701 | wake_up_interruptible_sync_poll(&u->peer_wait, | ||
702 | POLLOUT | POLLWRNORM | POLLWRBAND); | ||
703 | |||
704 | + if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { | ||
705 | + err = -EAGAIN; /* Hope less harmful than -EPERM. */ | ||
706 | + goto out_unlock; | ||
707 | + } | ||
708 | if (msg->msg_name) | ||
709 | unix_copy_addr(msg, skb->sk); | ||
710 | |||
711 | diff --git a/security/Kconfig b/security/Kconfig | ||
712 | index bf4ec46..4ce2bf2 100644 | ||
713 | --- a/security/Kconfig | ||
714 | +++ b/security/Kconfig | ||
715 | @@ -168,5 +168,7 @@ config DEFAULT_SECURITY | ||
716 | default "yama" if DEFAULT_SECURITY_YAMA | ||
717 | default "" if DEFAULT_SECURITY_DAC | ||
718 | |||
719 | +source security/ccsecurity/Kconfig | ||
720 | + | ||
721 | endmenu | ||
722 | |||
723 | diff --git a/security/Makefile b/security/Makefile | ||
724 | index 05f1c93..4a42724 100644 | ||
725 | --- a/security/Makefile | ||
726 | +++ b/security/Makefile | ||
727 | @@ -27,3 +27,6 @@ obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o | ||
728 | # Object integrity file lists | ||
729 | subdir-$(CONFIG_INTEGRITY) += integrity | ||
730 | obj-$(CONFIG_INTEGRITY) += integrity/ | ||
731 | + | ||
732 | +subdir-$(CONFIG_CCSECURITY) += ccsecurity | ||
733 | +obj-$(CONFIG_CCSECURITY) += ccsecurity/ | ||
734 | diff --git a/security/security.c b/security/security.c | ||
735 | index c1c7cd1..a8ed3d0 100644 | ||
736 | --- a/security/security.c | ||
737 | +++ b/security/security.c | ||
738 | @@ -226,7 +226,10 @@ int security_syslog(int type) | ||
739 | |||
740 | int security_settime(const struct timespec *ts, const struct timezone *tz) | ||
741 | { | ||
742 | - return security_ops->settime(ts, tz); | ||
743 | + int error = security_ops->settime(ts, tz); | ||
744 | + if (!error) | ||
745 | + error = ccs_settime(ts, tz); | ||
746 | + return error; | ||
747 | } | ||
748 | |||
749 | int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) | ||
750 | @@ -303,17 +306,26 @@ int security_sb_statfs(struct dentry *dentry) | ||
751 | int security_sb_mount(const char *dev_name, struct path *path, | ||
752 | const char *type, unsigned long flags, void *data) | ||
753 | { | ||
754 | - return security_ops->sb_mount(dev_name, path, type, flags, data); | ||
755 | + int error = security_ops->sb_mount(dev_name, path, type, flags, data); | ||
756 | + if (!error) | ||
757 | + error = ccs_sb_mount(dev_name, path, type, flags, data); | ||
758 | + return error; | ||
759 | } | ||
760 | |||
761 | int security_sb_umount(struct vfsmount *mnt, int flags) | ||
762 | { | ||
763 | - return security_ops->sb_umount(mnt, flags); | ||
764 | + int error = security_ops->sb_umount(mnt, flags); | ||
765 | + if (!error) | ||
766 | + error = ccs_sb_umount(mnt, flags); | ||
767 | + return error; | ||
768 | } | ||
769 | |||
770 | int security_sb_pivotroot(struct path *old_path, struct path *new_path) | ||
771 | { | ||
772 | - return security_ops->sb_pivotroot(old_path, new_path); | ||
773 | + int error = security_ops->sb_pivotroot(old_path, new_path); | ||
774 | + if (!error) | ||
775 | + error = ccs_sb_pivotroot(old_path, new_path); | ||
776 | + return error; | ||
777 | } | ||
778 | |||
779 | int security_sb_set_mnt_opts(struct super_block *sb, | ||
780 | @@ -410,32 +422,48 @@ EXPORT_SYMBOL(security_old_inode_init_security); | ||
781 | int security_path_mknod(struct path *dir, struct dentry *dentry, umode_t mode, | ||
782 | unsigned int dev) | ||
783 | { | ||
784 | + int error; | ||
785 | if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) | ||
786 | return 0; | ||
787 | + error = ccs_path_mknod(dir, dentry, mode, dev); | ||
788 | + if (error) | ||
789 | + return error; | ||
790 | return security_ops->path_mknod(dir, dentry, mode, dev); | ||
791 | } | ||
792 | EXPORT_SYMBOL(security_path_mknod); | ||
793 | |||
794 | int security_path_mkdir(struct path *dir, struct dentry *dentry, umode_t mode) | ||
795 | { | ||
796 | + int error; | ||
797 | if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) | ||
798 | return 0; | ||
799 | + error = ccs_path_mkdir(dir, dentry, mode); | ||
800 | + if (error) | ||
801 | + return error; | ||
802 | return security_ops->path_mkdir(dir, dentry, mode); | ||
803 | } | ||
804 | EXPORT_SYMBOL(security_path_mkdir); | ||
805 | |||
806 | int security_path_rmdir(struct path *dir, struct dentry *dentry) | ||
807 | { | ||
808 | + int error; | ||
809 | if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) | ||
810 | return 0; | ||
811 | + error = ccs_path_rmdir(dir, dentry); | ||
812 | + if (error) | ||
813 | + return error; | ||
814 | return security_ops->path_rmdir(dir, dentry); | ||
815 | } | ||
816 | EXPORT_SYMBOL(security_path_rmdir); | ||
817 | |||
818 | int security_path_unlink(struct path *dir, struct dentry *dentry) | ||
819 | { | ||
820 | + int error; | ||
821 | if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) | ||
822 | return 0; | ||
823 | + error = ccs_path_unlink(dir, dentry); | ||
824 | + if (error) | ||
825 | + return error; | ||
826 | return security_ops->path_unlink(dir, dentry); | ||
827 | } | ||
828 | EXPORT_SYMBOL(security_path_unlink); | ||
829 | @@ -443,8 +471,12 @@ EXPORT_SYMBOL(security_path_unlink); | ||
830 | int security_path_symlink(struct path *dir, struct dentry *dentry, | ||
831 | const char *old_name) | ||
832 | { | ||
833 | + int error; | ||
834 | if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) | ||
835 | return 0; | ||
836 | + error = ccs_path_symlink(dir, dentry, old_name); | ||
837 | + if (error) | ||
838 | + return error; | ||
839 | return security_ops->path_symlink(dir, dentry, old_name); | ||
840 | } | ||
841 | EXPORT_SYMBOL(security_path_symlink); | ||
842 | @@ -452,8 +484,12 @@ EXPORT_SYMBOL(security_path_symlink); | ||
843 | int security_path_link(struct dentry *old_dentry, struct path *new_dir, | ||
844 | struct dentry *new_dentry) | ||
845 | { | ||
846 | + int error; | ||
847 | if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)))) | ||
848 | return 0; | ||
849 | + error = ccs_path_link(old_dentry, new_dir, new_dentry); | ||
850 | + if (error) | ||
851 | + return error; | ||
852 | return security_ops->path_link(old_dentry, new_dir, new_dentry); | ||
853 | } | ||
854 | EXPORT_SYMBOL(security_path_link); | ||
855 | @@ -462,6 +498,7 @@ int security_path_rename(struct path *old_dir, struct dentry *old_dentry, | ||
856 | struct path *new_dir, struct dentry *new_dentry, | ||
857 | unsigned int flags) | ||
858 | { | ||
859 | + int error; | ||
860 | if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) || | ||
861 | (d_is_positive(new_dentry) && IS_PRIVATE(d_backing_inode(new_dentry))))) | ||
862 | return 0; | ||
863 | @@ -471,8 +508,15 @@ int security_path_rename(struct path *old_dir, struct dentry *old_dentry, | ||
864 | old_dir, old_dentry); | ||
865 | if (err) | ||
866 | return err; | ||
867 | + err = ccs_path_rename(new_dir, new_dentry, old_dir, | ||
868 | + old_dentry); | ||
869 | + if (err) | ||
870 | + return err; | ||
871 | } | ||
872 | |||
873 | + error = ccs_path_rename(old_dir, old_dentry, new_dir, new_dentry); | ||
874 | + if (error) | ||
875 | + return error; | ||
876 | return security_ops->path_rename(old_dir, old_dentry, new_dir, | ||
877 | new_dentry); | ||
878 | } | ||
879 | @@ -480,30 +524,45 @@ EXPORT_SYMBOL(security_path_rename); | ||
880 | |||
881 | int security_path_truncate(struct path *path) | ||
882 | { | ||
883 | + int error; | ||
884 | if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) | ||
885 | return 0; | ||
886 | + error = ccs_path_truncate(path); | ||
887 | + if (error) | ||
888 | + return error; | ||
889 | return security_ops->path_truncate(path); | ||
890 | } | ||
891 | EXPORT_SYMBOL(security_path_truncate); | ||
892 | |||
893 | int security_path_chmod(struct path *path, umode_t mode) | ||
894 | { | ||
895 | + int error; | ||
896 | if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) | ||
897 | return 0; | ||
898 | + error = ccs_path_chmod(path, mode); | ||
899 | + if (error) | ||
900 | + return error; | ||
901 | return security_ops->path_chmod(path, mode); | ||
902 | } | ||
903 | EXPORT_SYMBOL(security_path_chmod); | ||
904 | |||
905 | int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) | ||
906 | { | ||
907 | + int error; | ||
908 | if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) | ||
909 | return 0; | ||
910 | + error = ccs_path_chown(path, uid, gid); | ||
911 | + if (error) | ||
912 | + return error; | ||
913 | return security_ops->path_chown(path, uid, gid); | ||
914 | } | ||
915 | EXPORT_SYMBOL(security_path_chown); | ||
916 | |||
917 | int security_path_chroot(struct path *path) | ||
918 | { | ||
919 | + int error = ccs_path_chroot(path); | ||
920 | + if (error) | ||
921 | + return error; | ||
922 | return security_ops->path_chroot(path); | ||
923 | } | ||
924 | #endif | ||
925 | @@ -618,9 +677,13 @@ EXPORT_SYMBOL_GPL(security_inode_setattr); | ||
926 | |||
927 | int security_inode_getattr(const struct path *path) | ||
928 | { | ||
929 | + int error; | ||
930 | if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) | ||
931 | return 0; | ||
932 | - return security_ops->inode_getattr(path); | ||
933 | + error = security_ops->inode_getattr(path); | ||
934 | + if (!error) | ||
935 | + error = ccs_inode_getattr(path); | ||
936 | + return error; | ||
937 | } | ||
938 | |||
939 | int security_inode_setxattr(struct dentry *dentry, const char *name, | ||
940 | @@ -738,7 +801,10 @@ void security_file_free(struct file *file) | ||
941 | |||
942 | int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
943 | { | ||
944 | - return security_ops->file_ioctl(file, cmd, arg); | ||
945 | + int error = security_ops->file_ioctl(file, cmd, arg); | ||
946 | + if (!error) | ||
947 | + error = ccs_file_ioctl(file, cmd, arg); | ||
948 | + return error; | ||
949 | } | ||
950 | |||
951 | static inline unsigned long mmap_prot(struct file *file, unsigned long prot) | ||
952 | @@ -804,7 +870,10 @@ int security_file_lock(struct file *file, unsigned int cmd) | ||
953 | |||
954 | int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg) | ||
955 | { | ||
956 | - return security_ops->file_fcntl(file, cmd, arg); | ||
957 | + int error = security_ops->file_fcntl(file, cmd, arg); | ||
958 | + if (!error) | ||
959 | + error = ccs_file_fcntl(file, cmd, arg); | ||
960 | + return error; | ||
961 | } | ||
962 | |||
963 | void security_file_set_fowner(struct file *file) | ||
964 | @@ -828,6 +897,8 @@ int security_file_open(struct file *file, const struct cred *cred) | ||
965 | int ret; | ||
966 | |||
967 | ret = security_ops->file_open(file, cred); | ||
968 | + if (!ret) | ||
969 | + ret = ccs_file_open(file, cred); | ||
970 | if (ret) | ||
971 | return ret; | ||
972 | |||
973 | @@ -1178,7 +1249,10 @@ EXPORT_SYMBOL(security_unix_may_send); | ||
974 | |||
975 | int security_socket_create(int family, int type, int protocol, int kern) | ||
976 | { | ||
977 | - return security_ops->socket_create(family, type, protocol, kern); | ||
978 | + int error = security_ops->socket_create(family, type, protocol, kern); | ||
979 | + if (!error) | ||
980 | + error = ccs_socket_create(family, type, protocol, kern); | ||
981 | + return error; | ||
982 | } | ||
983 | |||
984 | int security_socket_post_create(struct socket *sock, int family, | ||
985 | @@ -1190,17 +1264,26 @@ int security_socket_post_create(struct socket *sock, int family, | ||
986 | |||
987 | int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) | ||
988 | { | ||
989 | - return security_ops->socket_bind(sock, address, addrlen); | ||
990 | + int error = security_ops->socket_bind(sock, address, addrlen); | ||
991 | + if (!error) | ||
992 | + error = ccs_socket_bind(sock, address, addrlen); | ||
993 | + return error; | ||
994 | } | ||
995 | |||
996 | int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) | ||
997 | { | ||
998 | - return security_ops->socket_connect(sock, address, addrlen); | ||
999 | + int error = security_ops->socket_connect(sock, address, addrlen); | ||
1000 | + if (!error) | ||
1001 | + error = ccs_socket_connect(sock, address, addrlen); | ||
1002 | + return error; | ||
1003 | } | ||
1004 | |||
1005 | int security_socket_listen(struct socket *sock, int backlog) | ||
1006 | { | ||
1007 | - return security_ops->socket_listen(sock, backlog); | ||
1008 | + int error = security_ops->socket_listen(sock, backlog); | ||
1009 | + if (!error) | ||
1010 | + error = ccs_socket_listen(sock, backlog); | ||
1011 | + return error; | ||
1012 | } | ||
1013 | |||
1014 | int security_socket_accept(struct socket *sock, struct socket *newsock) | ||
1015 | @@ -1210,7 +1293,10 @@ int security_socket_accept(struct socket *sock, struct socket *newsock) | ||
1016 | |||
1017 | int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) | ||
1018 | { | ||
1019 | - return security_ops->socket_sendmsg(sock, msg, size); | ||
1020 | + int error = security_ops->socket_sendmsg(sock, msg, size); | ||
1021 | + if (!error) | ||
1022 | + error = ccs_socket_sendmsg(sock, msg, size); | ||
1023 | + return error; | ||
1024 | } | ||
1025 | |||
1026 | int security_socket_recvmsg(struct socket *sock, struct msghdr *msg, | ||
1027 | -- | ||
1028 | 1.9.1 | ||
1029 | |||
diff --git a/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto_security.patch b/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto_security.patch new file mode 100644 index 0000000..33a69a1 --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto_security.patch | |||
@@ -0,0 +1,17920 @@ | |||
1 | From d4a329ee417f1b75ba087828bb4b2f62c1ef57bb Mon Sep 17 00:00:00 2001 | ||
2 | From: Auto Configured <auto.configured> | ||
3 | Date: Sun, 25 Oct 2015 12:19:45 -0700 | ||
4 | Subject: [PATCH] Ci add ccsecuroty | ||
5 | |||
6 | Signed-off-by: Auto Configured <auto.configured> | ||
7 | --- | ||
8 | include/linux/ccsecurity.h | 926 +++++ | ||
9 | include/linux/lsm2ccsecurity.h | 181 + | ||
10 | security/ccsecurity/Config.in | 83 + | ||
11 | security/ccsecurity/Kconfig | 190 + | ||
12 | security/ccsecurity/Makefile | 122 + | ||
13 | security/ccsecurity/gc.c | 1036 ++++++ | ||
14 | security/ccsecurity/internal.h | 2090 +++++++++++ | ||
15 | security/ccsecurity/load_policy.c | 352 ++ | ||
16 | security/ccsecurity/lsm2ccsecurity.c | 192 + | ||
17 | security/ccsecurity/memory.c | 356 ++ | ||
18 | security/ccsecurity/permission.c | 5025 ++++++++++++++++++++++++++ | ||
19 | security/ccsecurity/policy_io.c | 6484 ++++++++++++++++++++++++++++++++++ | ||
20 | security/ccsecurity/realpath.c | 767 ++++ | ||
21 | 13 files changed, 17804 insertions(+) | ||
22 | create mode 100644 include/linux/ccsecurity.h | ||
23 | create mode 100644 include/linux/lsm2ccsecurity.h | ||
24 | create mode 100644 security/ccsecurity/Config.in | ||
25 | create mode 100644 security/ccsecurity/Kconfig | ||
26 | create mode 100644 security/ccsecurity/Makefile | ||
27 | create mode 100644 security/ccsecurity/gc.c | ||
28 | create mode 100644 security/ccsecurity/internal.h | ||
29 | create mode 100644 security/ccsecurity/load_policy.c | ||
30 | create mode 100644 security/ccsecurity/lsm2ccsecurity.c | ||
31 | create mode 100644 security/ccsecurity/memory.c | ||
32 | create mode 100644 security/ccsecurity/permission.c | ||
33 | create mode 100644 security/ccsecurity/policy_io.c | ||
34 | create mode 100644 security/ccsecurity/realpath.c | ||
35 | |||
36 | diff --git a/include/linux/ccsecurity.h b/include/linux/ccsecurity.h | ||
37 | new file mode 100644 | ||
38 | index 0000000..6c1ca2b | ||
39 | --- /dev/null | ||
40 | +++ b/include/linux/ccsecurity.h | ||
41 | @@ -0,0 +1,926 @@ | ||
42 | +/* | ||
43 | + * include/linux/ccsecurity.h | ||
44 | + * | ||
45 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | ||
46 | + * | ||
47 | + * Version: 1.8.4 2015/05/05 | ||
48 | + */ | ||
49 | + | ||
50 | +#ifndef _LINUX_CCSECURITY_H | ||
51 | +#define _LINUX_CCSECURITY_H | ||
52 | + | ||
53 | +#include <linux/version.h> | ||
54 | + | ||
55 | +#ifndef __user | ||
56 | +#define __user | ||
57 | +#endif | ||
58 | + | ||
59 | +struct nameidata; | ||
60 | +struct path; | ||
61 | +struct dentry; | ||
62 | +struct vfsmount; | ||
63 | +struct linux_binprm; | ||
64 | +struct pt_regs; | ||
65 | +struct file; | ||
66 | +struct ctl_table; | ||
67 | +struct socket; | ||
68 | +struct sockaddr; | ||
69 | +struct sock; | ||
70 | +struct sk_buff; | ||
71 | +struct msghdr; | ||
72 | +struct pid_namespace; | ||
73 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) | ||
74 | +int search_binary_handler(struct linux_binprm *bprm); | ||
75 | +#else | ||
76 | +int search_binary_handler(struct linux_binprm *bprm, struct pt_regs *regs); | ||
77 | +#endif | ||
78 | + | ||
79 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) | ||
80 | +#include <linux/lsm2ccsecurity.h> | ||
81 | +#endif | ||
82 | + | ||
83 | +#ifdef CONFIG_CCSECURITY | ||
84 | + | ||
85 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) | ||
86 | +/* Obtain prototype of __d_path(). */ | ||
87 | +#include <linux/dcache.h> | ||
88 | +#endif | ||
89 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) | ||
90 | +/* Obtain definition of kuid_t and kgid_t. */ | ||
91 | +#include <linux/uidgid.h> | ||
92 | +#endif | ||
93 | + | ||
94 | +/* For exporting variables and functions. */ | ||
95 | +struct ccsecurity_exports { | ||
96 | + void (*load_policy) (const char *filename); | ||
97 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) && defined(CONFIG_SECURITY) | ||
98 | + void (*add_hooks) (void); | ||
99 | +#endif | ||
100 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) | ||
101 | + char * (*d_absolute_path) (const struct path *, char *, int); | ||
102 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | ||
103 | + typeof(__d_path) (*__d_path); | ||
104 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) | ||
105 | + spinlock_t *vfsmount_lock; | ||
106 | +#endif | ||
107 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | ||
108 | + struct task_struct * (*find_task_by_vpid) (pid_t nr); | ||
109 | + struct task_struct * (*find_task_by_pid_ns) (pid_t nr, | ||
110 | + struct pid_namespace *ns); | ||
111 | +#endif | ||
112 | +}; | ||
113 | + | ||
114 | +/* For doing access control. */ | ||
115 | +struct ccsecurity_operations { | ||
116 | + void (*check_profile) (void); | ||
117 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) | ||
118 | + int (*chroot_permission) (struct path *path); | ||
119 | + int (*pivot_root_permission) (struct path *old_path, | ||
120 | + struct path *new_path); | ||
121 | + int (*mount_permission) (const char *dev_name, struct path *path, | ||
122 | + const char *type, unsigned long flags, | ||
123 | + void *data_page); | ||
124 | +#else | ||
125 | + int (*chroot_permission) (struct nameidata *nd); | ||
126 | + int (*pivot_root_permission) (struct nameidata *old_nd, | ||
127 | + struct nameidata *new_nd); | ||
128 | + int (*mount_permission) (const char *dev_name, struct nameidata *nd, | ||
129 | + const char *type, unsigned long flags, | ||
130 | + void *data_page); | ||
131 | +#endif | ||
132 | + int (*umount_permission) (struct vfsmount *mnt, int flags); | ||
133 | + _Bool (*lport_reserved) (const u16 port); | ||
134 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | ||
135 | + void (*save_open_mode) (int mode); | ||
136 | + void (*clear_open_mode) (void); | ||
137 | + int (*open_permission) (struct dentry *dentry, struct vfsmount *mnt, | ||
138 | + const int flag); | ||
139 | +#else | ||
140 | + int (*open_permission) (struct file *file); | ||
141 | +#endif | ||
142 | + int (*ptrace_permission) (long request, long pid); | ||
143 | + int (*ioctl_permission) (struct file *filp, unsigned int cmd, | ||
144 | + unsigned long arg); | ||
145 | + int (*parse_table) (int __user *name, int nlen, void __user *oldval, | ||
146 | + void __user *newval, struct ctl_table *table); | ||
147 | + _Bool (*capable) (const u8 operation); | ||
148 | + int (*mknod_permission) (struct dentry *dentry, struct vfsmount *mnt, | ||
149 | + unsigned int mode, unsigned int dev); | ||
150 | + int (*mkdir_permission) (struct dentry *dentry, struct vfsmount *mnt, | ||
151 | + unsigned int mode); | ||
152 | + int (*rmdir_permission) (struct dentry *dentry, struct vfsmount *mnt); | ||
153 | + int (*unlink_permission) (struct dentry *dentry, struct vfsmount *mnt); | ||
154 | + int (*symlink_permission) (struct dentry *dentry, struct vfsmount *mnt, | ||
155 | + const char *from); | ||
156 | + int (*truncate_permission) (struct dentry *dentry, | ||
157 | + struct vfsmount *mnt); | ||
158 | + int (*rename_permission) (struct dentry *old_dentry, | ||
159 | + struct dentry *new_dentry, | ||
160 | + struct vfsmount *mnt); | ||
161 | + int (*link_permission) (struct dentry *old_dentry, | ||
162 | + struct dentry *new_dentry, | ||
163 | + struct vfsmount *mnt); | ||
164 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | ||
165 | + int (*open_exec_permission) (struct dentry *dentry, | ||
166 | + struct vfsmount *mnt); | ||
167 | + int (*uselib_permission) (struct dentry *dentry, struct vfsmount *mnt); | ||
168 | +#endif | ||
169 | + int (*fcntl_permission) (struct file *file, unsigned int cmd, | ||
170 | + unsigned long arg); | ||
171 | + int (*kill_permission) (pid_t pid, int sig); | ||
172 | + int (*tgkill_permission) (pid_t tgid, pid_t pid, int sig); | ||
173 | + int (*tkill_permission) (pid_t pid, int sig); | ||
174 | + int (*socket_create_permission) (int family, int type, int protocol); | ||
175 | + int (*socket_listen_permission) (struct socket *sock); | ||
176 | + int (*socket_connect_permission) (struct socket *sock, | ||
177 | + struct sockaddr *addr, int addr_len); | ||
178 | + int (*socket_bind_permission) (struct socket *sock, | ||
179 | + struct sockaddr *addr, int addr_len); | ||
180 | + int (*socket_post_accept_permission) (struct socket *sock, | ||
181 | + struct socket *newsock); | ||
182 | + int (*socket_sendmsg_permission) (struct socket *sock, | ||
183 | + struct msghdr *msg, int size); | ||
184 | + int (*socket_post_recvmsg_permission) (struct sock *sk, | ||
185 | + struct sk_buff *skb, int flags); | ||
186 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) | ||
187 | + int (*chown_permission) (struct dentry *dentry, struct vfsmount *mnt, | ||
188 | + kuid_t user, kgid_t group); | ||
189 | +#else | ||
190 | + int (*chown_permission) (struct dentry *dentry, struct vfsmount *mnt, | ||
191 | + uid_t user, gid_t group); | ||
192 | +#endif | ||
193 | + int (*chmod_permission) (struct dentry *dentry, struct vfsmount *mnt, | ||
194 | + mode_t mode); | ||
195 | + int (*getattr_permission) (struct vfsmount *mnt, | ||
196 | + struct dentry *dentry); | ||
197 | + int (*sigqueue_permission) (pid_t pid, int sig); | ||
198 | + int (*tgsigqueue_permission) (pid_t tgid, pid_t pid, int sig); | ||
199 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) | ||
200 | + int (*search_binary_handler) (struct linux_binprm *bprm); | ||
201 | +#else | ||
202 | + int (*search_binary_handler) (struct linux_binprm *bprm, | ||
203 | + struct pt_regs *regs); | ||
204 | +#endif | ||
205 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | ||
206 | + int (*alloc_task_security) (const struct task_struct *task); | ||
207 | + void (*free_task_security) (const struct task_struct *task); | ||
208 | +#endif | ||
209 | + _Bool disabled; | ||
210 | +}; | ||
211 | + | ||
212 | +extern struct ccsecurity_operations ccsecurity_ops; | ||
213 | + | ||
214 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) | ||
215 | + | ||
216 | +static inline int ccs_chroot_permission(struct path *path) | ||
217 | +{ | ||
218 | + int (*func) (struct path *) = ccsecurity_ops.chroot_permission; | ||
219 | + return func ? func(path) : 0; | ||
220 | +} | ||
221 | + | ||
222 | +static inline int ccs_pivot_root_permission(struct path *old_path, | ||
223 | + struct path *new_path) | ||
224 | +{ | ||
225 | + int (*func) (struct path *, struct path *) | ||
226 | + = ccsecurity_ops.pivot_root_permission; | ||
227 | + return func ? func(old_path, new_path) : 0; | ||
228 | +} | ||
229 | + | ||
230 | +static inline int ccs_mount_permission(const char *dev_name, struct path *path, | ||
231 | + const char *type, unsigned long flags, | ||
232 | + void *data_page) | ||
233 | +{ | ||
234 | + int (*func) (const char *, struct path *, const char *, unsigned long, | ||
235 | + void *) = ccsecurity_ops.mount_permission; | ||
236 | + return func ? func(dev_name, path, type, flags, data_page) : 0; | ||
237 | +} | ||
238 | + | ||
239 | +#else | ||
240 | + | ||
241 | +static inline int ccs_chroot_permission(struct nameidata *nd) | ||
242 | +{ | ||
243 | + int (*func) (struct nameidata *) = ccsecurity_ops.chroot_permission; | ||
244 | + return func ? func(nd) : 0; | ||
245 | +} | ||
246 | + | ||
247 | +static inline int ccs_pivot_root_permission(struct nameidata *old_nd, | ||
248 | + struct nameidata *new_nd) | ||
249 | +{ | ||
250 | + int (*func) (struct nameidata *, struct nameidata *) | ||
251 | + = ccsecurity_ops.pivot_root_permission; | ||
252 | + return func ? func(old_nd, new_nd) : 0; | ||
253 | +} | ||
254 | + | ||
255 | +static inline int ccs_mount_permission(const char *dev_name, | ||
256 | + struct nameidata *nd, const char *type, | ||
257 | + unsigned long flags, void *data_page) | ||
258 | +{ | ||
259 | + int (*func) (const char *, struct nameidata *, const char *, | ||
260 | + unsigned long, void *) = ccsecurity_ops.mount_permission; | ||
261 | + return func ? func(dev_name, nd, type, flags, data_page) : 0; | ||
262 | +} | ||
263 | + | ||
264 | +#endif | ||
265 | + | ||
266 | +static inline int ccs_umount_permission(struct vfsmount *mnt, int flags) | ||
267 | +{ | ||
268 | + int (*func) (struct vfsmount *, int) | ||
269 | + = ccsecurity_ops.umount_permission; | ||
270 | + return func ? func(mnt, flags) : 0; | ||
271 | +} | ||
272 | + | ||
273 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | ||
274 | + | ||
275 | +static inline void ccs_save_open_mode(int mode) | ||
276 | +{ | ||
277 | + void (*func) (int) = ccsecurity_ops.save_open_mode; | ||
278 | + if (func) | ||
279 | + func(mode); | ||
280 | +} | ||
281 | + | ||
282 | +static inline void ccs_clear_open_mode(void) | ||
283 | +{ | ||
284 | + void (*func) (void) = ccsecurity_ops.clear_open_mode; | ||
285 | + if (func) | ||
286 | + func(); | ||
287 | +} | ||
288 | + | ||
289 | +static inline int ccs_open_permission(struct dentry *dentry, | ||
290 | + struct vfsmount *mnt, const int flag) | ||
291 | +{ | ||
292 | + int (*func) (struct dentry *, struct vfsmount *, const int) | ||
293 | + = ccsecurity_ops.open_permission; | ||
294 | + return func ? func(dentry, mnt, flag) : 0; | ||
295 | +} | ||
296 | + | ||
297 | +#else | ||
298 | + | ||
299 | +static inline int ccs_open_permission(struct file *filp) | ||
300 | +{ | ||
301 | + int (*func) (struct file *) = ccsecurity_ops.open_permission; | ||
302 | + return func ? func(filp) : 0; | ||
303 | +} | ||
304 | + | ||
305 | +#endif | ||
306 | + | ||
307 | +static inline int ccs_fcntl_permission(struct file *file, unsigned int cmd, | ||
308 | + unsigned long arg) | ||
309 | +{ | ||
310 | + int (*func) (struct file *, unsigned int, unsigned long) | ||
311 | + = ccsecurity_ops.fcntl_permission; | ||
312 | + return func ? func(file, cmd, arg) : 0; | ||
313 | +} | ||
314 | + | ||
315 | +static inline int ccs_ioctl_permission(struct file *filp, unsigned int cmd, | ||
316 | + unsigned long arg) | ||
317 | +{ | ||
318 | + int (*func) (struct file *, unsigned int, unsigned long) | ||
319 | + = ccsecurity_ops.ioctl_permission; | ||
320 | + return func ? func(filp, cmd, arg) : 0; | ||
321 | +} | ||
322 | + | ||
323 | +static inline int ccs_parse_table(int __user *name, int nlen, | ||
324 | + void __user *oldval, void __user *newval, | ||
325 | + struct ctl_table *table) | ||
326 | +{ | ||
327 | + int (*func) (int __user *, int, void __user *, void __user *, | ||
328 | + struct ctl_table *) = ccsecurity_ops.parse_table; | ||
329 | + return func ? func(name, nlen, oldval, newval, table) : 0; | ||
330 | +} | ||
331 | + | ||
332 | +static inline int ccs_mknod_permission(struct dentry *dentry, | ||
333 | + struct vfsmount *mnt, unsigned int mode, | ||
334 | + unsigned int dev) | ||
335 | +{ | ||
336 | + int (*func) (struct dentry *, struct vfsmount *, unsigned int, | ||
337 | + unsigned int) = ccsecurity_ops.mknod_permission; | ||
338 | + return func ? func(dentry, mnt, mode, dev) : 0; | ||
339 | +} | ||
340 | + | ||
341 | +static inline int ccs_mkdir_permission(struct dentry *dentry, | ||
342 | + struct vfsmount *mnt, unsigned int mode) | ||
343 | +{ | ||
344 | + int (*func) (struct dentry *, struct vfsmount *, unsigned int) | ||
345 | + = ccsecurity_ops.mkdir_permission; | ||
346 | + return func ? func(dentry, mnt, mode) : 0; | ||
347 | +} | ||
348 | + | ||
349 | +static inline int ccs_rmdir_permission(struct dentry *dentry, | ||
350 | + struct vfsmount *mnt) | ||
351 | +{ | ||
352 | + int (*func) (struct dentry *, struct vfsmount *) | ||
353 | + = ccsecurity_ops.rmdir_permission; | ||
354 | + return func ? func(dentry, mnt) : 0; | ||
355 | +} | ||
356 | + | ||
357 | +static inline int ccs_unlink_permission(struct dentry *dentry, | ||
358 | + struct vfsmount *mnt) | ||
359 | +{ | ||
360 | + int (*func) (struct dentry *, struct vfsmount *) | ||
361 | + = ccsecurity_ops.unlink_permission; | ||
362 | + return func ? func(dentry, mnt) : 0; | ||
363 | +} | ||
364 | + | ||
365 | +static inline int ccs_symlink_permission(struct dentry *dentry, | ||
366 | + struct vfsmount *mnt, | ||
367 | + const char *from) | ||
368 | +{ | ||
369 | + int (*func) (struct dentry *, struct vfsmount *, const char *) | ||
370 | + = ccsecurity_ops.symlink_permission; | ||
371 | + return func ? func(dentry, mnt, from) : 0; | ||
372 | +} | ||
373 | + | ||
374 | +static inline int ccs_truncate_permission(struct dentry *dentry, | ||
375 | + struct vfsmount *mnt) | ||
376 | +{ | ||
377 | + int (*func) (struct dentry *, struct vfsmount *) | ||
378 | + = ccsecurity_ops.truncate_permission; | ||
379 | + return func ? func(dentry, mnt) : 0; | ||
380 | +} | ||
381 | + | ||
382 | +static inline int ccs_rename_permission(struct dentry *old_dentry, | ||
383 | + struct dentry *new_dentry, | ||
384 | + struct vfsmount *mnt) | ||
385 | +{ | ||
386 | + int (*func) (struct dentry *, struct dentry *, struct vfsmount *) | ||
387 | + = ccsecurity_ops.rename_permission; | ||
388 | + return func ? func(old_dentry, new_dentry, mnt) : 0; | ||
389 | +} | ||
390 | + | ||
391 | +static inline int ccs_link_permission(struct dentry *old_dentry, | ||
392 | + struct dentry *new_dentry, | ||
393 | + struct vfsmount *mnt) | ||
394 | +{ | ||
395 | + int (*func) (struct dentry *, struct dentry *, struct vfsmount *) | ||
396 | + = ccsecurity_ops.link_permission; | ||
397 | + return func ? func(old_dentry, new_dentry, mnt) : 0; | ||
398 | +} | ||
399 | + | ||
400 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | ||
401 | + | ||
402 | +static inline int ccs_open_exec_permission(struct dentry *dentry, | ||
403 | + struct vfsmount *mnt) | ||
404 | +{ | ||
405 | + int (*func) (struct dentry *, struct vfsmount *) | ||
406 | + = ccsecurity_ops.open_exec_permission; | ||
407 | + return func ? func(dentry, mnt) : 0; | ||
408 | +} | ||
409 | + | ||
410 | +static inline int ccs_uselib_permission(struct dentry *dentry, | ||
411 | + struct vfsmount *mnt) | ||
412 | +{ | ||
413 | + int (*func) (struct dentry *, struct vfsmount *) | ||
414 | + = ccsecurity_ops.uselib_permission; | ||
415 | + return func ? func(dentry, mnt) : 0; | ||
416 | +} | ||
417 | + | ||
418 | +#endif | ||
419 | + | ||
420 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) | ||
421 | + | ||
422 | +static inline int ccs_chown_permission(struct dentry *dentry, | ||
423 | + struct vfsmount *mnt, kuid_t user, | ||
424 | + kgid_t group) | ||
425 | +{ | ||
426 | + int (*func) (struct dentry *, struct vfsmount *, kuid_t, kgid_t) | ||
427 | + = ccsecurity_ops.chown_permission; | ||
428 | + return func ? func(dentry, mnt, user, group) : 0; | ||
429 | +} | ||
430 | + | ||
431 | +#else | ||
432 | + | ||
433 | +static inline int ccs_chown_permission(struct dentry *dentry, | ||
434 | + struct vfsmount *mnt, uid_t user, | ||
435 | + gid_t group) | ||
436 | +{ | ||
437 | + int (*func) (struct dentry *, struct vfsmount *, uid_t, gid_t) | ||
438 | + = ccsecurity_ops.chown_permission; | ||
439 | + return func ? func(dentry, mnt, user, group) : 0; | ||
440 | +} | ||
441 | + | ||
442 | +#endif | ||
443 | + | ||
444 | +static inline int ccs_chmod_permission(struct dentry *dentry, | ||
445 | + struct vfsmount *mnt, mode_t mode) | ||
446 | +{ | ||
447 | + int (*func) (struct dentry *, struct vfsmount *, mode_t) | ||
448 | + = ccsecurity_ops.chmod_permission; | ||
449 | + return func ? func(dentry, mnt, mode) : 0; | ||
450 | +} | ||
451 | + | ||
452 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) | ||
453 | + | ||
454 | +static inline int ccs_search_binary_handler(struct linux_binprm *bprm) | ||
455 | +{ | ||
456 | + return ccsecurity_ops.search_binary_handler(bprm); | ||
457 | +} | ||
458 | + | ||
459 | +#else | ||
460 | + | ||
461 | +static inline int ccs_search_binary_handler(struct linux_binprm *bprm, | ||
462 | + struct pt_regs *regs) | ||
463 | +{ | ||
464 | + return ccsecurity_ops.search_binary_handler(bprm, regs); | ||
465 | +} | ||
466 | + | ||
467 | +#endif | ||
468 | + | ||
469 | +#else | ||
470 | + | ||
471 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) | ||
472 | + | ||
473 | +static inline int ccs_chroot_permission(struct path *path) | ||
474 | +{ | ||
475 | + return 0; | ||
476 | +} | ||
477 | + | ||
478 | +static inline int ccs_pivot_root_permission(struct path *old_path, | ||
479 | + struct path *new_path) | ||
480 | +{ | ||
481 | + return 0; | ||
482 | +} | ||
483 | + | ||
484 | +static inline int ccs_mount_permission(const char *dev_name, struct path *path, | ||
485 | + const char *type, unsigned long flags, | ||
486 | + void *data_page) | ||
487 | +{ | ||
488 | + return 0; | ||
489 | +} | ||
490 | + | ||
491 | +#else | ||
492 | + | ||
493 | +static inline int ccs_chroot_permission(struct nameidata *nd) | ||
494 | +{ | ||
495 | + return 0; | ||
496 | +} | ||
497 | + | ||
498 | +static inline int ccs_pivot_root_permission(struct nameidata *old_nd, | ||
499 | + struct nameidata *new_nd) | ||
500 | +{ | ||
501 | + return 0; | ||
502 | +} | ||
503 | + | ||
504 | +static inline int ccs_mount_permission(const char *dev_name, | ||
505 | + struct nameidata *nd, const char *type, | ||
506 | + unsigned long flags, void *data_page) | ||
507 | +{ | ||
508 | + return 0; | ||
509 | +} | ||
510 | + | ||
511 | +#endif | ||
512 | + | ||
513 | +static inline int ccs_umount_permission(struct vfsmount *mnt, int flags) | ||
514 | +{ | ||
515 | + return 0; | ||
516 | +} | ||
517 | + | ||
518 | +static inline void ccs_save_open_mode(int mode) | ||
519 | +{ | ||
520 | +} | ||
521 | + | ||
522 | +static inline void ccs_clear_open_mode(void) | ||
523 | +{ | ||
524 | +} | ||
525 | + | ||
526 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | ||
527 | + | ||
528 | +static inline int ccs_open_permission(struct dentry *dentry, | ||
529 | + struct vfsmount *mnt, const int flag) | ||
530 | +{ | ||
531 | + return 0; | ||
532 | +} | ||
533 | + | ||
534 | +#else | ||
535 | + | ||
536 | +static inline int ccs_open_permission(struct file *filp) | ||
537 | +{ | ||
538 | + return 0; | ||
539 | +} | ||
540 | + | ||
541 | +#endif | ||
542 | + | ||
543 | +static inline int ccs_ioctl_permission(struct file *filp, unsigned int cmd, | ||
544 | + unsigned long arg) | ||
545 | +{ | ||
546 | + return 0; | ||
547 | +} | ||
548 | + | ||
549 | +static inline int ccs_parse_table(int __user *name, int nlen, | ||
550 | + void __user *oldval, void __user *newval, | ||
551 | + struct ctl_table *table) | ||
552 | +{ | ||
553 | + return 0; | ||
554 | +} | ||
555 | + | ||
556 | +static inline int ccs_mknod_permission(struct dentry *dentry, | ||
557 | + struct vfsmount *mnt, unsigned int mode, | ||
558 | + unsigned int dev) | ||
559 | +{ | ||
560 | + return 0; | ||
561 | +} | ||
562 | + | ||
563 | +static inline int ccs_mkdir_permission(struct dentry *dentry, | ||
564 | + struct vfsmount *mnt, unsigned int mode) | ||
565 | +{ | ||
566 | + return 0; | ||
567 | +} | ||
568 | + | ||
569 | +static inline int ccs_rmdir_permission(struct dentry *dentry, | ||
570 | + struct vfsmount *mnt) | ||
571 | +{ | ||
572 | + return 0; | ||
573 | +} | ||
574 | + | ||
575 | +static inline int ccs_unlink_permission(struct dentry *dentry, | ||
576 | + struct vfsmount *mnt) | ||
577 | +{ | ||
578 | + return 0; | ||
579 | +} | ||
580 | + | ||
581 | +static inline int ccs_symlink_permission(struct dentry *dentry, | ||
582 | + struct vfsmount *mnt, | ||
583 | + const char *from) | ||
584 | +{ | ||
585 | + return 0; | ||
586 | +} | ||
587 | + | ||
588 | +static inline int ccs_truncate_permission(struct dentry *dentry, | ||
589 | + struct vfsmount *mnt) | ||
590 | +{ | ||
591 | + return 0; | ||
592 | +} | ||
593 | + | ||
594 | +static inline int ccs_rename_permission(struct dentry *old_dentry, | ||
595 | + struct dentry *new_dentry, | ||
596 | + struct vfsmount *mnt) | ||
597 | +{ | ||
598 | + return 0; | ||
599 | +} | ||
600 | + | ||
601 | +static inline int ccs_link_permission(struct dentry *old_dentry, | ||
602 | + struct dentry *new_dentry, | ||
603 | + struct vfsmount *mnt) | ||
604 | +{ | ||
605 | + return 0; | ||
606 | +} | ||
607 | + | ||
608 | +static inline int ccs_open_exec_permission(struct dentry *dentry, | ||
609 | + struct vfsmount *mnt) | ||
610 | +{ | ||
611 | + return 0; | ||
612 | +} | ||
613 | + | ||
614 | +static inline int ccs_uselib_permission(struct dentry *dentry, | ||
615 | + struct vfsmount *mnt) | ||
616 | +{ | ||
617 | + return 0; | ||
618 | +} | ||
619 | + | ||
620 | +static inline int ccs_fcntl_permission(struct file *file, unsigned int cmd, | ||
621 | + unsigned long arg) | ||
622 | +{ | ||
623 | + return 0; | ||
624 | +} | ||
625 | + | ||
626 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) | ||
627 | + | ||
628 | +static inline int ccs_chown_permission(struct dentry *dentry, | ||
629 | + struct vfsmount *mnt, kuid_t user, | ||
630 | + kgid_t group) | ||
631 | +{ | ||
632 | + return 0; | ||
633 | +} | ||
634 | + | ||
635 | +#else | ||
636 | + | ||
637 | +static inline int ccs_chown_permission(struct dentry *dentry, | ||
638 | + struct vfsmount *mnt, uid_t user, | ||
639 | + gid_t group) | ||
640 | +{ | ||
641 | + return 0; | ||
642 | +} | ||
643 | + | ||
644 | +#endif | ||
645 | + | ||
646 | +static inline int ccs_chmod_permission(struct dentry *dentry, | ||
647 | + struct vfsmount *mnt, mode_t mode) | ||
648 | +{ | ||
649 | + return 0; | ||
650 | +} | ||
651 | + | ||
652 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) | ||
653 | + | ||
654 | +static inline int ccs_search_binary_handler(struct linux_binprm *bprm) | ||
655 | +{ | ||
656 | + return search_binary_handler(bprm); | ||
657 | +} | ||
658 | + | ||
659 | +#else | ||
660 | + | ||
661 | +static inline int ccs_search_binary_handler(struct linux_binprm *bprm, | ||
662 | + struct pt_regs *regs) | ||
663 | +{ | ||
664 | + return search_binary_handler(bprm, regs); | ||
665 | +} | ||
666 | + | ||
667 | +#endif | ||
668 | + | ||
669 | +#endif | ||
670 | + | ||
671 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | ||
672 | + | ||
673 | +static inline int ccs_alloc_task_security(const struct task_struct *task) | ||
674 | +{ | ||
675 | + int (*func) (const struct task_struct *) | ||
676 | + = ccsecurity_ops.alloc_task_security; | ||
677 | + return func ? func(task) : 0; | ||
678 | +} | ||
679 | + | ||
680 | +static inline void ccs_free_task_security(const struct task_struct *task) | ||
681 | +{ | ||
682 | + void (*func) (const struct task_struct *) | ||
683 | + = ccsecurity_ops.free_task_security; | ||
684 | + if (func) | ||
685 | + func(task); | ||
686 | +} | ||
687 | + | ||
688 | +#else | ||
689 | + | ||
690 | +static inline int ccs_alloc_task_security(const struct task_struct *task) | ||
691 | +{ | ||
692 | + return 0; | ||
693 | +} | ||
694 | + | ||
695 | +static inline void ccs_free_task_security(const struct task_struct *task) | ||
696 | +{ | ||
697 | +} | ||
698 | + | ||
699 | +#endif | ||
700 | + | ||
701 | +#ifdef CONFIG_CCSECURITY_FILE_GETATTR | ||
702 | + | ||
703 | +static inline int ccs_getattr_permission(struct vfsmount *mnt, | ||
704 | + struct dentry *dentry) | ||
705 | +{ | ||
706 | + int (*func) (struct vfsmount *, struct dentry *) | ||
707 | + = ccsecurity_ops.getattr_permission; | ||
708 | + return func ? func(mnt, dentry) : 0; | ||
709 | +} | ||
710 | + | ||
711 | +#else | ||
712 | + | ||
713 | +static inline int ccs_getattr_permission(struct vfsmount *mnt, | ||
714 | + struct dentry *dentry) | ||
715 | +{ | ||
716 | + return 0; | ||
717 | +} | ||
718 | + | ||
719 | +#endif | ||
720 | + | ||
721 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
722 | + | ||
723 | +static inline int ccs_socket_listen_permission(struct socket *sock) | ||
724 | +{ | ||
725 | + int (*func) (struct socket *) | ||
726 | + = ccsecurity_ops.socket_listen_permission; | ||
727 | + return func ? func(sock) : 0; | ||
728 | +} | ||
729 | + | ||
730 | +static inline int ccs_socket_connect_permission(struct socket *sock, | ||
731 | + struct sockaddr *addr, | ||
732 | + int addr_len) | ||
733 | +{ | ||
734 | + int (*func) (struct socket *, struct sockaddr *, int) | ||
735 | + = ccsecurity_ops.socket_connect_permission; | ||
736 | + return func ? func(sock, addr, addr_len) : 0; | ||
737 | +} | ||
738 | + | ||
739 | +static inline int ccs_socket_bind_permission(struct socket *sock, | ||
740 | + struct sockaddr *addr, | ||
741 | + int addr_len) | ||
742 | +{ | ||
743 | + int (*func) (struct socket *, struct sockaddr *, int) | ||
744 | + = ccsecurity_ops.socket_bind_permission; | ||
745 | + return func ? func(sock, addr, addr_len) : 0; | ||
746 | +} | ||
747 | + | ||
748 | +static inline int ccs_socket_post_accept_permission(struct socket *sock, | ||
749 | + struct socket *newsock) | ||
750 | +{ | ||
751 | + int (*func) (struct socket *, struct socket *) | ||
752 | + = ccsecurity_ops.socket_post_accept_permission; | ||
753 | + return func ? func(sock, newsock) : 0; | ||
754 | +} | ||
755 | + | ||
756 | +static inline int ccs_socket_sendmsg_permission(struct socket *sock, | ||
757 | + struct msghdr *msg, | ||
758 | + int size) | ||
759 | +{ | ||
760 | + int (*func) (struct socket *, struct msghdr *, int) | ||
761 | + = ccsecurity_ops.socket_sendmsg_permission; | ||
762 | + return func ? func(sock, msg, size) : 0; | ||
763 | +} | ||
764 | + | ||
765 | +#else | ||
766 | + | ||
767 | +static inline int ccs_socket_listen_permission(struct socket *sock) | ||
768 | +{ | ||
769 | + return 0; | ||
770 | +} | ||
771 | + | ||
772 | +static inline int ccs_socket_connect_permission(struct socket *sock, | ||
773 | + struct sockaddr *addr, | ||
774 | + int addr_len) | ||
775 | +{ | ||
776 | + return 0; | ||
777 | +} | ||
778 | + | ||
779 | +static inline int ccs_socket_bind_permission(struct socket *sock, | ||
780 | + struct sockaddr *addr, | ||
781 | + int addr_len) | ||
782 | +{ | ||
783 | + return 0; | ||
784 | +} | ||
785 | + | ||
786 | +static inline int ccs_socket_post_accept_permission(struct socket *sock, | ||
787 | + struct socket *newsock) | ||
788 | +{ | ||
789 | + return 0; | ||
790 | +} | ||
791 | + | ||
792 | +static inline int ccs_socket_sendmsg_permission(struct socket *sock, | ||
793 | + struct msghdr *msg, | ||
794 | + int size) | ||
795 | +{ | ||
796 | + return 0; | ||
797 | +} | ||
798 | + | ||
799 | +#endif | ||
800 | + | ||
801 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
802 | + | ||
803 | +static inline int ccs_socket_post_recvmsg_permission(struct sock *sk, | ||
804 | + struct sk_buff *skb, | ||
805 | + int flags) | ||
806 | +{ | ||
807 | + int (*func) (struct sock *, struct sk_buff *, int) | ||
808 | + = ccsecurity_ops.socket_post_recvmsg_permission; | ||
809 | + return func ? func(sk, skb, flags) : 0; | ||
810 | +} | ||
811 | + | ||
812 | +#else | ||
813 | + | ||
814 | +static inline int ccs_socket_post_recvmsg_permission(struct sock *sk, | ||
815 | + struct sk_buff *skb, | ||
816 | + int flags) | ||
817 | +{ | ||
818 | + return 0; | ||
819 | +} | ||
820 | + | ||
821 | +#endif | ||
822 | + | ||
823 | +#ifdef CONFIG_CCSECURITY_PORTRESERVE | ||
824 | + | ||
825 | +static inline _Bool ccs_lport_reserved(const u16 port) | ||
826 | +{ | ||
827 | + _Bool (*func) (const u16) = ccsecurity_ops.lport_reserved; | ||
828 | + return func ? func(port) : 0; | ||
829 | +} | ||
830 | + | ||
831 | +#else | ||
832 | + | ||
833 | +static inline _Bool ccs_lport_reserved(const u16 port) | ||
834 | +{ | ||
835 | + return 0; | ||
836 | +} | ||
837 | + | ||
838 | +#endif | ||
839 | + | ||
840 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
841 | + | ||
842 | +static inline _Bool ccs_capable(const u8 operation) | ||
843 | +{ | ||
844 | + _Bool (*func) (const u8) = ccsecurity_ops.capable; | ||
845 | + return func ? func(operation) : 1; | ||
846 | +} | ||
847 | + | ||
848 | +static inline int ccs_socket_create_permission(int family, int type, | ||
849 | + int protocol) | ||
850 | +{ | ||
851 | + int (*func) (int, int, int) = ccsecurity_ops.socket_create_permission; | ||
852 | + return func ? func(family, type, protocol) : 0; | ||
853 | +} | ||
854 | + | ||
855 | +static inline int ccs_ptrace_permission(long request, long pid) | ||
856 | +{ | ||
857 | + int (*func) (long, long) = ccsecurity_ops.ptrace_permission; | ||
858 | + return func ? func(request, pid) : 0; | ||
859 | +} | ||
860 | + | ||
861 | +#else | ||
862 | + | ||
863 | +static inline _Bool ccs_capable(const u8 operation) | ||
864 | +{ | ||
865 | + return 1; | ||
866 | +} | ||
867 | + | ||
868 | +static inline int ccs_socket_create_permission(int family, int type, | ||
869 | + int protocol) | ||
870 | +{ | ||
871 | + return 0; | ||
872 | +} | ||
873 | + | ||
874 | +static inline int ccs_ptrace_permission(long request, long pid) | ||
875 | +{ | ||
876 | + return 0; | ||
877 | +} | ||
878 | + | ||
879 | +#endif | ||
880 | + | ||
881 | +#ifdef CONFIG_CCSECURITY_IPC | ||
882 | + | ||
883 | +static inline int ccs_kill_permission(pid_t pid, int sig) | ||
884 | +{ | ||
885 | + int (*func) (pid_t, int) = ccsecurity_ops.kill_permission; | ||
886 | + return func ? func(pid, sig) : 0; | ||
887 | +} | ||
888 | + | ||
889 | +static inline int ccs_tgkill_permission(pid_t tgid, pid_t pid, int sig) | ||
890 | +{ | ||
891 | + int (*func) (pid_t, pid_t, int) = ccsecurity_ops.tgkill_permission; | ||
892 | + return func ? func(tgid, pid, sig) : 0; | ||
893 | +} | ||
894 | + | ||
895 | +static inline int ccs_tkill_permission(pid_t pid, int sig) | ||
896 | +{ | ||
897 | + int (*func) (pid_t, int) = ccsecurity_ops.tkill_permission; | ||
898 | + return func ? func(pid, sig) : 0; | ||
899 | +} | ||
900 | + | ||
901 | +static inline int ccs_sigqueue_permission(pid_t pid, int sig) | ||
902 | +{ | ||
903 | + int (*func) (pid_t, int) = ccsecurity_ops.sigqueue_permission; | ||
904 | + return func ? func(pid, sig) : 0; | ||
905 | +} | ||
906 | + | ||
907 | +static inline int ccs_tgsigqueue_permission(pid_t tgid, pid_t pid, int sig) | ||
908 | +{ | ||
909 | + int (*func) (pid_t, pid_t, int) = ccsecurity_ops.tgsigqueue_permission; | ||
910 | + return func ? func(tgid, pid, sig) : 0; | ||
911 | +} | ||
912 | + | ||
913 | +#else | ||
914 | + | ||
915 | +static inline int ccs_kill_permission(pid_t pid, int sig) | ||
916 | +{ | ||
917 | + return 0; | ||
918 | +} | ||
919 | + | ||
920 | +static inline int ccs_tgkill_permission(pid_t tgid, pid_t pid, int sig) | ||
921 | +{ | ||
922 | + return 0; | ||
923 | +} | ||
924 | + | ||
925 | +static inline int ccs_tkill_permission(pid_t pid, int sig) | ||
926 | +{ | ||
927 | + return 0; | ||
928 | +} | ||
929 | + | ||
930 | +static inline int ccs_sigqueue_permission(pid_t pid, int sig) | ||
931 | +{ | ||
932 | + return 0; | ||
933 | +} | ||
934 | + | ||
935 | +static inline int ccs_tgsigqueue_permission(pid_t tgid, pid_t pid, int sig) | ||
936 | +{ | ||
937 | + return 0; | ||
938 | +} | ||
939 | + | ||
940 | +#endif | ||
941 | + | ||
942 | +/* Index numbers for Capability Controls. */ | ||
943 | +enum ccs_capability_acl_index { | ||
944 | + /* socket(PF_ROUTE, *, *) */ | ||
945 | + CCS_USE_ROUTE_SOCKET, | ||
946 | + /* socket(PF_PACKET, *, *) */ | ||
947 | + CCS_USE_PACKET_SOCKET, | ||
948 | + /* sys_reboot() */ | ||
949 | + CCS_SYS_REBOOT, | ||
950 | + /* sys_vhangup() */ | ||
951 | + CCS_SYS_VHANGUP, | ||
952 | + /* do_settimeofday(), sys_adjtimex() */ | ||
953 | + CCS_SYS_SETTIME, | ||
954 | + /* sys_nice(), sys_setpriority() */ | ||
955 | + CCS_SYS_NICE, | ||
956 | + /* sys_sethostname(), sys_setdomainname() */ | ||
957 | + CCS_SYS_SETHOSTNAME, | ||
958 | + /* sys_create_module(), sys_init_module(), sys_delete_module() */ | ||
959 | + CCS_USE_KERNEL_MODULE, | ||
960 | + /* sys_kexec_load() */ | ||
961 | + CCS_SYS_KEXEC_LOAD, | ||
962 | + /* sys_ptrace() */ | ||
963 | + CCS_SYS_PTRACE, | ||
964 | + CCS_MAX_CAPABILITY_INDEX | ||
965 | +}; | ||
966 | + | ||
967 | +#endif | ||
968 | diff --git a/include/linux/lsm2ccsecurity.h b/include/linux/lsm2ccsecurity.h | ||
969 | new file mode 100644 | ||
970 | index 0000000..ab4ea5c | ||
971 | --- /dev/null | ||
972 | +++ b/include/linux/lsm2ccsecurity.h | ||
973 | @@ -0,0 +1,181 @@ | ||
974 | +/* | ||
975 | + * include/linux/lsm2ccsecurity.h | ||
976 | + * | ||
977 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | ||
978 | + * | ||
979 | + * Version: 1.8.4 2015/07/11 | ||
980 | + */ | ||
981 | + | ||
982 | +#ifndef _LINUX_LSM2CCSECURITY_H | ||
983 | +#define _LINUX_LSM2CCSECURITY_H | ||
984 | + | ||
985 | +#include <linux/version.h> | ||
986 | +#include <linux/uidgid.h> | ||
987 | + | ||
988 | +#ifdef CONFIG_CCSECURITY | ||
989 | + | ||
990 | +int ccs_settime(const struct timespec *ts, const struct timezone *tz); | ||
991 | +int ccs_sb_mount(const char *dev_name, struct path *path, const char *type, | ||
992 | + unsigned long flags, void *data); | ||
993 | +int ccs_sb_umount(struct vfsmount *mnt, int flags); | ||
994 | +int ccs_sb_pivotroot(struct path *old_path, struct path *new_path); | ||
995 | + | ||
996 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) | ||
997 | +int ccs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry); | ||
998 | +#else | ||
999 | +int ccs_inode_getattr(const struct path *path); | ||
1000 | +#endif | ||
1001 | +int ccs_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg); | ||
1002 | +int ccs_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg); | ||
1003 | +int ccs_file_open(struct file *file, const struct cred *cred); | ||
1004 | +int ccs_socket_create(int family, int type, int protocol, int kern); | ||
1005 | +int ccs_socket_bind(struct socket *sock, struct sockaddr *address, | ||
1006 | + int addrlen); | ||
1007 | +int ccs_socket_connect(struct socket *sock, struct sockaddr *address, | ||
1008 | + int addrlen); | ||
1009 | +int ccs_socket_listen(struct socket *sock, int backlog); | ||
1010 | +int ccs_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size); | ||
1011 | +int ccs_path_unlink(struct path *dir, struct dentry *dentry); | ||
1012 | +int ccs_path_mkdir(struct path *dir, struct dentry *dentry, umode_t mode); | ||
1013 | +int ccs_path_rmdir(struct path *dir, struct dentry *dentry); | ||
1014 | +int ccs_path_mknod(struct path *dir, struct dentry *dentry, umode_t mode, | ||
1015 | + unsigned int dev); | ||
1016 | +int ccs_path_truncate(struct path *path); | ||
1017 | +int ccs_path_symlink(struct path *dir, struct dentry *dentry, | ||
1018 | + const char *old_name); | ||
1019 | +int ccs_path_link(struct dentry *old_dentry, struct path *new_dir, | ||
1020 | + struct dentry *new_dentry); | ||
1021 | +int ccs_path_rename(struct path *old_dir, struct dentry *old_dentry, | ||
1022 | + struct path *new_dir, struct dentry *new_dentry); | ||
1023 | +int ccs_path_chmod(struct path *path, umode_t mode); | ||
1024 | +int ccs_path_chown(struct path *path, kuid_t uid, kgid_t gid); | ||
1025 | +int ccs_path_chroot(struct path *path); | ||
1026 | + | ||
1027 | +#else | ||
1028 | + | ||
1029 | +static inline int ccs_settime(const struct timespec *ts, | ||
1030 | + const struct timezone *tz) | ||
1031 | +{ | ||
1032 | + return 0; | ||
1033 | +} | ||
1034 | +static inline int ccs_sb_mount(const char *dev_name, struct path *path, | ||
1035 | + const char *type, unsigned long flags, | ||
1036 | + void *data) | ||
1037 | +{ | ||
1038 | + return 0; | ||
1039 | +} | ||
1040 | +static inline int ccs_sb_umount(struct vfsmount *mnt, int flags) | ||
1041 | +{ | ||
1042 | + return 0; | ||
1043 | +} | ||
1044 | +static inline int ccs_sb_pivotroot(struct path *old_path, | ||
1045 | + struct path *new_path) | ||
1046 | +{ | ||
1047 | + return 0; | ||
1048 | +} | ||
1049 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) | ||
1050 | +static inline int ccs_inode_getattr(struct vfsmount *mnt, | ||
1051 | + struct dentry *dentry) | ||
1052 | +{ | ||
1053 | + return 0; | ||
1054 | +} | ||
1055 | +#else | ||
1056 | +static inline int ccs_inode_getattr(const struct path *path) | ||
1057 | +{ | ||
1058 | + return 0; | ||
1059 | +} | ||
1060 | +#endif | ||
1061 | +static inline int ccs_file_ioctl(struct file *file, unsigned int cmd, | ||
1062 | + unsigned long arg) | ||
1063 | +{ | ||
1064 | + return 0; | ||
1065 | +} | ||
1066 | +static inline int ccs_file_fcntl(struct file *file, unsigned int cmd, | ||
1067 | + unsigned long arg) | ||
1068 | +{ | ||
1069 | + return 0; | ||
1070 | +} | ||
1071 | +static inline int ccs_file_open(struct file *file, const struct cred *cred) | ||
1072 | +{ | ||
1073 | + return 0; | ||
1074 | +} | ||
1075 | +static inline int ccs_socket_create(int family, int type, int protocol, | ||
1076 | + int kern) | ||
1077 | +{ | ||
1078 | + return 0; | ||
1079 | +} | ||
1080 | +static inline int ccs_socket_bind(struct socket *sock, | ||
1081 | + struct sockaddr *address, int addrlen) | ||
1082 | +{ | ||
1083 | + return 0; | ||
1084 | +} | ||
1085 | +static inline int ccs_socket_connect(struct socket *sock, | ||
1086 | + struct sockaddr *address, int addrlen) | ||
1087 | +{ | ||
1088 | + return 0; | ||
1089 | +} | ||
1090 | +static inline int ccs_socket_listen(struct socket *sock, int backlog) | ||
1091 | +{ | ||
1092 | + return 0; | ||
1093 | +} | ||
1094 | +static inline int ccs_socket_sendmsg(struct socket *sock, struct msghdr *msg, | ||
1095 | + int size) | ||
1096 | +{ | ||
1097 | + return 0; | ||
1098 | +} | ||
1099 | +static inline int ccs_path_unlink(struct path *dir, struct dentry *dentry) | ||
1100 | +{ | ||
1101 | + return 0; | ||
1102 | +} | ||
1103 | +static inline int ccs_path_mkdir(struct path *dir, struct dentry *dentry, | ||
1104 | + umode_t mode) | ||
1105 | +{ | ||
1106 | + return 0; | ||
1107 | +} | ||
1108 | +static inline int ccs_path_rmdir(struct path *dir, struct dentry *dentry) | ||
1109 | +{ | ||
1110 | + return 0; | ||
1111 | +} | ||
1112 | +static inline int ccs_path_mknod(struct path *dir, struct dentry *dentry, | ||
1113 | + umode_t mode, unsigned int dev) | ||
1114 | +{ | ||
1115 | + return 0; | ||
1116 | +} | ||
1117 | +static inline int ccs_path_truncate(struct path *path) | ||
1118 | +{ | ||
1119 | + return 0; | ||
1120 | +} | ||
1121 | +static inline int ccs_path_symlink(struct path *dir, struct dentry *dentry, | ||
1122 | + const char *old_name) | ||
1123 | +{ | ||
1124 | + return 0; | ||
1125 | +} | ||
1126 | +static inline int ccs_path_link(struct dentry *old_dentry, | ||
1127 | + struct path *new_dir, | ||
1128 | + struct dentry *new_dentry) | ||
1129 | +{ | ||
1130 | + return 0; | ||
1131 | +} | ||
1132 | +static inline int ccs_path_rename(struct path *old_dir, | ||
1133 | + struct dentry *old_dentry, | ||
1134 | + struct path *new_dir, | ||
1135 | + struct dentry *new_dentry) | ||
1136 | +{ | ||
1137 | + return 0; | ||
1138 | +} | ||
1139 | +static inline int ccs_path_chmod(struct path *path, umode_t mode) | ||
1140 | +{ | ||
1141 | + return 0; | ||
1142 | +} | ||
1143 | +static inline int ccs_path_chown(struct path *path, kuid_t uid, kgid_t gid) | ||
1144 | +{ | ||
1145 | + return 0; | ||
1146 | +} | ||
1147 | +static inline int ccs_path_chroot(struct path *path) | ||
1148 | +{ | ||
1149 | + return 0; | ||
1150 | +} | ||
1151 | + | ||
1152 | +#endif /* defined(CONFIG_CCSECURITY) */ | ||
1153 | + | ||
1154 | +#endif /* !defined(_LINUX_LSM2CCSECURITY_H) */ | ||
1155 | diff --git a/security/ccsecurity/Config.in b/security/ccsecurity/Config.in | ||
1156 | new file mode 100644 | ||
1157 | index 0000000..da39bad6 | ||
1158 | --- /dev/null | ||
1159 | +++ b/security/ccsecurity/Config.in | ||
1160 | @@ -0,0 +1,83 @@ | ||
1161 | +# | ||
1162 | +# Mandatory Access Control configuration | ||
1163 | +# | ||
1164 | +mainmenu_option next_comment | ||
1165 | +comment 'Security options' | ||
1166 | + | ||
1167 | +[ -z "$CONFIG_CCSECURITY" ] && define_bool CONFIG_CCSECURITY y | ||
1168 | +bool 'CCSecurity support' CONFIG_CCSECURITY | ||
1169 | + | ||
1170 | +if [ "$CONFIG_CCSECURITY" = "y" ]; then | ||
1171 | + | ||
1172 | + [ -z "$CONFIG_CCSECURITY_LKM" ] && define_bool CONFIG_CCSECURITY_LKM n | ||
1173 | + bool 'Compile as loadable kernel module' CONFIG_CCSECURITY_LKM | ||
1174 | + | ||
1175 | + [ -z "$CONFIG_CCSECURITY_DISABLE_BY_DEFAULT" ] && define_bool CONFIG_CCSECURITY_DISABLE_BY_DEFAULT n | ||
1176 | + bool 'Disable by default' CONFIG_CCSECURITY_DISABLE_BY_DEFAULT | ||
1177 | + | ||
1178 | + [ -z "$CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY" ] && define_int CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY 2048 | ||
1179 | + [ $CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY -lt 0 ] && define_int CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY 0 | ||
1180 | + int 'Default maximal count for learning mode' CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY | ||
1181 | + | ||
1182 | + [ -z "$CONFIG_CCSECURITY_MAX_AUDIT_LOG" ] && define_int CONFIG_CCSECURITY_MAX_AUDIT_LOG 1024 | ||
1183 | + [ $CONFIG_CCSECURITY_MAX_AUDIT_LOG -lt 0 ] && define_int CONFIG_CCSECURITY_MAX_AUDIT_LOG 0 | ||
1184 | + int 'Default maximal count for audit log' CONFIG_CCSECURITY_MAX_AUDIT_LOG | ||
1185 | + | ||
1186 | + [ -z "$CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER" ] && define_bool CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER n | ||
1187 | + bool 'Activate without calling userspace policy loader.' CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | ||
1188 | + | ||
1189 | + if [ "$CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER" = "n" ]; then | ||
1190 | + | ||
1191 | + define_string CONFIG_CCSECURITY_POLICY_LOADER "/sbin/ccs-init" | ||
1192 | + string 'Location of userspace policy loader' CONFIG_CCSECURITY_POLICY_LOADER "/sbin/ccs-init" | ||
1193 | + | ||
1194 | + define_string CONFIG_CCSECURITY_ACTIVATION_TRIGGER "/sbin/init" | ||
1195 | + string 'Trigger for calling userspace policy loader' CONFIG_CCSECURITY_ACTIVATION_TRIGGER "/sbin/init" | ||
1196 | + | ||
1197 | + fi | ||
1198 | + | ||
1199 | + [ -z "$CONFIG_CCSECURITY_FILE_READDIR" ] && define_bool CONFIG_CCSECURITY_FILE_READDIR y | ||
1200 | + bool "Enable readdir operation restriction." CONFIG_CCSECURITY_FILE_READDIR | ||
1201 | + | ||
1202 | + [ -z "$CONFIG_CCSECURITY_FILE_GETATTR" ] && define_bool CONFIG_CCSECURITY_FILE_GETATTR y | ||
1203 | + bool "Enable getattr operation restriction." CONFIG_CCSECURITY_FILE_GETATTR | ||
1204 | + | ||
1205 | + if [ "$CONFIG_NET" = "y" ]; then | ||
1206 | + | ||
1207 | + [ -z "$CONFIG_CCSECURITY_NETWORK" ] && define_bool CONFIG_CCSECURITY_NETWORK y | ||
1208 | + bool "Enable socket operation restriction." CONFIG_CCSECURITY_NETWORK | ||
1209 | + | ||
1210 | + if [ "$CONFIG_CCSECURITY_NETWORK" = "y" ]; then | ||
1211 | + | ||
1212 | + #[ -z "$CONFIG_CCSECURITY_NETWORK_RECVMSG" ] && | ||
1213 | + define_bool CONFIG_CCSECURITY_NETWORK_RECVMSG y | ||
1214 | + | ||
1215 | + fi | ||
1216 | + | ||
1217 | + fi | ||
1218 | + | ||
1219 | + [ -z "$CONFIG_CCSECURITY_CAPABILITY" ] && define_bool CONFIG_CCSECURITY_CAPABILITY y | ||
1220 | + bool "Enable non-POSIX capability operation restriction." CONFIG_CCSECURITY_CAPABILITY | ||
1221 | + | ||
1222 | + [ -z "$CONFIG_CCSECURITY_IPC" ] && define_bool CONFIG_CCSECURITY_IPC y | ||
1223 | + bool "Enable IPC operation restriction." CONFIG_CCSECURITY_IPC | ||
1224 | + | ||
1225 | + [ -z "$CONFIG_CCSECURITY_MISC" ] && define_bool CONFIG_CCSECURITY_MISC y | ||
1226 | + bool "Enable environment variable names restriction." CONFIG_CCSECURITY_MISC | ||
1227 | + | ||
1228 | + [ -z "$CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER" ] && define_bool CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER y | ||
1229 | + bool "Enable execute handler functionality." CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
1230 | + | ||
1231 | + [ -z "$CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION" ] && define_bool CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION y | ||
1232 | + bool "Enable domain transition without program execution request." CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
1233 | + | ||
1234 | + if [ "$CONFIG_NET" = "y" ]; then | ||
1235 | + | ||
1236 | + [ -z "$CONFIG_CCSECURITY_PORTRESERVE" ] && define_bool CONFIG_CCSECURITY_PORTRESERVE y | ||
1237 | + bool "Enable local port reserver." CONFIG_CCSECURITY_PORTRESERVE | ||
1238 | + | ||
1239 | + fi | ||
1240 | + | ||
1241 | +fi | ||
1242 | + | ||
1243 | +endmenu | ||
1244 | diff --git a/security/ccsecurity/Kconfig b/security/ccsecurity/Kconfig | ||
1245 | new file mode 100644 | ||
1246 | index 0000000..8d7b200 | ||
1247 | --- /dev/null | ||
1248 | +++ b/security/ccsecurity/Kconfig | ||
1249 | @@ -0,0 +1,190 @@ | ||
1250 | +config CCSECURITY | ||
1251 | + bool "CCSecurity support" | ||
1252 | + default y | ||
1253 | + help | ||
1254 | + Say Y here to support non-LSM version of TOMOYO Linux. | ||
1255 | + http://tomoyo.osdn.jp/ | ||
1256 | + | ||
1257 | +config CCSECURITY_LKM | ||
1258 | + bool "Compile as loadable kernel module" | ||
1259 | + default n | ||
1260 | + depends on CCSECURITY && MODULES | ||
1261 | + help | ||
1262 | + This version of TOMOYO depends on patching the kernel source in order | ||
1263 | + to insert some hooks which LSM does not provide. Therefore, | ||
1264 | + recompiling the kernel is inevitable. But if you want to keep | ||
1265 | + vmlinux's size as small as possible, you can compile most part of | ||
1266 | + TOMOYO as a loadable kernel module by saying Y here. | ||
1267 | + | ||
1268 | +config CCSECURITY_DISABLE_BY_DEFAULT | ||
1269 | + bool "Disable by default" | ||
1270 | + default n | ||
1271 | + depends on CCSECURITY | ||
1272 | + help | ||
1273 | + Say Y here if you want TOMOYO disabled by default. | ||
1274 | + To enable TOMOYO, pass ccsecurity=on to kernel command line. | ||
1275 | + To disable TOMOYO, pass ccsecurity=off to kernel command line. | ||
1276 | + | ||
1277 | +config CCSECURITY_USE_EXTERNAL_TASK_SECURITY | ||
1278 | + bool "Do not modify 'struct task_struct' in order to keep KABI" | ||
1279 | + default n | ||
1280 | + depends on CCSECURITY | ||
1281 | + help | ||
1282 | + Say Y here if you want to keep KABI for prebuilt kernel modules | ||
1283 | + unchanged. TOMOYO needs "struct ccs_domain_info *" and "u32" for each | ||
1284 | + "struct task_struct". But embedding these variables into | ||
1285 | + "struct task_struct" breaks KABI for prebuilt kernel modules (which | ||
1286 | + means that you will need to rebuild prebuilt kernel modules). | ||
1287 | + If you say Y here, these variables are managed outside | ||
1288 | + "struct task_struct" rather than embedding into "struct task_struct", | ||
1289 | + but accessing these variables becomes slower because lookup operation | ||
1290 | + is performed every time the current thread needs to access them. | ||
1291 | + | ||
1292 | +config CCSECURITY_MAX_ACCEPT_ENTRY | ||
1293 | + int "Default maximal count for learning mode" | ||
1294 | + default 2048 | ||
1295 | + range 0 2147483647 | ||
1296 | + depends on CCSECURITY | ||
1297 | + help | ||
1298 | + This is the default value for maximal ACL entries | ||
1299 | + that are automatically appended into policy at "learning mode". | ||
1300 | + Some programs access thousands of objects, so running | ||
1301 | + such programs in "learning mode" dulls the system response | ||
1302 | + and consumes much memory. | ||
1303 | + This is the safeguard for such programs. | ||
1304 | + | ||
1305 | +config CCSECURITY_MAX_AUDIT_LOG | ||
1306 | + int "Default maximal count for audit log" | ||
1307 | + default 1024 | ||
1308 | + range 0 2147483647 | ||
1309 | + depends on CCSECURITY | ||
1310 | + help | ||
1311 | + This is the default value for maximal entries for | ||
1312 | + audit logs that the kernel can hold on memory. | ||
1313 | + You can read the log via /proc/ccs/audit. | ||
1314 | + If you don't need audit logs, you may set this value to 0. | ||
1315 | + | ||
1316 | +config CCSECURITY_OMIT_USERSPACE_LOADER | ||
1317 | + bool "Activate without calling userspace policy loader." | ||
1318 | + default n | ||
1319 | + depends on CCSECURITY | ||
1320 | + ---help--- | ||
1321 | + Say Y here if you want to activate access control as soon as built-in | ||
1322 | + policy was loaded. This option will be useful for systems where | ||
1323 | + operations which can lead to the hijacking of the boot sequence are | ||
1324 | + needed before loading the policy. For example, you can activate | ||
1325 | + immediately after loading the fixed part of policy which will allow | ||
1326 | + only operations needed for mounting a partition which contains the | ||
1327 | + variant part of policy and verifying (e.g. running GPG check) and | ||
1328 | + loading the variant part of policy. Since you can start using | ||
1329 | + enforcing mode from the beginning, you can reduce the possibility of | ||
1330 | + hijacking the boot sequence. | ||
1331 | + | ||
1332 | + If you say Y to both "Compile as loadable kernel module" option and | ||
1333 | + "Activate without calling userspace policy loader." option, be sure | ||
1334 | + to excplicitly load the kernel module from the userspace, for | ||
1335 | + the kernel will not call /sbin/ccs-init when /sbin/init starts. | ||
1336 | + | ||
1337 | +config CCSECURITY_POLICY_LOADER | ||
1338 | + string "Location of userspace policy loader" | ||
1339 | + default "/sbin/ccs-init" | ||
1340 | + depends on CCSECURITY | ||
1341 | + depends on !CCSECURITY_OMIT_USERSPACE_LOADER | ||
1342 | + ---help--- | ||
1343 | + This is the default pathname of policy loader which is called before | ||
1344 | + activation. You can override this setting via CCS_loader= kernel | ||
1345 | + command line option. | ||
1346 | + | ||
1347 | +config CCSECURITY_ACTIVATION_TRIGGER | ||
1348 | + string "Trigger for calling userspace policy loader" | ||
1349 | + default "/sbin/init" | ||
1350 | + depends on CCSECURITY | ||
1351 | + depends on !CCSECURITY_OMIT_USERSPACE_LOADER | ||
1352 | + ---help--- | ||
1353 | + This is the default pathname of activation trigger. | ||
1354 | + You can override this setting via CCS_trigger= kernel command line | ||
1355 | + option. For example, if you pass init=/bin/systemd option, you may | ||
1356 | + want to also pass CCS_trigger=/bin/systemd option. | ||
1357 | + | ||
1358 | + Say Y here if you want to enable only specific functionality in order | ||
1359 | + to reduce object file size. | ||
1360 | + | ||
1361 | +config CCSECURITY_FILE_READDIR | ||
1362 | + bool "Enable readdir operation restriction." | ||
1363 | + default y | ||
1364 | + depends on CCSECURITY | ||
1365 | + ---help--- | ||
1366 | + Say Y here if you want to enable analysis/restriction of opening | ||
1367 | + directories for reading. Reading directory entries is a commonly | ||
1368 | + requested operation and damage caused by not restricting it as MAC | ||
1369 | + might be acceptable for you. | ||
1370 | + | ||
1371 | +config CCSECURITY_FILE_GETATTR | ||
1372 | + bool "Enable getattr operation restriction." | ||
1373 | + default y | ||
1374 | + depends on CCSECURITY | ||
1375 | + ---help--- | ||
1376 | + Say Y here if you want to enable analysis/restriction of getting | ||
1377 | + information of files. Getting file's information is a commonly | ||
1378 | + requested operation and damage caused by not restricting it as MAC | ||
1379 | + might be acceptable for you. | ||
1380 | + | ||
1381 | +config CCSECURITY_NETWORK | ||
1382 | + bool "Enable socket operation restriction." | ||
1383 | + default y | ||
1384 | + depends on NET | ||
1385 | + depends on CCSECURITY | ||
1386 | + ---help--- | ||
1387 | + Say Y here if you want to enable analysis/restriction of INET and | ||
1388 | + UNIX domain socket's operations. | ||
1389 | + | ||
1390 | +config CCSECURITY_CAPABILITY | ||
1391 | + bool "Enable non-POSIX capability operation restriction." | ||
1392 | + default y | ||
1393 | + depends on CCSECURITY | ||
1394 | + ---help--- | ||
1395 | + Say Y here if you want to enable analysis/restriction of non-POSIX | ||
1396 | + capabilities. | ||
1397 | + | ||
1398 | +config CCSECURITY_IPC | ||
1399 | + bool "Enable IPC operation restriction." | ||
1400 | + default y | ||
1401 | + depends on CCSECURITY | ||
1402 | + ---help--- | ||
1403 | + Say Y here if you want to enable analysis/restriction of sending | ||
1404 | + signals. | ||
1405 | + | ||
1406 | +config CCSECURITY_MISC | ||
1407 | + bool "Enable environment variable names restriction." | ||
1408 | + default y | ||
1409 | + depends on CCSECURITY | ||
1410 | + ---help--- | ||
1411 | + Say Y here if you want to enable analysis/restriction of environment | ||
1412 | + variable names passed upon program execution request. | ||
1413 | + | ||
1414 | +config CCSECURITY_TASK_EXECUTE_HANDLER | ||
1415 | + bool "Enable execute handler functionality." | ||
1416 | + default y | ||
1417 | + depends on CCSECURITY | ||
1418 | + ---help--- | ||
1419 | + Say Y here if you want to enable execute handler functionality. | ||
1420 | + | ||
1421 | +config CCSECURITY_TASK_DOMAIN_TRANSITION | ||
1422 | + bool "Enable domain transition without program execution request." | ||
1423 | + default y | ||
1424 | + depends on CCSECURITY | ||
1425 | + ---help--- | ||
1426 | + Say Y here if you want to enable domain transition without involving | ||
1427 | + program execution request. | ||
1428 | + | ||
1429 | +config CCSECURITY_PORTRESERVE | ||
1430 | + bool "Enable local port reserver." | ||
1431 | + default y | ||
1432 | + depends on NET | ||
1433 | + depends on CCSECURITY | ||
1434 | + ---help--- | ||
1435 | + Say Y here if you want to implement | ||
1436 | + /proc/sys/net/ipv4/ip_local_reserved_ports as a MAC policy. | ||
1437 | + | ||
1438 | +config CCSECURITY_NETWORK_RECVMSG | ||
1439 | + def_bool CCSECURITY_NETWORK | ||
1440 | diff --git a/security/ccsecurity/Makefile b/security/ccsecurity/Makefile | ||
1441 | new file mode 100644 | ||
1442 | index 0000000..79c3632 | ||
1443 | --- /dev/null | ||
1444 | +++ b/security/ccsecurity/Makefile | ||
1445 | @@ -0,0 +1,122 @@ | ||
1446 | +ccsecurity-objs := permission.o gc.o memory.o policy_io.o realpath.o | ||
1447 | + | ||
1448 | +ifeq ($(VERSION)$(PATCHLEVEL),24) | ||
1449 | + | ||
1450 | +ifdef CONFIG_CCSECURITY | ||
1451 | +O_TARGET := ccsecurity.o | ||
1452 | +ifdef CONFIG_CCSECURITY_LKM | ||
1453 | +all_targets: load_policy.o | ||
1454 | +obj-m := ccsecurity.o | ||
1455 | +obj-y := $(ccsecurity-objs) | ||
1456 | +else | ||
1457 | +all_targets: ccsecurity.o | ||
1458 | +obj-y := load_policy.o $(ccsecurity-objs) | ||
1459 | +endif | ||
1460 | +export-objs := load_policy.o | ||
1461 | +endif | ||
1462 | +include $(TOPDIR)/Rules.make | ||
1463 | + | ||
1464 | +policy/profile.conf: | ||
1465 | + @mkdir -p policy/ | ||
1466 | + @echo Creating an empty policy/profile.conf | ||
1467 | + @touch $@ | ||
1468 | + | ||
1469 | +policy/exception_policy.conf: | ||
1470 | + @mkdir -p policy/ | ||
1471 | + @echo Creating a default policy/exception_policy.conf | ||
1472 | + @echo initialize_domain /sbin/modprobe from any >> $@ | ||
1473 | + @echo initialize_domain /sbin/hotplug from any >> $@ | ||
1474 | + | ||
1475 | +policy/domain_policy.conf: | ||
1476 | + @mkdir -p policy/ | ||
1477 | + @echo Creating an empty policy/domain_policy.conf | ||
1478 | + @touch $@ | ||
1479 | + | ||
1480 | +policy/manager.conf: | ||
1481 | + @mkdir -p policy/ | ||
1482 | + @echo Creating an empty policy/manager.conf | ||
1483 | + @touch $@ | ||
1484 | + | ||
1485 | +policy/stat.conf: | ||
1486 | + @mkdir -p policy/ | ||
1487 | + @echo Creating an empty policy/stat.conf | ||
1488 | + @touch $@ | ||
1489 | + | ||
1490 | +builtin-policy.h: policy/profile.conf policy/exception_policy.conf policy/domain_policy.conf policy/manager.conf policy/stat.conf | ||
1491 | + @echo Generating built-in policy for TOMOYO 1.8.x. | ||
1492 | + @echo "static char ccs_builtin_profile[] __initdata =" > $@.tmp | ||
1493 | + @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < policy/profile.conf >> $@.tmp | ||
1494 | + @echo "\"\";" >> $@.tmp | ||
1495 | + @echo "static char ccs_builtin_exception_policy[] __initdata =" >> $@.tmp | ||
1496 | + @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < policy/exception_policy.conf >> $@.tmp | ||
1497 | + @echo "\"\";" >> $@.tmp | ||
1498 | + @echo "static char ccs_builtin_domain_policy[] __initdata =" >> $@.tmp | ||
1499 | + @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < policy/domain_policy.conf >> $@.tmp | ||
1500 | + @echo "\"\";" >> $@.tmp | ||
1501 | + @echo "static char ccs_builtin_manager[] __initdata =" >> $@.tmp | ||
1502 | + @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < policy/manager.conf >> $@.tmp | ||
1503 | + @echo "\"\";" >> $@.tmp | ||
1504 | + @echo "static char ccs_builtin_stat[] __initdata =" >> $@.tmp | ||
1505 | + @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < policy/stat.conf >> $@.tmp | ||
1506 | + @echo "\"\";" >> $@.tmp | ||
1507 | + @mv $@.tmp $@ | ||
1508 | + | ||
1509 | +policy_io.o: builtin-policy.h | ||
1510 | + | ||
1511 | +else | ||
1512 | + | ||
1513 | +obj-y += load_policy.o | ||
1514 | +ifdef CONFIG_CCSECURITY_LKM | ||
1515 | +obj-m += ccsecurity.o | ||
1516 | +else | ||
1517 | +obj-y += ccsecurity.o | ||
1518 | +endif | ||
1519 | + | ||
1520 | +$(obj)/policy/profile.conf: | ||
1521 | + @mkdir -p $(obj)/policy/ | ||
1522 | + @echo Creating an empty policy/profile.conf | ||
1523 | + @touch $@ | ||
1524 | + | ||
1525 | +$(obj)/policy/exception_policy.conf: | ||
1526 | + @mkdir -p $(obj)/policy/ | ||
1527 | + @echo Creating a default policy/exception_policy.conf | ||
1528 | + @echo initialize_domain /sbin/modprobe from any >> $@ | ||
1529 | + @echo initialize_domain /sbin/hotplug from any >> $@ | ||
1530 | + | ||
1531 | +$(obj)/policy/domain_policy.conf: | ||
1532 | + @mkdir -p $(obj)/policy/ | ||
1533 | + @echo Creating an empty policy/domain_policy.conf | ||
1534 | + @touch $@ | ||
1535 | + | ||
1536 | +$(obj)/policy/manager.conf: | ||
1537 | + @mkdir -p $(obj)/policy/ | ||
1538 | + @echo Creating an empty policy/manager.conf | ||
1539 | + @touch $@ | ||
1540 | + | ||
1541 | +$(obj)/policy/stat.conf: | ||
1542 | + @mkdir -p $(obj)/policy/ | ||
1543 | + @echo Creating an empty policy/stat.conf | ||
1544 | + @touch $@ | ||
1545 | + | ||
1546 | +$(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf | ||
1547 | + @echo Generating built-in policy for TOMOYO 1.8.x. | ||
1548 | + @echo "static char ccs_builtin_profile[] __initdata =" > $@.tmp | ||
1549 | + @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp | ||
1550 | + @echo "\"\";" >> $@.tmp | ||
1551 | + @echo "static char ccs_builtin_exception_policy[] __initdata =" >> $@.tmp | ||
1552 | + @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/exception_policy.conf >> $@.tmp | ||
1553 | + @echo "\"\";" >> $@.tmp | ||
1554 | + @echo "static char ccs_builtin_domain_policy[] __initdata =" >> $@.tmp | ||
1555 | + @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/domain_policy.conf >> $@.tmp | ||
1556 | + @echo "\"\";" >> $@.tmp | ||
1557 | + @echo "static char ccs_builtin_manager[] __initdata =" >> $@.tmp | ||
1558 | + @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/manager.conf >> $@.tmp | ||
1559 | + @echo "\"\";" >> $@.tmp | ||
1560 | + @echo "static char ccs_builtin_stat[] __initdata =" >> $@.tmp | ||
1561 | + @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/stat.conf >> $@.tmp | ||
1562 | + @echo "\"\";" >> $@.tmp | ||
1563 | + @mv $@.tmp $@ | ||
1564 | + | ||
1565 | +$(obj)/policy_io.o: $(obj)/builtin-policy.h | ||
1566 | + | ||
1567 | +endif | ||
1568 | diff --git a/security/ccsecurity/gc.c b/security/ccsecurity/gc.c | ||
1569 | new file mode 100644 | ||
1570 | index 0000000..0a578ab | ||
1571 | --- /dev/null | ||
1572 | +++ b/security/ccsecurity/gc.c | ||
1573 | @@ -0,0 +1,1036 @@ | ||
1574 | +/* | ||
1575 | + * security/ccsecurity/gc.c | ||
1576 | + * | ||
1577 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | ||
1578 | + * | ||
1579 | + * Version: 1.8.4 2015/05/05 | ||
1580 | + */ | ||
1581 | + | ||
1582 | +#include "internal.h" | ||
1583 | + | ||
1584 | +/***** SECTION1: Constants definition *****/ | ||
1585 | + | ||
1586 | +/* For compatibility with older kernels. */ | ||
1587 | +#ifndef for_each_process | ||
1588 | +#define for_each_process for_each_task | ||
1589 | +#endif | ||
1590 | + | ||
1591 | +/* The list for "struct ccs_io_buffer". */ | ||
1592 | +static LIST_HEAD(ccs_io_buffer_list); | ||
1593 | +/* Lock for protecting ccs_io_buffer_list. */ | ||
1594 | +static DEFINE_SPINLOCK(ccs_io_buffer_list_lock); | ||
1595 | + | ||
1596 | +/***** SECTION2: Structure definition *****/ | ||
1597 | + | ||
1598 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | ||
1599 | + | ||
1600 | +/* | ||
1601 | + * Lock for syscall users. | ||
1602 | + * | ||
1603 | + * This lock is used for protecting single SRCU section for 2.6.18 and | ||
1604 | + * earlier kernels because they don't have SRCU support. | ||
1605 | + */ | ||
1606 | +struct ccs_lock_struct { | ||
1607 | + int counter_idx; /* Currently active index (0 or 1). */ | ||
1608 | + int counter[2]; /* Current users. Protected by ccs_counter_lock. */ | ||
1609 | +}; | ||
1610 | + | ||
1611 | +#endif | ||
1612 | + | ||
1613 | +/***** SECTION3: Prototype definition section *****/ | ||
1614 | + | ||
1615 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | ||
1616 | +int ccs_lock(void); | ||
1617 | +#endif | ||
1618 | +void ccs_del_acl(struct list_head *element); | ||
1619 | +void ccs_del_condition(struct list_head *element); | ||
1620 | +void ccs_notify_gc(struct ccs_io_buffer *head, const bool is_register); | ||
1621 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | ||
1622 | +void ccs_unlock(const int idx); | ||
1623 | +#endif | ||
1624 | + | ||
1625 | +static bool ccs_domain_used_by_task(struct ccs_domain_info *domain); | ||
1626 | +static bool ccs_name_used_by_io_buffer(const char *string, const size_t size); | ||
1627 | +static bool ccs_struct_used_by_io_buffer(const struct list_head *element); | ||
1628 | +static int ccs_gc_thread(void *unused); | ||
1629 | +static void ccs_collect_acl(struct list_head *list); | ||
1630 | +static void ccs_collect_entry(void); | ||
1631 | +static void ccs_collect_member(const enum ccs_policy_id id, | ||
1632 | + struct list_head *member_list); | ||
1633 | +static void ccs_memory_free(const void *ptr, const enum ccs_policy_id type); | ||
1634 | +static void ccs_put_name_union(struct ccs_name_union *ptr); | ||
1635 | +static void ccs_put_number_union(struct ccs_number_union *ptr); | ||
1636 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | ||
1637 | +static void ccs_synchronize_counter(void); | ||
1638 | +#endif | ||
1639 | +static void ccs_try_to_gc(const enum ccs_policy_id type, | ||
1640 | + struct list_head *element); | ||
1641 | + | ||
1642 | +/***** SECTION4: Standalone functions section *****/ | ||
1643 | + | ||
1644 | +/***** SECTION5: Variables definition section *****/ | ||
1645 | + | ||
1646 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
1647 | + | ||
1648 | +/* | ||
1649 | + * Lock for syscall users. | ||
1650 | + * | ||
1651 | + * This lock is held for only protecting single SRCU section. | ||
1652 | + */ | ||
1653 | +struct srcu_struct ccs_ss; | ||
1654 | + | ||
1655 | +#else | ||
1656 | + | ||
1657 | +static struct ccs_lock_struct ccs_counter; | ||
1658 | +/* Lock for protecting ccs_counter. */ | ||
1659 | +static DEFINE_SPINLOCK(ccs_counter_lock); | ||
1660 | + | ||
1661 | +#endif | ||
1662 | + | ||
1663 | +/***** SECTION6: Dependent functions section *****/ | ||
1664 | + | ||
1665 | +/** | ||
1666 | + * ccs_memory_free - Free memory for elements. | ||
1667 | + * | ||
1668 | + * @ptr: Pointer to allocated memory. | ||
1669 | + * @type: One of values in "enum ccs_policy_id". | ||
1670 | + * | ||
1671 | + * Returns nothing. | ||
1672 | + * | ||
1673 | + * Caller holds ccs_policy_lock mutex. | ||
1674 | + */ | ||
1675 | +static void ccs_memory_free(const void *ptr, const enum ccs_policy_id type) | ||
1676 | +{ | ||
1677 | + /* Size of an element. */ | ||
1678 | + static const u8 e[CCS_MAX_POLICY] = { | ||
1679 | +#ifdef CONFIG_CCSECURITY_PORTRESERVE | ||
1680 | + [CCS_ID_RESERVEDPORT] = sizeof(struct ccs_reserved), | ||
1681 | +#endif | ||
1682 | + [CCS_ID_GROUP] = sizeof(struct ccs_group), | ||
1683 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
1684 | + [CCS_ID_ADDRESS_GROUP] = sizeof(struct ccs_address_group), | ||
1685 | +#endif | ||
1686 | + [CCS_ID_PATH_GROUP] = sizeof(struct ccs_path_group), | ||
1687 | + [CCS_ID_NUMBER_GROUP] = sizeof(struct ccs_number_group), | ||
1688 | + [CCS_ID_AGGREGATOR] = sizeof(struct ccs_aggregator), | ||
1689 | + [CCS_ID_TRANSITION_CONTROL] | ||
1690 | + = sizeof(struct ccs_transition_control), | ||
1691 | + [CCS_ID_MANAGER] = sizeof(struct ccs_manager), | ||
1692 | + /* [CCS_ID_CONDITION] = "struct ccs_condition"->size, */ | ||
1693 | + /* [CCS_ID_NAME] = "struct ccs_name"->size, */ | ||
1694 | + /* [CCS_ID_ACL] = a["struct ccs_acl_info"->type], */ | ||
1695 | + [CCS_ID_DOMAIN] = sizeof(struct ccs_domain_info), | ||
1696 | + }; | ||
1697 | + /* Size of a domain ACL element. */ | ||
1698 | + static const u8 a[] = { | ||
1699 | + [CCS_TYPE_PATH_ACL] = sizeof(struct ccs_path_acl), | ||
1700 | + [CCS_TYPE_PATH2_ACL] = sizeof(struct ccs_path2_acl), | ||
1701 | + [CCS_TYPE_PATH_NUMBER_ACL] | ||
1702 | + = sizeof(struct ccs_path_number_acl), | ||
1703 | + [CCS_TYPE_MKDEV_ACL] = sizeof(struct ccs_mkdev_acl), | ||
1704 | + [CCS_TYPE_MOUNT_ACL] = sizeof(struct ccs_mount_acl), | ||
1705 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
1706 | + [CCS_TYPE_INET_ACL] = sizeof(struct ccs_inet_acl), | ||
1707 | + [CCS_TYPE_UNIX_ACL] = sizeof(struct ccs_unix_acl), | ||
1708 | +#endif | ||
1709 | +#ifdef CONFIG_CCSECURITY_MISC | ||
1710 | + [CCS_TYPE_ENV_ACL] = sizeof(struct ccs_env_acl), | ||
1711 | +#endif | ||
1712 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
1713 | + [CCS_TYPE_CAPABILITY_ACL] = sizeof(struct ccs_capability_acl), | ||
1714 | +#endif | ||
1715 | +#ifdef CONFIG_CCSECURITY_IPC | ||
1716 | + [CCS_TYPE_SIGNAL_ACL] = sizeof(struct ccs_signal_acl), | ||
1717 | +#endif | ||
1718 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
1719 | + [CCS_TYPE_AUTO_EXECUTE_HANDLER] | ||
1720 | + = sizeof(struct ccs_handler_acl), | ||
1721 | + [CCS_TYPE_DENIED_EXECUTE_HANDLER] | ||
1722 | + = sizeof(struct ccs_handler_acl), | ||
1723 | +#endif | ||
1724 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
1725 | + [CCS_TYPE_AUTO_TASK_ACL] = sizeof(struct ccs_task_acl), | ||
1726 | + [CCS_TYPE_MANUAL_TASK_ACL] = sizeof(struct ccs_task_acl), | ||
1727 | +#endif | ||
1728 | + }; | ||
1729 | + size_t size; | ||
1730 | + if (type == CCS_ID_ACL) | ||
1731 | + size = a[container_of(ptr, typeof(struct ccs_acl_info), | ||
1732 | + list)->type]; | ||
1733 | + else if (type == CCS_ID_NAME) | ||
1734 | + size = container_of(ptr, typeof(struct ccs_name), | ||
1735 | + head.list)->size; | ||
1736 | + else if (type == CCS_ID_CONDITION) | ||
1737 | + size = container_of(ptr, typeof(struct ccs_condition), | ||
1738 | + head.list)->size; | ||
1739 | + else | ||
1740 | + size = e[type]; | ||
1741 | + ccs_memory_used[CCS_MEMORY_POLICY] -= ccs_round2(size); | ||
1742 | + kfree(ptr); | ||
1743 | +} | ||
1744 | + | ||
1745 | +/** | ||
1746 | + * ccs_put_name_union - Drop reference on "struct ccs_name_union". | ||
1747 | + * | ||
1748 | + * @ptr: Pointer to "struct ccs_name_union". | ||
1749 | + * | ||
1750 | + * Returns nothing. | ||
1751 | + */ | ||
1752 | +static void ccs_put_name_union(struct ccs_name_union *ptr) | ||
1753 | +{ | ||
1754 | + ccs_put_group(ptr->group); | ||
1755 | + ccs_put_name(ptr->filename); | ||
1756 | +} | ||
1757 | + | ||
1758 | +/** | ||
1759 | + * ccs_put_number_union - Drop reference on "struct ccs_number_union". | ||
1760 | + * | ||
1761 | + * @ptr: Pointer to "struct ccs_number_union". | ||
1762 | + * | ||
1763 | + * Returns nothing. | ||
1764 | + */ | ||
1765 | +static void ccs_put_number_union(struct ccs_number_union *ptr) | ||
1766 | +{ | ||
1767 | + ccs_put_group(ptr->group); | ||
1768 | +} | ||
1769 | + | ||
1770 | +/** | ||
1771 | + * ccs_struct_used_by_io_buffer - Check whether the list element is used by /proc/ccs/ users or not. | ||
1772 | + * | ||
1773 | + * @element: Pointer to "struct list_head". | ||
1774 | + * | ||
1775 | + * Returns true if @element is used by /proc/ccs/ users, false otherwise. | ||
1776 | + */ | ||
1777 | +static bool ccs_struct_used_by_io_buffer(const struct list_head *element) | ||
1778 | +{ | ||
1779 | + struct ccs_io_buffer *head; | ||
1780 | + bool in_use = false; | ||
1781 | + spin_lock(&ccs_io_buffer_list_lock); | ||
1782 | + list_for_each_entry(head, &ccs_io_buffer_list, list) { | ||
1783 | + head->users++; | ||
1784 | + spin_unlock(&ccs_io_buffer_list_lock); | ||
1785 | + mutex_lock(&head->io_sem); | ||
1786 | + if (head->r.domain == element || head->r.group == element || | ||
1787 | + head->r.acl == element || &head->w.domain->list == element) | ||
1788 | + in_use = true; | ||
1789 | + mutex_unlock(&head->io_sem); | ||
1790 | + spin_lock(&ccs_io_buffer_list_lock); | ||
1791 | + head->users--; | ||
1792 | + if (in_use) | ||
1793 | + break; | ||
1794 | + } | ||
1795 | + spin_unlock(&ccs_io_buffer_list_lock); | ||
1796 | + return in_use; | ||
1797 | +} | ||
1798 | + | ||
1799 | +/** | ||
1800 | + * ccs_name_used_by_io_buffer - Check whether the string is used by /proc/ccs/ users or not. | ||
1801 | + * | ||
1802 | + * @string: String to check. | ||
1803 | + * @size: Memory allocated for @string . | ||
1804 | + * | ||
1805 | + * Returns true if @string is used by /proc/ccs/ users, false otherwise. | ||
1806 | + */ | ||
1807 | +static bool ccs_name_used_by_io_buffer(const char *string, const size_t size) | ||
1808 | +{ | ||
1809 | + struct ccs_io_buffer *head; | ||
1810 | + bool in_use = false; | ||
1811 | + spin_lock(&ccs_io_buffer_list_lock); | ||
1812 | + list_for_each_entry(head, &ccs_io_buffer_list, list) { | ||
1813 | + int i; | ||
1814 | + head->users++; | ||
1815 | + spin_unlock(&ccs_io_buffer_list_lock); | ||
1816 | + mutex_lock(&head->io_sem); | ||
1817 | + for (i = 0; i < CCS_MAX_IO_READ_QUEUE; i++) { | ||
1818 | + const char *w = head->r.w[i]; | ||
1819 | + if (w < string || w > string + size) | ||
1820 | + continue; | ||
1821 | + in_use = true; | ||
1822 | + break; | ||
1823 | + } | ||
1824 | + mutex_unlock(&head->io_sem); | ||
1825 | + spin_lock(&ccs_io_buffer_list_lock); | ||
1826 | + head->users--; | ||
1827 | + if (in_use) | ||
1828 | + break; | ||
1829 | + } | ||
1830 | + spin_unlock(&ccs_io_buffer_list_lock); | ||
1831 | + return in_use; | ||
1832 | +} | ||
1833 | + | ||
1834 | +/** | ||
1835 | + * ccs_del_transition_control - Delete members in "struct ccs_transition_control". | ||
1836 | + * | ||
1837 | + * @element: Pointer to "struct list_head". | ||
1838 | + * | ||
1839 | + * Returns nothing. | ||
1840 | + */ | ||
1841 | +static inline void ccs_del_transition_control(struct list_head *element) | ||
1842 | +{ | ||
1843 | + struct ccs_transition_control *ptr = | ||
1844 | + container_of(element, typeof(*ptr), head.list); | ||
1845 | + ccs_put_name(ptr->domainname); | ||
1846 | + ccs_put_name(ptr->program); | ||
1847 | +} | ||
1848 | + | ||
1849 | +/** | ||
1850 | + * ccs_del_aggregator - Delete members in "struct ccs_aggregator". | ||
1851 | + * | ||
1852 | + * @element: Pointer to "struct list_head". | ||
1853 | + * | ||
1854 | + * Returns nothing. | ||
1855 | + */ | ||
1856 | +static inline void ccs_del_aggregator(struct list_head *element) | ||
1857 | +{ | ||
1858 | + struct ccs_aggregator *ptr = | ||
1859 | + container_of(element, typeof(*ptr), head.list); | ||
1860 | + ccs_put_name(ptr->original_name); | ||
1861 | + ccs_put_name(ptr->aggregated_name); | ||
1862 | +} | ||
1863 | + | ||
1864 | +/** | ||
1865 | + * ccs_del_manager - Delete members in "struct ccs_manager". | ||
1866 | + * | ||
1867 | + * @element: Pointer to "struct list_head". | ||
1868 | + * | ||
1869 | + * Returns nothing. | ||
1870 | + */ | ||
1871 | +static inline void ccs_del_manager(struct list_head *element) | ||
1872 | +{ | ||
1873 | + struct ccs_manager *ptr = | ||
1874 | + container_of(element, typeof(*ptr), head.list); | ||
1875 | + ccs_put_name(ptr->manager); | ||
1876 | +} | ||
1877 | + | ||
1878 | +/** | ||
1879 | + * ccs_domain_used_by_task - Check whether the given pointer is referenced by a task. | ||
1880 | + * | ||
1881 | + * @domain: Pointer to "struct ccs_domain_info". | ||
1882 | + * | ||
1883 | + * Returns true if @domain is in use, false otherwise. | ||
1884 | + */ | ||
1885 | +static bool ccs_domain_used_by_task(struct ccs_domain_info *domain) | ||
1886 | +{ | ||
1887 | + bool in_use = false; | ||
1888 | + /* | ||
1889 | + * Don't delete this domain if somebody is doing execve(). | ||
1890 | + * | ||
1891 | + * Since ccs_finish_execve() first reverts ccs_domain_info and then | ||
1892 | + * updates ccs_flags, we need smp_rmb() to make sure that GC first | ||
1893 | + * checks ccs_flags and then checks ccs_domain_info. | ||
1894 | + */ | ||
1895 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | ||
1896 | + int idx; | ||
1897 | + rcu_read_lock(); | ||
1898 | + for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) { | ||
1899 | + struct ccs_security *ptr; | ||
1900 | + struct list_head *list = &ccs_task_security_list[idx]; | ||
1901 | + list_for_each_entry_rcu(ptr, list, list) { | ||
1902 | + if (!(ptr->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { | ||
1903 | + smp_rmb(); /* Avoid out of order execution. */ | ||
1904 | + if (ptr->ccs_domain_info != domain) | ||
1905 | + continue; | ||
1906 | + } | ||
1907 | + in_use = true; | ||
1908 | + goto out; | ||
1909 | + } | ||
1910 | + } | ||
1911 | +out: | ||
1912 | + rcu_read_unlock(); | ||
1913 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) | ||
1914 | + struct task_struct *g; | ||
1915 | + struct task_struct *t; | ||
1916 | + ccs_tasklist_lock(); | ||
1917 | + do_each_thread(g, t) { | ||
1918 | + if (!(t->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { | ||
1919 | + smp_rmb(); /* Avoid out of order execution. */ | ||
1920 | + if (t->ccs_domain_info != domain) | ||
1921 | + continue; | ||
1922 | + } | ||
1923 | + in_use = true; | ||
1924 | + goto out; | ||
1925 | + } while_each_thread(g, t); | ||
1926 | +out: | ||
1927 | + ccs_tasklist_unlock(); | ||
1928 | +#else | ||
1929 | + struct task_struct *p; | ||
1930 | + ccs_tasklist_lock(); | ||
1931 | + for_each_process(p) { | ||
1932 | + if (!(p->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { | ||
1933 | + smp_rmb(); /* Avoid out of order execution. */ | ||
1934 | + if (p->ccs_domain_info != domain) | ||
1935 | + continue; | ||
1936 | + } | ||
1937 | + in_use = true; | ||
1938 | + break; | ||
1939 | + } | ||
1940 | + ccs_tasklist_unlock(); | ||
1941 | +#endif | ||
1942 | + return in_use; | ||
1943 | +} | ||
1944 | + | ||
1945 | +/** | ||
1946 | + * ccs_del_acl - Delete members in "struct ccs_acl_info". | ||
1947 | + * | ||
1948 | + * @element: Pointer to "struct list_head". | ||
1949 | + * | ||
1950 | + * Returns nothing. | ||
1951 | + */ | ||
1952 | +void ccs_del_acl(struct list_head *element) | ||
1953 | +{ | ||
1954 | + struct ccs_acl_info *acl = container_of(element, typeof(*acl), list); | ||
1955 | + ccs_put_condition(acl->cond); | ||
1956 | + switch (acl->type) { | ||
1957 | + case CCS_TYPE_PATH_ACL: | ||
1958 | + { | ||
1959 | + struct ccs_path_acl *entry = | ||
1960 | + container_of(acl, typeof(*entry), head); | ||
1961 | + ccs_put_name_union(&entry->name); | ||
1962 | + } | ||
1963 | + break; | ||
1964 | + case CCS_TYPE_PATH2_ACL: | ||
1965 | + { | ||
1966 | + struct ccs_path2_acl *entry = | ||
1967 | + container_of(acl, typeof(*entry), head); | ||
1968 | + ccs_put_name_union(&entry->name1); | ||
1969 | + ccs_put_name_union(&entry->name2); | ||
1970 | + } | ||
1971 | + break; | ||
1972 | + case CCS_TYPE_PATH_NUMBER_ACL: | ||
1973 | + { | ||
1974 | + struct ccs_path_number_acl *entry = | ||
1975 | + container_of(acl, typeof(*entry), head); | ||
1976 | + ccs_put_name_union(&entry->name); | ||
1977 | + ccs_put_number_union(&entry->number); | ||
1978 | + } | ||
1979 | + break; | ||
1980 | + case CCS_TYPE_MKDEV_ACL: | ||
1981 | + { | ||
1982 | + struct ccs_mkdev_acl *entry = | ||
1983 | + container_of(acl, typeof(*entry), head); | ||
1984 | + ccs_put_name_union(&entry->name); | ||
1985 | + ccs_put_number_union(&entry->mode); | ||
1986 | + ccs_put_number_union(&entry->major); | ||
1987 | + ccs_put_number_union(&entry->minor); | ||
1988 | + } | ||
1989 | + break; | ||
1990 | + case CCS_TYPE_MOUNT_ACL: | ||
1991 | + { | ||
1992 | + struct ccs_mount_acl *entry = | ||
1993 | + container_of(acl, typeof(*entry), head); | ||
1994 | + ccs_put_name_union(&entry->dev_name); | ||
1995 | + ccs_put_name_union(&entry->dir_name); | ||
1996 | + ccs_put_name_union(&entry->fs_type); | ||
1997 | + ccs_put_number_union(&entry->flags); | ||
1998 | + } | ||
1999 | + break; | ||
2000 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
2001 | + case CCS_TYPE_INET_ACL: | ||
2002 | + { | ||
2003 | + struct ccs_inet_acl *entry = | ||
2004 | + container_of(acl, typeof(*entry), head); | ||
2005 | + ccs_put_group(entry->address.group); | ||
2006 | + ccs_put_number_union(&entry->port); | ||
2007 | + } | ||
2008 | + break; | ||
2009 | + case CCS_TYPE_UNIX_ACL: | ||
2010 | + { | ||
2011 | + struct ccs_unix_acl *entry = | ||
2012 | + container_of(acl, typeof(*entry), head); | ||
2013 | + ccs_put_name_union(&entry->name); | ||
2014 | + } | ||
2015 | + break; | ||
2016 | +#endif | ||
2017 | +#ifdef CONFIG_CCSECURITY_MISC | ||
2018 | + case CCS_TYPE_ENV_ACL: | ||
2019 | + { | ||
2020 | + struct ccs_env_acl *entry = | ||
2021 | + container_of(acl, typeof(*entry), head); | ||
2022 | + ccs_put_name(entry->env); | ||
2023 | + } | ||
2024 | + break; | ||
2025 | +#endif | ||
2026 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
2027 | + case CCS_TYPE_CAPABILITY_ACL: | ||
2028 | + { | ||
2029 | + /* Nothing to do. */ | ||
2030 | + } | ||
2031 | + break; | ||
2032 | +#endif | ||
2033 | +#ifdef CONFIG_CCSECURITY_IPC | ||
2034 | + case CCS_TYPE_SIGNAL_ACL: | ||
2035 | + { | ||
2036 | + struct ccs_signal_acl *entry = | ||
2037 | + container_of(acl, typeof(*entry), head); | ||
2038 | + ccs_put_number_union(&entry->sig); | ||
2039 | + ccs_put_name(entry->domainname); | ||
2040 | + } | ||
2041 | + break; | ||
2042 | +#endif | ||
2043 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
2044 | + case CCS_TYPE_AUTO_EXECUTE_HANDLER: | ||
2045 | + case CCS_TYPE_DENIED_EXECUTE_HANDLER: | ||
2046 | + { | ||
2047 | + struct ccs_handler_acl *entry = | ||
2048 | + container_of(acl, typeof(*entry), head); | ||
2049 | + ccs_put_name(entry->handler); | ||
2050 | + } | ||
2051 | + break; | ||
2052 | +#endif | ||
2053 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
2054 | + case CCS_TYPE_AUTO_TASK_ACL: | ||
2055 | + case CCS_TYPE_MANUAL_TASK_ACL: | ||
2056 | + { | ||
2057 | + struct ccs_task_acl *entry = | ||
2058 | + container_of(acl, typeof(*entry), head); | ||
2059 | + ccs_put_name(entry->domainname); | ||
2060 | + } | ||
2061 | + break; | ||
2062 | +#endif | ||
2063 | + } | ||
2064 | +} | ||
2065 | + | ||
2066 | +/** | ||
2067 | + * ccs_del_domain - Delete members in "struct ccs_domain_info". | ||
2068 | + * | ||
2069 | + * @element: Pointer to "struct list_head". | ||
2070 | + * | ||
2071 | + * Returns nothing. | ||
2072 | + * | ||
2073 | + * Caller holds ccs_policy_lock mutex. | ||
2074 | + */ | ||
2075 | +static inline void ccs_del_domain(struct list_head *element) | ||
2076 | +{ | ||
2077 | + struct ccs_domain_info *domain = | ||
2078 | + container_of(element, typeof(*domain), list); | ||
2079 | + struct ccs_acl_info *acl; | ||
2080 | + struct ccs_acl_info *tmp; | ||
2081 | + /* | ||
2082 | + * Since this domain is referenced from neither "struct ccs_io_buffer" | ||
2083 | + * nor "struct task_struct", we can delete elements without checking | ||
2084 | + * for is_deleted flag. | ||
2085 | + */ | ||
2086 | + list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { | ||
2087 | + ccs_del_acl(&acl->list); | ||
2088 | + ccs_memory_free(acl, CCS_ID_ACL); | ||
2089 | + } | ||
2090 | + ccs_put_name(domain->domainname); | ||
2091 | +} | ||
2092 | + | ||
2093 | +/** | ||
2094 | + * ccs_del_path_group - Delete members in "struct ccs_path_group". | ||
2095 | + * | ||
2096 | + * @element: Pointer to "struct list_head". | ||
2097 | + * | ||
2098 | + * Returns nothing. | ||
2099 | + */ | ||
2100 | +static inline void ccs_del_path_group(struct list_head *element) | ||
2101 | +{ | ||
2102 | + struct ccs_path_group *member = | ||
2103 | + container_of(element, typeof(*member), head.list); | ||
2104 | + ccs_put_name(member->member_name); | ||
2105 | +} | ||
2106 | + | ||
2107 | +/** | ||
2108 | + * ccs_del_group - Delete "struct ccs_group". | ||
2109 | + * | ||
2110 | + * @element: Pointer to "struct list_head". | ||
2111 | + * | ||
2112 | + * Returns nothing. | ||
2113 | + */ | ||
2114 | +static inline void ccs_del_group(struct list_head *element) | ||
2115 | +{ | ||
2116 | + struct ccs_group *group = | ||
2117 | + container_of(element, typeof(*group), head.list); | ||
2118 | + ccs_put_name(group->group_name); | ||
2119 | +} | ||
2120 | + | ||
2121 | +/** | ||
2122 | + * ccs_del_address_group - Delete members in "struct ccs_address_group". | ||
2123 | + * | ||
2124 | + * @element: Pointer to "struct list_head". | ||
2125 | + * | ||
2126 | + * Returns nothing. | ||
2127 | + */ | ||
2128 | +static inline void ccs_del_address_group(struct list_head *element) | ||
2129 | +{ | ||
2130 | + /* Nothing to do. */ | ||
2131 | +} | ||
2132 | + | ||
2133 | +/** | ||
2134 | + * ccs_del_number_group - Delete members in "struct ccs_number_group". | ||
2135 | + * | ||
2136 | + * @element: Pointer to "struct list_head". | ||
2137 | + * | ||
2138 | + * Returns nothing. | ||
2139 | + */ | ||
2140 | +static inline void ccs_del_number_group(struct list_head *element) | ||
2141 | +{ | ||
2142 | + /* Nothing to do. */ | ||
2143 | +} | ||
2144 | + | ||
2145 | +/** | ||
2146 | + * ccs_del_reservedport - Delete members in "struct ccs_reserved". | ||
2147 | + * | ||
2148 | + * @element: Pointer to "struct list_head". | ||
2149 | + * | ||
2150 | + * Returns nothing. | ||
2151 | + */ | ||
2152 | +static inline void ccs_del_reservedport(struct list_head *element) | ||
2153 | +{ | ||
2154 | + /* Nothing to do. */ | ||
2155 | +} | ||
2156 | + | ||
2157 | +/** | ||
2158 | + * ccs_del_condition - Delete members in "struct ccs_condition". | ||
2159 | + * | ||
2160 | + * @element: Pointer to "struct list_head". | ||
2161 | + * | ||
2162 | + * Returns nothing. | ||
2163 | + */ | ||
2164 | +void ccs_del_condition(struct list_head *element) | ||
2165 | +{ | ||
2166 | + struct ccs_condition *cond = container_of(element, typeof(*cond), | ||
2167 | + head.list); | ||
2168 | + const u16 condc = cond->condc; | ||
2169 | + const u16 numbers_count = cond->numbers_count; | ||
2170 | + const u16 names_count = cond->names_count; | ||
2171 | + const u16 argc = cond->argc; | ||
2172 | + const u16 envc = cond->envc; | ||
2173 | + unsigned int i; | ||
2174 | + const struct ccs_condition_element *condp | ||
2175 | + = (const struct ccs_condition_element *) (cond + 1); | ||
2176 | + struct ccs_number_union *numbers_p | ||
2177 | + = (struct ccs_number_union *) (condp + condc); | ||
2178 | + struct ccs_name_union *names_p | ||
2179 | + = (struct ccs_name_union *) (numbers_p + numbers_count); | ||
2180 | + const struct ccs_argv *argv | ||
2181 | + = (const struct ccs_argv *) (names_p + names_count); | ||
2182 | + const struct ccs_envp *envp | ||
2183 | + = (const struct ccs_envp *) (argv + argc); | ||
2184 | + for (i = 0; i < numbers_count; i++) | ||
2185 | + ccs_put_number_union(numbers_p++); | ||
2186 | + for (i = 0; i < names_count; i++) | ||
2187 | + ccs_put_name_union(names_p++); | ||
2188 | + for (i = 0; i < argc; argv++, i++) | ||
2189 | + ccs_put_name(argv->value); | ||
2190 | + for (i = 0; i < envc; envp++, i++) { | ||
2191 | + ccs_put_name(envp->name); | ||
2192 | + ccs_put_name(envp->value); | ||
2193 | + } | ||
2194 | + ccs_put_name(cond->transit); | ||
2195 | +} | ||
2196 | + | ||
2197 | +/** | ||
2198 | + * ccs_del_name - Delete members in "struct ccs_name". | ||
2199 | + * | ||
2200 | + * @element: Pointer to "struct list_head". | ||
2201 | + * | ||
2202 | + * Returns nothing. | ||
2203 | + */ | ||
2204 | +static inline void ccs_del_name(struct list_head *element) | ||
2205 | +{ | ||
2206 | + /* Nothing to do. */ | ||
2207 | +} | ||
2208 | + | ||
2209 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | ||
2210 | + | ||
2211 | +/** | ||
2212 | + * ccs_lock - Alternative for srcu_read_lock(). | ||
2213 | + * | ||
2214 | + * Returns index number which has to be passed to ccs_unlock(). | ||
2215 | + */ | ||
2216 | +int ccs_lock(void) | ||
2217 | +{ | ||
2218 | + int idx; | ||
2219 | + spin_lock(&ccs_counter_lock); | ||
2220 | + idx = ccs_counter.counter_idx; | ||
2221 | + ccs_counter.counter[idx]++; | ||
2222 | + spin_unlock(&ccs_counter_lock); | ||
2223 | + return idx; | ||
2224 | +} | ||
2225 | + | ||
2226 | +/** | ||
2227 | + * ccs_unlock - Alternative for srcu_read_unlock(). | ||
2228 | + * | ||
2229 | + * @idx: Index number returned by ccs_lock(). | ||
2230 | + * | ||
2231 | + * Returns nothing. | ||
2232 | + */ | ||
2233 | +void ccs_unlock(const int idx) | ||
2234 | +{ | ||
2235 | + spin_lock(&ccs_counter_lock); | ||
2236 | + ccs_counter.counter[idx]--; | ||
2237 | + spin_unlock(&ccs_counter_lock); | ||
2238 | +} | ||
2239 | + | ||
2240 | +/** | ||
2241 | + * ccs_synchronize_counter - Alternative for synchronize_srcu(). | ||
2242 | + * | ||
2243 | + * Returns nothing. | ||
2244 | + */ | ||
2245 | +static void ccs_synchronize_counter(void) | ||
2246 | +{ | ||
2247 | + int idx; | ||
2248 | + int v; | ||
2249 | + /* | ||
2250 | + * Change currently active counter's index. Make it visible to other | ||
2251 | + * threads by doing it with ccs_counter_lock held. | ||
2252 | + * This function is called by garbage collector thread, and the garbage | ||
2253 | + * collector thread is exclusive. Therefore, it is guaranteed that | ||
2254 | + * SRCU grace period has expired when returning from this function. | ||
2255 | + */ | ||
2256 | + spin_lock(&ccs_counter_lock); | ||
2257 | + idx = ccs_counter.counter_idx; | ||
2258 | + ccs_counter.counter_idx ^= 1; | ||
2259 | + v = ccs_counter.counter[idx]; | ||
2260 | + spin_unlock(&ccs_counter_lock); | ||
2261 | + /* Wait for previously active counter to become 0. */ | ||
2262 | + while (v) { | ||
2263 | + ssleep(1); | ||
2264 | + spin_lock(&ccs_counter_lock); | ||
2265 | + v = ccs_counter.counter[idx]; | ||
2266 | + spin_unlock(&ccs_counter_lock); | ||
2267 | + } | ||
2268 | +} | ||
2269 | + | ||
2270 | +#endif | ||
2271 | + | ||
2272 | +/** | ||
2273 | + * ccs_try_to_gc - Try to kfree() an entry. | ||
2274 | + * | ||
2275 | + * @type: One of values in "enum ccs_policy_id". | ||
2276 | + * @element: Pointer to "struct list_head". | ||
2277 | + * | ||
2278 | + * Returns nothing. | ||
2279 | + * | ||
2280 | + * Caller holds ccs_policy_lock mutex. | ||
2281 | + */ | ||
2282 | +static void ccs_try_to_gc(const enum ccs_policy_id type, | ||
2283 | + struct list_head *element) | ||
2284 | +{ | ||
2285 | + /* | ||
2286 | + * __list_del_entry() guarantees that the list element became no longer | ||
2287 | + * reachable from the list which the element was originally on (e.g. | ||
2288 | + * ccs_domain_list). Also, synchronize_srcu() guarantees that the list | ||
2289 | + * element became no longer referenced by syscall users. | ||
2290 | + */ | ||
2291 | + __list_del_entry(element); | ||
2292 | + mutex_unlock(&ccs_policy_lock); | ||
2293 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
2294 | + synchronize_srcu(&ccs_ss); | ||
2295 | +#else | ||
2296 | + ccs_synchronize_counter(); | ||
2297 | +#endif | ||
2298 | + /* | ||
2299 | + * However, there are two users which may still be using the list | ||
2300 | + * element. We need to defer until both users forget this element. | ||
2301 | + * | ||
2302 | + * Don't kfree() until "struct ccs_io_buffer"->r.{domain,group,acl} and | ||
2303 | + * "struct ccs_io_buffer"->w.domain forget this element. | ||
2304 | + */ | ||
2305 | + if (ccs_struct_used_by_io_buffer(element)) | ||
2306 | + goto reinject; | ||
2307 | + switch (type) { | ||
2308 | + case CCS_ID_TRANSITION_CONTROL: | ||
2309 | + ccs_del_transition_control(element); | ||
2310 | + break; | ||
2311 | + case CCS_ID_MANAGER: | ||
2312 | + ccs_del_manager(element); | ||
2313 | + break; | ||
2314 | + case CCS_ID_AGGREGATOR: | ||
2315 | + ccs_del_aggregator(element); | ||
2316 | + break; | ||
2317 | + case CCS_ID_GROUP: | ||
2318 | + ccs_del_group(element); | ||
2319 | + break; | ||
2320 | + case CCS_ID_PATH_GROUP: | ||
2321 | + ccs_del_path_group(element); | ||
2322 | + break; | ||
2323 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
2324 | + case CCS_ID_ADDRESS_GROUP: | ||
2325 | + ccs_del_address_group(element); | ||
2326 | + break; | ||
2327 | +#endif | ||
2328 | + case CCS_ID_NUMBER_GROUP: | ||
2329 | + ccs_del_number_group(element); | ||
2330 | + break; | ||
2331 | +#ifdef CONFIG_CCSECURITY_PORTRESERVE | ||
2332 | + case CCS_ID_RESERVEDPORT: | ||
2333 | + ccs_del_reservedport(element); | ||
2334 | + break; | ||
2335 | +#endif | ||
2336 | + case CCS_ID_CONDITION: | ||
2337 | + ccs_del_condition(element); | ||
2338 | + break; | ||
2339 | + case CCS_ID_NAME: | ||
2340 | + /* | ||
2341 | + * Don't kfree() until all "struct ccs_io_buffer"->r.w[] forget | ||
2342 | + * this element. | ||
2343 | + */ | ||
2344 | + if (ccs_name_used_by_io_buffer | ||
2345 | + (container_of(element, typeof(struct ccs_name), | ||
2346 | + head.list)->entry.name, | ||
2347 | + container_of(element, typeof(struct ccs_name), | ||
2348 | + head.list)->size)) | ||
2349 | + goto reinject; | ||
2350 | + ccs_del_name(element); | ||
2351 | + break; | ||
2352 | + case CCS_ID_ACL: | ||
2353 | + ccs_del_acl(element); | ||
2354 | + break; | ||
2355 | + case CCS_ID_DOMAIN: | ||
2356 | + /* | ||
2357 | + * Don't kfree() until all "struct task_struct" forget this | ||
2358 | + * element. | ||
2359 | + */ | ||
2360 | + if (ccs_domain_used_by_task | ||
2361 | + (container_of(element, typeof(struct ccs_domain_info), | ||
2362 | + list))) | ||
2363 | + goto reinject; | ||
2364 | + break; | ||
2365 | + case CCS_MAX_POLICY: | ||
2366 | + break; | ||
2367 | + } | ||
2368 | + mutex_lock(&ccs_policy_lock); | ||
2369 | + if (type == CCS_ID_DOMAIN) | ||
2370 | + ccs_del_domain(element); | ||
2371 | + ccs_memory_free(element, type); | ||
2372 | + return; | ||
2373 | +reinject: | ||
2374 | + /* | ||
2375 | + * We can safely reinject this element here bacause | ||
2376 | + * (1) Appending list elements and removing list elements are protected | ||
2377 | + * by ccs_policy_lock mutex. | ||
2378 | + * (2) Only this function removes list elements and this function is | ||
2379 | + * exclusively executed by ccs_gc_mutex mutex. | ||
2380 | + * are true. | ||
2381 | + */ | ||
2382 | + mutex_lock(&ccs_policy_lock); | ||
2383 | + list_add_rcu(element, element->prev); | ||
2384 | +} | ||
2385 | + | ||
2386 | +/** | ||
2387 | + * ccs_collect_member - Delete elements with "struct ccs_acl_head". | ||
2388 | + * | ||
2389 | + * @id: One of values in "enum ccs_policy_id". | ||
2390 | + * @member_list: Pointer to "struct list_head". | ||
2391 | + * | ||
2392 | + * Returns nothing. | ||
2393 | + * | ||
2394 | + * Caller holds ccs_policy_lock mutex. | ||
2395 | + */ | ||
2396 | +static void ccs_collect_member(const enum ccs_policy_id id, | ||
2397 | + struct list_head *member_list) | ||
2398 | +{ | ||
2399 | + struct ccs_acl_head *member; | ||
2400 | + struct ccs_acl_head *tmp; | ||
2401 | + list_for_each_entry_safe(member, tmp, member_list, list) { | ||
2402 | + if (!member->is_deleted) | ||
2403 | + continue; | ||
2404 | + member->is_deleted = CCS_GC_IN_PROGRESS; | ||
2405 | + ccs_try_to_gc(id, &member->list); | ||
2406 | + } | ||
2407 | +} | ||
2408 | + | ||
2409 | +/** | ||
2410 | + * ccs_collect_acl - Delete elements in "struct ccs_domain_info". | ||
2411 | + * | ||
2412 | + * @list: Pointer to "struct list_head". | ||
2413 | + * | ||
2414 | + * Returns nothing. | ||
2415 | + * | ||
2416 | + * Caller holds ccs_policy_lock mutex. | ||
2417 | + */ | ||
2418 | +static void ccs_collect_acl(struct list_head *list) | ||
2419 | +{ | ||
2420 | + struct ccs_acl_info *acl; | ||
2421 | + struct ccs_acl_info *tmp; | ||
2422 | + list_for_each_entry_safe(acl, tmp, list, list) { | ||
2423 | + if (!acl->is_deleted) | ||
2424 | + continue; | ||
2425 | + acl->is_deleted = CCS_GC_IN_PROGRESS; | ||
2426 | + ccs_try_to_gc(CCS_ID_ACL, &acl->list); | ||
2427 | + } | ||
2428 | +} | ||
2429 | + | ||
2430 | +/** | ||
2431 | + * ccs_collect_entry - Try to kfree() deleted elements. | ||
2432 | + * | ||
2433 | + * Returns nothing. | ||
2434 | + */ | ||
2435 | +static void ccs_collect_entry(void) | ||
2436 | +{ | ||
2437 | + int i; | ||
2438 | + enum ccs_policy_id id; | ||
2439 | + struct ccs_policy_namespace *ns; | ||
2440 | + mutex_lock(&ccs_policy_lock); | ||
2441 | + { | ||
2442 | + struct ccs_domain_info *domain; | ||
2443 | + struct ccs_domain_info *tmp; | ||
2444 | + list_for_each_entry_safe(domain, tmp, &ccs_domain_list, list) { | ||
2445 | + ccs_collect_acl(&domain->acl_info_list); | ||
2446 | + if (!domain->is_deleted || | ||
2447 | + ccs_domain_used_by_task(domain)) | ||
2448 | + continue; | ||
2449 | + ccs_try_to_gc(CCS_ID_DOMAIN, &domain->list); | ||
2450 | + } | ||
2451 | + } | ||
2452 | + list_for_each_entry(ns, &ccs_namespace_list, namespace_list) { | ||
2453 | + for (id = 0; id < CCS_MAX_POLICY; id++) | ||
2454 | + ccs_collect_member(id, &ns->policy_list[id]); | ||
2455 | + for (i = 0; i < CCS_MAX_ACL_GROUPS; i++) | ||
2456 | + ccs_collect_acl(&ns->acl_group[i]); | ||
2457 | + } | ||
2458 | + { | ||
2459 | + struct ccs_shared_acl_head *ptr; | ||
2460 | + struct ccs_shared_acl_head *tmp; | ||
2461 | + list_for_each_entry_safe(ptr, tmp, &ccs_condition_list, list) { | ||
2462 | + if (atomic_read(&ptr->users) > 0) | ||
2463 | + continue; | ||
2464 | + atomic_set(&ptr->users, CCS_GC_IN_PROGRESS); | ||
2465 | + ccs_try_to_gc(CCS_ID_CONDITION, &ptr->list); | ||
2466 | + } | ||
2467 | + } | ||
2468 | + list_for_each_entry(ns, &ccs_namespace_list, namespace_list) { | ||
2469 | + for (i = 0; i < CCS_MAX_GROUP; i++) { | ||
2470 | + struct list_head *list = &ns->group_list[i]; | ||
2471 | + struct ccs_group *group; | ||
2472 | + struct ccs_group *tmp; | ||
2473 | + switch (i) { | ||
2474 | + case 0: | ||
2475 | + id = CCS_ID_PATH_GROUP; | ||
2476 | + break; | ||
2477 | + case 1: | ||
2478 | + id = CCS_ID_NUMBER_GROUP; | ||
2479 | + break; | ||
2480 | + default: | ||
2481 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
2482 | + id = CCS_ID_ADDRESS_GROUP; | ||
2483 | +#else | ||
2484 | + continue; | ||
2485 | +#endif | ||
2486 | + break; | ||
2487 | + } | ||
2488 | + list_for_each_entry_safe(group, tmp, list, head.list) { | ||
2489 | + ccs_collect_member(id, &group->member_list); | ||
2490 | + if (!list_empty(&group->member_list) || | ||
2491 | + atomic_read(&group->head.users) > 0) | ||
2492 | + continue; | ||
2493 | + atomic_set(&group->head.users, | ||
2494 | + CCS_GC_IN_PROGRESS); | ||
2495 | + ccs_try_to_gc(CCS_ID_GROUP, &group->head.list); | ||
2496 | + } | ||
2497 | + } | ||
2498 | + } | ||
2499 | + for (i = 0; i < CCS_MAX_HASH; i++) { | ||
2500 | + struct list_head *list = &ccs_name_list[i]; | ||
2501 | + struct ccs_shared_acl_head *ptr; | ||
2502 | + struct ccs_shared_acl_head *tmp; | ||
2503 | + list_for_each_entry_safe(ptr, tmp, list, list) { | ||
2504 | + if (atomic_read(&ptr->users) > 0) | ||
2505 | + continue; | ||
2506 | + atomic_set(&ptr->users, CCS_GC_IN_PROGRESS); | ||
2507 | + ccs_try_to_gc(CCS_ID_NAME, &ptr->list); | ||
2508 | + } | ||
2509 | + } | ||
2510 | + mutex_unlock(&ccs_policy_lock); | ||
2511 | +} | ||
2512 | + | ||
2513 | +/** | ||
2514 | + * ccs_gc_thread - Garbage collector thread function. | ||
2515 | + * | ||
2516 | + * @unused: Unused. | ||
2517 | + * | ||
2518 | + * Returns 0. | ||
2519 | + */ | ||
2520 | +static int ccs_gc_thread(void *unused) | ||
2521 | +{ | ||
2522 | + /* Garbage collector thread is exclusive. */ | ||
2523 | + static DEFINE_MUTEX(ccs_gc_mutex); | ||
2524 | + if (!mutex_trylock(&ccs_gc_mutex)) | ||
2525 | + goto out; | ||
2526 | +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 6) | ||
2527 | + /* daemonize() not needed. */ | ||
2528 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) | ||
2529 | + daemonize("GC for CCS"); | ||
2530 | +#else | ||
2531 | + daemonize(); | ||
2532 | + reparent_to_init(); | ||
2533 | +#if defined(TASK_DEAD) | ||
2534 | + { | ||
2535 | + struct task_struct *task = current; | ||
2536 | + spin_lock_irq(&task->sighand->siglock); | ||
2537 | + siginitsetinv(&task->blocked, 0); | ||
2538 | + recalc_sigpending(); | ||
2539 | + spin_unlock_irq(&task->sighand->siglock); | ||
2540 | + } | ||
2541 | +#else | ||
2542 | + { | ||
2543 | + struct task_struct *task = current; | ||
2544 | + spin_lock_irq(&task->sigmask_lock); | ||
2545 | + siginitsetinv(&task->blocked, 0); | ||
2546 | + recalc_sigpending(task); | ||
2547 | + spin_unlock_irq(&task->sigmask_lock); | ||
2548 | + } | ||
2549 | +#endif | ||
2550 | + snprintf(current->comm, sizeof(current->comm) - 1, "GC for CCS"); | ||
2551 | +#endif | ||
2552 | + ccs_collect_entry(); | ||
2553 | + { | ||
2554 | + struct ccs_io_buffer *head; | ||
2555 | + struct ccs_io_buffer *tmp; | ||
2556 | + spin_lock(&ccs_io_buffer_list_lock); | ||
2557 | + list_for_each_entry_safe(head, tmp, &ccs_io_buffer_list, | ||
2558 | + list) { | ||
2559 | + if (head->users) | ||
2560 | + continue; | ||
2561 | + list_del(&head->list); | ||
2562 | + kfree(head->read_buf); | ||
2563 | + kfree(head->write_buf); | ||
2564 | + kfree(head); | ||
2565 | + } | ||
2566 | + spin_unlock(&ccs_io_buffer_list_lock); | ||
2567 | + } | ||
2568 | + mutex_unlock(&ccs_gc_mutex); | ||
2569 | +out: | ||
2570 | + /* This acts as do_exit(0). */ | ||
2571 | + return 0; | ||
2572 | +} | ||
2573 | + | ||
2574 | +/** | ||
2575 | + * ccs_notify_gc - Register/unregister /proc/ccs/ users. | ||
2576 | + * | ||
2577 | + * @head: Pointer to "struct ccs_io_buffer". | ||
2578 | + * @is_register: True if register, false if unregister. | ||
2579 | + * | ||
2580 | + * Returns nothing. | ||
2581 | + */ | ||
2582 | +void ccs_notify_gc(struct ccs_io_buffer *head, const bool is_register) | ||
2583 | +{ | ||
2584 | + bool is_write = false; | ||
2585 | + spin_lock(&ccs_io_buffer_list_lock); | ||
2586 | + if (is_register) { | ||
2587 | + head->users = 1; | ||
2588 | + list_add(&head->list, &ccs_io_buffer_list); | ||
2589 | + } else { | ||
2590 | + is_write = head->write_buf != NULL; | ||
2591 | + if (!--head->users) { | ||
2592 | + list_del(&head->list); | ||
2593 | + kfree(head->read_buf); | ||
2594 | + kfree(head->write_buf); | ||
2595 | + kfree(head); | ||
2596 | + } | ||
2597 | + } | ||
2598 | + spin_unlock(&ccs_io_buffer_list_lock); | ||
2599 | + if (is_write) { | ||
2600 | +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 6) | ||
2601 | + struct task_struct *task = kthread_create(ccs_gc_thread, NULL, | ||
2602 | + "GC for CCS"); | ||
2603 | + if (!IS_ERR(task)) | ||
2604 | + wake_up_process(task); | ||
2605 | +#else | ||
2606 | + kernel_thread(ccs_gc_thread, NULL, 0); | ||
2607 | +#endif | ||
2608 | + } | ||
2609 | +} | ||
2610 | diff --git a/security/ccsecurity/internal.h b/security/ccsecurity/internal.h | ||
2611 | new file mode 100644 | ||
2612 | index 0000000..3f703f2 | ||
2613 | --- /dev/null | ||
2614 | +++ b/security/ccsecurity/internal.h | ||
2615 | @@ -0,0 +1,2090 @@ | ||
2616 | +/* | ||
2617 | + * security/ccsecurity/internal.h | ||
2618 | + * | ||
2619 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | ||
2620 | + * | ||
2621 | + * Version: 1.8.4 2015/05/05 | ||
2622 | + */ | ||
2623 | + | ||
2624 | +#ifndef _SECURITY_CCSECURITY_INTERNAL_H | ||
2625 | +#define _SECURITY_CCSECURITY_INTERNAL_H | ||
2626 | + | ||
2627 | +#include <linux/version.h> | ||
2628 | +#include <linux/types.h> | ||
2629 | +#include <linux/kernel.h> | ||
2630 | +#include <linux/string.h> | ||
2631 | +#include <linux/mm.h> | ||
2632 | +#include <linux/utime.h> | ||
2633 | +#include <linux/file.h> | ||
2634 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 38) | ||
2635 | +#include <linux/smp_lock.h> | ||
2636 | +#endif | ||
2637 | +#include <linux/module.h> | ||
2638 | +#include <linux/init.h> | ||
2639 | +#include <linux/slab.h> | ||
2640 | +#include <linux/highmem.h> | ||
2641 | +#include <linux/poll.h> | ||
2642 | +#include <linux/binfmts.h> | ||
2643 | +#include <linux/delay.h> | ||
2644 | +#include <linux/sched.h> | ||
2645 | +#include <linux/dcache.h> | ||
2646 | +#include <linux/mount.h> | ||
2647 | +#include <linux/net.h> | ||
2648 | +#include <linux/inet.h> | ||
2649 | +#include <linux/in.h> | ||
2650 | +#include <linux/in6.h> | ||
2651 | +#include <linux/un.h> | ||
2652 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
2653 | +#include <linux/fs.h> | ||
2654 | +#endif | ||
2655 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) | ||
2656 | +#include <linux/namei.h> | ||
2657 | +#endif | ||
2658 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) | ||
2659 | +#include <linux/fs_struct.h> | ||
2660 | +#endif | ||
2661 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) | ||
2662 | +#include <linux/namespace.h> | ||
2663 | +#endif | ||
2664 | +#include <linux/proc_fs.h> | ||
2665 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) || defined(RHEL_MAJOR) | ||
2666 | +#include <linux/hash.h> | ||
2667 | +#endif | ||
2668 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) || (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL)) | ||
2669 | +#include <linux/sysctl.h> | ||
2670 | +#endif | ||
2671 | +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 6) | ||
2672 | +#include <linux/kthread.h> | ||
2673 | +#endif | ||
2674 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) | ||
2675 | +#include <linux/magic.h> | ||
2676 | +#endif | ||
2677 | +#include <stdarg.h> | ||
2678 | +#include <asm/uaccess.h> | ||
2679 | +#include <net/sock.h> | ||
2680 | +#include <net/af_unix.h> | ||
2681 | +#include <net/ip.h> | ||
2682 | +#include <net/ipv6.h> | ||
2683 | +#include <net/udp.h> | ||
2684 | + | ||
2685 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
2686 | +#define sk_family family | ||
2687 | +#define sk_protocol protocol | ||
2688 | +#define sk_type type | ||
2689 | +#endif | ||
2690 | + | ||
2691 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) | ||
2692 | + | ||
2693 | +/* Structure for holding "struct vfsmount *" and "struct dentry *". */ | ||
2694 | +struct path { | ||
2695 | + struct vfsmount *mnt; | ||
2696 | + struct dentry *dentry; | ||
2697 | +}; | ||
2698 | + | ||
2699 | +#endif | ||
2700 | + | ||
2701 | +#ifndef __printf | ||
2702 | +#define __printf(a,b) __attribute__((format(printf,a,b))) | ||
2703 | +#endif | ||
2704 | +#ifndef __packed | ||
2705 | +#define __packed __attribute__((__packed__)) | ||
2706 | +#endif | ||
2707 | +#ifndef bool | ||
2708 | +#define bool _Bool | ||
2709 | +#endif | ||
2710 | +#ifndef false | ||
2711 | +#define false 0 | ||
2712 | +#endif | ||
2713 | +#ifndef true | ||
2714 | +#define true 1 | ||
2715 | +#endif | ||
2716 | + | ||
2717 | +#ifndef __user | ||
2718 | +#define __user | ||
2719 | +#endif | ||
2720 | + | ||
2721 | +#ifndef current_uid | ||
2722 | +#define current_uid() (current->uid) | ||
2723 | +#endif | ||
2724 | +#ifndef current_gid | ||
2725 | +#define current_gid() (current->gid) | ||
2726 | +#endif | ||
2727 | +#ifndef current_euid | ||
2728 | +#define current_euid() (current->euid) | ||
2729 | +#endif | ||
2730 | +#ifndef current_egid | ||
2731 | +#define current_egid() (current->egid) | ||
2732 | +#endif | ||
2733 | +#ifndef current_suid | ||
2734 | +#define current_suid() (current->suid) | ||
2735 | +#endif | ||
2736 | +#ifndef current_sgid | ||
2737 | +#define current_sgid() (current->sgid) | ||
2738 | +#endif | ||
2739 | +#ifndef current_fsuid | ||
2740 | +#define current_fsuid() (current->fsuid) | ||
2741 | +#endif | ||
2742 | +#ifndef current_fsgid | ||
2743 | +#define current_fsgid() (current->fsgid) | ||
2744 | +#endif | ||
2745 | + | ||
2746 | +#ifndef DEFINE_SPINLOCK | ||
2747 | +#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED | ||
2748 | +#endif | ||
2749 | + | ||
2750 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) | ||
2751 | +#define mutex semaphore | ||
2752 | +#define mutex_init(mutex) init_MUTEX(mutex) | ||
2753 | +#define mutex_unlock(mutex) up(mutex) | ||
2754 | +#define mutex_lock(mutex) down(mutex) | ||
2755 | +#define mutex_lock_interruptible(mutex) down_interruptible(mutex) | ||
2756 | +#define mutex_trylock(mutex) (!down_trylock(mutex)) | ||
2757 | +#define DEFINE_MUTEX(mutexname) DECLARE_MUTEX(mutexname) | ||
2758 | +#endif | ||
2759 | + | ||
2760 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) | ||
2761 | +#define MS_UNBINDABLE (1<<17) /* change to unbindable */ | ||
2762 | +#define MS_PRIVATE (1<<18) /* change to private */ | ||
2763 | +#define MS_SLAVE (1<<19) /* change to slave */ | ||
2764 | +#define MS_SHARED (1<<20) /* change to shared */ | ||
2765 | +#endif | ||
2766 | + | ||
2767 | +#ifndef container_of | ||
2768 | +#define container_of(ptr, type, member) ({ \ | ||
2769 | + const typeof(((type *)0)->member) *__mptr = (ptr); \ | ||
2770 | + (type *)((char *)__mptr - offsetof(type, member)); }) | ||
2771 | +#endif | ||
2772 | + | ||
2773 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) | ||
2774 | +#define smp_read_barrier_depends smp_rmb | ||
2775 | +#endif | ||
2776 | + | ||
2777 | +#ifndef ACCESS_ONCE | ||
2778 | +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) | ||
2779 | +#endif | ||
2780 | + | ||
2781 | +#ifndef rcu_dereference | ||
2782 | +#define rcu_dereference(p) ({ \ | ||
2783 | + typeof(p) _________p1 = ACCESS_ONCE(p); \ | ||
2784 | + smp_read_barrier_depends(); /* see RCU */ \ | ||
2785 | + (_________p1); \ | ||
2786 | + }) | ||
2787 | +#endif | ||
2788 | + | ||
2789 | +#ifndef rcu_assign_pointer | ||
2790 | +#define rcu_assign_pointer(p, v) \ | ||
2791 | + ({ \ | ||
2792 | + if (!__builtin_constant_p(v) || \ | ||
2793 | + ((v) != NULL)) \ | ||
2794 | + smp_wmb(); /* see RCU */ \ | ||
2795 | + (p) = (v); \ | ||
2796 | + }) | ||
2797 | +#endif | ||
2798 | + | ||
2799 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) | ||
2800 | +#define f_vfsmnt f_path.mnt | ||
2801 | +#endif | ||
2802 | + | ||
2803 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) | ||
2804 | + | ||
2805 | +/** | ||
2806 | + * kzalloc() - Allocate memory. The memory is set to zero. | ||
2807 | + * | ||
2808 | + * @size: Size to allocate. | ||
2809 | + * @flags: GFP flags. | ||
2810 | + * | ||
2811 | + * Returns pointer to allocated memory on success, NULL otherwise. | ||
2812 | + * | ||
2813 | + * This is for compatibility with older kernels. | ||
2814 | + * | ||
2815 | + * Since several distributions backported kzalloc(), I define it as a macro | ||
2816 | + * rather than an inlined function in order to avoid multiple definition error. | ||
2817 | + */ | ||
2818 | +#define kzalloc(size, flags) ({ \ | ||
2819 | + void *ret = kmalloc((size), (flags)); \ | ||
2820 | + if (ret) \ | ||
2821 | + memset(ret, 0, (size)); \ | ||
2822 | + ret; }) | ||
2823 | + | ||
2824 | +#endif | ||
2825 | + | ||
2826 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) | ||
2827 | + | ||
2828 | +/** | ||
2829 | + * path_put - Drop reference on "struct path". | ||
2830 | + * | ||
2831 | + * @path: Pointer to "struct path". | ||
2832 | + * | ||
2833 | + * Returns nothing. | ||
2834 | + * | ||
2835 | + * This is for compatibility with older kernels. | ||
2836 | + */ | ||
2837 | +static inline void path_put(struct path *path) | ||
2838 | +{ | ||
2839 | + dput(path->dentry); | ||
2840 | + mntput(path->mnt); | ||
2841 | +} | ||
2842 | + | ||
2843 | +#endif | ||
2844 | + | ||
2845 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
2846 | + | ||
2847 | +/** | ||
2848 | + * __list_add_rcu - Insert a new entry between two known consecutive entries. | ||
2849 | + * | ||
2850 | + * @new: Pointer to "struct list_head". | ||
2851 | + * @prev: Pointer to "struct list_head". | ||
2852 | + * @next: Pointer to "struct list_head". | ||
2853 | + * | ||
2854 | + * Returns nothing. | ||
2855 | + * | ||
2856 | + * This is for compatibility with older kernels. | ||
2857 | + */ | ||
2858 | +static inline void __list_add_rcu(struct list_head *new, | ||
2859 | + struct list_head *prev, | ||
2860 | + struct list_head *next) | ||
2861 | +{ | ||
2862 | + new->next = next; | ||
2863 | + new->prev = prev; | ||
2864 | + rcu_assign_pointer(prev->next, new); | ||
2865 | + next->prev = new; | ||
2866 | +} | ||
2867 | + | ||
2868 | +/** | ||
2869 | + * list_add_tail_rcu - Add a new entry to rcu-protected list. | ||
2870 | + * | ||
2871 | + * @new: Pointer to "struct list_head". | ||
2872 | + * @head: Pointer to "struct list_head". | ||
2873 | + * | ||
2874 | + * Returns nothing. | ||
2875 | + * | ||
2876 | + * This is for compatibility with older kernels. | ||
2877 | + */ | ||
2878 | +static inline void list_add_tail_rcu(struct list_head *new, | ||
2879 | + struct list_head *head) | ||
2880 | +{ | ||
2881 | + __list_add_rcu(new, head->prev, head); | ||
2882 | +} | ||
2883 | + | ||
2884 | +/** | ||
2885 | + * list_add_rcu - Add a new entry to rcu-protected list. | ||
2886 | + * | ||
2887 | + * @new: Pointer to "struct list_head". | ||
2888 | + * @head: Pointer to "struct list_head". | ||
2889 | + * | ||
2890 | + * Returns nothing. | ||
2891 | + * | ||
2892 | + * This is for compatibility with older kernels. | ||
2893 | + */ | ||
2894 | +static inline void list_add_rcu(struct list_head *new, struct list_head *head) | ||
2895 | +{ | ||
2896 | + __list_add_rcu(new, head, head->next); | ||
2897 | +} | ||
2898 | + | ||
2899 | +#endif | ||
2900 | + | ||
2901 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) | ||
2902 | + | ||
2903 | +/** | ||
2904 | + * __list_del_entry - Deletes entry from list without re-initialization. | ||
2905 | + * | ||
2906 | + * @entry: Pointer to "struct list_head". | ||
2907 | + * | ||
2908 | + * Returns nothing. | ||
2909 | + * | ||
2910 | + * This is for compatibility with older kernels. | ||
2911 | + */ | ||
2912 | +static inline void __list_del_entry(struct list_head *entry) | ||
2913 | +{ | ||
2914 | + __list_del(entry->prev, entry->next); | ||
2915 | +} | ||
2916 | + | ||
2917 | +#endif | ||
2918 | + | ||
2919 | +#ifndef list_for_each_entry_safe | ||
2920 | + | ||
2921 | +/** | ||
2922 | + * list_for_each_entry_safe - Iterate over list of given type safe against removal of list entry. | ||
2923 | + * | ||
2924 | + * @pos: The "type *" to use as a loop cursor. | ||
2925 | + * @n: Another "type *" to use as temporary storage. | ||
2926 | + * @head: Pointer to "struct list_head". | ||
2927 | + * @member: The name of the list_struct within the struct. | ||
2928 | + * | ||
2929 | + * This is for compatibility with older kernels. | ||
2930 | + */ | ||
2931 | +#define list_for_each_entry_safe(pos, n, head, member) \ | ||
2932 | + for (pos = list_entry((head)->next, typeof(*pos), member), \ | ||
2933 | + n = list_entry(pos->member.next, typeof(*pos), member); \ | ||
2934 | + &pos->member != (head); \ | ||
2935 | + pos = n, n = list_entry(n->member.next, typeof(*n), member)) | ||
2936 | + | ||
2937 | +#endif | ||
2938 | + | ||
2939 | +#ifndef srcu_dereference | ||
2940 | + | ||
2941 | +/** | ||
2942 | + * srcu_dereference - Fetch SRCU-protected pointer with checking. | ||
2943 | + * | ||
2944 | + * @p: The pointer to read, prior to dereferencing. | ||
2945 | + * @ss: Pointer to "struct srcu_struct". | ||
2946 | + * | ||
2947 | + * Returns @p. | ||
2948 | + * | ||
2949 | + * This is for compatibility with older kernels. | ||
2950 | + */ | ||
2951 | +#define srcu_dereference(p, ss) rcu_dereference(p) | ||
2952 | + | ||
2953 | +#endif | ||
2954 | + | ||
2955 | +#ifndef list_for_each_entry_srcu | ||
2956 | + | ||
2957 | +/** | ||
2958 | + * list_for_each_entry_srcu - Iterate over rcu list of given type. | ||
2959 | + * | ||
2960 | + * @pos: The type * to use as a loop cursor. | ||
2961 | + * @head: The head for your list. | ||
2962 | + * @member: The name of the list_struct within the struct. | ||
2963 | + * @ss: Pointer to "struct srcu_struct". | ||
2964 | + * | ||
2965 | + * As of 2.6.36, this macro is not provided because only TOMOYO wants it. | ||
2966 | + */ | ||
2967 | +#define list_for_each_entry_srcu(pos, head, member, ss) \ | ||
2968 | + for (pos = list_entry(srcu_dereference((head)->next, ss), \ | ||
2969 | + typeof(*pos), member); \ | ||
2970 | + prefetch(pos->member.next), &pos->member != (head); \ | ||
2971 | + pos = list_entry(srcu_dereference(pos->member.next, ss), \ | ||
2972 | + typeof(*pos), member)) | ||
2973 | + | ||
2974 | +#endif | ||
2975 | + | ||
2976 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 30) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 9)) | ||
2977 | + | ||
2978 | +#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 4, 21) | ||
2979 | +#undef ssleep | ||
2980 | +#endif | ||
2981 | + | ||
2982 | +#ifndef ssleep | ||
2983 | + | ||
2984 | +/** | ||
2985 | + * ssleep - Sleep for specified seconds. | ||
2986 | + * | ||
2987 | + * @secs: Seconds to sleep. | ||
2988 | + * | ||
2989 | + * Returns nothing. | ||
2990 | + * | ||
2991 | + * This is for compatibility with older kernels. | ||
2992 | + * | ||
2993 | + * Since several distributions backported ssleep(), I define it as a macro | ||
2994 | + * rather than an inlined function in order to avoid multiple definition error. | ||
2995 | + */ | ||
2996 | +#define ssleep(secs) { \ | ||
2997 | + set_current_state(TASK_UNINTERRUPTIBLE); \ | ||
2998 | + schedule_timeout((HZ * secs) + 1); \ | ||
2999 | + } | ||
3000 | + | ||
3001 | +#endif | ||
3002 | + | ||
3003 | +#endif | ||
3004 | + | ||
3005 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) | ||
3006 | + | ||
3007 | +/** | ||
3008 | + * from_kuid - Convert kuid_t to uid_t. | ||
3009 | + * | ||
3010 | + * @ns: Unused. | ||
3011 | + * @uid: kuid_t value. | ||
3012 | + * | ||
3013 | + * Returns uid seen from init's user namespace. | ||
3014 | + */ | ||
3015 | +#define from_kuid(ns, uid) (uid) | ||
3016 | + | ||
3017 | +/** | ||
3018 | + * from_kgid - Convert kgid_t to gid_t. | ||
3019 | + * | ||
3020 | + * @ns: Unused. | ||
3021 | + * @gid: kgid_t value. | ||
3022 | + * | ||
3023 | + * Returns gid seen from init's user namespace. | ||
3024 | + */ | ||
3025 | +#define from_kgid(ns, gid) (gid) | ||
3026 | + | ||
3027 | +/** | ||
3028 | + * uid_eq - Check whether the uids are equals or not. | ||
3029 | + * | ||
3030 | + * @left: Uid seen from current user namespace. | ||
3031 | + * @right: Uid seen from current user namespace. | ||
3032 | + * | ||
3033 | + * Returns true if uid is root in init's user namespace, false otherwise. | ||
3034 | + */ | ||
3035 | +#define uid_eq(left, right) ((left) == (right)) | ||
3036 | +#define GLOBAL_ROOT_UID 0 | ||
3037 | + | ||
3038 | +#endif | ||
3039 | + | ||
3040 | +/* | ||
3041 | + * TOMOYO specific part start. | ||
3042 | + */ | ||
3043 | + | ||
3044 | +#include <linux/ccsecurity.h> | ||
3045 | + | ||
3046 | +/* Enumeration definition for internal use. */ | ||
3047 | + | ||
3048 | +/* Index numbers for Access Controls. */ | ||
3049 | +enum ccs_acl_entry_type_index { | ||
3050 | + CCS_TYPE_PATH_ACL, | ||
3051 | + CCS_TYPE_PATH2_ACL, | ||
3052 | + CCS_TYPE_PATH_NUMBER_ACL, | ||
3053 | + CCS_TYPE_MKDEV_ACL, | ||
3054 | + CCS_TYPE_MOUNT_ACL, | ||
3055 | +#ifdef CONFIG_CCSECURITY_MISC | ||
3056 | + CCS_TYPE_ENV_ACL, | ||
3057 | +#endif | ||
3058 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
3059 | + CCS_TYPE_CAPABILITY_ACL, | ||
3060 | +#endif | ||
3061 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
3062 | + CCS_TYPE_INET_ACL, | ||
3063 | + CCS_TYPE_UNIX_ACL, | ||
3064 | +#endif | ||
3065 | +#ifdef CONFIG_CCSECURITY_IPC | ||
3066 | + CCS_TYPE_SIGNAL_ACL, | ||
3067 | +#endif | ||
3068 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
3069 | + CCS_TYPE_AUTO_EXECUTE_HANDLER, | ||
3070 | + CCS_TYPE_DENIED_EXECUTE_HANDLER, | ||
3071 | +#endif | ||
3072 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
3073 | + CCS_TYPE_AUTO_TASK_ACL, | ||
3074 | + CCS_TYPE_MANUAL_TASK_ACL, | ||
3075 | +#endif | ||
3076 | +}; | ||
3077 | + | ||
3078 | +/* Index numbers for "struct ccs_condition". */ | ||
3079 | +enum ccs_conditions_index { | ||
3080 | + CCS_TASK_UID, /* current_uid() */ | ||
3081 | + CCS_TASK_EUID, /* current_euid() */ | ||
3082 | + CCS_TASK_SUID, /* current_suid() */ | ||
3083 | + CCS_TASK_FSUID, /* current_fsuid() */ | ||
3084 | + CCS_TASK_GID, /* current_gid() */ | ||
3085 | + CCS_TASK_EGID, /* current_egid() */ | ||
3086 | + CCS_TASK_SGID, /* current_sgid() */ | ||
3087 | + CCS_TASK_FSGID, /* current_fsgid() */ | ||
3088 | + CCS_TASK_PID, /* sys_getpid() */ | ||
3089 | + CCS_TASK_PPID, /* sys_getppid() */ | ||
3090 | + CCS_EXEC_ARGC, /* "struct linux_binprm *"->argc */ | ||
3091 | + CCS_EXEC_ENVC, /* "struct linux_binprm *"->envc */ | ||
3092 | + CCS_TYPE_IS_SOCKET, /* S_IFSOCK */ | ||
3093 | + CCS_TYPE_IS_SYMLINK, /* S_IFLNK */ | ||
3094 | + CCS_TYPE_IS_FILE, /* S_IFREG */ | ||
3095 | + CCS_TYPE_IS_BLOCK_DEV, /* S_IFBLK */ | ||
3096 | + CCS_TYPE_IS_DIRECTORY, /* S_IFDIR */ | ||
3097 | + CCS_TYPE_IS_CHAR_DEV, /* S_IFCHR */ | ||
3098 | + CCS_TYPE_IS_FIFO, /* S_IFIFO */ | ||
3099 | + CCS_MODE_SETUID, /* S_ISUID */ | ||
3100 | + CCS_MODE_SETGID, /* S_ISGID */ | ||
3101 | + CCS_MODE_STICKY, /* S_ISVTX */ | ||
3102 | + CCS_MODE_OWNER_READ, /* S_IRUSR */ | ||
3103 | + CCS_MODE_OWNER_WRITE, /* S_IWUSR */ | ||
3104 | + CCS_MODE_OWNER_EXECUTE, /* S_IXUSR */ | ||
3105 | + CCS_MODE_GROUP_READ, /* S_IRGRP */ | ||
3106 | + CCS_MODE_GROUP_WRITE, /* S_IWGRP */ | ||
3107 | + CCS_MODE_GROUP_EXECUTE, /* S_IXGRP */ | ||
3108 | + CCS_MODE_OTHERS_READ, /* S_IROTH */ | ||
3109 | + CCS_MODE_OTHERS_WRITE, /* S_IWOTH */ | ||
3110 | + CCS_MODE_OTHERS_EXECUTE, /* S_IXOTH */ | ||
3111 | + CCS_TASK_TYPE, /* ((u8) task->ccs_flags) & | ||
3112 | + CCS_TASK_IS_EXECUTE_HANDLER */ | ||
3113 | + CCS_TASK_EXECUTE_HANDLER, /* CCS_TASK_IS_EXECUTE_HANDLER */ | ||
3114 | + CCS_EXEC_REALPATH, | ||
3115 | + CCS_SYMLINK_TARGET, | ||
3116 | + CCS_PATH1_UID, | ||
3117 | + CCS_PATH1_GID, | ||
3118 | + CCS_PATH1_INO, | ||
3119 | + CCS_PATH1_MAJOR, | ||
3120 | + CCS_PATH1_MINOR, | ||
3121 | + CCS_PATH1_PERM, | ||
3122 | + CCS_PATH1_TYPE, | ||
3123 | + CCS_PATH1_DEV_MAJOR, | ||
3124 | + CCS_PATH1_DEV_MINOR, | ||
3125 | + CCS_PATH2_UID, | ||
3126 | + CCS_PATH2_GID, | ||
3127 | + CCS_PATH2_INO, | ||
3128 | + CCS_PATH2_MAJOR, | ||
3129 | + CCS_PATH2_MINOR, | ||
3130 | + CCS_PATH2_PERM, | ||
3131 | + CCS_PATH2_TYPE, | ||
3132 | + CCS_PATH2_DEV_MAJOR, | ||
3133 | + CCS_PATH2_DEV_MINOR, | ||
3134 | + CCS_PATH1_PARENT_UID, | ||
3135 | + CCS_PATH1_PARENT_GID, | ||
3136 | + CCS_PATH1_PARENT_INO, | ||
3137 | + CCS_PATH1_PARENT_PERM, | ||
3138 | + CCS_PATH2_PARENT_UID, | ||
3139 | + CCS_PATH2_PARENT_GID, | ||
3140 | + CCS_PATH2_PARENT_INO, | ||
3141 | + CCS_PATH2_PARENT_PERM, | ||
3142 | + CCS_MAX_CONDITION_KEYWORD, | ||
3143 | + CCS_NUMBER_UNION, | ||
3144 | + CCS_NAME_UNION, | ||
3145 | + CCS_ARGV_ENTRY, | ||
3146 | + CCS_ENVP_ENTRY, | ||
3147 | +}; | ||
3148 | + | ||
3149 | +/* Index numbers for domain's attributes. */ | ||
3150 | +enum ccs_domain_info_flags_index { | ||
3151 | + /* Quota warnning flag. */ | ||
3152 | + CCS_DIF_QUOTA_WARNED, | ||
3153 | + /* | ||
3154 | + * This domain was unable to create a new domain at | ||
3155 | + * ccs_find_next_domain() because the name of the domain to be created | ||
3156 | + * was too long or it could not allocate memory. | ||
3157 | + * More than one process continued execve() without domain transition. | ||
3158 | + */ | ||
3159 | + CCS_DIF_TRANSITION_FAILED, | ||
3160 | + CCS_MAX_DOMAIN_INFO_FLAGS | ||
3161 | +}; | ||
3162 | + | ||
3163 | +/* Index numbers for audit type. */ | ||
3164 | +enum ccs_grant_log { | ||
3165 | + /* Follow profile's configuration. */ | ||
3166 | + CCS_GRANTLOG_AUTO, | ||
3167 | + /* Do not generate grant log. */ | ||
3168 | + CCS_GRANTLOG_NO, | ||
3169 | + /* Generate grant_log. */ | ||
3170 | + CCS_GRANTLOG_YES, | ||
3171 | +}; | ||
3172 | + | ||
3173 | +/* Index numbers for group entries. */ | ||
3174 | +enum ccs_group_id { | ||
3175 | + CCS_PATH_GROUP, | ||
3176 | + CCS_NUMBER_GROUP, | ||
3177 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
3178 | + CCS_ADDRESS_GROUP, | ||
3179 | +#endif | ||
3180 | + CCS_MAX_GROUP | ||
3181 | +}; | ||
3182 | + | ||
3183 | +/* Index numbers for category of functionality. */ | ||
3184 | +enum ccs_mac_category_index { | ||
3185 | + CCS_MAC_CATEGORY_FILE, | ||
3186 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
3187 | + CCS_MAC_CATEGORY_NETWORK, | ||
3188 | +#endif | ||
3189 | +#ifdef CONFIG_CCSECURITY_MISC | ||
3190 | + CCS_MAC_CATEGORY_MISC, | ||
3191 | +#endif | ||
3192 | +#ifdef CONFIG_CCSECURITY_IPC | ||
3193 | + CCS_MAC_CATEGORY_IPC, | ||
3194 | +#endif | ||
3195 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
3196 | + CCS_MAC_CATEGORY_CAPABILITY, | ||
3197 | +#endif | ||
3198 | + CCS_MAX_MAC_CATEGORY_INDEX | ||
3199 | +}; | ||
3200 | + | ||
3201 | +/* Index numbers for functionality. */ | ||
3202 | +enum ccs_mac_index { | ||
3203 | + CCS_MAC_FILE_EXECUTE, | ||
3204 | + CCS_MAC_FILE_OPEN, | ||
3205 | + CCS_MAC_FILE_CREATE, | ||
3206 | + CCS_MAC_FILE_UNLINK, | ||
3207 | +#ifdef CONFIG_CCSECURITY_FILE_GETATTR | ||
3208 | + CCS_MAC_FILE_GETATTR, | ||
3209 | +#endif | ||
3210 | + CCS_MAC_FILE_MKDIR, | ||
3211 | + CCS_MAC_FILE_RMDIR, | ||
3212 | + CCS_MAC_FILE_MKFIFO, | ||
3213 | + CCS_MAC_FILE_MKSOCK, | ||
3214 | + CCS_MAC_FILE_TRUNCATE, | ||
3215 | + CCS_MAC_FILE_SYMLINK, | ||
3216 | + CCS_MAC_FILE_MKBLOCK, | ||
3217 | + CCS_MAC_FILE_MKCHAR, | ||
3218 | + CCS_MAC_FILE_LINK, | ||
3219 | + CCS_MAC_FILE_RENAME, | ||
3220 | + CCS_MAC_FILE_CHMOD, | ||
3221 | + CCS_MAC_FILE_CHOWN, | ||
3222 | + CCS_MAC_FILE_CHGRP, | ||
3223 | + CCS_MAC_FILE_IOCTL, | ||
3224 | + CCS_MAC_FILE_CHROOT, | ||
3225 | + CCS_MAC_FILE_MOUNT, | ||
3226 | + CCS_MAC_FILE_UMOUNT, | ||
3227 | + CCS_MAC_FILE_PIVOT_ROOT, | ||
3228 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
3229 | + CCS_MAC_NETWORK_INET_STREAM_BIND, | ||
3230 | + CCS_MAC_NETWORK_INET_STREAM_LISTEN, | ||
3231 | + CCS_MAC_NETWORK_INET_STREAM_CONNECT, | ||
3232 | + CCS_MAC_NETWORK_INET_STREAM_ACCEPT, | ||
3233 | + CCS_MAC_NETWORK_INET_DGRAM_BIND, | ||
3234 | + CCS_MAC_NETWORK_INET_DGRAM_SEND, | ||
3235 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
3236 | + CCS_MAC_NETWORK_INET_DGRAM_RECV, | ||
3237 | +#endif | ||
3238 | + CCS_MAC_NETWORK_INET_RAW_BIND, | ||
3239 | + CCS_MAC_NETWORK_INET_RAW_SEND, | ||
3240 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
3241 | + CCS_MAC_NETWORK_INET_RAW_RECV, | ||
3242 | +#endif | ||
3243 | + CCS_MAC_NETWORK_UNIX_STREAM_BIND, | ||
3244 | + CCS_MAC_NETWORK_UNIX_STREAM_LISTEN, | ||
3245 | + CCS_MAC_NETWORK_UNIX_STREAM_CONNECT, | ||
3246 | + CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT, | ||
3247 | + CCS_MAC_NETWORK_UNIX_DGRAM_BIND, | ||
3248 | + CCS_MAC_NETWORK_UNIX_DGRAM_SEND, | ||
3249 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
3250 | + CCS_MAC_NETWORK_UNIX_DGRAM_RECV, | ||
3251 | +#endif | ||
3252 | + CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND, | ||
3253 | + CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN, | ||
3254 | + CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT, | ||
3255 | + CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT, | ||
3256 | +#endif | ||
3257 | +#ifdef CONFIG_CCSECURITY_MISC | ||
3258 | + CCS_MAC_ENVIRON, | ||
3259 | +#endif | ||
3260 | +#ifdef CONFIG_CCSECURITY_IPC | ||
3261 | + CCS_MAC_SIGNAL, | ||
3262 | +#endif | ||
3263 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
3264 | + CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET, | ||
3265 | + CCS_MAC_CAPABILITY_USE_PACKET_SOCKET, | ||
3266 | + CCS_MAC_CAPABILITY_SYS_REBOOT, | ||
3267 | + CCS_MAC_CAPABILITY_SYS_VHANGUP, | ||
3268 | + CCS_MAC_CAPABILITY_SYS_SETTIME, | ||
3269 | + CCS_MAC_CAPABILITY_SYS_NICE, | ||
3270 | + CCS_MAC_CAPABILITY_SYS_SETHOSTNAME, | ||
3271 | + CCS_MAC_CAPABILITY_USE_KERNEL_MODULE, | ||
3272 | + CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD, | ||
3273 | + CCS_MAC_CAPABILITY_SYS_PTRACE, | ||
3274 | +#endif | ||
3275 | + CCS_MAX_MAC_INDEX | ||
3276 | +}; | ||
3277 | + | ||
3278 | +/* Index numbers for /proc/ccs/stat interface. */ | ||
3279 | +enum ccs_memory_stat_type { | ||
3280 | + CCS_MEMORY_POLICY, | ||
3281 | + CCS_MEMORY_AUDIT, | ||
3282 | + CCS_MEMORY_QUERY, | ||
3283 | + CCS_MAX_MEMORY_STAT | ||
3284 | +}; | ||
3285 | + | ||
3286 | +/* Index numbers for access controls with one pathname and three numbers. */ | ||
3287 | +enum ccs_mkdev_acl_index { | ||
3288 | + CCS_TYPE_MKBLOCK, | ||
3289 | + CCS_TYPE_MKCHAR, | ||
3290 | + CCS_MAX_MKDEV_OPERATION | ||
3291 | +}; | ||
3292 | + | ||
3293 | +/* Index numbers for operation mode. */ | ||
3294 | +enum ccs_mode_value { | ||
3295 | + CCS_CONFIG_DISABLED, | ||
3296 | + CCS_CONFIG_LEARNING, | ||
3297 | + CCS_CONFIG_PERMISSIVE, | ||
3298 | + CCS_CONFIG_ENFORCING, | ||
3299 | + CCS_CONFIG_MAX_MODE, | ||
3300 | + CCS_CONFIG_WANT_REJECT_LOG = 64, | ||
3301 | + CCS_CONFIG_WANT_GRANT_LOG = 128, | ||
3302 | + CCS_CONFIG_USE_DEFAULT = 255, | ||
3303 | +}; | ||
3304 | + | ||
3305 | +/* Index numbers for socket operations. */ | ||
3306 | +enum ccs_network_acl_index { | ||
3307 | + CCS_NETWORK_BIND, /* bind() operation. */ | ||
3308 | + CCS_NETWORK_LISTEN, /* listen() operation. */ | ||
3309 | + CCS_NETWORK_CONNECT, /* connect() operation. */ | ||
3310 | + CCS_NETWORK_ACCEPT, /* accept() operation. */ | ||
3311 | + CCS_NETWORK_SEND, /* send() operation. */ | ||
3312 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
3313 | + CCS_NETWORK_RECV, /* recv() operation. */ | ||
3314 | +#endif | ||
3315 | + CCS_MAX_NETWORK_OPERATION | ||
3316 | +}; | ||
3317 | + | ||
3318 | +/* Index numbers for access controls with two pathnames. */ | ||
3319 | +enum ccs_path2_acl_index { | ||
3320 | + CCS_TYPE_LINK, | ||
3321 | + CCS_TYPE_RENAME, | ||
3322 | + CCS_TYPE_PIVOT_ROOT, | ||
3323 | + CCS_MAX_PATH2_OPERATION | ||
3324 | +}; | ||
3325 | + | ||
3326 | +/* Index numbers for access controls with one pathname. */ | ||
3327 | +enum ccs_path_acl_index { | ||
3328 | + CCS_TYPE_EXECUTE, | ||
3329 | + CCS_TYPE_READ, | ||
3330 | + CCS_TYPE_WRITE, | ||
3331 | + CCS_TYPE_APPEND, | ||
3332 | + CCS_TYPE_UNLINK, | ||
3333 | +#ifdef CONFIG_CCSECURITY_FILE_GETATTR | ||
3334 | + CCS_TYPE_GETATTR, | ||
3335 | +#endif | ||
3336 | + CCS_TYPE_RMDIR, | ||
3337 | + CCS_TYPE_TRUNCATE, | ||
3338 | + CCS_TYPE_SYMLINK, | ||
3339 | + CCS_TYPE_CHROOT, | ||
3340 | + CCS_TYPE_UMOUNT, | ||
3341 | + CCS_MAX_PATH_OPERATION | ||
3342 | +}; | ||
3343 | + | ||
3344 | +/* Index numbers for access controls with one pathname and one number. */ | ||
3345 | +enum ccs_path_number_acl_index { | ||
3346 | + CCS_TYPE_CREATE, | ||
3347 | + CCS_TYPE_MKDIR, | ||
3348 | + CCS_TYPE_MKFIFO, | ||
3349 | + CCS_TYPE_MKSOCK, | ||
3350 | + CCS_TYPE_IOCTL, | ||
3351 | + CCS_TYPE_CHMOD, | ||
3352 | + CCS_TYPE_CHOWN, | ||
3353 | + CCS_TYPE_CHGRP, | ||
3354 | + CCS_MAX_PATH_NUMBER_OPERATION | ||
3355 | +}; | ||
3356 | + | ||
3357 | +/* Index numbers for stat(). */ | ||
3358 | +enum ccs_path_stat_index { | ||
3359 | + /* Do not change this order. */ | ||
3360 | + CCS_PATH1, | ||
3361 | + CCS_PATH1_PARENT, | ||
3362 | + CCS_PATH2, | ||
3363 | + CCS_PATH2_PARENT, | ||
3364 | + CCS_MAX_PATH_STAT | ||
3365 | +}; | ||
3366 | + | ||
3367 | +/* Index numbers for entry type. */ | ||
3368 | +enum ccs_policy_id { | ||
3369 | +#ifdef CONFIG_CCSECURITY_PORTRESERVE | ||
3370 | + CCS_ID_RESERVEDPORT, | ||
3371 | +#endif | ||
3372 | + CCS_ID_GROUP, | ||
3373 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
3374 | + CCS_ID_ADDRESS_GROUP, | ||
3375 | +#endif | ||
3376 | + CCS_ID_PATH_GROUP, | ||
3377 | + CCS_ID_NUMBER_GROUP, | ||
3378 | + CCS_ID_AGGREGATOR, | ||
3379 | + CCS_ID_TRANSITION_CONTROL, | ||
3380 | + CCS_ID_MANAGER, | ||
3381 | + CCS_ID_CONDITION, | ||
3382 | + CCS_ID_NAME, | ||
3383 | + CCS_ID_ACL, | ||
3384 | + CCS_ID_DOMAIN, | ||
3385 | + CCS_MAX_POLICY | ||
3386 | +}; | ||
3387 | + | ||
3388 | +/* Index numbers for /proc/ccs/stat interface. */ | ||
3389 | +enum ccs_policy_stat_type { | ||
3390 | + /* Do not change this order. */ | ||
3391 | + CCS_STAT_POLICY_UPDATES, | ||
3392 | + CCS_STAT_POLICY_LEARNING, /* == CCS_CONFIG_LEARNING */ | ||
3393 | + CCS_STAT_POLICY_PERMISSIVE, /* == CCS_CONFIG_PERMISSIVE */ | ||
3394 | + CCS_STAT_POLICY_ENFORCING, /* == CCS_CONFIG_ENFORCING */ | ||
3395 | + CCS_MAX_POLICY_STAT | ||
3396 | +}; | ||
3397 | + | ||
3398 | +/* Index numbers for profile's PREFERENCE values. */ | ||
3399 | +enum ccs_pref_index { | ||
3400 | + CCS_PREF_MAX_AUDIT_LOG, | ||
3401 | + CCS_PREF_MAX_LEARNING_ENTRY, | ||
3402 | + CCS_PREF_ENFORCING_PENALTY, | ||
3403 | + CCS_MAX_PREF | ||
3404 | +}; | ||
3405 | + | ||
3406 | +/* Index numbers for /proc/ccs/ interfaces. */ | ||
3407 | +enum ccs_proc_interface_index { | ||
3408 | + CCS_DOMAIN_POLICY, | ||
3409 | + CCS_EXCEPTION_POLICY, | ||
3410 | + CCS_PROCESS_STATUS, | ||
3411 | + CCS_STAT, | ||
3412 | + CCS_AUDIT, | ||
3413 | + CCS_VERSION, | ||
3414 | + CCS_PROFILE, | ||
3415 | + CCS_QUERY, | ||
3416 | + CCS_MANAGER, | ||
3417 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
3418 | + CCS_EXECUTE_HANDLER, | ||
3419 | +#endif | ||
3420 | +}; | ||
3421 | + | ||
3422 | +/* Index numbers for special mount operations. */ | ||
3423 | +enum ccs_special_mount { | ||
3424 | + CCS_MOUNT_BIND, /* mount --bind /source /dest */ | ||
3425 | + CCS_MOUNT_MOVE, /* mount --move /old /new */ | ||
3426 | + CCS_MOUNT_REMOUNT, /* mount -o remount /dir */ | ||
3427 | + CCS_MOUNT_MAKE_UNBINDABLE, /* mount --make-unbindable /dir */ | ||
3428 | + CCS_MOUNT_MAKE_PRIVATE, /* mount --make-private /dir */ | ||
3429 | + CCS_MOUNT_MAKE_SLAVE, /* mount --make-slave /dir */ | ||
3430 | + CCS_MOUNT_MAKE_SHARED, /* mount --make-shared /dir */ | ||
3431 | + CCS_MAX_SPECIAL_MOUNT | ||
3432 | +}; | ||
3433 | + | ||
3434 | +/* Index numbers for domain transition control keywords. */ | ||
3435 | +enum ccs_transition_type { | ||
3436 | + /* Do not change this order, */ | ||
3437 | + CCS_TRANSITION_CONTROL_NO_RESET, | ||
3438 | + CCS_TRANSITION_CONTROL_RESET, | ||
3439 | + CCS_TRANSITION_CONTROL_NO_INITIALIZE, | ||
3440 | + CCS_TRANSITION_CONTROL_INITIALIZE, | ||
3441 | + CCS_TRANSITION_CONTROL_NO_KEEP, | ||
3442 | + CCS_TRANSITION_CONTROL_KEEP, | ||
3443 | + CCS_MAX_TRANSITION_TYPE | ||
3444 | +}; | ||
3445 | + | ||
3446 | +/* Index numbers for type of numeric values. */ | ||
3447 | +enum ccs_value_type { | ||
3448 | + CCS_VALUE_TYPE_INVALID, | ||
3449 | + CCS_VALUE_TYPE_DECIMAL, | ||
3450 | + CCS_VALUE_TYPE_OCTAL, | ||
3451 | + CCS_VALUE_TYPE_HEXADECIMAL, | ||
3452 | +}; | ||
3453 | + | ||
3454 | +/* Constants definition for internal use. */ | ||
3455 | + | ||
3456 | +/* | ||
3457 | + * TOMOYO uses this hash only when appending a string into the string table. | ||
3458 | + * Frequency of appending strings is very low. So we don't need large (e.g. | ||
3459 | + * 64k) hash size. 256 will be sufficient. | ||
3460 | + */ | ||
3461 | +#define CCS_HASH_BITS 8 | ||
3462 | +#define CCS_MAX_HASH (1u << CCS_HASH_BITS) | ||
3463 | + | ||
3464 | +/* | ||
3465 | + * TOMOYO checks only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET. | ||
3466 | + * Therefore, we don't need SOCK_MAX. | ||
3467 | + */ | ||
3468 | +#define CCS_SOCK_MAX 6 | ||
3469 | + | ||
3470 | +/* Size of temporary buffer for execve() operation. */ | ||
3471 | +#define CCS_EXEC_TMPSIZE 4096 | ||
3472 | + | ||
3473 | +/* Garbage collector is trying to kfree() this element. */ | ||
3474 | +#define CCS_GC_IN_PROGRESS -1 | ||
3475 | + | ||
3476 | +/* Profile number is an integer between 0 and 255. */ | ||
3477 | +#define CCS_MAX_PROFILES 256 | ||
3478 | + | ||
3479 | +/* Group number is an integer between 0 and 255. */ | ||
3480 | +#define CCS_MAX_ACL_GROUPS 256 | ||
3481 | + | ||
3482 | +/* Current thread is doing open(O_RDONLY | O_TRUNC) ? */ | ||
3483 | +#define CCS_OPEN_FOR_READ_TRUNCATE 1 | ||
3484 | +/* Current thread is doing open(3) ? */ | ||
3485 | +#define CCS_OPEN_FOR_IOCTL_ONLY 2 | ||
3486 | +/* Current thread is doing do_execve() ? */ | ||
3487 | +#define CCS_TASK_IS_IN_EXECVE 4 | ||
3488 | +/* Current thread is running as an execute handler program? */ | ||
3489 | +#define CCS_TASK_IS_EXECUTE_HANDLER 8 | ||
3490 | +/* Current thread is allowed to modify policy via /proc/ccs/ interface? */ | ||
3491 | +#define CCS_TASK_IS_MANAGER 16 | ||
3492 | + | ||
3493 | +/* | ||
3494 | + * Retry this request. Returned by ccs_supervisor() if policy violation has | ||
3495 | + * occurred in enforcing mode and the userspace daemon decided to retry. | ||
3496 | + * | ||
3497 | + * We must choose a positive value in order to distinguish "granted" (which is | ||
3498 | + * 0) and "rejected" (which is a negative value) and "retry". | ||
3499 | + */ | ||
3500 | +#define CCS_RETRY_REQUEST 1 | ||
3501 | + | ||
3502 | +/* Ignore gfp flags which are not supported. */ | ||
3503 | +#ifndef __GFP_HIGHIO | ||
3504 | +#define __GFP_HIGHIO 0 | ||
3505 | +#endif | ||
3506 | +#ifndef __GFP_NOWARN | ||
3507 | +#define __GFP_NOWARN 0 | ||
3508 | +#endif | ||
3509 | +#ifndef __GFP_NORETRY | ||
3510 | +#define __GFP_NORETRY 0 | ||
3511 | +#endif | ||
3512 | +#ifndef __GFP_NOMEMALLOC | ||
3513 | +#define __GFP_NOMEMALLOC 0 | ||
3514 | +#endif | ||
3515 | + | ||
3516 | +/* The gfp flags used by TOMOYO. */ | ||
3517 | +#define CCS_GFP_FLAGS (__GFP_WAIT | __GFP_IO | __GFP_HIGHIO | __GFP_NOWARN | \ | ||
3518 | + __GFP_NORETRY | __GFP_NOMEMALLOC) | ||
3519 | + | ||
3520 | +/* Size of read buffer for /proc/ccs/ interface. */ | ||
3521 | +#define CCS_MAX_IO_READ_QUEUE 64 | ||
3522 | + | ||
3523 | +/* Structure definition for internal use. */ | ||
3524 | + | ||
3525 | +/* Common header for holding ACL entries. */ | ||
3526 | +struct ccs_acl_head { | ||
3527 | + struct list_head list; | ||
3528 | + s8 is_deleted; /* true or false or CCS_GC_IN_PROGRESS */ | ||
3529 | +} __packed; | ||
3530 | + | ||
3531 | +/* Common header for shared entries. */ | ||
3532 | +struct ccs_shared_acl_head { | ||
3533 | + struct list_head list; | ||
3534 | + atomic_t users; | ||
3535 | +} __packed; | ||
3536 | + | ||
3537 | +/* Common header for individual entries. */ | ||
3538 | +struct ccs_acl_info { | ||
3539 | + struct list_head list; | ||
3540 | + struct ccs_condition *cond; /* Maybe NULL. */ | ||
3541 | + s8 is_deleted; /* true or false or CCS_GC_IN_PROGRESS */ | ||
3542 | + u8 type; /* One of values in "enum ccs_acl_entry_type_index". */ | ||
3543 | + u16 perm; | ||
3544 | +} __packed; | ||
3545 | + | ||
3546 | +/* Structure for holding a word. */ | ||
3547 | +struct ccs_name_union { | ||
3548 | + /* Either @filename or @group is NULL. */ | ||
3549 | + const struct ccs_path_info *filename; | ||
3550 | + struct ccs_group *group; | ||
3551 | +}; | ||
3552 | + | ||
3553 | +/* Structure for holding a number. */ | ||
3554 | +struct ccs_number_union { | ||
3555 | + unsigned long values[2]; | ||
3556 | + struct ccs_group *group; /* Maybe NULL. */ | ||
3557 | + /* One of values in "enum ccs_value_type". */ | ||
3558 | + u8 value_type[2]; | ||
3559 | +}; | ||
3560 | + | ||
3561 | +/* Structure for holding an IP address. */ | ||
3562 | +struct ccs_ipaddr_union { | ||
3563 | + struct in6_addr ip[2]; /* Big endian. */ | ||
3564 | + struct ccs_group *group; /* Pointer to address group. */ | ||
3565 | + bool is_ipv6; /* Valid only if @group == NULL. */ | ||
3566 | +}; | ||
3567 | + | ||
3568 | +/* Structure for "path_group"/"number_group"/"address_group" directive. */ | ||
3569 | +struct ccs_group { | ||
3570 | + struct ccs_shared_acl_head head; | ||
3571 | + /* Name of group (without leading '@'). */ | ||
3572 | + const struct ccs_path_info *group_name; | ||
3573 | + /* | ||
3574 | + * List of "struct ccs_path_group" or "struct ccs_number_group" or | ||
3575 | + * "struct ccs_address_group". | ||
3576 | + */ | ||
3577 | + struct list_head member_list; | ||
3578 | +}; | ||
3579 | + | ||
3580 | +/* Structure for "path_group" directive. */ | ||
3581 | +struct ccs_path_group { | ||
3582 | + struct ccs_acl_head head; | ||
3583 | + const struct ccs_path_info *member_name; | ||
3584 | +}; | ||
3585 | + | ||
3586 | +/* Structure for "number_group" directive. */ | ||
3587 | +struct ccs_number_group { | ||
3588 | + struct ccs_acl_head head; | ||
3589 | + struct ccs_number_union number; | ||
3590 | +}; | ||
3591 | + | ||
3592 | +/* Structure for "address_group" directive. */ | ||
3593 | +struct ccs_address_group { | ||
3594 | + struct ccs_acl_head head; | ||
3595 | + /* Structure for holding an IP address. */ | ||
3596 | + struct ccs_ipaddr_union address; | ||
3597 | +}; | ||
3598 | + | ||
3599 | +/* Subset of "struct stat". Used by conditional ACL and audit logs. */ | ||
3600 | +struct ccs_mini_stat { | ||
3601 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) | ||
3602 | + kuid_t uid; | ||
3603 | + kgid_t gid; | ||
3604 | +#else | ||
3605 | + uid_t uid; | ||
3606 | + gid_t gid; | ||
3607 | +#endif | ||
3608 | + ino_t ino; | ||
3609 | + umode_t mode; | ||
3610 | + dev_t dev; | ||
3611 | + dev_t rdev; | ||
3612 | +}; | ||
3613 | + | ||
3614 | +/* Structure for dumping argv[] and envp[] of "struct linux_binprm". */ | ||
3615 | +struct ccs_page_dump { | ||
3616 | + struct page *page; /* Previously dumped page. */ | ||
3617 | + char *data; /* Contents of "page". Size is PAGE_SIZE. */ | ||
3618 | +}; | ||
3619 | + | ||
3620 | +/* Structure for attribute checks in addition to pathname checks. */ | ||
3621 | +struct ccs_obj_info { | ||
3622 | + /* True if ccs_get_attributes() was already called, false otherwise. */ | ||
3623 | + bool validate_done; | ||
3624 | + /* True if @stat[] is valid. */ | ||
3625 | + bool stat_valid[CCS_MAX_PATH_STAT]; | ||
3626 | + /* First pathname. Initialized with { NULL, NULL } if no path. */ | ||
3627 | + struct path path1; | ||
3628 | + /* Second pathname. Initialized with { NULL, NULL } if no path. */ | ||
3629 | + struct path path2; | ||
3630 | + /* | ||
3631 | + * Information on @path1, @path1's parent directory, @path2, @path2's | ||
3632 | + * parent directory. | ||
3633 | + */ | ||
3634 | + struct ccs_mini_stat stat[CCS_MAX_PATH_STAT]; | ||
3635 | + /* | ||
3636 | + * Content of symbolic link to be created. NULL for operations other | ||
3637 | + * than symlink(). | ||
3638 | + */ | ||
3639 | + struct ccs_path_info *symlink_target; | ||
3640 | +}; | ||
3641 | + | ||
3642 | +/* Structure for entries which follows "struct ccs_condition". */ | ||
3643 | +struct ccs_condition_element { | ||
3644 | + /* | ||
3645 | + * Left hand operand. A "struct ccs_argv" for CCS_ARGV_ENTRY, a | ||
3646 | + * "struct ccs_envp" for CCS_ENVP_ENTRY is attached to the tail | ||
3647 | + * of the array of this struct. | ||
3648 | + */ | ||
3649 | + u8 left; | ||
3650 | + /* | ||
3651 | + * Right hand operand. A "struct ccs_number_union" for | ||
3652 | + * CCS_NUMBER_UNION, a "struct ccs_name_union" for CCS_NAME_UNION is | ||
3653 | + * attached to the tail of the array of this struct. | ||
3654 | + */ | ||
3655 | + u8 right; | ||
3656 | + /* Equation operator. True if equals or overlaps, false otherwise. */ | ||
3657 | + bool equals; | ||
3658 | +}; | ||
3659 | + | ||
3660 | +/* Structure for optional arguments. */ | ||
3661 | +struct ccs_condition { | ||
3662 | + struct ccs_shared_acl_head head; | ||
3663 | + u32 size; /* Memory size allocated for this entry. */ | ||
3664 | + u16 condc; /* Number of conditions in this struct. */ | ||
3665 | + u16 numbers_count; /* Number of "struct ccs_number_union values". */ | ||
3666 | + u16 names_count; /* Number of "struct ccs_name_union names". */ | ||
3667 | + u16 argc; /* Number of "struct ccs_argv". */ | ||
3668 | + u16 envc; /* Number of "struct ccs_envp". */ | ||
3669 | + u8 grant_log; /* One of values in "enum ccs_grant_log". */ | ||
3670 | + bool exec_transit; /* True if transit is for "file execute". */ | ||
3671 | + const struct ccs_path_info *transit; /* Maybe NULL. */ | ||
3672 | + /* | ||
3673 | + * struct ccs_condition_element condition[condc]; | ||
3674 | + * struct ccs_number_union values[numbers_count]; | ||
3675 | + * struct ccs_name_union names[names_count]; | ||
3676 | + * struct ccs_argv argv[argc]; | ||
3677 | + * struct ccs_envp envp[envc]; | ||
3678 | + */ | ||
3679 | +}; | ||
3680 | + | ||
3681 | +struct ccs_execve; | ||
3682 | +struct ccs_policy_namespace; | ||
3683 | + | ||
3684 | +/* Structure for request info. */ | ||
3685 | +struct ccs_request_info { | ||
3686 | + /* | ||
3687 | + * For holding parameters specific to operations which deal files. | ||
3688 | + * NULL if not dealing files. | ||
3689 | + */ | ||
3690 | + struct ccs_obj_info *obj; | ||
3691 | + /* | ||
3692 | + * For holding parameters specific to execve() request. | ||
3693 | + * NULL if not dealing do_execve(). | ||
3694 | + */ | ||
3695 | + struct ccs_execve *ee; | ||
3696 | + /* | ||
3697 | + * For holding parameters. | ||
3698 | + * Pointers in this union are not NULL except path->matched_path. | ||
3699 | + */ | ||
3700 | + union { | ||
3701 | + struct { | ||
3702 | + const struct ccs_path_info *filename; | ||
3703 | + /* | ||
3704 | + * For using wildcards at ccs_find_next_domain(). | ||
3705 | + * | ||
3706 | + * The matched_acl cannot be used because it may refer | ||
3707 | + * a "struct ccs_path_acl" with ->is_group == true. | ||
3708 | + * We want to use exact "struct ccs_path_info" rather | ||
3709 | + * than "struct ccs_path_acl". | ||
3710 | + */ | ||
3711 | + const struct ccs_path_info *matched_path; | ||
3712 | + /* One of values in "enum ccs_path_acl_index". */ | ||
3713 | + u8 operation; | ||
3714 | + } path; | ||
3715 | + struct { | ||
3716 | + const struct ccs_path_info *filename1; | ||
3717 | + const struct ccs_path_info *filename2; | ||
3718 | + /* One of values in "enum ccs_path2_acl_index". */ | ||
3719 | + u8 operation; | ||
3720 | + } path2; | ||
3721 | + struct { | ||
3722 | + const struct ccs_path_info *filename; | ||
3723 | + unsigned int mode; | ||
3724 | + unsigned int major; | ||
3725 | + unsigned int minor; | ||
3726 | + /* One of values in "enum ccs_mkdev_acl_index". */ | ||
3727 | + u8 operation; | ||
3728 | + } mkdev; | ||
3729 | + struct { | ||
3730 | + const struct ccs_path_info *filename; | ||
3731 | + unsigned long number; | ||
3732 | + /* | ||
3733 | + * One of values in "enum ccs_path_number_acl_index". | ||
3734 | + */ | ||
3735 | + u8 operation; | ||
3736 | + } path_number; | ||
3737 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
3738 | + struct { | ||
3739 | + const u32 *address; /* Big endian. */ | ||
3740 | + u16 port; /* Host endian. */ | ||
3741 | + /* One of values smaller than CCS_SOCK_MAX. */ | ||
3742 | + u8 protocol; | ||
3743 | + /* One of values in "enum ccs_network_acl_index". */ | ||
3744 | + u8 operation; | ||
3745 | + bool is_ipv6; | ||
3746 | + } inet_network; | ||
3747 | + struct { | ||
3748 | + const struct ccs_path_info *address; | ||
3749 | + /* One of values smaller than CCS_SOCK_MAX. */ | ||
3750 | + u8 protocol; | ||
3751 | + /* One of values in "enum ccs_network_acl_index". */ | ||
3752 | + u8 operation; | ||
3753 | + } unix_network; | ||
3754 | +#endif | ||
3755 | +#ifdef CONFIG_CCSECURITY_MISC | ||
3756 | + struct { | ||
3757 | + const struct ccs_path_info *name; | ||
3758 | + } environ; | ||
3759 | +#endif | ||
3760 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
3761 | + struct { | ||
3762 | + /* One of values in "enum ccs_capability_acl_index". */ | ||
3763 | + u8 operation; | ||
3764 | + } capability; | ||
3765 | +#endif | ||
3766 | +#ifdef CONFIG_CCSECURITY_IPC | ||
3767 | + struct { | ||
3768 | + const char *dest_pattern; | ||
3769 | + int sig; | ||
3770 | + } signal; | ||
3771 | +#endif | ||
3772 | + struct { | ||
3773 | + const struct ccs_path_info *type; | ||
3774 | + const struct ccs_path_info *dir; | ||
3775 | + const struct ccs_path_info *dev; | ||
3776 | + unsigned long flags; | ||
3777 | + int need_dev; | ||
3778 | + } mount; | ||
3779 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
3780 | + struct { | ||
3781 | + const struct ccs_path_info *domainname; | ||
3782 | + } task; | ||
3783 | +#endif | ||
3784 | + } param; | ||
3785 | + /* | ||
3786 | + * For updating current->ccs_domain_info at ccs_update_task_domain(). | ||
3787 | + * Initialized to NULL at ccs_init_request_info(). | ||
3788 | + * Matching "struct ccs_acl_info" is copied if access request was | ||
3789 | + * granted. Re-initialized to NULL at ccs_update_task_domain(). | ||
3790 | + */ | ||
3791 | + struct ccs_acl_info *matched_acl; | ||
3792 | + u8 param_type; /* One of values in "enum ccs_acl_entry_type_index". */ | ||
3793 | + bool granted; /* True if granted, false otherwise. */ | ||
3794 | + /* True if current thread should not be carried sleep penalty. */ | ||
3795 | + bool dont_sleep_on_enforce_error; | ||
3796 | + /* | ||
3797 | + * For counting number of retries made for this request. | ||
3798 | + * This counter is incremented whenever ccs_supervisor() returned | ||
3799 | + * CCS_RETRY_REQUEST. | ||
3800 | + */ | ||
3801 | + u8 retry; | ||
3802 | + /* | ||
3803 | + * For holding profile number used for this request. | ||
3804 | + * One of values between 0 and CCS_MAX_PROFILES - 1. | ||
3805 | + */ | ||
3806 | + u8 profile; | ||
3807 | + /* | ||
3808 | + * For holding operation mode used for this request. | ||
3809 | + * One of CCS_CONFIG_DISABLED, CCS_CONFIG_LEARNING, | ||
3810 | + * CCS_CONFIG_PERMISSIVE, CCS_CONFIG_ENFORCING. | ||
3811 | + */ | ||
3812 | + u8 mode; | ||
3813 | + /* | ||
3814 | + * For holding operation index used for this request. | ||
3815 | + * Used by ccs_init_request_info() / ccs_get_mode() / | ||
3816 | + * ccs_write_log(). One of values in "enum ccs_mac_index". | ||
3817 | + */ | ||
3818 | + u8 type; | ||
3819 | +}; | ||
3820 | + | ||
3821 | +/* Structure for holding a token. */ | ||
3822 | +struct ccs_path_info { | ||
3823 | + const char *name; | ||
3824 | + u32 hash; /* = full_name_hash(name, strlen(name)) */ | ||
3825 | + u16 total_len; /* = strlen(name) */ | ||
3826 | + u16 const_len; /* = ccs_const_part_length(name) */ | ||
3827 | + bool is_dir; /* = ccs_strendswith(name, "/") */ | ||
3828 | + bool is_patterned; /* = const_len < total_len */ | ||
3829 | +}; | ||
3830 | + | ||
3831 | +/* Structure for execve() operation. */ | ||
3832 | +struct ccs_execve { | ||
3833 | + struct ccs_request_info r; | ||
3834 | + struct ccs_obj_info obj; | ||
3835 | + struct linux_binprm *bprm; | ||
3836 | + struct ccs_domain_info *previous_domain; | ||
3837 | + const struct ccs_path_info *transition; | ||
3838 | + /* For execute_handler */ | ||
3839 | + const struct ccs_path_info *handler; | ||
3840 | + char *handler_path; /* = kstrdup(handler->name, CCS_GFP_FLAGS) */ | ||
3841 | + /* For dumping argv[] and envp[]. */ | ||
3842 | + struct ccs_page_dump dump; | ||
3843 | + /* For temporary use. */ | ||
3844 | + char *tmp; /* Size is CCS_EXEC_TMPSIZE bytes */ | ||
3845 | +}; | ||
3846 | + | ||
3847 | +/* Structure for domain information. */ | ||
3848 | +struct ccs_domain_info { | ||
3849 | + struct list_head list; | ||
3850 | + struct list_head acl_info_list; | ||
3851 | + /* Name of this domain. Never NULL. */ | ||
3852 | + const struct ccs_path_info *domainname; | ||
3853 | + /* Namespace for this domain. Never NULL. */ | ||
3854 | + struct ccs_policy_namespace *ns; | ||
3855 | + /* Group numbers to use. */ | ||
3856 | + unsigned long group[CCS_MAX_ACL_GROUPS / BITS_PER_LONG]; | ||
3857 | + u8 profile; /* Profile number to use. */ | ||
3858 | + bool is_deleted; /* Delete flag. */ | ||
3859 | + bool flags[CCS_MAX_DOMAIN_INFO_FLAGS]; | ||
3860 | +}; | ||
3861 | + | ||
3862 | +/* | ||
3863 | + * Structure for "reset_domain"/"no_reset_domain"/"initialize_domain"/ | ||
3864 | + * "no_initialize_domain"/"keep_domain"/"no_keep_domain" keyword. | ||
3865 | + */ | ||
3866 | +struct ccs_transition_control { | ||
3867 | + struct ccs_acl_head head; | ||
3868 | + u8 type; /* One of values in "enum ccs_transition_type" */ | ||
3869 | + bool is_last_name; /* True if the domainname is ccs_last_word(). */ | ||
3870 | + const struct ccs_path_info *domainname; /* Maybe NULL */ | ||
3871 | + const struct ccs_path_info *program; /* Maybe NULL */ | ||
3872 | +}; | ||
3873 | + | ||
3874 | +/* Structure for "aggregator" keyword. */ | ||
3875 | +struct ccs_aggregator { | ||
3876 | + struct ccs_acl_head head; | ||
3877 | + const struct ccs_path_info *original_name; | ||
3878 | + const struct ccs_path_info *aggregated_name; | ||
3879 | +}; | ||
3880 | + | ||
3881 | +/* Structure for "deny_autobind" keyword. */ | ||
3882 | +struct ccs_reserved { | ||
3883 | + struct ccs_acl_head head; | ||
3884 | + struct ccs_number_union port; | ||
3885 | +}; | ||
3886 | + | ||
3887 | +/* Structure for policy manager. */ | ||
3888 | +struct ccs_manager { | ||
3889 | + struct ccs_acl_head head; | ||
3890 | + /* A path to program or a domainname. */ | ||
3891 | + const struct ccs_path_info *manager; | ||
3892 | +}; | ||
3893 | + | ||
3894 | +/* Structure for argv[]. */ | ||
3895 | +struct ccs_argv { | ||
3896 | + unsigned long index; | ||
3897 | + const struct ccs_path_info *value; | ||
3898 | + bool is_not; | ||
3899 | +}; | ||
3900 | + | ||
3901 | +/* Structure for envp[]. */ | ||
3902 | +struct ccs_envp { | ||
3903 | + const struct ccs_path_info *name; | ||
3904 | + const struct ccs_path_info *value; | ||
3905 | + bool is_not; | ||
3906 | +}; | ||
3907 | + | ||
3908 | +/* | ||
3909 | + * Structure for "task auto_execute_handler" and "task denied_execute_handler" | ||
3910 | + * directive. | ||
3911 | + * | ||
3912 | + * If "task auto_execute_handler" directive exists and the current process is | ||
3913 | + * not an execute handler, all execve() requests are replaced by execve() | ||
3914 | + * requests of a program specified by "task auto_execute_handler" directive. | ||
3915 | + * If the current process is an execute handler, "task auto_execute_handler" | ||
3916 | + * and "task denied_execute_handler" directives are ignored. | ||
3917 | + * The program specified by "task execute_handler" validates execve() | ||
3918 | + * parameters and executes the original execve() requests if appropriate. | ||
3919 | + * | ||
3920 | + * "task denied_execute_handler" directive is used only when execve() request | ||
3921 | + * was rejected in enforcing mode (i.e. CONFIG::file::execute={ mode=enforcing | ||
3922 | + * }). The program specified by "task denied_execute_handler" does whatever it | ||
3923 | + * wants to do (e.g. silently terminate, change firewall settings, redirect the | ||
3924 | + * user to honey pot etc.). | ||
3925 | + */ | ||
3926 | +struct ccs_handler_acl { | ||
3927 | + struct ccs_acl_info head; /* type = CCS_TYPE_*_EXECUTE_HANDLER */ | ||
3928 | + const struct ccs_path_info *handler; /* Pointer to single pathname. */ | ||
3929 | +}; | ||
3930 | + | ||
3931 | +/* | ||
3932 | + * Structure for "task auto_domain_transition" and | ||
3933 | + * "task manual_domain_transition" directive. | ||
3934 | + */ | ||
3935 | +struct ccs_task_acl { | ||
3936 | + struct ccs_acl_info head; /* type = CCS_TYPE_*_TASK_ACL */ | ||
3937 | + /* Pointer to domainname. */ | ||
3938 | + const struct ccs_path_info *domainname; | ||
3939 | +}; | ||
3940 | + | ||
3941 | +/* | ||
3942 | + * Structure for "file execute", "file read", "file write", "file append", | ||
3943 | + * "file unlink", "file getattr", "file rmdir", "file truncate", | ||
3944 | + * "file symlink", "file chroot" and "file unmount" directive. | ||
3945 | + */ | ||
3946 | +struct ccs_path_acl { | ||
3947 | + struct ccs_acl_info head; /* type = CCS_TYPE_PATH_ACL */ | ||
3948 | + struct ccs_name_union name; | ||
3949 | +}; | ||
3950 | + | ||
3951 | +/* | ||
3952 | + * Structure for "file rename", "file link" and "file pivot_root" directive. | ||
3953 | + */ | ||
3954 | +struct ccs_path2_acl { | ||
3955 | + struct ccs_acl_info head; /* type = CCS_TYPE_PATH2_ACL */ | ||
3956 | + struct ccs_name_union name1; | ||
3957 | + struct ccs_name_union name2; | ||
3958 | +}; | ||
3959 | + | ||
3960 | +/* | ||
3961 | + * Structure for "file create", "file mkdir", "file mkfifo", "file mksock", | ||
3962 | + * "file ioctl", "file chmod", "file chown" and "file chgrp" directive. | ||
3963 | + */ | ||
3964 | +struct ccs_path_number_acl { | ||
3965 | + struct ccs_acl_info head; /* type = CCS_TYPE_PATH_NUMBER_ACL */ | ||
3966 | + struct ccs_name_union name; | ||
3967 | + struct ccs_number_union number; | ||
3968 | +}; | ||
3969 | + | ||
3970 | +/* Structure for "file mkblock" and "file mkchar" directive. */ | ||
3971 | +struct ccs_mkdev_acl { | ||
3972 | + struct ccs_acl_info head; /* type = CCS_TYPE_MKDEV_ACL */ | ||
3973 | + struct ccs_name_union name; | ||
3974 | + struct ccs_number_union mode; | ||
3975 | + struct ccs_number_union major; | ||
3976 | + struct ccs_number_union minor; | ||
3977 | +}; | ||
3978 | + | ||
3979 | +/* Structure for "file mount" directive. */ | ||
3980 | +struct ccs_mount_acl { | ||
3981 | + struct ccs_acl_info head; /* type = CCS_TYPE_MOUNT_ACL */ | ||
3982 | + struct ccs_name_union dev_name; | ||
3983 | + struct ccs_name_union dir_name; | ||
3984 | + struct ccs_name_union fs_type; | ||
3985 | + struct ccs_number_union flags; | ||
3986 | +}; | ||
3987 | + | ||
3988 | +/* Structure for "misc env" directive in domain policy. */ | ||
3989 | +struct ccs_env_acl { | ||
3990 | + struct ccs_acl_info head; /* type = CCS_TYPE_ENV_ACL */ | ||
3991 | + const struct ccs_path_info *env; /* environment variable */ | ||
3992 | +}; | ||
3993 | + | ||
3994 | +/* Structure for "capability" directive. */ | ||
3995 | +struct ccs_capability_acl { | ||
3996 | + struct ccs_acl_info head; /* type = CCS_TYPE_CAPABILITY_ACL */ | ||
3997 | + u8 operation; /* One of values in "enum ccs_capability_acl_index". */ | ||
3998 | +}; | ||
3999 | + | ||
4000 | +/* Structure for "ipc signal" directive. */ | ||
4001 | +struct ccs_signal_acl { | ||
4002 | + struct ccs_acl_info head; /* type = CCS_TYPE_SIGNAL_ACL */ | ||
4003 | + struct ccs_number_union sig; | ||
4004 | + /* Pointer to destination pattern. */ | ||
4005 | + const struct ccs_path_info *domainname; | ||
4006 | +}; | ||
4007 | + | ||
4008 | +/* Structure for "network inet" directive. */ | ||
4009 | +struct ccs_inet_acl { | ||
4010 | + struct ccs_acl_info head; /* type = CCS_TYPE_INET_ACL */ | ||
4011 | + u8 protocol; | ||
4012 | + struct ccs_ipaddr_union address; | ||
4013 | + struct ccs_number_union port; | ||
4014 | +}; | ||
4015 | + | ||
4016 | +/* Structure for "network unix" directive. */ | ||
4017 | +struct ccs_unix_acl { | ||
4018 | + struct ccs_acl_info head; /* type = CCS_TYPE_UNIX_ACL */ | ||
4019 | + u8 protocol; | ||
4020 | + struct ccs_name_union name; | ||
4021 | +}; | ||
4022 | + | ||
4023 | +/* Structure for holding string data. */ | ||
4024 | +struct ccs_name { | ||
4025 | + struct ccs_shared_acl_head head; | ||
4026 | + int size; /* Memory size allocated for this entry. */ | ||
4027 | + struct ccs_path_info entry; | ||
4028 | +}; | ||
4029 | + | ||
4030 | +/* Structure for holding a line from /proc/ccs/ interface. */ | ||
4031 | +struct ccs_acl_param { | ||
4032 | + char *data; /* Unprocessed data. */ | ||
4033 | + struct list_head *list; /* List to add or remove. */ | ||
4034 | + struct ccs_policy_namespace *ns; /* Namespace to use. */ | ||
4035 | + bool is_delete; /* True if it is a delete request. */ | ||
4036 | + union ccs_acl_union { | ||
4037 | + struct ccs_acl_info acl_info; | ||
4038 | + struct ccs_handler_acl handler_acl; | ||
4039 | + struct ccs_task_acl task_acl; | ||
4040 | + struct ccs_path_acl path_acl; | ||
4041 | + struct ccs_path2_acl path2_acl; | ||
4042 | + struct ccs_path_number_acl path_number_acl; | ||
4043 | + struct ccs_mkdev_acl mkdev_acl; | ||
4044 | + struct ccs_mount_acl mount_acl; | ||
4045 | + struct ccs_env_acl env_acl; | ||
4046 | + struct ccs_capability_acl capability_acl; | ||
4047 | + struct ccs_signal_acl signal_acl; | ||
4048 | + struct ccs_inet_acl inet_acl; | ||
4049 | + struct ccs_unix_acl unix_acl; | ||
4050 | + /**/ | ||
4051 | + struct ccs_acl_head acl_head; | ||
4052 | + struct ccs_transition_control transition_control; | ||
4053 | + struct ccs_aggregator aggregator; | ||
4054 | + struct ccs_reserved reserved; | ||
4055 | + struct ccs_manager manager; | ||
4056 | + struct ccs_path_group path_group; | ||
4057 | + struct ccs_number_group number_group; | ||
4058 | + struct ccs_address_group address_group; | ||
4059 | + } e; | ||
4060 | +}; | ||
4061 | + | ||
4062 | +/* Structure for reading/writing policy via /proc/ccs/ interfaces. */ | ||
4063 | +struct ccs_io_buffer { | ||
4064 | + /* Exclusive lock for this structure. */ | ||
4065 | + struct mutex io_sem; | ||
4066 | + char __user *read_user_buf; | ||
4067 | + size_t read_user_buf_avail; | ||
4068 | + struct { | ||
4069 | + struct list_head *ns; | ||
4070 | + struct list_head *domain; | ||
4071 | + struct list_head *group; | ||
4072 | + struct list_head *acl; | ||
4073 | + size_t avail; | ||
4074 | + unsigned int step; | ||
4075 | + unsigned int query_index; | ||
4076 | + u16 index; | ||
4077 | + u16 cond_index; | ||
4078 | + u8 acl_group_index; | ||
4079 | + u8 cond_step; | ||
4080 | + u8 bit; | ||
4081 | + u8 w_pos; | ||
4082 | + bool eof; | ||
4083 | + bool print_this_domain_only; | ||
4084 | + bool print_transition_related_only; | ||
4085 | + bool print_cond_part; | ||
4086 | + const char *w[CCS_MAX_IO_READ_QUEUE]; | ||
4087 | + } r; | ||
4088 | + struct { | ||
4089 | + struct ccs_policy_namespace *ns; | ||
4090 | + struct ccs_domain_info *domain; | ||
4091 | + size_t avail; | ||
4092 | + bool is_delete; | ||
4093 | + } w; | ||
4094 | + /* Buffer for reading. */ | ||
4095 | + char *read_buf; | ||
4096 | + /* Size of read buffer. */ | ||
4097 | + size_t readbuf_size; | ||
4098 | + /* Buffer for writing. */ | ||
4099 | + char *write_buf; | ||
4100 | + /* Size of write buffer. */ | ||
4101 | + size_t writebuf_size; | ||
4102 | + /* Type of interface. */ | ||
4103 | + enum ccs_proc_interface_index type; | ||
4104 | + /* Users counter protected by ccs_io_buffer_list_lock. */ | ||
4105 | + u8 users; | ||
4106 | + /* List for telling GC not to kfree() elements. */ | ||
4107 | + struct list_head list; | ||
4108 | +}; | ||
4109 | + | ||
4110 | +/* Structure for /proc/ccs/profile interface. */ | ||
4111 | +struct ccs_profile { | ||
4112 | + const struct ccs_path_info *comment; | ||
4113 | + u8 default_config; | ||
4114 | + u8 config[CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX]; | ||
4115 | + unsigned int pref[CCS_MAX_PREF]; | ||
4116 | +}; | ||
4117 | + | ||
4118 | +/* Structure for representing YYYY/MM/DD hh/mm/ss. */ | ||
4119 | +struct ccs_time { | ||
4120 | + u16 year; | ||
4121 | + u8 month; | ||
4122 | + u8 day; | ||
4123 | + u8 hour; | ||
4124 | + u8 min; | ||
4125 | + u8 sec; | ||
4126 | +}; | ||
4127 | + | ||
4128 | +/* Structure for policy namespace. */ | ||
4129 | +struct ccs_policy_namespace { | ||
4130 | + /* Profile table. Memory is allocated as needed. */ | ||
4131 | + struct ccs_profile *profile_ptr[CCS_MAX_PROFILES]; | ||
4132 | + /* List of "struct ccs_group". */ | ||
4133 | + struct list_head group_list[CCS_MAX_GROUP]; | ||
4134 | + /* List of policy. */ | ||
4135 | + struct list_head policy_list[CCS_MAX_POLICY]; | ||
4136 | + /* The global ACL referred by "use_group" keyword. */ | ||
4137 | + struct list_head acl_group[CCS_MAX_ACL_GROUPS]; | ||
4138 | + /* List for connecting to ccs_namespace_list list. */ | ||
4139 | + struct list_head namespace_list; | ||
4140 | + /* Profile version. Currently only 20150505 is supported. */ | ||
4141 | + unsigned int profile_version; | ||
4142 | + /* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */ | ||
4143 | + const char *name; | ||
4144 | +}; | ||
4145 | + | ||
4146 | +/* Prototype definition for "struct ccsecurity_operations". */ | ||
4147 | + | ||
4148 | +void __init ccs_permission_init(void); | ||
4149 | +void __init ccs_mm_init(void); | ||
4150 | + | ||
4151 | +/* Prototype definition for internal use. */ | ||
4152 | + | ||
4153 | +bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos, | ||
4154 | + struct ccs_page_dump *dump); | ||
4155 | +bool ccs_memory_ok(const void *ptr, const unsigned int size); | ||
4156 | +char *ccs_encode(const char *str); | ||
4157 | +char *ccs_encode2(const char *str, int str_len); | ||
4158 | +char *ccs_realpath(const struct path *path); | ||
4159 | +const char *ccs_get_exe(void); | ||
4160 | +const struct ccs_path_info *ccs_get_name(const char *name); | ||
4161 | +int ccs_audit_log(struct ccs_request_info *r); | ||
4162 | +int ccs_check_acl(struct ccs_request_info *r); | ||
4163 | +int ccs_init_request_info(struct ccs_request_info *r, const u8 index); | ||
4164 | +struct ccs_domain_info *ccs_assign_domain(const char *domainname, | ||
4165 | + const bool transit); | ||
4166 | +u8 ccs_get_config(const u8 profile, const u8 index); | ||
4167 | +void *ccs_commit_ok(void *data, const unsigned int size); | ||
4168 | +void ccs_del_acl(struct list_head *element); | ||
4169 | +void ccs_del_condition(struct list_head *element); | ||
4170 | +void ccs_fill_path_info(struct ccs_path_info *ptr); | ||
4171 | +void ccs_get_attributes(struct ccs_obj_info *obj); | ||
4172 | +void ccs_notify_gc(struct ccs_io_buffer *head, const bool is_register); | ||
4173 | +void ccs_transition_failed(const char *domainname); | ||
4174 | +void ccs_warn_oom(const char *function); | ||
4175 | +void ccs_write_log(struct ccs_request_info *r, const char *fmt, ...) | ||
4176 | + __printf(2, 3); | ||
4177 | + | ||
4178 | +/* Variable definition for internal use. */ | ||
4179 | + | ||
4180 | +extern bool ccs_policy_loaded; | ||
4181 | +extern const char * const ccs_dif[CCS_MAX_DOMAIN_INFO_FLAGS]; | ||
4182 | +extern const u8 ccs_c2mac[CCS_MAX_CAPABILITY_INDEX]; | ||
4183 | +extern const u8 ccs_pn2mac[CCS_MAX_PATH_NUMBER_OPERATION]; | ||
4184 | +extern const u8 ccs_pnnn2mac[CCS_MAX_MKDEV_OPERATION]; | ||
4185 | +extern const u8 ccs_pp2mac[CCS_MAX_PATH2_OPERATION]; | ||
4186 | +extern struct ccs_domain_info ccs_kernel_domain; | ||
4187 | +extern struct list_head ccs_condition_list; | ||
4188 | +extern struct list_head ccs_domain_list; | ||
4189 | +extern struct list_head ccs_name_list[CCS_MAX_HASH]; | ||
4190 | +extern struct list_head ccs_namespace_list; | ||
4191 | +extern struct mutex ccs_policy_lock; | ||
4192 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
4193 | +extern struct srcu_struct ccs_ss; | ||
4194 | +#endif | ||
4195 | +extern unsigned int ccs_memory_quota[CCS_MAX_MEMORY_STAT]; | ||
4196 | +extern unsigned int ccs_memory_used[CCS_MAX_MEMORY_STAT]; | ||
4197 | + | ||
4198 | +/* Inlined functions for internal use. */ | ||
4199 | + | ||
4200 | +/** | ||
4201 | + * ccs_pathcmp - strcmp() for "struct ccs_path_info" structure. | ||
4202 | + * | ||
4203 | + * @a: Pointer to "struct ccs_path_info". | ||
4204 | + * @b: Pointer to "struct ccs_path_info". | ||
4205 | + * | ||
4206 | + * Returns true if @a != @b, false otherwise. | ||
4207 | + */ | ||
4208 | +static inline bool ccs_pathcmp(const struct ccs_path_info *a, | ||
4209 | + const struct ccs_path_info *b) | ||
4210 | +{ | ||
4211 | + return a->hash != b->hash || strcmp(a->name, b->name); | ||
4212 | +} | ||
4213 | + | ||
4214 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
4215 | + | ||
4216 | +/** | ||
4217 | + * ccs_read_lock - Take lock for protecting policy. | ||
4218 | + * | ||
4219 | + * Returns index number for ccs_read_unlock(). | ||
4220 | + */ | ||
4221 | +static inline int ccs_read_lock(void) | ||
4222 | +{ | ||
4223 | + return srcu_read_lock(&ccs_ss); | ||
4224 | +} | ||
4225 | + | ||
4226 | +/** | ||
4227 | + * ccs_read_unlock - Release lock for protecting policy. | ||
4228 | + * | ||
4229 | + * @idx: Index number returned by ccs_read_lock(). | ||
4230 | + * | ||
4231 | + * Returns nothing. | ||
4232 | + */ | ||
4233 | +static inline void ccs_read_unlock(const int idx) | ||
4234 | +{ | ||
4235 | + srcu_read_unlock(&ccs_ss, idx); | ||
4236 | +} | ||
4237 | + | ||
4238 | +#else | ||
4239 | + | ||
4240 | +int ccs_lock(void); | ||
4241 | +void ccs_unlock(const int idx); | ||
4242 | + | ||
4243 | +/** | ||
4244 | + * ccs_read_lock - Take lock for protecting policy. | ||
4245 | + * | ||
4246 | + * Returns index number for ccs_read_unlock(). | ||
4247 | + */ | ||
4248 | +static inline int ccs_read_lock(void) | ||
4249 | +{ | ||
4250 | + return ccs_lock(); | ||
4251 | +} | ||
4252 | + | ||
4253 | +/** | ||
4254 | + * ccs_read_unlock - Release lock for protecting policy. | ||
4255 | + * | ||
4256 | + * @idx: Index number returned by ccs_read_lock(). | ||
4257 | + * | ||
4258 | + * Returns nothing. | ||
4259 | + */ | ||
4260 | +static inline void ccs_read_unlock(const int idx) | ||
4261 | +{ | ||
4262 | + ccs_unlock(idx); | ||
4263 | +} | ||
4264 | + | ||
4265 | +#endif | ||
4266 | + | ||
4267 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) | ||
4268 | + | ||
4269 | +/** | ||
4270 | + * ccs_tasklist_lock - Take lock for reading list of "struct task_struct". | ||
4271 | + * | ||
4272 | + * Returns nothing. | ||
4273 | + */ | ||
4274 | +static inline void ccs_tasklist_lock(void) | ||
4275 | +{ | ||
4276 | + rcu_read_lock(); | ||
4277 | +} | ||
4278 | + | ||
4279 | +/** | ||
4280 | + * ccs_tasklist_unlock - Release lock for reading list of "struct task_struct". | ||
4281 | + * | ||
4282 | + * Returns nothing. | ||
4283 | + */ | ||
4284 | +static inline void ccs_tasklist_unlock(void) | ||
4285 | +{ | ||
4286 | + rcu_read_unlock(); | ||
4287 | +} | ||
4288 | + | ||
4289 | +#else | ||
4290 | + | ||
4291 | +/** | ||
4292 | + * ccs_tasklist_lock - Take lock for reading list of "struct task_struct". | ||
4293 | + * | ||
4294 | + * Returns nothing. | ||
4295 | + */ | ||
4296 | +static inline void ccs_tasklist_lock(void) | ||
4297 | +{ | ||
4298 | + read_lock(&tasklist_lock); | ||
4299 | +} | ||
4300 | + | ||
4301 | +/** | ||
4302 | + * ccs_tasklist_unlock - Release lock for reading list of "struct task_struct". | ||
4303 | + * | ||
4304 | + * Returns nothing. | ||
4305 | + */ | ||
4306 | +static inline void ccs_tasklist_unlock(void) | ||
4307 | +{ | ||
4308 | + read_unlock(&tasklist_lock); | ||
4309 | +} | ||
4310 | + | ||
4311 | +#endif | ||
4312 | + | ||
4313 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | ||
4314 | + | ||
4315 | +/** | ||
4316 | + * ccs_sys_getppid - Copy of getppid(). | ||
4317 | + * | ||
4318 | + * Returns parent process's PID. | ||
4319 | + * | ||
4320 | + * Alpha does not have getppid() defined. To be able to build this module on | ||
4321 | + * Alpha, I have to copy getppid() from kernel/timer.c. | ||
4322 | + */ | ||
4323 | +static inline pid_t ccs_sys_getppid(void) | ||
4324 | +{ | ||
4325 | + pid_t pid; | ||
4326 | + rcu_read_lock(); | ||
4327 | + pid = task_tgid_vnr(rcu_dereference(current->real_parent)); | ||
4328 | + rcu_read_unlock(); | ||
4329 | + return pid; | ||
4330 | +} | ||
4331 | + | ||
4332 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) | ||
4333 | + | ||
4334 | +/** | ||
4335 | + * ccs_sys_getppid - Copy of getppid(). | ||
4336 | + * | ||
4337 | + * Returns parent process's PID. | ||
4338 | + * | ||
4339 | + * This function was rewritten to use RCU in 2.6.16.34. However, distributors | ||
4340 | + * which use earlier kernels (e.g. 2.6.8/2.6.9) did not backport the bugfix. | ||
4341 | + * Therefore, I'm using code for 2.6.16.34 for earlier kernels. | ||
4342 | + */ | ||
4343 | +static inline pid_t ccs_sys_getppid(void) | ||
4344 | +{ | ||
4345 | + pid_t pid; | ||
4346 | + rcu_read_lock(); | ||
4347 | +#if (defined(RHEL_MAJOR) && RHEL_MAJOR == 5) || (defined(AX_MAJOR) && AX_MAJOR == 3) | ||
4348 | + pid = rcu_dereference(current->parent)->tgid; | ||
4349 | +#elif defined(CONFIG_UTRACE) | ||
4350 | + /* | ||
4351 | + * RHEL 5.0 kernel does not have RHEL_MAJOR/RHEL_MINOR defined. | ||
4352 | + * Assume RHEL 5.0 if CONFIG_UTRACE is defined. | ||
4353 | + */ | ||
4354 | + pid = rcu_dereference(current->parent)->tgid; | ||
4355 | +#else | ||
4356 | + pid = rcu_dereference(current->real_parent)->tgid; | ||
4357 | +#endif | ||
4358 | + rcu_read_unlock(); | ||
4359 | + return pid; | ||
4360 | +} | ||
4361 | + | ||
4362 | +#else | ||
4363 | + | ||
4364 | +/** | ||
4365 | + * ccs_sys_getppid - Copy of getppid(). | ||
4366 | + * | ||
4367 | + * Returns parent process's PID. | ||
4368 | + * | ||
4369 | + * I can't use code for 2.6.16.34 for 2.4 kernels because 2.4 kernels does not | ||
4370 | + * have RCU. Therefore, I'm using pessimistic lock (i.e. tasklist_lock | ||
4371 | + * spinlock). | ||
4372 | + */ | ||
4373 | +static inline pid_t ccs_sys_getppid(void) | ||
4374 | +{ | ||
4375 | + pid_t pid; | ||
4376 | + read_lock(&tasklist_lock); | ||
4377 | +#ifdef TASK_DEAD | ||
4378 | + pid = current->group_leader->real_parent->tgid; | ||
4379 | +#else | ||
4380 | + pid = current->p_opptr->pid; | ||
4381 | +#endif | ||
4382 | + read_unlock(&tasklist_lock); | ||
4383 | + return pid; | ||
4384 | +} | ||
4385 | + | ||
4386 | +#endif | ||
4387 | + | ||
4388 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | ||
4389 | + | ||
4390 | +/** | ||
4391 | + * ccs_sys_getpid - Copy of getpid(). | ||
4392 | + * | ||
4393 | + * Returns current thread's PID. | ||
4394 | + * | ||
4395 | + * Alpha does not have getpid() defined. To be able to build this module on | ||
4396 | + * Alpha, I have to copy getpid() from kernel/timer.c. | ||
4397 | + */ | ||
4398 | +static inline pid_t ccs_sys_getpid(void) | ||
4399 | +{ | ||
4400 | + return task_tgid_vnr(current); | ||
4401 | +} | ||
4402 | + | ||
4403 | +#else | ||
4404 | + | ||
4405 | +/** | ||
4406 | + * ccs_sys_getpid - Copy of getpid(). | ||
4407 | + * | ||
4408 | + * Returns current thread's PID. | ||
4409 | + */ | ||
4410 | +static inline pid_t ccs_sys_getpid(void) | ||
4411 | +{ | ||
4412 | + return current->tgid; | ||
4413 | +} | ||
4414 | + | ||
4415 | +#endif | ||
4416 | + | ||
4417 | +/** | ||
4418 | + * ccs_get_mode - Get mode for specified functionality. | ||
4419 | + * | ||
4420 | + * @profile: Profile number. | ||
4421 | + * @index: Functionality number. | ||
4422 | + * | ||
4423 | + * Returns mode. | ||
4424 | + */ | ||
4425 | +static inline u8 ccs_get_mode(const u8 profile, const u8 index) | ||
4426 | +{ | ||
4427 | + return ccs_get_config(profile, index) & (CCS_CONFIG_MAX_MODE - 1); | ||
4428 | +} | ||
4429 | + | ||
4430 | +#if defined(CONFIG_SLOB) | ||
4431 | + | ||
4432 | +/** | ||
4433 | + * ccs_round2 - Round up to power of 2 for calculating memory usage. | ||
4434 | + * | ||
4435 | + * @size: Size to be rounded up. | ||
4436 | + * | ||
4437 | + * Returns @size. | ||
4438 | + * | ||
4439 | + * Since SLOB does not round up, this function simply returns @size. | ||
4440 | + */ | ||
4441 | +static inline int ccs_round2(size_t size) | ||
4442 | +{ | ||
4443 | + return size; | ||
4444 | +} | ||
4445 | + | ||
4446 | +#else | ||
4447 | + | ||
4448 | +/** | ||
4449 | + * ccs_round2 - Round up to power of 2 for calculating memory usage. | ||
4450 | + * | ||
4451 | + * @size: Size to be rounded up. | ||
4452 | + * | ||
4453 | + * Returns rounded size. | ||
4454 | + * | ||
4455 | + * Strictly speaking, SLAB may be able to allocate (e.g.) 96 bytes instead of | ||
4456 | + * (e.g.) 128 bytes. | ||
4457 | + */ | ||
4458 | +static inline int ccs_round2(size_t size) | ||
4459 | +{ | ||
4460 | +#if PAGE_SIZE == 4096 | ||
4461 | + size_t bsize = 32; | ||
4462 | +#else | ||
4463 | + size_t bsize = 64; | ||
4464 | +#endif | ||
4465 | + if (!size) | ||
4466 | + return 0; | ||
4467 | + while (size > bsize) | ||
4468 | + bsize <<= 1; | ||
4469 | + return bsize; | ||
4470 | +} | ||
4471 | + | ||
4472 | +#endif | ||
4473 | + | ||
4474 | +/** | ||
4475 | + * ccs_put_condition - Drop reference on "struct ccs_condition". | ||
4476 | + * | ||
4477 | + * @cond: Pointer to "struct ccs_condition". Maybe NULL. | ||
4478 | + * | ||
4479 | + * Returns nothing. | ||
4480 | + */ | ||
4481 | +static inline void ccs_put_condition(struct ccs_condition *cond) | ||
4482 | +{ | ||
4483 | + if (cond) | ||
4484 | + atomic_dec(&cond->head.users); | ||
4485 | +} | ||
4486 | + | ||
4487 | +/** | ||
4488 | + * ccs_put_group - Drop reference on "struct ccs_group". | ||
4489 | + * | ||
4490 | + * @group: Pointer to "struct ccs_group". Maybe NULL. | ||
4491 | + * | ||
4492 | + * Returns nothing. | ||
4493 | + */ | ||
4494 | +static inline void ccs_put_group(struct ccs_group *group) | ||
4495 | +{ | ||
4496 | + if (group) | ||
4497 | + atomic_dec(&group->head.users); | ||
4498 | +} | ||
4499 | + | ||
4500 | +/** | ||
4501 | + * ccs_put_name - Drop reference on "struct ccs_name". | ||
4502 | + * | ||
4503 | + * @name: Pointer to "struct ccs_path_info". Maybe NULL. | ||
4504 | + * | ||
4505 | + * Returns nothing. | ||
4506 | + */ | ||
4507 | +static inline void ccs_put_name(const struct ccs_path_info *name) | ||
4508 | +{ | ||
4509 | + if (name) | ||
4510 | + atomic_dec(&container_of(name, struct ccs_name, entry)-> | ||
4511 | + head.users); | ||
4512 | +} | ||
4513 | + | ||
4514 | +/* For importing variables and functions. */ | ||
4515 | +extern const struct ccsecurity_exports ccsecurity_exports; | ||
4516 | + | ||
4517 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | ||
4518 | + | ||
4519 | +/* | ||
4520 | + * Structure for holding "struct ccs_domain_info *" and "struct ccs_execve *" | ||
4521 | + * and "u32 ccs_flags" for each "struct task_struct". | ||
4522 | + * | ||
4523 | + * "struct ccs_domain_info *" and "u32 ccs_flags" for each "struct task_struct" | ||
4524 | + * are maintained outside that "struct task_struct". Therefore, ccs_security | ||
4525 | + * != task_struct . This keeps KABI for distributor's prebuilt kernels but | ||
4526 | + * entails slow access. | ||
4527 | + * | ||
4528 | + * Memory for this structure is allocated when current thread tries to access | ||
4529 | + * it. Therefore, if memory allocation failed, current thread will be killed by | ||
4530 | + * SIGKILL. Note that if current->pid == 1, sending SIGKILL won't work. | ||
4531 | + */ | ||
4532 | +struct ccs_security { | ||
4533 | + struct list_head list; | ||
4534 | + const struct task_struct *task; | ||
4535 | + struct ccs_domain_info *ccs_domain_info; | ||
4536 | + u32 ccs_flags; | ||
4537 | + struct rcu_head rcu; | ||
4538 | +}; | ||
4539 | + | ||
4540 | +#define CCS_TASK_SECURITY_HASH_BITS 12 | ||
4541 | +#define CCS_MAX_TASK_SECURITY_HASH (1u << CCS_TASK_SECURITY_HASH_BITS) | ||
4542 | +extern struct list_head ccs_task_security_list[CCS_MAX_TASK_SECURITY_HASH]; | ||
4543 | + | ||
4544 | +struct ccs_security *ccs_find_task_security(const struct task_struct *task); | ||
4545 | + | ||
4546 | +/** | ||
4547 | + * ccs_current_security - Get "struct ccs_security" for current thread. | ||
4548 | + * | ||
4549 | + * Returns pointer to "struct ccs_security" for current thread. | ||
4550 | + */ | ||
4551 | +static inline struct ccs_security *ccs_current_security(void) | ||
4552 | +{ | ||
4553 | + return ccs_find_task_security(current); | ||
4554 | +} | ||
4555 | + | ||
4556 | +/** | ||
4557 | + * ccs_task_domain - Get "struct ccs_domain_info" for specified thread. | ||
4558 | + * | ||
4559 | + * @task: Pointer to "struct task_struct". | ||
4560 | + * | ||
4561 | + * Returns pointer to "struct ccs_security" for specified thread. | ||
4562 | + */ | ||
4563 | +static inline struct ccs_domain_info *ccs_task_domain(struct task_struct *task) | ||
4564 | +{ | ||
4565 | + struct ccs_domain_info *domain; | ||
4566 | + rcu_read_lock(); | ||
4567 | + domain = ccs_find_task_security(task)->ccs_domain_info; | ||
4568 | + rcu_read_unlock(); | ||
4569 | + return domain; | ||
4570 | +} | ||
4571 | + | ||
4572 | +/** | ||
4573 | + * ccs_current_domain - Get "struct ccs_domain_info" for current thread. | ||
4574 | + * | ||
4575 | + * Returns pointer to "struct ccs_domain_info" for current thread. | ||
4576 | + */ | ||
4577 | +static inline struct ccs_domain_info *ccs_current_domain(void) | ||
4578 | +{ | ||
4579 | + return ccs_find_task_security(current)->ccs_domain_info; | ||
4580 | +} | ||
4581 | + | ||
4582 | +/** | ||
4583 | + * ccs_task_flags - Get flags for specified thread. | ||
4584 | + * | ||
4585 | + * @task: Pointer to "struct task_struct". | ||
4586 | + * | ||
4587 | + * Returns flags for specified thread. | ||
4588 | + */ | ||
4589 | +static inline u32 ccs_task_flags(struct task_struct *task) | ||
4590 | +{ | ||
4591 | + u32 ccs_flags; | ||
4592 | + rcu_read_lock(); | ||
4593 | + ccs_flags = ccs_find_task_security(task)->ccs_flags; | ||
4594 | + rcu_read_unlock(); | ||
4595 | + return ccs_flags; | ||
4596 | +} | ||
4597 | + | ||
4598 | +/** | ||
4599 | + * ccs_current_flags - Get flags for current thread. | ||
4600 | + * | ||
4601 | + * Returns flags for current thread. | ||
4602 | + */ | ||
4603 | +static inline u32 ccs_current_flags(void) | ||
4604 | +{ | ||
4605 | + return ccs_find_task_security(current)->ccs_flags; | ||
4606 | +} | ||
4607 | + | ||
4608 | +#else | ||
4609 | + | ||
4610 | +/* | ||
4611 | + * "struct ccs_domain_info *" and "u32 ccs_flags" for each "struct task_struct" | ||
4612 | + * are maintained inside that "struct task_struct". Therefore, ccs_security == | ||
4613 | + * task_struct . This allows fast access but breaks KABI checks for | ||
4614 | + * distributor's prebuilt kernels due to changes in "struct task_struct". | ||
4615 | + */ | ||
4616 | +#define ccs_security task_struct | ||
4617 | + | ||
4618 | +/** | ||
4619 | + * ccs_find_task_security - Find "struct ccs_security" for given task. | ||
4620 | + * | ||
4621 | + * @task: Pointer to "struct task_struct". | ||
4622 | + * | ||
4623 | + * Returns pointer to "struct ccs_security". | ||
4624 | + */ | ||
4625 | +static inline struct ccs_security *ccs_find_task_security(struct task_struct * | ||
4626 | + task) | ||
4627 | +{ | ||
4628 | + return task; | ||
4629 | +} | ||
4630 | + | ||
4631 | +/** | ||
4632 | + * ccs_current_security - Get "struct ccs_security" for current thread. | ||
4633 | + * | ||
4634 | + * Returns pointer to "struct ccs_security" for current thread. | ||
4635 | + */ | ||
4636 | +static inline struct ccs_security *ccs_current_security(void) | ||
4637 | +{ | ||
4638 | + return ccs_find_task_security(current); | ||
4639 | +} | ||
4640 | + | ||
4641 | +/** | ||
4642 | + * ccs_task_domain - Get "struct ccs_domain_info" for specified thread. | ||
4643 | + * | ||
4644 | + * @task: Pointer to "struct task_struct". | ||
4645 | + * | ||
4646 | + * Returns pointer to "struct ccs_security" for specified thread. | ||
4647 | + */ | ||
4648 | +static inline struct ccs_domain_info *ccs_task_domain(struct task_struct *task) | ||
4649 | +{ | ||
4650 | + struct ccs_domain_info *domain = task->ccs_domain_info; | ||
4651 | + return domain ? domain : &ccs_kernel_domain; | ||
4652 | +} | ||
4653 | + | ||
4654 | +/** | ||
4655 | + * ccs_current_domain - Get "struct ccs_domain_info" for current thread. | ||
4656 | + * | ||
4657 | + * Returns pointer to "struct ccs_domain_info" for current thread. | ||
4658 | + * | ||
4659 | + * If current thread does not belong to a domain (which is true for initial | ||
4660 | + * init_task in order to hide ccs_kernel_domain from this module), | ||
4661 | + * current thread enters into ccs_kernel_domain. | ||
4662 | + */ | ||
4663 | +static inline struct ccs_domain_info *ccs_current_domain(void) | ||
4664 | +{ | ||
4665 | + struct task_struct *task = current; | ||
4666 | + if (!task->ccs_domain_info) | ||
4667 | + task->ccs_domain_info = &ccs_kernel_domain; | ||
4668 | + return task->ccs_domain_info; | ||
4669 | +} | ||
4670 | + | ||
4671 | +/** | ||
4672 | + * ccs_task_flags - Get flags for specified thread. | ||
4673 | + * | ||
4674 | + * @task: Pointer to "struct task_struct". | ||
4675 | + * | ||
4676 | + * Returns flags for specified thread. | ||
4677 | + */ | ||
4678 | +static inline u32 ccs_task_flags(struct task_struct *task) | ||
4679 | +{ | ||
4680 | + return ccs_find_task_security(task)->ccs_flags; | ||
4681 | +} | ||
4682 | + | ||
4683 | +/** | ||
4684 | + * ccs_current_flags - Get flags for current thread. | ||
4685 | + * | ||
4686 | + * Returns flags for current thread. | ||
4687 | + */ | ||
4688 | +static inline u32 ccs_current_flags(void) | ||
4689 | +{ | ||
4690 | + return ccs_find_task_security(current)->ccs_flags; | ||
4691 | +} | ||
4692 | + | ||
4693 | +#endif | ||
4694 | + | ||
4695 | +/** | ||
4696 | + * ccs_current_namespace - Get "struct ccs_policy_namespace" for current thread. | ||
4697 | + * | ||
4698 | + * Returns pointer to "struct ccs_policy_namespace" for current thread. | ||
4699 | + */ | ||
4700 | +static inline struct ccs_policy_namespace *ccs_current_namespace(void) | ||
4701 | +{ | ||
4702 | + return ccs_current_domain()->ns; | ||
4703 | +} | ||
4704 | + | ||
4705 | +#endif | ||
4706 | diff --git a/security/ccsecurity/load_policy.c b/security/ccsecurity/load_policy.c | ||
4707 | new file mode 100644 | ||
4708 | index 0000000..fc4ae30 | ||
4709 | --- /dev/null | ||
4710 | +++ b/security/ccsecurity/load_policy.c | ||
4711 | @@ -0,0 +1,352 @@ | ||
4712 | +/* | ||
4713 | + * security/ccsecurity/load_policy.c | ||
4714 | + * | ||
4715 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | ||
4716 | + * | ||
4717 | + * Version: 1.8.4 2015/05/05 | ||
4718 | + */ | ||
4719 | + | ||
4720 | +#include <linux/version.h> | ||
4721 | +#include <linux/module.h> | ||
4722 | +#include <linux/init.h> | ||
4723 | +#include <linux/binfmts.h> | ||
4724 | +#include <linux/sched.h> | ||
4725 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
4726 | +#include <linux/kmod.h> | ||
4727 | +/* | ||
4728 | + * Regarding 2.4 kernels, we need to define __KERNEL_SYSCALLS__ in order to use | ||
4729 | + * waitpid() because call_usermodehelper() does not support UMH_WAIT_PROC. | ||
4730 | + */ | ||
4731 | +#define __KERNEL_SYSCALLS__ | ||
4732 | +#include <linux/unistd.h> | ||
4733 | +#else | ||
4734 | +#include <linux/fs.h> | ||
4735 | +#include <linux/namei.h> | ||
4736 | +#endif | ||
4737 | +#ifndef LOOKUP_POSITIVE | ||
4738 | +#define LOOKUP_POSITIVE 0 | ||
4739 | +#endif | ||
4740 | + | ||
4741 | +/* | ||
4742 | + * TOMOYO specific part start. | ||
4743 | + */ | ||
4744 | + | ||
4745 | +#include <linux/ccsecurity.h> | ||
4746 | + | ||
4747 | +/** | ||
4748 | + * ccs_setup - Set enable/disable upon boot. | ||
4749 | + * | ||
4750 | + * @str: "off" to disable, "on" to enable. | ||
4751 | + * | ||
4752 | + * Returns 0. | ||
4753 | + */ | ||
4754 | +static int __init ccs_setup(char *str) | ||
4755 | +{ | ||
4756 | + if (!strcmp(str, "off")) | ||
4757 | + ccsecurity_ops.disabled = 1; | ||
4758 | + else if (!strcmp(str, "on")) | ||
4759 | + ccsecurity_ops.disabled = 0; | ||
4760 | + return 0; | ||
4761 | +} | ||
4762 | + | ||
4763 | +__setup("ccsecurity=", ccs_setup); | ||
4764 | + | ||
4765 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) | ||
4766 | +#include "lsm2ccsecurity.c" | ||
4767 | +#endif | ||
4768 | + | ||
4769 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | ||
4770 | + | ||
4771 | +/* Path to the policy loader. (default = CONFIG_CCSECURITY_POLICY_LOADER) */ | ||
4772 | +static const char *ccs_loader; | ||
4773 | + | ||
4774 | +/** | ||
4775 | + * ccs_loader_setup - Set policy loader. | ||
4776 | + * | ||
4777 | + * @str: Program to use as a policy loader (e.g. /sbin/ccs-init ). | ||
4778 | + * | ||
4779 | + * Returns 0. | ||
4780 | + */ | ||
4781 | +static int __init ccs_loader_setup(char *str) | ||
4782 | +{ | ||
4783 | + ccs_loader = str; | ||
4784 | + return 0; | ||
4785 | +} | ||
4786 | + | ||
4787 | +__setup("CCS_loader=", ccs_loader_setup); | ||
4788 | + | ||
4789 | +/** | ||
4790 | + * ccs_policy_loader_exists - Check whether /sbin/ccs-init exists. | ||
4791 | + * | ||
4792 | + * Returns true if /sbin/ccs-init exists, false otherwise. | ||
4793 | + */ | ||
4794 | +static _Bool ccs_policy_loader_exists(void) | ||
4795 | +{ | ||
4796 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) | ||
4797 | + struct path path; | ||
4798 | + if (!ccs_loader) | ||
4799 | + ccs_loader = CONFIG_CCSECURITY_POLICY_LOADER; | ||
4800 | + if (kern_path(ccs_loader, LOOKUP_FOLLOW | LOOKUP_POSITIVE, | ||
4801 | + &path) == 0) { | ||
4802 | + path_put(&path); | ||
4803 | + return 1; | ||
4804 | + } | ||
4805 | +#else | ||
4806 | + struct nameidata nd; | ||
4807 | + if (!ccs_loader) | ||
4808 | + ccs_loader = CONFIG_CCSECURITY_POLICY_LOADER; | ||
4809 | + if (path_lookup(ccs_loader, LOOKUP_FOLLOW | LOOKUP_POSITIVE, | ||
4810 | + &nd) == 0) { | ||
4811 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) | ||
4812 | + path_put(&nd.path); | ||
4813 | +#else | ||
4814 | + path_release(&nd); | ||
4815 | +#endif | ||
4816 | + return 1; | ||
4817 | + } | ||
4818 | +#endif | ||
4819 | + printk(KERN_INFO "Not activating Mandatory Access Control " | ||
4820 | + "as %s does not exist.\n", ccs_loader); | ||
4821 | + return 0; | ||
4822 | +} | ||
4823 | + | ||
4824 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
4825 | + | ||
4826 | +/** | ||
4827 | + * ccs_run_loader - Start /sbin/ccs-init. | ||
4828 | + * | ||
4829 | + * @unused: Not used. | ||
4830 | + * | ||
4831 | + * Returns PID of /sbin/ccs-init on success, negative value otherwise. | ||
4832 | + */ | ||
4833 | +static int ccs_run_loader(void *unused) | ||
4834 | +{ | ||
4835 | + char *argv[2]; | ||
4836 | + char *envp[3]; | ||
4837 | + printk(KERN_INFO "Calling %s to load policy. Please wait.\n", | ||
4838 | + ccs_loader); | ||
4839 | + argv[0] = (char *) ccs_loader; | ||
4840 | + argv[1] = NULL; | ||
4841 | + envp[0] = "HOME=/"; | ||
4842 | + envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | ||
4843 | + envp[2] = NULL; | ||
4844 | + return exec_usermodehelper(argv[0], argv, envp); | ||
4845 | +} | ||
4846 | + | ||
4847 | +#endif | ||
4848 | + | ||
4849 | +/* Path to the trigger. (default = CONFIG_CCSECURITY_ACTIVATION_TRIGGER) */ | ||
4850 | +static const char *ccs_trigger; | ||
4851 | + | ||
4852 | +/** | ||
4853 | + * ccs_trigger_setup - Set trigger for activation. | ||
4854 | + * | ||
4855 | + * @str: Program to use as an activation trigger (e.g. /sbin/init ). | ||
4856 | + * | ||
4857 | + * Returns 0. | ||
4858 | + */ | ||
4859 | +static int __init ccs_trigger_setup(char *str) | ||
4860 | +{ | ||
4861 | + ccs_trigger = str; | ||
4862 | + return 0; | ||
4863 | +} | ||
4864 | + | ||
4865 | +__setup("CCS_trigger=", ccs_trigger_setup); | ||
4866 | + | ||
4867 | +/** | ||
4868 | + * ccs_load_policy - Run external policy loader to load policy. | ||
4869 | + * | ||
4870 | + * @filename: The program about to start. | ||
4871 | + * | ||
4872 | + * Returns nothing. | ||
4873 | + * | ||
4874 | + * This function checks whether @filename is /sbin/init, and if so | ||
4875 | + * invoke /sbin/ccs-init and wait for the termination of /sbin/ccs-init | ||
4876 | + * and then continues invocation of /sbin/init. | ||
4877 | + * /sbin/ccs-init reads policy files in /etc/ccs/ directory and | ||
4878 | + * writes to /proc/ccs/ interfaces. | ||
4879 | + */ | ||
4880 | +static void ccs_load_policy(const char *filename) | ||
4881 | +{ | ||
4882 | + static _Bool done; | ||
4883 | + if (ccsecurity_ops.disabled || done) | ||
4884 | + return; | ||
4885 | + if (!ccs_trigger) | ||
4886 | + ccs_trigger = CONFIG_CCSECURITY_ACTIVATION_TRIGGER; | ||
4887 | + if (strcmp(filename, ccs_trigger)) | ||
4888 | + return; | ||
4889 | + if (!ccs_policy_loader_exists()) | ||
4890 | + return; | ||
4891 | + done = 1; | ||
4892 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) | ||
4893 | + { | ||
4894 | + char *argv[2]; | ||
4895 | + char *envp[3]; | ||
4896 | + printk(KERN_INFO "Calling %s to load policy. Please wait.\n", | ||
4897 | + ccs_loader); | ||
4898 | + argv[0] = (char *) ccs_loader; | ||
4899 | + argv[1] = NULL; | ||
4900 | + envp[0] = "HOME=/"; | ||
4901 | + envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | ||
4902 | + envp[2] = NULL; | ||
4903 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) || defined(UMH_WAIT_PROC) | ||
4904 | + call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); | ||
4905 | +#else | ||
4906 | + call_usermodehelper(argv[0], argv, envp, 1); | ||
4907 | +#endif | ||
4908 | + } | ||
4909 | +#elif defined(TASK_DEAD) | ||
4910 | + { | ||
4911 | + /* Copied from kernel/kmod.c */ | ||
4912 | + struct task_struct *task = current; | ||
4913 | + pid_t pid = kernel_thread(ccs_run_loader, NULL, 0); | ||
4914 | + sigset_t tmpsig; | ||
4915 | + spin_lock_irq(&task->sighand->siglock); | ||
4916 | + tmpsig = task->blocked; | ||
4917 | + siginitsetinv(&task->blocked, | ||
4918 | + sigmask(SIGKILL) | sigmask(SIGSTOP)); | ||
4919 | + recalc_sigpending(); | ||
4920 | + spin_unlock_irq(&task->sighand->siglock); | ||
4921 | + if (pid >= 0) | ||
4922 | + waitpid(pid, NULL, __WCLONE); | ||
4923 | + spin_lock_irq(&task->sighand->siglock); | ||
4924 | + task->blocked = tmpsig; | ||
4925 | + recalc_sigpending(); | ||
4926 | + spin_unlock_irq(&task->sighand->siglock); | ||
4927 | + } | ||
4928 | +#else | ||
4929 | + { | ||
4930 | + /* Copied from kernel/kmod.c */ | ||
4931 | + struct task_struct *task = current; | ||
4932 | + pid_t pid = kernel_thread(ccs_run_loader, NULL, 0); | ||
4933 | + sigset_t tmpsig; | ||
4934 | + spin_lock_irq(&task->sigmask_lock); | ||
4935 | + tmpsig = task->blocked; | ||
4936 | + siginitsetinv(&task->blocked, | ||
4937 | + sigmask(SIGKILL) | sigmask(SIGSTOP)); | ||
4938 | + recalc_sigpending(task); | ||
4939 | + spin_unlock_irq(&task->sigmask_lock); | ||
4940 | + if (pid >= 0) | ||
4941 | + waitpid(pid, NULL, __WCLONE); | ||
4942 | + spin_lock_irq(&task->sigmask_lock); | ||
4943 | + task->blocked = tmpsig; | ||
4944 | + recalc_sigpending(task); | ||
4945 | + spin_unlock_irq(&task->sigmask_lock); | ||
4946 | + } | ||
4947 | +#endif | ||
4948 | + if (ccsecurity_ops.check_profile) | ||
4949 | + ccsecurity_ops.check_profile(); | ||
4950 | + else | ||
4951 | + panic("Failed to load policy."); | ||
4952 | +} | ||
4953 | + | ||
4954 | +#endif | ||
4955 | + | ||
4956 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) | ||
4957 | + | ||
4958 | +/** | ||
4959 | + * __ccs_search_binary_handler - Load policy before calling search_binary_handler(). | ||
4960 | + * | ||
4961 | + * @bprm: Pointer to "struct linux_binprm". | ||
4962 | + * | ||
4963 | + * Returns 0 on success, negative value otherwise. | ||
4964 | + */ | ||
4965 | +static int __ccs_search_binary_handler(struct linux_binprm *bprm) | ||
4966 | +{ | ||
4967 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | ||
4968 | + ccs_load_policy(bprm->filename); | ||
4969 | +#endif | ||
4970 | + /* | ||
4971 | + * ccs_load_policy() executes /sbin/ccs-init if bprm->filename is | ||
4972 | + * /sbin/init. /sbin/ccs-init executes /etc/ccs/ccs-load-module to | ||
4973 | + * load loadable kernel module. The loadable kernel module modifies | ||
4974 | + * "struct ccsecurity_ops". Thus, we need to transfer control to | ||
4975 | + * __ccs_search_binary_handler() in security/ccsecurity/permission.c | ||
4976 | + * if "struct ccsecurity_ops" was modified. | ||
4977 | + */ | ||
4978 | + if (ccsecurity_ops.search_binary_handler | ||
4979 | + != __ccs_search_binary_handler) | ||
4980 | + return ccsecurity_ops.search_binary_handler(bprm); | ||
4981 | + return search_binary_handler(bprm); | ||
4982 | +} | ||
4983 | + | ||
4984 | +#else | ||
4985 | + | ||
4986 | +/** | ||
4987 | + * __ccs_search_binary_handler - Load policy before calling search_binary_handler(). | ||
4988 | + * | ||
4989 | + * @bprm: Pointer to "struct linux_binprm". | ||
4990 | + * @regs: Pointer to "struct pt_regs". | ||
4991 | + * | ||
4992 | + * Returns 0 on success, negative value otherwise. | ||
4993 | + */ | ||
4994 | +static int __ccs_search_binary_handler(struct linux_binprm *bprm, | ||
4995 | + struct pt_regs *regs) | ||
4996 | +{ | ||
4997 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | ||
4998 | + ccs_load_policy(bprm->filename); | ||
4999 | +#endif | ||
5000 | + /* | ||
5001 | + * ccs_load_policy() executes /sbin/ccs-init if bprm->filename is | ||
5002 | + * /sbin/init. /sbin/ccs-init executes /etc/ccs/ccs-load-module to | ||
5003 | + * load loadable kernel module. The loadable kernel module modifies | ||
5004 | + * "struct ccsecurity_ops". Thus, we need to transfer control to | ||
5005 | + * __ccs_search_binary_handler() in security/ccsecurity/permission.c | ||
5006 | + * if "struct ccsecurity_ops" was modified. | ||
5007 | + */ | ||
5008 | + if (ccsecurity_ops.search_binary_handler | ||
5009 | + != __ccs_search_binary_handler) | ||
5010 | + return ccsecurity_ops.search_binary_handler(bprm, regs); | ||
5011 | + return search_binary_handler(bprm, regs); | ||
5012 | +} | ||
5013 | + | ||
5014 | +#endif | ||
5015 | + | ||
5016 | +/* | ||
5017 | + * Some exports for loadable kernel module part. | ||
5018 | + * | ||
5019 | + * Although scripts/checkpatch.pl complains about use of "extern" in C file, | ||
5020 | + * we don't put these into security/ccsecurity/internal.h because we want to | ||
5021 | + * split built-in part and loadable kernel module part. | ||
5022 | + */ | ||
5023 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) && LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) | ||
5024 | +extern spinlock_t vfsmount_lock; | ||
5025 | +#endif | ||
5026 | + | ||
5027 | +/* For exporting variables and functions. */ | ||
5028 | +const struct ccsecurity_exports ccsecurity_exports = { | ||
5029 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | ||
5030 | + .load_policy = ccs_load_policy, | ||
5031 | +#endif | ||
5032 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) && defined(CONFIG_SECURITY) | ||
5033 | + .add_hooks = ccs_add_hooks, | ||
5034 | +#endif | ||
5035 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) | ||
5036 | + .d_absolute_path = d_absolute_path, | ||
5037 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | ||
5038 | + .__d_path = __d_path, | ||
5039 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) | ||
5040 | + .vfsmount_lock = &vfsmount_lock, | ||
5041 | +#endif | ||
5042 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | ||
5043 | + .find_task_by_vpid = find_task_by_vpid, | ||
5044 | + .find_task_by_pid_ns = find_task_by_pid_ns, | ||
5045 | +#endif | ||
5046 | +}; | ||
5047 | +#ifdef CONFIG_CCSECURITY_LKM | ||
5048 | +/* Only ccsecurity module need to access this struct. */ | ||
5049 | +EXPORT_SYMBOL_GPL(ccsecurity_exports); | ||
5050 | +#endif | ||
5051 | + | ||
5052 | +/* Members are updated by loadable kernel module. */ | ||
5053 | +struct ccsecurity_operations ccsecurity_ops = { | ||
5054 | + .search_binary_handler = __ccs_search_binary_handler, | ||
5055 | +#ifdef CONFIG_CCSECURITY_DISABLE_BY_DEFAULT | ||
5056 | + .disabled = 1, | ||
5057 | +#endif | ||
5058 | +}; | ||
5059 | +/* | ||
5060 | + * Non-GPL modules might need to access this struct via inlined functions | ||
5061 | + * embedded into include/linux/security.h and include/net/ip.h | ||
5062 | + */ | ||
5063 | +EXPORT_SYMBOL(ccsecurity_ops); | ||
5064 | diff --git a/security/ccsecurity/lsm2ccsecurity.c b/security/ccsecurity/lsm2ccsecurity.c | ||
5065 | new file mode 100644 | ||
5066 | index 0000000..b81d8ed | ||
5067 | --- /dev/null | ||
5068 | +++ b/security/ccsecurity/lsm2ccsecurity.c | ||
5069 | @@ -0,0 +1,192 @@ | ||
5070 | +/* | ||
5071 | + * security/ccsecurity/lsm2ccsecurity.c | ||
5072 | + * | ||
5073 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | ||
5074 | + * | ||
5075 | + * Version: 1.8.4 2015/07/11 | ||
5076 | + */ | ||
5077 | + | ||
5078 | +#include <linux/path.h> | ||
5079 | +#include <linux/security.h> | ||
5080 | +#include <linux/ccsecurity.h> | ||
5081 | + | ||
5082 | +int ccs_settime(const struct timespec *ts, const struct timezone *tz) | ||
5083 | +{ | ||
5084 | + return ccs_capable(CCS_SYS_SETTIME) ? 0 : -EPERM; | ||
5085 | +} | ||
5086 | + | ||
5087 | +int ccs_sb_mount(const char *dev_name, struct path *path, const char *type, | ||
5088 | + unsigned long flags, void *data) | ||
5089 | +{ | ||
5090 | + return ccs_mount_permission(dev_name, path, type, flags, data); | ||
5091 | +} | ||
5092 | + | ||
5093 | +int ccs_sb_umount(struct vfsmount *mnt, int flags) | ||
5094 | +{ | ||
5095 | + return ccs_umount_permission(mnt, flags); | ||
5096 | +} | ||
5097 | + | ||
5098 | +int ccs_sb_pivotroot(struct path *old_path, struct path *new_path) | ||
5099 | +{ | ||
5100 | + return ccs_pivot_root_permission(old_path, new_path); | ||
5101 | +} | ||
5102 | + | ||
5103 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) | ||
5104 | +int ccs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) | ||
5105 | +{ | ||
5106 | + return ccs_getattr_permission(mnt, dentry); | ||
5107 | +} | ||
5108 | +#else | ||
5109 | +int ccs_inode_getattr(const struct path *path) | ||
5110 | +{ | ||
5111 | + return ccs_getattr_permission(path->mnt, path->dentry); | ||
5112 | +} | ||
5113 | +#endif | ||
5114 | + | ||
5115 | +int ccs_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
5116 | +{ | ||
5117 | + return ccs_ioctl_permission(file, cmd, arg); | ||
5118 | +} | ||
5119 | + | ||
5120 | +int ccs_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg) | ||
5121 | +{ | ||
5122 | + return ccs_fcntl_permission(file, cmd, arg); | ||
5123 | +} | ||
5124 | + | ||
5125 | +int ccs_file_open(struct file *file, const struct cred *cred) | ||
5126 | +{ | ||
5127 | + return ccs_open_permission(file); | ||
5128 | +} | ||
5129 | + | ||
5130 | +int ccs_socket_create(int family, int type, int protocol, int kern) | ||
5131 | +{ | ||
5132 | + return ccs_socket_create_permission(family, type, protocol); | ||
5133 | +} | ||
5134 | + | ||
5135 | +int ccs_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) | ||
5136 | +{ | ||
5137 | + return ccs_socket_bind_permission(sock, address, addrlen); | ||
5138 | +} | ||
5139 | + | ||
5140 | +int ccs_socket_connect(struct socket *sock, struct sockaddr *address, | ||
5141 | + int addrlen) | ||
5142 | +{ | ||
5143 | + return ccs_socket_connect_permission(sock, address, addrlen); | ||
5144 | +} | ||
5145 | + | ||
5146 | +int ccs_socket_listen(struct socket *sock, int backlog) | ||
5147 | +{ | ||
5148 | + return ccs_socket_listen_permission(sock); | ||
5149 | +} | ||
5150 | + | ||
5151 | +int ccs_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) | ||
5152 | +{ | ||
5153 | + return ccs_socket_sendmsg_permission(sock, msg, size); | ||
5154 | +} | ||
5155 | + | ||
5156 | +int ccs_path_unlink(struct path *dir, struct dentry *dentry) | ||
5157 | +{ | ||
5158 | + return ccs_unlink_permission(dentry, dir->mnt); | ||
5159 | +} | ||
5160 | + | ||
5161 | +int ccs_path_mkdir(struct path *dir, struct dentry *dentry, umode_t mode) | ||
5162 | +{ | ||
5163 | + return ccs_mkdir_permission(dentry, dir->mnt, mode); | ||
5164 | +} | ||
5165 | + | ||
5166 | +int ccs_path_rmdir(struct path *dir, struct dentry *dentry) | ||
5167 | +{ | ||
5168 | + return ccs_rmdir_permission(dentry, dir->mnt); | ||
5169 | +} | ||
5170 | + | ||
5171 | +int ccs_path_mknod(struct path *dir, struct dentry *dentry, umode_t mode, | ||
5172 | + unsigned int dev) | ||
5173 | +{ | ||
5174 | + return ccs_mknod_permission(dentry, dir->mnt, mode, dev); | ||
5175 | +} | ||
5176 | + | ||
5177 | +int ccs_path_truncate(struct path *path) | ||
5178 | +{ | ||
5179 | + return ccs_truncate_permission(path->dentry, path->mnt); | ||
5180 | +} | ||
5181 | + | ||
5182 | +int ccs_path_symlink(struct path *dir, struct dentry *dentry, | ||
5183 | + const char *old_name) | ||
5184 | +{ | ||
5185 | + return ccs_symlink_permission(dentry, dir->mnt, old_name); | ||
5186 | +} | ||
5187 | + | ||
5188 | +int ccs_path_link(struct dentry *old_dentry, struct path *new_dir, | ||
5189 | + struct dentry *new_dentry) | ||
5190 | +{ | ||
5191 | + return ccs_link_permission(old_dentry, new_dentry, new_dir->mnt); | ||
5192 | +} | ||
5193 | + | ||
5194 | +int ccs_path_rename(struct path *old_dir, struct dentry *old_dentry, | ||
5195 | + struct path *new_dir, struct dentry *new_dentry) | ||
5196 | +{ | ||
5197 | + return ccs_rename_permission(old_dentry, new_dentry, new_dir->mnt); | ||
5198 | +} | ||
5199 | + | ||
5200 | +int ccs_path_chmod(struct path *path, umode_t mode) | ||
5201 | +{ | ||
5202 | + return ccs_chmod_permission(path->dentry, path->mnt, mode); | ||
5203 | +} | ||
5204 | + | ||
5205 | +int ccs_path_chown(struct path *path, kuid_t uid, kgid_t gid) | ||
5206 | +{ | ||
5207 | + return ccs_chown_permission(path->dentry, path->mnt, uid, gid); | ||
5208 | +} | ||
5209 | + | ||
5210 | +int ccs_path_chroot(struct path *path) | ||
5211 | +{ | ||
5212 | + return ccs_chroot_permission(path); | ||
5213 | +} | ||
5214 | + | ||
5215 | +#if !defined(CONFIG_SECURITY_PATH) | ||
5216 | +EXPORT_SYMBOL(ccs_path_mkdir); | ||
5217 | +EXPORT_SYMBOL(ccs_path_mknod); | ||
5218 | +EXPORT_SYMBOL(ccs_path_unlink); | ||
5219 | +EXPORT_SYMBOL(ccs_path_rename); | ||
5220 | +#endif | ||
5221 | + | ||
5222 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) && defined(CONFIG_SECURITY) | ||
5223 | + | ||
5224 | +#include <linux/lsm_hooks.h> | ||
5225 | + | ||
5226 | +static struct security_hook_list ccsecurity_hooks[] = { | ||
5227 | + LSM_HOOK_INIT(settime, ccs_settime), | ||
5228 | + LSM_HOOK_INIT(sb_mount, ccs_sb_mount), | ||
5229 | + LSM_HOOK_INIT(sb_umount, ccs_sb_umount), | ||
5230 | + LSM_HOOK_INIT(sb_pivotroot, ccs_sb_pivotroot), | ||
5231 | + LSM_HOOK_INIT(inode_getattr, ccs_inode_getattr), | ||
5232 | + LSM_HOOK_INIT(file_ioctl, ccs_file_ioctl), | ||
5233 | + LSM_HOOK_INIT(file_fcntl, ccs_file_fcntl), | ||
5234 | + LSM_HOOK_INIT(file_open, ccs_file_open), | ||
5235 | +#if defined(CONFIG_SECURITY_NETWORK) | ||
5236 | + LSM_HOOK_INIT(socket_create, ccs_socket_create), | ||
5237 | + LSM_HOOK_INIT(socket_bind, ccs_socket_bind), | ||
5238 | + LSM_HOOK_INIT(socket_connect, ccs_socket_connect), | ||
5239 | + LSM_HOOK_INIT(socket_listen, ccs_socket_listen), | ||
5240 | + LSM_HOOK_INIT(socket_sendmsg, ccs_socket_sendmsg), | ||
5241 | +#endif | ||
5242 | +#if defined(CONFIG_SECURITY_PATH) | ||
5243 | + LSM_HOOK_INIT(path_unlink, ccs_path_unlink), | ||
5244 | + LSM_HOOK_INIT(path_mkdir, ccs_path_mkdir), | ||
5245 | + LSM_HOOK_INIT(path_rmdir, ccs_path_rmdir), | ||
5246 | + LSM_HOOK_INIT(path_mknod, ccs_path_mknod), | ||
5247 | + LSM_HOOK_INIT(path_truncate, ccs_path_truncate), | ||
5248 | + LSM_HOOK_INIT(path_symlink, ccs_path_symlink), | ||
5249 | + LSM_HOOK_INIT(path_link, ccs_path_link), | ||
5250 | + LSM_HOOK_INIT(path_rename, ccs_path_rename), | ||
5251 | + LSM_HOOK_INIT(path_chmod, ccs_path_chmod), | ||
5252 | + LSM_HOOK_INIT(path_chown, ccs_path_chown), | ||
5253 | + LSM_HOOK_INIT(path_chroot, ccs_path_chroot), | ||
5254 | +#endif | ||
5255 | +}; | ||
5256 | + | ||
5257 | +static void ccs_add_hooks(void) | ||
5258 | +{ | ||
5259 | + security_add_hooks(ccsecurity_hooks, ARRAY_SIZE(ccsecurity_hooks)); | ||
5260 | +} | ||
5261 | +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) && defined(CONFIG_SECURITY) */ | ||
5262 | diff --git a/security/ccsecurity/memory.c b/security/ccsecurity/memory.c | ||
5263 | new file mode 100644 | ||
5264 | index 0000000..6514a26 | ||
5265 | --- /dev/null | ||
5266 | +++ b/security/ccsecurity/memory.c | ||
5267 | @@ -0,0 +1,356 @@ | ||
5268 | +/* | ||
5269 | + * security/ccsecurity/memory.c | ||
5270 | + * | ||
5271 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | ||
5272 | + * | ||
5273 | + * Version: 1.8.4 2015/05/05 | ||
5274 | + */ | ||
5275 | + | ||
5276 | +#include "internal.h" | ||
5277 | + | ||
5278 | +/***** SECTION1: Constants definition *****/ | ||
5279 | + | ||
5280 | +/***** SECTION2: Structure definition *****/ | ||
5281 | + | ||
5282 | +/***** SECTION3: Prototype definition section *****/ | ||
5283 | + | ||
5284 | +bool ccs_memory_ok(const void *ptr, const unsigned int size); | ||
5285 | +const struct ccs_path_info *ccs_get_name(const char *name); | ||
5286 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | ||
5287 | +struct ccs_security *ccs_find_task_security(const struct task_struct *task); | ||
5288 | +#endif | ||
5289 | +void *ccs_commit_ok(void *data, const unsigned int size); | ||
5290 | +void __init ccs_mm_init(void); | ||
5291 | +void ccs_warn_oom(const char *function); | ||
5292 | + | ||
5293 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | ||
5294 | +static int __ccs_alloc_task_security(const struct task_struct *task); | ||
5295 | +static void __ccs_free_task_security(const struct task_struct *task); | ||
5296 | +static void ccs_add_task_security(struct ccs_security *ptr, | ||
5297 | + struct list_head *list); | ||
5298 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) | ||
5299 | +static void ccs_rcu_free(struct rcu_head *rcu); | ||
5300 | +#else | ||
5301 | +static void ccs_rcu_free(void *arg); | ||
5302 | +#endif | ||
5303 | +#endif | ||
5304 | + | ||
5305 | +/***** SECTION4: Standalone functions section *****/ | ||
5306 | + | ||
5307 | +/***** SECTION5: Variables definition section *****/ | ||
5308 | + | ||
5309 | +/* Memoy currently used by policy/audit log/query. */ | ||
5310 | +unsigned int ccs_memory_used[CCS_MAX_MEMORY_STAT]; | ||
5311 | + | ||
5312 | +/* Memory quota for "policy"/"audit log"/"query". */ | ||
5313 | +unsigned int ccs_memory_quota[CCS_MAX_MEMORY_STAT]; | ||
5314 | + | ||
5315 | +/* The list for "struct ccs_name". */ | ||
5316 | +struct list_head ccs_name_list[CCS_MAX_HASH]; | ||
5317 | + | ||
5318 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | ||
5319 | + | ||
5320 | +/* Dummy security context for avoiding NULL pointer dereference. */ | ||
5321 | +static struct ccs_security ccs_oom_security = { | ||
5322 | + .ccs_domain_info = &ccs_kernel_domain | ||
5323 | +}; | ||
5324 | + | ||
5325 | +/* Dummy security context for avoiding NULL pointer dereference. */ | ||
5326 | +static struct ccs_security ccs_default_security = { | ||
5327 | + .ccs_domain_info = &ccs_kernel_domain | ||
5328 | +}; | ||
5329 | + | ||
5330 | +/* List of "struct ccs_security". */ | ||
5331 | +struct list_head ccs_task_security_list[CCS_MAX_TASK_SECURITY_HASH]; | ||
5332 | +/* Lock for protecting ccs_task_security_list[]. */ | ||
5333 | +static DEFINE_SPINLOCK(ccs_task_security_list_lock); | ||
5334 | + | ||
5335 | +#endif | ||
5336 | + | ||
5337 | +/***** SECTION6: Dependent functions section *****/ | ||
5338 | + | ||
5339 | +/** | ||
5340 | + * ccs_warn_oom - Print out of memory warning message. | ||
5341 | + * | ||
5342 | + * @function: Function's name. | ||
5343 | + * | ||
5344 | + * Returns nothing. | ||
5345 | + */ | ||
5346 | +void ccs_warn_oom(const char *function) | ||
5347 | +{ | ||
5348 | + /* Reduce error messages. */ | ||
5349 | + static pid_t ccs_last_pid; | ||
5350 | + const pid_t pid = current->pid; | ||
5351 | + if (ccs_last_pid != pid) { | ||
5352 | + printk(KERN_WARNING "ERROR: Out of memory at %s.\n", | ||
5353 | + function); | ||
5354 | + ccs_last_pid = pid; | ||
5355 | + } | ||
5356 | + if (!ccs_policy_loaded) | ||
5357 | + panic("MAC Initialization failed.\n"); | ||
5358 | +} | ||
5359 | + | ||
5360 | +/** | ||
5361 | + * ccs_memory_ok - Check memory quota. | ||
5362 | + * | ||
5363 | + * @ptr: Pointer to allocated memory. Maybe NULL. | ||
5364 | + * @size: Size in byte. Not used if @ptr is NULL. | ||
5365 | + * | ||
5366 | + * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. | ||
5367 | + * | ||
5368 | + * Caller holds ccs_policy_lock mutex. | ||
5369 | + */ | ||
5370 | +bool ccs_memory_ok(const void *ptr, const unsigned int size) | ||
5371 | +{ | ||
5372 | + if (ptr) { | ||
5373 | + const size_t s = ccs_round2(size); | ||
5374 | + ccs_memory_used[CCS_MEMORY_POLICY] += s; | ||
5375 | + if (!ccs_memory_quota[CCS_MEMORY_POLICY] || | ||
5376 | + ccs_memory_used[CCS_MEMORY_POLICY] <= | ||
5377 | + ccs_memory_quota[CCS_MEMORY_POLICY]) | ||
5378 | + return true; | ||
5379 | + ccs_memory_used[CCS_MEMORY_POLICY] -= s; | ||
5380 | + } | ||
5381 | + ccs_warn_oom(__func__); | ||
5382 | + return false; | ||
5383 | +} | ||
5384 | + | ||
5385 | +/** | ||
5386 | + * ccs_commit_ok - Allocate memory and check memory quota. | ||
5387 | + * | ||
5388 | + * @data: Data to copy from. | ||
5389 | + * @size: Size in byte. | ||
5390 | + * | ||
5391 | + * Returns pointer to allocated memory on success, NULL otherwise. | ||
5392 | + * @data is zero-cleared on success. | ||
5393 | + * | ||
5394 | + * Caller holds ccs_policy_lock mutex. | ||
5395 | + */ | ||
5396 | +void *ccs_commit_ok(void *data, const unsigned int size) | ||
5397 | +{ | ||
5398 | + void *ptr = kmalloc(size, CCS_GFP_FLAGS); | ||
5399 | + if (ccs_memory_ok(ptr, size)) { | ||
5400 | + memmove(ptr, data, size); | ||
5401 | + memset(data, 0, size); | ||
5402 | + return ptr; | ||
5403 | + } | ||
5404 | + kfree(ptr); | ||
5405 | + return NULL; | ||
5406 | +} | ||
5407 | + | ||
5408 | +/** | ||
5409 | + * ccs_get_name - Allocate memory for string data. | ||
5410 | + * | ||
5411 | + * @name: The string to store into the permernent memory. | ||
5412 | + * | ||
5413 | + * Returns pointer to "struct ccs_path_info" on success, NULL otherwise. | ||
5414 | + */ | ||
5415 | +const struct ccs_path_info *ccs_get_name(const char *name) | ||
5416 | +{ | ||
5417 | + struct ccs_name *ptr; | ||
5418 | + unsigned int hash; | ||
5419 | + int len; | ||
5420 | + int allocated_len; | ||
5421 | + struct list_head *head; | ||
5422 | + | ||
5423 | + if (!name) | ||
5424 | + return NULL; | ||
5425 | + len = strlen(name) + 1; | ||
5426 | + hash = full_name_hash((const unsigned char *) name, len - 1); | ||
5427 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) || defined(RHEL_MAJOR) | ||
5428 | + head = &ccs_name_list[hash_long(hash, CCS_HASH_BITS)]; | ||
5429 | +#else | ||
5430 | + head = &ccs_name_list[hash % CCS_MAX_HASH]; | ||
5431 | +#endif | ||
5432 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | ||
5433 | + return NULL; | ||
5434 | + list_for_each_entry(ptr, head, head.list) { | ||
5435 | + if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) || | ||
5436 | + atomic_read(&ptr->head.users) == CCS_GC_IN_PROGRESS) | ||
5437 | + continue; | ||
5438 | + atomic_inc(&ptr->head.users); | ||
5439 | + goto out; | ||
5440 | + } | ||
5441 | + allocated_len = sizeof(*ptr) + len; | ||
5442 | + ptr = kzalloc(allocated_len, CCS_GFP_FLAGS); | ||
5443 | + if (ccs_memory_ok(ptr, allocated_len)) { | ||
5444 | + ptr->entry.name = ((char *) ptr) + sizeof(*ptr); | ||
5445 | + memmove((char *) ptr->entry.name, name, len); | ||
5446 | + atomic_set(&ptr->head.users, 1); | ||
5447 | + ccs_fill_path_info(&ptr->entry); | ||
5448 | + ptr->size = allocated_len; | ||
5449 | + list_add_tail(&ptr->head.list, head); | ||
5450 | + } else { | ||
5451 | + kfree(ptr); | ||
5452 | + ptr = NULL; | ||
5453 | + } | ||
5454 | +out: | ||
5455 | + mutex_unlock(&ccs_policy_lock); | ||
5456 | + return ptr ? &ptr->entry : NULL; | ||
5457 | +} | ||
5458 | + | ||
5459 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | ||
5460 | + | ||
5461 | +/** | ||
5462 | + * ccs_add_task_security - Add "struct ccs_security" to list. | ||
5463 | + * | ||
5464 | + * @ptr: Pointer to "struct ccs_security". | ||
5465 | + * @list: Pointer to "struct list_head". | ||
5466 | + * | ||
5467 | + * Returns nothing. | ||
5468 | + */ | ||
5469 | +static void ccs_add_task_security(struct ccs_security *ptr, | ||
5470 | + struct list_head *list) | ||
5471 | +{ | ||
5472 | + unsigned long flags; | ||
5473 | + spin_lock_irqsave(&ccs_task_security_list_lock, flags); | ||
5474 | + list_add_rcu(&ptr->list, list); | ||
5475 | + spin_unlock_irqrestore(&ccs_task_security_list_lock, flags); | ||
5476 | +} | ||
5477 | + | ||
5478 | +/** | ||
5479 | + * __ccs_alloc_task_security - Allocate memory for new tasks. | ||
5480 | + * | ||
5481 | + * @task: Pointer to "struct task_struct". | ||
5482 | + * | ||
5483 | + * Returns 0 on success, negative value otherwise. | ||
5484 | + */ | ||
5485 | +static int __ccs_alloc_task_security(const struct task_struct *task) | ||
5486 | +{ | ||
5487 | + struct ccs_security *old_security = ccs_current_security(); | ||
5488 | + struct ccs_security *new_security = kzalloc(sizeof(*new_security), | ||
5489 | + GFP_KERNEL); | ||
5490 | + struct list_head *list = &ccs_task_security_list | ||
5491 | + [hash_ptr((void *) task, CCS_TASK_SECURITY_HASH_BITS)]; | ||
5492 | + if (!new_security) | ||
5493 | + return -ENOMEM; | ||
5494 | + new_security->task = task; | ||
5495 | + new_security->ccs_domain_info = old_security->ccs_domain_info; | ||
5496 | + new_security->ccs_flags = old_security->ccs_flags; | ||
5497 | + ccs_add_task_security(new_security, list); | ||
5498 | + return 0; | ||
5499 | +} | ||
5500 | + | ||
5501 | +/** | ||
5502 | + * ccs_find_task_security - Find "struct ccs_security" for given task. | ||
5503 | + * | ||
5504 | + * @task: Pointer to "struct task_struct". | ||
5505 | + * | ||
5506 | + * Returns pointer to "struct ccs_security" on success, &ccs_oom_security on | ||
5507 | + * out of memory, &ccs_default_security otherwise. | ||
5508 | + * | ||
5509 | + * If @task is current thread and "struct ccs_security" for current thread was | ||
5510 | + * not found, I try to allocate it. But if allocation failed, current thread | ||
5511 | + * will be killed by SIGKILL. Note that if current->pid == 1, sending SIGKILL | ||
5512 | + * won't work. | ||
5513 | + */ | ||
5514 | +struct ccs_security *ccs_find_task_security(const struct task_struct *task) | ||
5515 | +{ | ||
5516 | + struct ccs_security *ptr; | ||
5517 | + struct list_head *list = &ccs_task_security_list | ||
5518 | + [hash_ptr((void *) task, CCS_TASK_SECURITY_HASH_BITS)]; | ||
5519 | + /* Make sure INIT_LIST_HEAD() in ccs_mm_init() takes effect. */ | ||
5520 | + while (!list->next); | ||
5521 | + rcu_read_lock(); | ||
5522 | + list_for_each_entry_rcu(ptr, list, list) { | ||
5523 | + if (ptr->task != task) | ||
5524 | + continue; | ||
5525 | + rcu_read_unlock(); | ||
5526 | + return ptr; | ||
5527 | + } | ||
5528 | + rcu_read_unlock(); | ||
5529 | + if (task != current) | ||
5530 | + return &ccs_default_security; | ||
5531 | + /* Use GFP_ATOMIC because caller may have called rcu_read_lock(). */ | ||
5532 | + ptr = kzalloc(sizeof(*ptr), GFP_ATOMIC); | ||
5533 | + if (!ptr) { | ||
5534 | + printk(KERN_WARNING "Unable to allocate memory for pid=%u\n", | ||
5535 | + task->pid); | ||
5536 | + send_sig(SIGKILL, current, 0); | ||
5537 | + return &ccs_oom_security; | ||
5538 | + } | ||
5539 | + *ptr = ccs_default_security; | ||
5540 | + ptr->task = task; | ||
5541 | + ccs_add_task_security(ptr, list); | ||
5542 | + return ptr; | ||
5543 | +} | ||
5544 | + | ||
5545 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) | ||
5546 | + | ||
5547 | +/** | ||
5548 | + * ccs_rcu_free - RCU callback for releasing "struct ccs_security". | ||
5549 | + * | ||
5550 | + * @rcu: Pointer to "struct rcu_head". | ||
5551 | + * | ||
5552 | + * Returns nothing. | ||
5553 | + */ | ||
5554 | +static void ccs_rcu_free(struct rcu_head *rcu) | ||
5555 | +{ | ||
5556 | + struct ccs_security *ptr = container_of(rcu, typeof(*ptr), rcu); | ||
5557 | + kfree(ptr); | ||
5558 | +} | ||
5559 | + | ||
5560 | +#else | ||
5561 | + | ||
5562 | +/** | ||
5563 | + * ccs_rcu_free - RCU callback for releasing "struct ccs_security". | ||
5564 | + * | ||
5565 | + * @arg: Pointer to "void". | ||
5566 | + * | ||
5567 | + * Returns nothing. | ||
5568 | + */ | ||
5569 | +static void ccs_rcu_free(void *arg) | ||
5570 | +{ | ||
5571 | + struct ccs_security *ptr = arg; | ||
5572 | + kfree(ptr); | ||
5573 | +} | ||
5574 | + | ||
5575 | +#endif | ||
5576 | + | ||
5577 | +/** | ||
5578 | + * __ccs_free_task_security - Release memory associated with "struct task_struct". | ||
5579 | + * | ||
5580 | + * @task: Pointer to "struct task_struct". | ||
5581 | + * | ||
5582 | + * Returns nothing. | ||
5583 | + */ | ||
5584 | +static void __ccs_free_task_security(const struct task_struct *task) | ||
5585 | +{ | ||
5586 | + unsigned long flags; | ||
5587 | + struct ccs_security *ptr = ccs_find_task_security(task); | ||
5588 | + if (ptr == &ccs_default_security || ptr == &ccs_oom_security) | ||
5589 | + return; | ||
5590 | + spin_lock_irqsave(&ccs_task_security_list_lock, flags); | ||
5591 | + list_del_rcu(&ptr->list); | ||
5592 | + spin_unlock_irqrestore(&ccs_task_security_list_lock, flags); | ||
5593 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) | ||
5594 | + call_rcu(&ptr->rcu, ccs_rcu_free); | ||
5595 | +#else | ||
5596 | + call_rcu(&ptr->rcu, ccs_rcu_free, ptr); | ||
5597 | +#endif | ||
5598 | +} | ||
5599 | + | ||
5600 | +#endif | ||
5601 | + | ||
5602 | +/** | ||
5603 | + * ccs_mm_init - Initialize mm related code. | ||
5604 | + * | ||
5605 | + * Returns nothing. | ||
5606 | + */ | ||
5607 | +void __init ccs_mm_init(void) | ||
5608 | +{ | ||
5609 | + int idx; | ||
5610 | + for (idx = 0; idx < CCS_MAX_HASH; idx++) | ||
5611 | + INIT_LIST_HEAD(&ccs_name_list[idx]); | ||
5612 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | ||
5613 | + for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) | ||
5614 | + INIT_LIST_HEAD(&ccs_task_security_list[idx]); | ||
5615 | +#endif | ||
5616 | + smp_wmb(); /* Avoid out of order execution. */ | ||
5617 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | ||
5618 | + ccsecurity_ops.alloc_task_security = __ccs_alloc_task_security; | ||
5619 | + ccsecurity_ops.free_task_security = __ccs_free_task_security; | ||
5620 | +#endif | ||
5621 | + ccs_kernel_domain.domainname = ccs_get_name("<kernel>"); | ||
5622 | + list_add_tail_rcu(&ccs_kernel_domain.list, &ccs_domain_list); | ||
5623 | +} | ||
5624 | diff --git a/security/ccsecurity/permission.c b/security/ccsecurity/permission.c | ||
5625 | new file mode 100644 | ||
5626 | index 0000000..d73c237 | ||
5627 | --- /dev/null | ||
5628 | +++ b/security/ccsecurity/permission.c | ||
5629 | @@ -0,0 +1,5025 @@ | ||
5630 | +/* | ||
5631 | + * security/ccsecurity/permission.c | ||
5632 | + * | ||
5633 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | ||
5634 | + * | ||
5635 | + * Version: 1.8.4 2015/05/05 | ||
5636 | + */ | ||
5637 | + | ||
5638 | +#include "internal.h" | ||
5639 | + | ||
5640 | +/***** SECTION1: Constants definition *****/ | ||
5641 | + | ||
5642 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | ||
5643 | + | ||
5644 | +/* | ||
5645 | + * may_open() receives open flags modified by open_to_namei_flags() until | ||
5646 | + * 2.6.32. We stop here in case some distributions backported ACC_MODE changes, | ||
5647 | + * for we can't determine whether may_open() receives open flags modified by | ||
5648 | + * open_to_namei_flags() or not. | ||
5649 | + */ | ||
5650 | +#ifdef ACC_MODE | ||
5651 | +#error ACC_MODE already defined. | ||
5652 | +#endif | ||
5653 | +#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) | ||
5654 | + | ||
5655 | +#if defined(RHEL_MAJOR) && RHEL_MAJOR == 6 | ||
5656 | +/* RHEL6 passes unmodified flags since 2.6.32-71.14.1.el6 . */ | ||
5657 | +#undef ACC_MODE | ||
5658 | +#define ACC_MODE(x) ("\004\002\006"[(x)&O_ACCMODE]) | ||
5659 | +#endif | ||
5660 | + | ||
5661 | +#endif | ||
5662 | + | ||
5663 | +/* String table for special mount operations. */ | ||
5664 | +static const char * const ccs_mounts[CCS_MAX_SPECIAL_MOUNT] = { | ||
5665 | + [CCS_MOUNT_BIND] = "--bind", | ||
5666 | + [CCS_MOUNT_MOVE] = "--move", | ||
5667 | + [CCS_MOUNT_REMOUNT] = "--remount", | ||
5668 | + [CCS_MOUNT_MAKE_UNBINDABLE] = "--make-unbindable", | ||
5669 | + [CCS_MOUNT_MAKE_PRIVATE] = "--make-private", | ||
5670 | + [CCS_MOUNT_MAKE_SLAVE] = "--make-slave", | ||
5671 | + [CCS_MOUNT_MAKE_SHARED] = "--make-shared", | ||
5672 | +}; | ||
5673 | + | ||
5674 | +/* Mapping table from "enum ccs_path_acl_index" to "enum ccs_mac_index". */ | ||
5675 | +static const u8 ccs_p2mac[CCS_MAX_PATH_OPERATION] = { | ||
5676 | + [CCS_TYPE_EXECUTE] = CCS_MAC_FILE_EXECUTE, | ||
5677 | + [CCS_TYPE_READ] = CCS_MAC_FILE_OPEN, | ||
5678 | + [CCS_TYPE_WRITE] = CCS_MAC_FILE_OPEN, | ||
5679 | + [CCS_TYPE_APPEND] = CCS_MAC_FILE_OPEN, | ||
5680 | + [CCS_TYPE_UNLINK] = CCS_MAC_FILE_UNLINK, | ||
5681 | +#ifdef CONFIG_CCSECURITY_FILE_GETATTR | ||
5682 | + [CCS_TYPE_GETATTR] = CCS_MAC_FILE_GETATTR, | ||
5683 | +#endif | ||
5684 | + [CCS_TYPE_RMDIR] = CCS_MAC_FILE_RMDIR, | ||
5685 | + [CCS_TYPE_TRUNCATE] = CCS_MAC_FILE_TRUNCATE, | ||
5686 | + [CCS_TYPE_SYMLINK] = CCS_MAC_FILE_SYMLINK, | ||
5687 | + [CCS_TYPE_CHROOT] = CCS_MAC_FILE_CHROOT, | ||
5688 | + [CCS_TYPE_UMOUNT] = CCS_MAC_FILE_UMOUNT, | ||
5689 | +}; | ||
5690 | + | ||
5691 | +/* Mapping table from "enum ccs_mkdev_acl_index" to "enum ccs_mac_index". */ | ||
5692 | +const u8 ccs_pnnn2mac[CCS_MAX_MKDEV_OPERATION] = { | ||
5693 | + [CCS_TYPE_MKBLOCK] = CCS_MAC_FILE_MKBLOCK, | ||
5694 | + [CCS_TYPE_MKCHAR] = CCS_MAC_FILE_MKCHAR, | ||
5695 | +}; | ||
5696 | + | ||
5697 | +/* Mapping table from "enum ccs_path2_acl_index" to "enum ccs_mac_index". */ | ||
5698 | +const u8 ccs_pp2mac[CCS_MAX_PATH2_OPERATION] = { | ||
5699 | + [CCS_TYPE_LINK] = CCS_MAC_FILE_LINK, | ||
5700 | + [CCS_TYPE_RENAME] = CCS_MAC_FILE_RENAME, | ||
5701 | + [CCS_TYPE_PIVOT_ROOT] = CCS_MAC_FILE_PIVOT_ROOT, | ||
5702 | +}; | ||
5703 | + | ||
5704 | +/* | ||
5705 | + * Mapping table from "enum ccs_path_number_acl_index" to "enum ccs_mac_index". | ||
5706 | + */ | ||
5707 | +const u8 ccs_pn2mac[CCS_MAX_PATH_NUMBER_OPERATION] = { | ||
5708 | + [CCS_TYPE_CREATE] = CCS_MAC_FILE_CREATE, | ||
5709 | + [CCS_TYPE_MKDIR] = CCS_MAC_FILE_MKDIR, | ||
5710 | + [CCS_TYPE_MKFIFO] = CCS_MAC_FILE_MKFIFO, | ||
5711 | + [CCS_TYPE_MKSOCK] = CCS_MAC_FILE_MKSOCK, | ||
5712 | + [CCS_TYPE_IOCTL] = CCS_MAC_FILE_IOCTL, | ||
5713 | + [CCS_TYPE_CHMOD] = CCS_MAC_FILE_CHMOD, | ||
5714 | + [CCS_TYPE_CHOWN] = CCS_MAC_FILE_CHOWN, | ||
5715 | + [CCS_TYPE_CHGRP] = CCS_MAC_FILE_CHGRP, | ||
5716 | +}; | ||
5717 | + | ||
5718 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
5719 | + | ||
5720 | +/* | ||
5721 | + * Mapping table from "enum ccs_network_acl_index" to "enum ccs_mac_index" for | ||
5722 | + * inet domain socket. | ||
5723 | + */ | ||
5724 | +static const u8 ccs_inet2mac[CCS_SOCK_MAX][CCS_MAX_NETWORK_OPERATION] = { | ||
5725 | + [SOCK_STREAM] = { | ||
5726 | + [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_INET_STREAM_BIND, | ||
5727 | + [CCS_NETWORK_LISTEN] = CCS_MAC_NETWORK_INET_STREAM_LISTEN, | ||
5728 | + [CCS_NETWORK_CONNECT] = CCS_MAC_NETWORK_INET_STREAM_CONNECT, | ||
5729 | + [CCS_NETWORK_ACCEPT] = CCS_MAC_NETWORK_INET_STREAM_ACCEPT, | ||
5730 | + }, | ||
5731 | + [SOCK_DGRAM] = { | ||
5732 | + [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_INET_DGRAM_BIND, | ||
5733 | + [CCS_NETWORK_SEND] = CCS_MAC_NETWORK_INET_DGRAM_SEND, | ||
5734 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
5735 | + [CCS_NETWORK_RECV] = CCS_MAC_NETWORK_INET_DGRAM_RECV, | ||
5736 | +#endif | ||
5737 | + }, | ||
5738 | + [SOCK_RAW] = { | ||
5739 | + [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_INET_RAW_BIND, | ||
5740 | + [CCS_NETWORK_SEND] = CCS_MAC_NETWORK_INET_RAW_SEND, | ||
5741 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
5742 | + [CCS_NETWORK_RECV] = CCS_MAC_NETWORK_INET_RAW_RECV, | ||
5743 | +#endif | ||
5744 | + }, | ||
5745 | +}; | ||
5746 | + | ||
5747 | +/* | ||
5748 | + * Mapping table from "enum ccs_network_acl_index" to "enum ccs_mac_index" for | ||
5749 | + * unix domain socket. | ||
5750 | + */ | ||
5751 | +static const u8 ccs_unix2mac[CCS_SOCK_MAX][CCS_MAX_NETWORK_OPERATION] = { | ||
5752 | + [SOCK_STREAM] = { | ||
5753 | + [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_UNIX_STREAM_BIND, | ||
5754 | + [CCS_NETWORK_LISTEN] = CCS_MAC_NETWORK_UNIX_STREAM_LISTEN, | ||
5755 | + [CCS_NETWORK_CONNECT] = CCS_MAC_NETWORK_UNIX_STREAM_CONNECT, | ||
5756 | + [CCS_NETWORK_ACCEPT] = CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT, | ||
5757 | + }, | ||
5758 | + [SOCK_DGRAM] = { | ||
5759 | + [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_UNIX_DGRAM_BIND, | ||
5760 | + [CCS_NETWORK_SEND] = CCS_MAC_NETWORK_UNIX_DGRAM_SEND, | ||
5761 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
5762 | + [CCS_NETWORK_RECV] = CCS_MAC_NETWORK_UNIX_DGRAM_RECV, | ||
5763 | +#endif | ||
5764 | + }, | ||
5765 | + [SOCK_SEQPACKET] = { | ||
5766 | + [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND, | ||
5767 | + [CCS_NETWORK_LISTEN] = CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN, | ||
5768 | + [CCS_NETWORK_CONNECT] = CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT, | ||
5769 | + [CCS_NETWORK_ACCEPT] = CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT, | ||
5770 | + }, | ||
5771 | +}; | ||
5772 | + | ||
5773 | +#endif | ||
5774 | + | ||
5775 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
5776 | + | ||
5777 | +/* | ||
5778 | + * Mapping table from "enum ccs_capability_acl_index" to "enum ccs_mac_index". | ||
5779 | + */ | ||
5780 | +const u8 ccs_c2mac[CCS_MAX_CAPABILITY_INDEX] = { | ||
5781 | + [CCS_USE_ROUTE_SOCKET] = CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET, | ||
5782 | + [CCS_USE_PACKET_SOCKET] = CCS_MAC_CAPABILITY_USE_PACKET_SOCKET, | ||
5783 | + [CCS_SYS_REBOOT] = CCS_MAC_CAPABILITY_SYS_REBOOT, | ||
5784 | + [CCS_SYS_VHANGUP] = CCS_MAC_CAPABILITY_SYS_VHANGUP, | ||
5785 | + [CCS_SYS_SETTIME] = CCS_MAC_CAPABILITY_SYS_SETTIME, | ||
5786 | + [CCS_SYS_NICE] = CCS_MAC_CAPABILITY_SYS_NICE, | ||
5787 | + [CCS_SYS_SETHOSTNAME] = CCS_MAC_CAPABILITY_SYS_SETHOSTNAME, | ||
5788 | + [CCS_USE_KERNEL_MODULE] = CCS_MAC_CAPABILITY_USE_KERNEL_MODULE, | ||
5789 | + [CCS_SYS_KEXEC_LOAD] = CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD, | ||
5790 | + [CCS_SYS_PTRACE] = CCS_MAC_CAPABILITY_SYS_PTRACE, | ||
5791 | +}; | ||
5792 | + | ||
5793 | +#endif | ||
5794 | + | ||
5795 | +/***** SECTION2: Structure definition *****/ | ||
5796 | + | ||
5797 | +/* Structure for holding inet domain socket's address. */ | ||
5798 | +struct ccs_inet_addr_info { | ||
5799 | + u16 port; /* In network byte order. */ | ||
5800 | + const u32 *address; /* In network byte order. */ | ||
5801 | + bool is_ipv6; | ||
5802 | +}; | ||
5803 | + | ||
5804 | +/* Structure for holding unix domain socket's address. */ | ||
5805 | +struct ccs_unix_addr_info { | ||
5806 | + u8 *addr; /* This may not be '\0' terminated string. */ | ||
5807 | + unsigned int addr_len; | ||
5808 | +}; | ||
5809 | + | ||
5810 | +/* Structure for holding socket address. */ | ||
5811 | +struct ccs_addr_info { | ||
5812 | + u8 protocol; | ||
5813 | + u8 operation; | ||
5814 | + struct ccs_inet_addr_info inet; | ||
5815 | + struct ccs_unix_addr_info unix0; | ||
5816 | +}; | ||
5817 | + | ||
5818 | +/***** SECTION3: Prototype definition section *****/ | ||
5819 | + | ||
5820 | +bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos, | ||
5821 | + struct ccs_page_dump *dump); | ||
5822 | +void ccs_get_attributes(struct ccs_obj_info *obj); | ||
5823 | + | ||
5824 | +static bool ccs_alphabet_char(const char c); | ||
5825 | +static bool ccs_argv(const unsigned int index, const char *arg_ptr, | ||
5826 | + const int argc, const struct ccs_argv *argv, u8 *checked); | ||
5827 | +static bool ccs_byte_range(const char *str); | ||
5828 | +static bool ccs_check_entry(struct ccs_request_info *r, | ||
5829 | + struct ccs_acl_info *ptr); | ||
5830 | +static bool ccs_check_mkdev_acl(struct ccs_request_info *r, | ||
5831 | + const struct ccs_acl_info *ptr); | ||
5832 | +static bool ccs_check_mount_acl(struct ccs_request_info *r, | ||
5833 | + const struct ccs_acl_info *ptr); | ||
5834 | +static bool ccs_check_path2_acl(struct ccs_request_info *r, | ||
5835 | + const struct ccs_acl_info *ptr); | ||
5836 | +static bool ccs_check_path_acl(struct ccs_request_info *r, | ||
5837 | + const struct ccs_acl_info *ptr); | ||
5838 | +static bool ccs_check_path_number_acl(struct ccs_request_info *r, | ||
5839 | + const struct ccs_acl_info *ptr); | ||
5840 | +static bool ccs_compare_number_union(const unsigned long value, | ||
5841 | + const struct ccs_number_union *ptr); | ||
5842 | +static bool ccs_condition(struct ccs_request_info *r, | ||
5843 | + const struct ccs_condition *cond); | ||
5844 | +static bool ccs_decimal(const char c); | ||
5845 | +static bool ccs_envp(const char *env_name, const char *env_value, | ||
5846 | + const int envc, const struct ccs_envp *envp, u8 *checked); | ||
5847 | +static bool ccs_file_matches_pattern(const char *filename, | ||
5848 | + const char *filename_end, | ||
5849 | + const char *pattern, | ||
5850 | + const char *pattern_end); | ||
5851 | +static bool ccs_file_matches_pattern2(const char *filename, | ||
5852 | + const char *filename_end, | ||
5853 | + const char *pattern, | ||
5854 | + const char *pattern_end); | ||
5855 | +static bool ccs_get_realpath(struct ccs_path_info *buf, struct path *path); | ||
5856 | +static bool ccs_hexadecimal(const char c); | ||
5857 | +static bool ccs_number_matches_group(const unsigned long min, | ||
5858 | + const unsigned long max, | ||
5859 | + const struct ccs_group *group); | ||
5860 | +static bool ccs_path_matches_pattern(const struct ccs_path_info *filename, | ||
5861 | + const struct ccs_path_info *pattern); | ||
5862 | +static bool ccs_path_matches_pattern2(const char *f, const char *p); | ||
5863 | +static bool ccs_scan_bprm(struct ccs_execve *ee, const u16 argc, | ||
5864 | + const struct ccs_argv *argv, const u16 envc, | ||
5865 | + const struct ccs_envp *envp); | ||
5866 | +static bool ccs_scan_exec_realpath(struct file *file, | ||
5867 | + const struct ccs_name_union *ptr, | ||
5868 | + const bool match); | ||
5869 | +static bool ccs_scan_transition(const struct list_head *list, | ||
5870 | + const struct ccs_path_info *domainname, | ||
5871 | + const struct ccs_path_info *program, | ||
5872 | + const char *last_name, | ||
5873 | + const enum ccs_transition_type type); | ||
5874 | +static const char *ccs_last_word(const char *name); | ||
5875 | +static const struct ccs_path_info *ccs_compare_name_union | ||
5876 | +(const struct ccs_path_info *name, const struct ccs_name_union *ptr); | ||
5877 | +static const struct ccs_path_info *ccs_path_matches_group | ||
5878 | +(const struct ccs_path_info *pathname, const struct ccs_group *group); | ||
5879 | +static enum ccs_transition_type ccs_transition_type | ||
5880 | +(const struct ccs_policy_namespace *ns, const struct ccs_path_info *domainname, | ||
5881 | + const struct ccs_path_info *program); | ||
5882 | +static int __ccs_chmod_permission(struct dentry *dentry, | ||
5883 | + struct vfsmount *vfsmnt, mode_t mode); | ||
5884 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) | ||
5885 | +static int __ccs_chown_permission(struct dentry *dentry, | ||
5886 | + struct vfsmount *vfsmnt, kuid_t user, | ||
5887 | + kgid_t group); | ||
5888 | +#else | ||
5889 | +static int __ccs_chown_permission(struct dentry *dentry, | ||
5890 | + struct vfsmount *vfsmnt, uid_t user, | ||
5891 | + gid_t group); | ||
5892 | +#endif | ||
5893 | +static int __ccs_chroot_permission(struct path *path); | ||
5894 | +static int __ccs_fcntl_permission(struct file *file, unsigned int cmd, | ||
5895 | + unsigned long arg); | ||
5896 | +static int __ccs_ioctl_permission(struct file *filp, unsigned int cmd, | ||
5897 | + unsigned long arg); | ||
5898 | +static int __ccs_link_permission(struct dentry *old_dentry, | ||
5899 | + struct dentry *new_dentry, | ||
5900 | + struct vfsmount *mnt); | ||
5901 | +static int __ccs_mkdir_permission(struct dentry *dentry, struct vfsmount *mnt, | ||
5902 | + unsigned int mode); | ||
5903 | +static int __ccs_mknod_permission(struct dentry *dentry, struct vfsmount *mnt, | ||
5904 | + const unsigned int mode, unsigned int dev); | ||
5905 | +static int __ccs_mount_permission(const char *dev_name, struct path *path, | ||
5906 | + const char *type, unsigned long flags, | ||
5907 | + void *data_page); | ||
5908 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | ||
5909 | +static int __ccs_open_exec_permission(struct dentry *dentry, | ||
5910 | + struct vfsmount *mnt); | ||
5911 | +#endif | ||
5912 | +static int __ccs_open_permission(struct dentry *dentry, struct vfsmount *mnt, | ||
5913 | + const int flag); | ||
5914 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) || (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL)) | ||
5915 | +static int __ccs_parse_table(int __user *name, int nlen, void __user *oldval, | ||
5916 | + void __user *newval, struct ctl_table *table); | ||
5917 | +#endif | ||
5918 | +static int __ccs_pivot_root_permission(struct path *old_path, | ||
5919 | + struct path *new_path); | ||
5920 | +static int __ccs_rename_permission(struct dentry *old_dentry, | ||
5921 | + struct dentry *new_dentry, | ||
5922 | + struct vfsmount *mnt); | ||
5923 | +static int __ccs_rmdir_permission(struct dentry *dentry, struct vfsmount *mnt); | ||
5924 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) | ||
5925 | +static int __ccs_search_binary_handler(struct linux_binprm *bprm); | ||
5926 | +#else | ||
5927 | +static int __ccs_search_binary_handler(struct linux_binprm *bprm, | ||
5928 | + struct pt_regs *regs); | ||
5929 | +#endif | ||
5930 | +static int __ccs_symlink_permission(struct dentry *dentry, | ||
5931 | + struct vfsmount *mnt, const char *from); | ||
5932 | +static int __ccs_truncate_permission(struct dentry *dentry, | ||
5933 | + struct vfsmount *mnt); | ||
5934 | +static int __ccs_umount_permission(struct vfsmount *mnt, int flags); | ||
5935 | +static int __ccs_unlink_permission(struct dentry *dentry, | ||
5936 | + struct vfsmount *mnt); | ||
5937 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | ||
5938 | +static int __ccs_uselib_permission(struct dentry *dentry, | ||
5939 | + struct vfsmount *mnt); | ||
5940 | +#endif | ||
5941 | +static int ccs_execute_permission(struct ccs_request_info *r, | ||
5942 | + const struct ccs_path_info *filename); | ||
5943 | +static int ccs_find_next_domain(struct ccs_execve *ee); | ||
5944 | +static int ccs_get_path(const char *pathname, struct path *path); | ||
5945 | +static int ccs_kern_path(const char *pathname, int flags, struct path *path); | ||
5946 | +static int ccs_mkdev_perm(const u8 operation, struct dentry *dentry, | ||
5947 | + struct vfsmount *mnt, const unsigned int mode, | ||
5948 | + unsigned int dev); | ||
5949 | +static int ccs_mount_acl(struct ccs_request_info *r, const char *dev_name, | ||
5950 | + struct path *dir, const char *type, | ||
5951 | + unsigned long flags); | ||
5952 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | ||
5953 | +static int ccs_new_open_permission(struct file *filp); | ||
5954 | +#endif | ||
5955 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24) | ||
5956 | +static int ccs_old_chroot_permission(struct nameidata *nd); | ||
5957 | +static int ccs_old_mount_permission(const char *dev_name, struct nameidata *nd, | ||
5958 | + const char *type, unsigned long flags, | ||
5959 | + void *data_page); | ||
5960 | +static int ccs_old_pivot_root_permission(struct nameidata *old_nd, | ||
5961 | + struct nameidata *new_nd); | ||
5962 | +#endif | ||
5963 | +static int ccs_path2_perm(const u8 operation, struct dentry *dentry1, | ||
5964 | + struct vfsmount *mnt1, struct dentry *dentry2, | ||
5965 | + struct vfsmount *mnt2); | ||
5966 | +static int ccs_path_number_perm(const u8 type, struct dentry *dentry, | ||
5967 | + struct vfsmount *vfsmnt, unsigned long number); | ||
5968 | +static int ccs_path_perm(const u8 operation, struct dentry *dentry, | ||
5969 | + struct vfsmount *mnt, const char *target); | ||
5970 | +static int ccs_path_permission(struct ccs_request_info *r, u8 operation, | ||
5971 | + const struct ccs_path_info *filename); | ||
5972 | +static int ccs_start_execve(struct linux_binprm *bprm, | ||
5973 | + struct ccs_execve **eep); | ||
5974 | +static int ccs_symlink_path(const char *pathname, struct ccs_path_info *name); | ||
5975 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | ||
5976 | +static void __ccs_clear_open_mode(void); | ||
5977 | +static void __ccs_save_open_mode(int mode); | ||
5978 | +#endif | ||
5979 | +static void ccs_add_slash(struct ccs_path_info *buf); | ||
5980 | +static void ccs_finish_execve(int retval, struct ccs_execve *ee); | ||
5981 | + | ||
5982 | +#ifdef CONFIG_CCSECURITY_MISC | ||
5983 | +static bool ccs_check_env_acl(struct ccs_request_info *r, | ||
5984 | + const struct ccs_acl_info *ptr); | ||
5985 | +static int ccs_env_perm(struct ccs_request_info *r, const char *env); | ||
5986 | +static int ccs_environ(struct ccs_execve *ee); | ||
5987 | +#endif | ||
5988 | + | ||
5989 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
5990 | +static bool __ccs_capable(const u8 operation); | ||
5991 | +static bool ccs_check_capability_acl(struct ccs_request_info *r, | ||
5992 | + const struct ccs_acl_info *ptr); | ||
5993 | +static bool ccs_kernel_service(void); | ||
5994 | +static int __ccs_ptrace_permission(long request, long pid); | ||
5995 | +static int __ccs_socket_create_permission(int family, int type, int protocol); | ||
5996 | +#endif | ||
5997 | + | ||
5998 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
5999 | +static bool ccs_address_matches_group(const bool is_ipv6, const u32 *address, | ||
6000 | + const struct ccs_group *group); | ||
6001 | +static bool ccs_check_inet_acl(struct ccs_request_info *r, | ||
6002 | + const struct ccs_acl_info *ptr); | ||
6003 | +static bool ccs_check_unix_acl(struct ccs_request_info *r, | ||
6004 | + const struct ccs_acl_info *ptr); | ||
6005 | +static bool ccs_kernel_service(void); | ||
6006 | +static int __ccs_socket_bind_permission(struct socket *sock, | ||
6007 | + struct sockaddr *addr, int addr_len); | ||
6008 | +static int __ccs_socket_connect_permission(struct socket *sock, | ||
6009 | + struct sockaddr *addr, | ||
6010 | + int addr_len); | ||
6011 | +static int __ccs_socket_listen_permission(struct socket *sock); | ||
6012 | +static int __ccs_socket_post_accept_permission(struct socket *sock, | ||
6013 | + struct socket *newsock); | ||
6014 | +static int __ccs_socket_sendmsg_permission(struct socket *sock, | ||
6015 | + struct msghdr *msg, int size); | ||
6016 | +static int ccs_check_inet_address(const struct sockaddr *addr, | ||
6017 | + const unsigned int addr_len, const u16 port, | ||
6018 | + struct ccs_addr_info *address); | ||
6019 | +static int ccs_check_unix_address(struct sockaddr *addr, | ||
6020 | + const unsigned int addr_len, | ||
6021 | + struct ccs_addr_info *address); | ||
6022 | +static int ccs_inet_entry(const struct ccs_addr_info *address); | ||
6023 | +static int ccs_unix_entry(const struct ccs_addr_info *address); | ||
6024 | +static u8 ccs_sock_family(struct sock *sk); | ||
6025 | +#endif | ||
6026 | + | ||
6027 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
6028 | +static int __ccs_socket_post_recvmsg_permission(struct sock *sk, | ||
6029 | + struct sk_buff *skb, | ||
6030 | + int flags); | ||
6031 | +#endif | ||
6032 | + | ||
6033 | +#ifdef CONFIG_CCSECURITY_IPC | ||
6034 | +static bool ccs_check_signal_acl(struct ccs_request_info *r, | ||
6035 | + const struct ccs_acl_info *ptr); | ||
6036 | +static int ccs_signal_acl(const int pid, const int sig); | ||
6037 | +static int ccs_signal_acl0(pid_t tgid, pid_t pid, int sig); | ||
6038 | +static int ccs_signal_acl2(const int sig, const int pid); | ||
6039 | +#endif | ||
6040 | + | ||
6041 | +#ifdef CONFIG_CCSECURITY_FILE_GETATTR | ||
6042 | +static int __ccs_getattr_permission(struct vfsmount *mnt, | ||
6043 | + struct dentry *dentry); | ||
6044 | +#endif | ||
6045 | + | ||
6046 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
6047 | +static bool ccs_find_execute_handler(struct ccs_execve *ee, const u8 type); | ||
6048 | +static int ccs_try_alt_exec(struct ccs_execve *ee); | ||
6049 | +static void ccs_unescape(unsigned char *dest); | ||
6050 | +#endif | ||
6051 | + | ||
6052 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
6053 | +static bool ccs_check_task_acl(struct ccs_request_info *r, | ||
6054 | + const struct ccs_acl_info *ptr); | ||
6055 | +#endif | ||
6056 | + | ||
6057 | +/***** SECTION4: Standalone functions section *****/ | ||
6058 | + | ||
6059 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | ||
6060 | + | ||
6061 | +/** | ||
6062 | + * ccs_copy_argv - Wrapper for copy_strings_kernel(). | ||
6063 | + * | ||
6064 | + * @arg: String to copy. | ||
6065 | + * @bprm: Pointer to "struct linux_binprm". | ||
6066 | + * | ||
6067 | + * Returns return value of copy_strings_kernel(). | ||
6068 | + */ | ||
6069 | +static inline int ccs_copy_argv(const char *arg, struct linux_binprm *bprm) | ||
6070 | +{ | ||
6071 | + const int ret = copy_strings_kernel(1, &arg, bprm); | ||
6072 | + if (ret >= 0) | ||
6073 | + bprm->argc++; | ||
6074 | + return ret; | ||
6075 | +} | ||
6076 | + | ||
6077 | +#else | ||
6078 | + | ||
6079 | +/** | ||
6080 | + * ccs_copy_argv - Wrapper for copy_strings_kernel(). | ||
6081 | + * | ||
6082 | + * @arg: String to copy. | ||
6083 | + * @bprm: Pointer to "struct linux_binprm". | ||
6084 | + * | ||
6085 | + * Returns return value of copy_strings_kernel(). | ||
6086 | + */ | ||
6087 | +static inline int ccs_copy_argv(char *arg, struct linux_binprm *bprm) | ||
6088 | +{ | ||
6089 | + const int ret = copy_strings_kernel(1, &arg, bprm); | ||
6090 | + if (ret >= 0) | ||
6091 | + bprm->argc++; | ||
6092 | + return ret; | ||
6093 | +} | ||
6094 | + | ||
6095 | +#endif | ||
6096 | + | ||
6097 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) | ||
6098 | + | ||
6099 | +/** | ||
6100 | + * get_fs_root - Get reference on root directory. | ||
6101 | + * | ||
6102 | + * @fs: Pointer to "struct fs_struct". | ||
6103 | + * @root: Pointer to "struct path". | ||
6104 | + * | ||
6105 | + * Returns nothing. | ||
6106 | + * | ||
6107 | + * This is for compatibility with older kernels. | ||
6108 | + */ | ||
6109 | +static inline void get_fs_root(struct fs_struct *fs, struct path *root) | ||
6110 | +{ | ||
6111 | + read_lock(&fs->lock); | ||
6112 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) | ||
6113 | + *root = fs->root; | ||
6114 | + path_get(root); | ||
6115 | +#else | ||
6116 | + root->dentry = dget(fs->root); | ||
6117 | + root->mnt = mntget(fs->rootmnt); | ||
6118 | +#endif | ||
6119 | + read_unlock(&fs->lock); | ||
6120 | +} | ||
6121 | + | ||
6122 | +#endif | ||
6123 | + | ||
6124 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
6125 | + | ||
6126 | +/** | ||
6127 | + * module_put - Put a reference on module. | ||
6128 | + * | ||
6129 | + * @module: Pointer to "struct module". Maybe NULL. | ||
6130 | + * | ||
6131 | + * Returns nothing. | ||
6132 | + * | ||
6133 | + * This is for compatibility with older kernels. | ||
6134 | + */ | ||
6135 | +static inline void module_put(struct module *module) | ||
6136 | +{ | ||
6137 | + if (module) | ||
6138 | + __MOD_DEC_USE_COUNT(module); | ||
6139 | +} | ||
6140 | + | ||
6141 | +#endif | ||
6142 | + | ||
6143 | +/** | ||
6144 | + * ccs_put_filesystem - Wrapper for put_filesystem(). | ||
6145 | + * | ||
6146 | + * @fstype: Pointer to "struct file_system_type". | ||
6147 | + * | ||
6148 | + * Returns nothing. | ||
6149 | + * | ||
6150 | + * Since put_filesystem() is not exported, I embed put_filesystem() here. | ||
6151 | + */ | ||
6152 | +static inline void ccs_put_filesystem(struct file_system_type *fstype) | ||
6153 | +{ | ||
6154 | + module_put(fstype->owner); | ||
6155 | +} | ||
6156 | + | ||
6157 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
6158 | + | ||
6159 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) | ||
6160 | +#if !defined(RHEL_MAJOR) || RHEL_MAJOR != 5 | ||
6161 | +#if !defined(AX_MAJOR) || AX_MAJOR != 3 | ||
6162 | + | ||
6163 | +/** | ||
6164 | + * ip_hdr - Get "struct iphdr". | ||
6165 | + * | ||
6166 | + * @skb: Pointer to "struct sk_buff". | ||
6167 | + * | ||
6168 | + * Returns pointer to "struct iphdr". | ||
6169 | + * | ||
6170 | + * This is for compatibility with older kernels. | ||
6171 | + */ | ||
6172 | +static inline struct iphdr *ip_hdr(const struct sk_buff *skb) | ||
6173 | +{ | ||
6174 | + return skb->nh.iph; | ||
6175 | +} | ||
6176 | + | ||
6177 | +/** | ||
6178 | + * udp_hdr - Get "struct udphdr". | ||
6179 | + * | ||
6180 | + * @skb: Pointer to "struct sk_buff". | ||
6181 | + * | ||
6182 | + * Returns pointer to "struct udphdr". | ||
6183 | + * | ||
6184 | + * This is for compatibility with older kernels. | ||
6185 | + */ | ||
6186 | +static inline struct udphdr *udp_hdr(const struct sk_buff *skb) | ||
6187 | +{ | ||
6188 | + return skb->h.uh; | ||
6189 | +} | ||
6190 | + | ||
6191 | +/** | ||
6192 | + * ipv6_hdr - Get "struct ipv6hdr". | ||
6193 | + * | ||
6194 | + * @skb: Pointer to "struct sk_buff". | ||
6195 | + * | ||
6196 | + * Returns pointer to "struct ipv6hdr". | ||
6197 | + * | ||
6198 | + * This is for compatibility with older kernels. | ||
6199 | + */ | ||
6200 | +static inline struct ipv6hdr *ipv6_hdr(const struct sk_buff *skb) | ||
6201 | +{ | ||
6202 | + return skb->nh.ipv6h; | ||
6203 | +} | ||
6204 | + | ||
6205 | +#endif | ||
6206 | +#endif | ||
6207 | +#endif | ||
6208 | + | ||
6209 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
6210 | + | ||
6211 | +/** | ||
6212 | + * skb_kill_datagram - Kill a datagram forcibly. | ||
6213 | + * | ||
6214 | + * @sk: Pointer to "struct sock". | ||
6215 | + * @skb: Pointer to "struct sk_buff". | ||
6216 | + * @flags: Flags passed to skb_recv_datagram(). | ||
6217 | + * | ||
6218 | + * Returns nothing. | ||
6219 | + */ | ||
6220 | +static inline void skb_kill_datagram(struct sock *sk, struct sk_buff *skb, | ||
6221 | + int flags) | ||
6222 | +{ | ||
6223 | + /* Clear queue. */ | ||
6224 | + if (flags & MSG_PEEK) { | ||
6225 | + int clear = 0; | ||
6226 | + spin_lock_irq(&sk->receive_queue.lock); | ||
6227 | + if (skb == skb_peek(&sk->receive_queue)) { | ||
6228 | + __skb_unlink(skb, &sk->receive_queue); | ||
6229 | + clear = 1; | ||
6230 | + } | ||
6231 | + spin_unlock_irq(&sk->receive_queue.lock); | ||
6232 | + if (clear) | ||
6233 | + kfree_skb(skb); | ||
6234 | + } | ||
6235 | + skb_free_datagram(sk, skb); | ||
6236 | +} | ||
6237 | + | ||
6238 | +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) | ||
6239 | + | ||
6240 | +/** | ||
6241 | + * skb_kill_datagram - Kill a datagram forcibly. | ||
6242 | + * | ||
6243 | + * @sk: Pointer to "struct sock". | ||
6244 | + * @skb: Pointer to "struct sk_buff". | ||
6245 | + * @flags: Flags passed to skb_recv_datagram(). | ||
6246 | + * | ||
6247 | + * Returns nothing. | ||
6248 | + */ | ||
6249 | +static inline void skb_kill_datagram(struct sock *sk, struct sk_buff *skb, | ||
6250 | + int flags) | ||
6251 | +{ | ||
6252 | + /* Clear queue. */ | ||
6253 | + if (flags & MSG_PEEK) { | ||
6254 | + int clear = 0; | ||
6255 | + spin_lock_bh(&sk->sk_receive_queue.lock); | ||
6256 | + if (skb == skb_peek(&sk->sk_receive_queue)) { | ||
6257 | + __skb_unlink(skb, &sk->sk_receive_queue); | ||
6258 | + clear = 1; | ||
6259 | + } | ||
6260 | + spin_unlock_bh(&sk->sk_receive_queue.lock); | ||
6261 | + if (clear) | ||
6262 | + kfree_skb(skb); | ||
6263 | + } | ||
6264 | + skb_free_datagram(sk, skb); | ||
6265 | +} | ||
6266 | + | ||
6267 | +#endif | ||
6268 | + | ||
6269 | +#endif | ||
6270 | + | ||
6271 | +/***** SECTION5: Variables definition section *****/ | ||
6272 | + | ||
6273 | +/* The initial domain. */ | ||
6274 | +struct ccs_domain_info ccs_kernel_domain; | ||
6275 | + | ||
6276 | +/* The list for "struct ccs_domain_info". */ | ||
6277 | +LIST_HEAD(ccs_domain_list); | ||
6278 | + | ||
6279 | +/***** SECTION6: Dependent functions section *****/ | ||
6280 | + | ||
6281 | +/** | ||
6282 | + * ccs_path_matches_group - Check whether the given pathname matches members of the given pathname group. | ||
6283 | + * | ||
6284 | + * @pathname: The name of pathname. | ||
6285 | + * @group: Pointer to "struct ccs_path_group". | ||
6286 | + * | ||
6287 | + * Returns matched member's pathname if @pathname matches pathnames in @group, | ||
6288 | + * NULL otherwise. | ||
6289 | + * | ||
6290 | + * Caller holds ccs_read_lock(). | ||
6291 | + */ | ||
6292 | +static const struct ccs_path_info *ccs_path_matches_group | ||
6293 | +(const struct ccs_path_info *pathname, const struct ccs_group *group) | ||
6294 | +{ | ||
6295 | + struct ccs_path_group *member; | ||
6296 | + list_for_each_entry_srcu(member, &group->member_list, head.list, | ||
6297 | + &ccs_ss) { | ||
6298 | + if (member->head.is_deleted) | ||
6299 | + continue; | ||
6300 | + if (!ccs_path_matches_pattern(pathname, member->member_name)) | ||
6301 | + continue; | ||
6302 | + return member->member_name; | ||
6303 | + } | ||
6304 | + return NULL; | ||
6305 | +} | ||
6306 | + | ||
6307 | +/** | ||
6308 | + * ccs_number_matches_group - Check whether the given number matches members of the given number group. | ||
6309 | + * | ||
6310 | + * @min: Min number. | ||
6311 | + * @max: Max number. | ||
6312 | + * @group: Pointer to "struct ccs_number_group". | ||
6313 | + * | ||
6314 | + * Returns true if @min and @max partially overlaps @group, false otherwise. | ||
6315 | + * | ||
6316 | + * Caller holds ccs_read_lock(). | ||
6317 | + */ | ||
6318 | +static bool ccs_number_matches_group(const unsigned long min, | ||
6319 | + const unsigned long max, | ||
6320 | + const struct ccs_group *group) | ||
6321 | +{ | ||
6322 | + struct ccs_number_group *member; | ||
6323 | + bool matched = false; | ||
6324 | + list_for_each_entry_srcu(member, &group->member_list, head.list, | ||
6325 | + &ccs_ss) { | ||
6326 | + if (member->head.is_deleted) | ||
6327 | + continue; | ||
6328 | + if (min > member->number.values[1] || | ||
6329 | + max < member->number.values[0]) | ||
6330 | + continue; | ||
6331 | + matched = true; | ||
6332 | + break; | ||
6333 | + } | ||
6334 | + return matched; | ||
6335 | +} | ||
6336 | + | ||
6337 | +/** | ||
6338 | + * ccs_check_entry - Do permission check. | ||
6339 | + * | ||
6340 | + * @r: Pointer to "struct ccs_request_info". | ||
6341 | + * @ptr: Pointer to "struct ccs_acl_info". | ||
6342 | + * | ||
6343 | + * Returns true on match, false otherwise. | ||
6344 | + * | ||
6345 | + * Caller holds ccs_read_lock(). | ||
6346 | + */ | ||
6347 | +static bool ccs_check_entry(struct ccs_request_info *r, | ||
6348 | + struct ccs_acl_info *ptr) | ||
6349 | +{ | ||
6350 | + if (ptr->is_deleted || ptr->type != r->param_type) | ||
6351 | + return false; | ||
6352 | + switch (r->param_type) { | ||
6353 | + case CCS_TYPE_PATH_ACL: | ||
6354 | + return ccs_check_path_acl(r, ptr); | ||
6355 | + case CCS_TYPE_PATH2_ACL: | ||
6356 | + return ccs_check_path2_acl(r, ptr); | ||
6357 | + case CCS_TYPE_PATH_NUMBER_ACL: | ||
6358 | + return ccs_check_path_number_acl(r, ptr); | ||
6359 | + case CCS_TYPE_MKDEV_ACL: | ||
6360 | + return ccs_check_mkdev_acl(r, ptr); | ||
6361 | + case CCS_TYPE_MOUNT_ACL: | ||
6362 | + return ccs_check_mount_acl(r, ptr); | ||
6363 | +#ifdef CONFIG_CCSECURITY_MISC | ||
6364 | + case CCS_TYPE_ENV_ACL: | ||
6365 | + return ccs_check_env_acl(r, ptr); | ||
6366 | +#endif | ||
6367 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
6368 | + case CCS_TYPE_CAPABILITY_ACL: | ||
6369 | + return ccs_check_capability_acl(r, ptr); | ||
6370 | +#endif | ||
6371 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
6372 | + case CCS_TYPE_INET_ACL: | ||
6373 | + return ccs_check_inet_acl(r, ptr); | ||
6374 | + case CCS_TYPE_UNIX_ACL: | ||
6375 | + return ccs_check_unix_acl(r, ptr); | ||
6376 | +#endif | ||
6377 | +#ifdef CONFIG_CCSECURITY_IPC | ||
6378 | + case CCS_TYPE_SIGNAL_ACL: | ||
6379 | + return ccs_check_signal_acl(r, ptr); | ||
6380 | +#endif | ||
6381 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
6382 | + case CCS_TYPE_MANUAL_TASK_ACL: | ||
6383 | + return ccs_check_task_acl(r, ptr); | ||
6384 | +#endif | ||
6385 | + } | ||
6386 | + return true; | ||
6387 | +} | ||
6388 | + | ||
6389 | +/** | ||
6390 | + * ccs_check_acl - Do permission check. | ||
6391 | + * | ||
6392 | + * @r: Pointer to "struct ccs_request_info". | ||
6393 | + * | ||
6394 | + * Returns 0 on success, negative value otherwise. | ||
6395 | + * | ||
6396 | + * Caller holds ccs_read_lock(). | ||
6397 | + */ | ||
6398 | +int ccs_check_acl(struct ccs_request_info *r) | ||
6399 | +{ | ||
6400 | + const struct ccs_domain_info *domain = ccs_current_domain(); | ||
6401 | + int error; | ||
6402 | + do { | ||
6403 | + struct ccs_acl_info *ptr; | ||
6404 | + const struct list_head *list = &domain->acl_info_list; | ||
6405 | + u16 i = 0; | ||
6406 | +retry: | ||
6407 | + list_for_each_entry_srcu(ptr, list, list, &ccs_ss) { | ||
6408 | + if (!ccs_check_entry(r, ptr)) | ||
6409 | + continue; | ||
6410 | + if (!ccs_condition(r, ptr->cond)) | ||
6411 | + continue; | ||
6412 | + r->matched_acl = ptr; | ||
6413 | + r->granted = true; | ||
6414 | + ccs_audit_log(r); | ||
6415 | + return 0; | ||
6416 | + } | ||
6417 | + for (; i < CCS_MAX_ACL_GROUPS; i++) { | ||
6418 | + if (!test_bit(i, domain->group)) | ||
6419 | + continue; | ||
6420 | + list = &domain->ns->acl_group[i++]; | ||
6421 | + goto retry; | ||
6422 | + } | ||
6423 | + r->granted = false; | ||
6424 | + error = ccs_audit_log(r); | ||
6425 | + } while (error == CCS_RETRY_REQUEST && | ||
6426 | + r->type != CCS_MAC_FILE_EXECUTE); | ||
6427 | + return error; | ||
6428 | +} | ||
6429 | + | ||
6430 | +/** | ||
6431 | + * ccs_last_word - Get last component of a domainname. | ||
6432 | + * | ||
6433 | + * @name: Domainname to check. | ||
6434 | + * | ||
6435 | + * Returns the last word of @name. | ||
6436 | + */ | ||
6437 | +static const char *ccs_last_word(const char *name) | ||
6438 | +{ | ||
6439 | + const char *cp = strrchr(name, ' '); | ||
6440 | + if (cp) | ||
6441 | + return cp + 1; | ||
6442 | + return name; | ||
6443 | +} | ||
6444 | + | ||
6445 | +/** | ||
6446 | + * ccs_scan_transition - Try to find specific domain transition type. | ||
6447 | + * | ||
6448 | + * @list: Pointer to "struct list_head". | ||
6449 | + * @domainname: The name of current domain. | ||
6450 | + * @program: The name of requested program. | ||
6451 | + * @last_name: The last component of @domainname. | ||
6452 | + * @type: One of values in "enum ccs_transition_type". | ||
6453 | + * | ||
6454 | + * Returns true if found one, false otherwise. | ||
6455 | + * | ||
6456 | + * Caller holds ccs_read_lock(). | ||
6457 | + */ | ||
6458 | +static bool ccs_scan_transition(const struct list_head *list, | ||
6459 | + const struct ccs_path_info *domainname, | ||
6460 | + const struct ccs_path_info *program, | ||
6461 | + const char *last_name, | ||
6462 | + const enum ccs_transition_type type) | ||
6463 | +{ | ||
6464 | + const struct ccs_transition_control *ptr; | ||
6465 | + list_for_each_entry_srcu(ptr, list, head.list, &ccs_ss) { | ||
6466 | + if (ptr->head.is_deleted || ptr->type != type) | ||
6467 | + continue; | ||
6468 | + if (ptr->domainname) { | ||
6469 | + if (!ptr->is_last_name) { | ||
6470 | + if (ptr->domainname != domainname) | ||
6471 | + continue; | ||
6472 | + } else { | ||
6473 | + /* | ||
6474 | + * Use direct strcmp() since this is | ||
6475 | + * unlikely used. | ||
6476 | + */ | ||
6477 | + if (strcmp(ptr->domainname->name, last_name)) | ||
6478 | + continue; | ||
6479 | + } | ||
6480 | + } | ||
6481 | + if (ptr->program && ccs_pathcmp(ptr->program, program)) | ||
6482 | + continue; | ||
6483 | + return true; | ||
6484 | + } | ||
6485 | + return false; | ||
6486 | +} | ||
6487 | + | ||
6488 | +/** | ||
6489 | + * ccs_transition_type - Get domain transition type. | ||
6490 | + * | ||
6491 | + * @ns: Pointer to "struct ccs_policy_namespace". | ||
6492 | + * @domainname: The name of current domain. | ||
6493 | + * @program: The name of requested program. | ||
6494 | + * | ||
6495 | + * Returns CCS_TRANSITION_CONTROL_TRANSIT if executing @program causes domain | ||
6496 | + * transition across namespaces, CCS_TRANSITION_CONTROL_INITIALIZE if executing | ||
6497 | + * @program reinitializes domain transition within that namespace, | ||
6498 | + * CCS_TRANSITION_CONTROL_KEEP if executing @program stays at @domainname , | ||
6499 | + * others otherwise. | ||
6500 | + * | ||
6501 | + * Caller holds ccs_read_lock(). | ||
6502 | + */ | ||
6503 | +static enum ccs_transition_type ccs_transition_type | ||
6504 | +(const struct ccs_policy_namespace *ns, const struct ccs_path_info *domainname, | ||
6505 | + const struct ccs_path_info *program) | ||
6506 | +{ | ||
6507 | + const char *last_name = ccs_last_word(domainname->name); | ||
6508 | + enum ccs_transition_type type = CCS_TRANSITION_CONTROL_NO_RESET; | ||
6509 | + while (type < CCS_MAX_TRANSITION_TYPE) { | ||
6510 | + const struct list_head * const list = | ||
6511 | + &ns->policy_list[CCS_ID_TRANSITION_CONTROL]; | ||
6512 | + if (!ccs_scan_transition(list, domainname, program, last_name, | ||
6513 | + type)) { | ||
6514 | + type++; | ||
6515 | + continue; | ||
6516 | + } | ||
6517 | + if (type != CCS_TRANSITION_CONTROL_NO_RESET && | ||
6518 | + type != CCS_TRANSITION_CONTROL_NO_INITIALIZE) | ||
6519 | + break; | ||
6520 | + /* | ||
6521 | + * Do not check for reset_domain if no_reset_domain matched. | ||
6522 | + * Do not check for initialize_domain if no_initialize_domain | ||
6523 | + * matched. | ||
6524 | + */ | ||
6525 | + type++; | ||
6526 | + type++; | ||
6527 | + } | ||
6528 | + return type; | ||
6529 | +} | ||
6530 | + | ||
6531 | +/** | ||
6532 | + * ccs_find_next_domain - Find a domain. | ||
6533 | + * | ||
6534 | + * @ee: Pointer to "struct ccs_execve". | ||
6535 | + * | ||
6536 | + * Returns 0 on success, negative value otherwise. | ||
6537 | + * | ||
6538 | + * Caller holds ccs_read_lock(). | ||
6539 | + */ | ||
6540 | +static int ccs_find_next_domain(struct ccs_execve *ee) | ||
6541 | +{ | ||
6542 | + struct ccs_request_info *r = &ee->r; | ||
6543 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
6544 | + const struct ccs_path_info *handler = ee->handler; | ||
6545 | +#endif | ||
6546 | + struct ccs_domain_info *domain = NULL; | ||
6547 | + struct ccs_domain_info * const old_domain = ccs_current_domain(); | ||
6548 | + struct linux_binprm *bprm = ee->bprm; | ||
6549 | + struct ccs_security *task = ccs_current_security(); | ||
6550 | + const struct ccs_path_info *candidate; | ||
6551 | + struct ccs_path_info exename; | ||
6552 | + int retval; | ||
6553 | + bool reject_on_transition_failure = false; | ||
6554 | + | ||
6555 | + /* Get symlink's pathname of program. */ | ||
6556 | + retval = ccs_symlink_path(bprm->filename, &exename); | ||
6557 | + if (retval < 0) | ||
6558 | + return retval; | ||
6559 | + | ||
6560 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
6561 | + if (handler) { | ||
6562 | + /* No permission check for execute handler. */ | ||
6563 | + candidate = &exename; | ||
6564 | + if (ccs_pathcmp(candidate, handler)) { | ||
6565 | + /* Failed to verify execute handler. */ | ||
6566 | + static u8 counter = 20; | ||
6567 | + if (counter) { | ||
6568 | + counter--; | ||
6569 | + printk(KERN_WARNING "Failed to verify: %s\n", | ||
6570 | + handler->name); | ||
6571 | + } | ||
6572 | + goto out; | ||
6573 | + } | ||
6574 | + } else | ||
6575 | +#endif | ||
6576 | + { | ||
6577 | + struct ccs_aggregator *ptr; | ||
6578 | + struct list_head *list; | ||
6579 | +retry: | ||
6580 | + /* Check 'aggregator' directive. */ | ||
6581 | + candidate = &exename; | ||
6582 | + list = &old_domain->ns->policy_list[CCS_ID_AGGREGATOR]; | ||
6583 | + list_for_each_entry_srcu(ptr, list, head.list, &ccs_ss) { | ||
6584 | + if (ptr->head.is_deleted || | ||
6585 | + !ccs_path_matches_pattern(candidate, | ||
6586 | + ptr->original_name)) | ||
6587 | + continue; | ||
6588 | + candidate = ptr->aggregated_name; | ||
6589 | + break; | ||
6590 | + } | ||
6591 | + | ||
6592 | + /* Check execute permission. */ | ||
6593 | + retval = ccs_execute_permission(r, candidate); | ||
6594 | + if (retval == CCS_RETRY_REQUEST) | ||
6595 | + goto retry; | ||
6596 | + if (retval < 0) | ||
6597 | + goto out; | ||
6598 | + /* | ||
6599 | + * To be able to specify domainnames with wildcards, use the | ||
6600 | + * pathname specified in the policy (which may contain | ||
6601 | + * wildcard) rather than the pathname passed to execve() | ||
6602 | + * (which never contains wildcard). | ||
6603 | + */ | ||
6604 | + if (r->param.path.matched_path) | ||
6605 | + candidate = r->param.path.matched_path; | ||
6606 | + } | ||
6607 | + /* | ||
6608 | + * Check for domain transition preference if "file execute" matched. | ||
6609 | + * If preference is given, make do_execve() fail if domain transition | ||
6610 | + * has failed, for domain transition preference should be used with | ||
6611 | + * destination domain defined. | ||
6612 | + */ | ||
6613 | + if (r->ee->transition) { | ||
6614 | + const char *domainname = r->ee->transition->name; | ||
6615 | + reject_on_transition_failure = true; | ||
6616 | + if (!strcmp(domainname, "keep")) | ||
6617 | + goto force_keep_domain; | ||
6618 | + if (!strcmp(domainname, "child")) | ||
6619 | + goto force_child_domain; | ||
6620 | + if (!strcmp(domainname, "reset")) | ||
6621 | + goto force_reset_domain; | ||
6622 | + if (!strcmp(domainname, "initialize")) | ||
6623 | + goto force_initialize_domain; | ||
6624 | + if (!strcmp(domainname, "parent")) { | ||
6625 | + char *cp; | ||
6626 | + strncpy(ee->tmp, old_domain->domainname->name, | ||
6627 | + CCS_EXEC_TMPSIZE - 1); | ||
6628 | + cp = strrchr(ee->tmp, ' '); | ||
6629 | + if (cp) | ||
6630 | + *cp = '\0'; | ||
6631 | + } else if (*domainname == '<') | ||
6632 | + strncpy(ee->tmp, domainname, CCS_EXEC_TMPSIZE - 1); | ||
6633 | + else | ||
6634 | + snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s %s", | ||
6635 | + old_domain->domainname->name, domainname); | ||
6636 | + goto force_jump_domain; | ||
6637 | + } | ||
6638 | + /* | ||
6639 | + * No domain transition preference specified. | ||
6640 | + * Calculate domain to transit to. | ||
6641 | + */ | ||
6642 | + switch (ccs_transition_type(old_domain->ns, old_domain->domainname, | ||
6643 | + candidate)) { | ||
6644 | + case CCS_TRANSITION_CONTROL_RESET: | ||
6645 | +force_reset_domain: | ||
6646 | + /* Transit to the root of specified namespace. */ | ||
6647 | + snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "<%s>", | ||
6648 | + candidate->name); | ||
6649 | + /* | ||
6650 | + * Make do_execve() fail if domain transition across namespaces | ||
6651 | + * has failed. | ||
6652 | + */ | ||
6653 | + reject_on_transition_failure = true; | ||
6654 | + break; | ||
6655 | + case CCS_TRANSITION_CONTROL_INITIALIZE: | ||
6656 | +force_initialize_domain: | ||
6657 | + /* Transit to the child of current namespace's root. */ | ||
6658 | + snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s %s", | ||
6659 | + old_domain->ns->name, candidate->name); | ||
6660 | + break; | ||
6661 | + case CCS_TRANSITION_CONTROL_KEEP: | ||
6662 | +force_keep_domain: | ||
6663 | + /* Keep current domain. */ | ||
6664 | + domain = old_domain; | ||
6665 | + break; | ||
6666 | + default: | ||
6667 | + if (old_domain == &ccs_kernel_domain && !ccs_policy_loaded) { | ||
6668 | + /* | ||
6669 | + * Needn't to transit from kernel domain before | ||
6670 | + * starting /sbin/init. But transit from kernel domain | ||
6671 | + * if executing initializers because they might start | ||
6672 | + * before /sbin/init. | ||
6673 | + */ | ||
6674 | + domain = old_domain; | ||
6675 | + break; | ||
6676 | + } | ||
6677 | +force_child_domain: | ||
6678 | + /* Normal domain transition. */ | ||
6679 | + snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s %s", | ||
6680 | + old_domain->domainname->name, candidate->name); | ||
6681 | + break; | ||
6682 | + } | ||
6683 | +force_jump_domain: | ||
6684 | + /* | ||
6685 | + * Tell GC that I started execve(). | ||
6686 | + * Also, tell open_exec() to check read permission. | ||
6687 | + */ | ||
6688 | + task->ccs_flags |= CCS_TASK_IS_IN_EXECVE; | ||
6689 | + /* | ||
6690 | + * Make task->ccs_flags visible to GC before changing | ||
6691 | + * task->ccs_domain_info. | ||
6692 | + */ | ||
6693 | + smp_wmb(); | ||
6694 | + /* | ||
6695 | + * Proceed to the next domain in order to allow reaching via PID. | ||
6696 | + * It will be reverted if execve() failed. Reverting is not good. | ||
6697 | + * But it is better than being unable to reach via PID in interactive | ||
6698 | + * enforcing mode. | ||
6699 | + */ | ||
6700 | + if (!domain) | ||
6701 | + domain = ccs_assign_domain(ee->tmp, true); | ||
6702 | + if (domain) | ||
6703 | + retval = 0; | ||
6704 | + else if (reject_on_transition_failure) { | ||
6705 | + printk(KERN_WARNING | ||
6706 | + "ERROR: Domain '%s' not ready.\n", ee->tmp); | ||
6707 | + retval = -ENOMEM; | ||
6708 | + } else if (r->mode == CCS_CONFIG_ENFORCING) | ||
6709 | + retval = -ENOMEM; | ||
6710 | + else { | ||
6711 | + retval = 0; | ||
6712 | + if (!old_domain->flags[CCS_DIF_TRANSITION_FAILED]) { | ||
6713 | + old_domain->flags[CCS_DIF_TRANSITION_FAILED] = true; | ||
6714 | + r->granted = false; | ||
6715 | + ccs_write_log(r, "%s", | ||
6716 | + ccs_dif[CCS_DIF_TRANSITION_FAILED]); | ||
6717 | + printk(KERN_WARNING | ||
6718 | + "ERROR: Domain '%s' not defined.\n", ee->tmp); | ||
6719 | + } | ||
6720 | + } | ||
6721 | +out: | ||
6722 | + kfree(exename.name); | ||
6723 | + return retval; | ||
6724 | +} | ||
6725 | + | ||
6726 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
6727 | + | ||
6728 | +/** | ||
6729 | + * ccs_unescape - Unescape escaped string. | ||
6730 | + * | ||
6731 | + * @dest: String to unescape. | ||
6732 | + * | ||
6733 | + * Returns nothing. | ||
6734 | + */ | ||
6735 | +static void ccs_unescape(unsigned char *dest) | ||
6736 | +{ | ||
6737 | + unsigned char *src = dest; | ||
6738 | + unsigned char c; | ||
6739 | + unsigned char d; | ||
6740 | + unsigned char e; | ||
6741 | + while (1) { | ||
6742 | + c = *src++; | ||
6743 | + if (!c) | ||
6744 | + break; | ||
6745 | + if (c != '\\') { | ||
6746 | + *dest++ = c; | ||
6747 | + continue; | ||
6748 | + } | ||
6749 | + c = *src++; | ||
6750 | + if (c == '\\') { | ||
6751 | + *dest++ = c; | ||
6752 | + continue; | ||
6753 | + } | ||
6754 | + if (c < '0' || c > '3') | ||
6755 | + break; | ||
6756 | + d = *src++; | ||
6757 | + if (d < '0' || d > '7') | ||
6758 | + break; | ||
6759 | + e = *src++; | ||
6760 | + if (e < '0' || e > '7') | ||
6761 | + break; | ||
6762 | + *dest++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0'); | ||
6763 | + } | ||
6764 | + *dest = '\0'; | ||
6765 | +} | ||
6766 | + | ||
6767 | +/** | ||
6768 | + * ccs_try_alt_exec - Try to start execute handler. | ||
6769 | + * | ||
6770 | + * @ee: Pointer to "struct ccs_execve". | ||
6771 | + * | ||
6772 | + * Returns 0 on success, negative value otherwise. | ||
6773 | + */ | ||
6774 | +static int ccs_try_alt_exec(struct ccs_execve *ee) | ||
6775 | +{ | ||
6776 | + /* | ||
6777 | + * Contents of modified bprm. | ||
6778 | + * The envp[] in original bprm is moved to argv[] so that | ||
6779 | + * the alternatively executed program won't be affected by | ||
6780 | + * some dangerous environment variables like LD_PRELOAD. | ||
6781 | + * | ||
6782 | + * modified bprm->argc | ||
6783 | + * = original bprm->argc + original bprm->envc + 7 | ||
6784 | + * modified bprm->envc | ||
6785 | + * = 0 | ||
6786 | + * | ||
6787 | + * modified bprm->argv[0] | ||
6788 | + * = the program's name specified by *_execute_handler | ||
6789 | + * modified bprm->argv[1] | ||
6790 | + * = ccs_current_domain()->domainname->name | ||
6791 | + * modified bprm->argv[2] | ||
6792 | + * = the current process's name | ||
6793 | + * modified bprm->argv[3] | ||
6794 | + * = the current process's information (e.g. uid/gid). | ||
6795 | + * modified bprm->argv[4] | ||
6796 | + * = original bprm->filename | ||
6797 | + * modified bprm->argv[5] | ||
6798 | + * = original bprm->argc in string expression | ||
6799 | + * modified bprm->argv[6] | ||
6800 | + * = original bprm->envc in string expression | ||
6801 | + * modified bprm->argv[7] | ||
6802 | + * = original bprm->argv[0] | ||
6803 | + * ... | ||
6804 | + * modified bprm->argv[bprm->argc + 6] | ||
6805 | + * = original bprm->argv[bprm->argc - 1] | ||
6806 | + * modified bprm->argv[bprm->argc + 7] | ||
6807 | + * = original bprm->envp[0] | ||
6808 | + * ... | ||
6809 | + * modified bprm->argv[bprm->envc + bprm->argc + 6] | ||
6810 | + * = original bprm->envp[bprm->envc - 1] | ||
6811 | + */ | ||
6812 | + struct linux_binprm *bprm = ee->bprm; | ||
6813 | + struct file *filp; | ||
6814 | + int retval; | ||
6815 | + const int original_argc = bprm->argc; | ||
6816 | + const int original_envc = bprm->envc; | ||
6817 | + | ||
6818 | + /* Close the requested program's dentry. */ | ||
6819 | + ee->obj.path1.dentry = NULL; | ||
6820 | + ee->obj.path1.mnt = NULL; | ||
6821 | + ee->obj.stat_valid[CCS_PATH1] = false; | ||
6822 | + ee->obj.stat_valid[CCS_PATH1_PARENT] = false; | ||
6823 | + ee->obj.validate_done = false; | ||
6824 | + allow_write_access(bprm->file); | ||
6825 | + fput(bprm->file); | ||
6826 | + bprm->file = NULL; | ||
6827 | + | ||
6828 | + /* Invalidate page dump cache. */ | ||
6829 | + ee->dump.page = NULL; | ||
6830 | + | ||
6831 | + /* Move envp[] to argv[] */ | ||
6832 | + bprm->argc += bprm->envc; | ||
6833 | + bprm->envc = 0; | ||
6834 | + | ||
6835 | + /* Set argv[6] */ | ||
6836 | + { | ||
6837 | + snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_envc); | ||
6838 | + retval = ccs_copy_argv(ee->tmp, bprm); | ||
6839 | + if (retval < 0) | ||
6840 | + goto out; | ||
6841 | + } | ||
6842 | + | ||
6843 | + /* Set argv[5] */ | ||
6844 | + { | ||
6845 | + snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_argc); | ||
6846 | + retval = ccs_copy_argv(ee->tmp, bprm); | ||
6847 | + if (retval < 0) | ||
6848 | + goto out; | ||
6849 | + } | ||
6850 | + | ||
6851 | + /* Set argv[4] */ | ||
6852 | + { | ||
6853 | + retval = ccs_copy_argv(bprm->filename, bprm); | ||
6854 | + if (retval < 0) | ||
6855 | + goto out; | ||
6856 | + } | ||
6857 | + | ||
6858 | + /* Set argv[3] */ | ||
6859 | + { | ||
6860 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) | ||
6861 | + /* | ||
6862 | + * Pass uid/gid seen from current user namespace, for these | ||
6863 | + * values are used by programs in current user namespace in | ||
6864 | + * order to decide whether to execve() or not (rather than by | ||
6865 | + * auditing daemon in init's user namespace). | ||
6866 | + */ | ||
6867 | + snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, | ||
6868 | + "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d " | ||
6869 | + "sgid=%d fsuid=%d fsgid=%d", ccs_sys_getpid(), | ||
6870 | + __kuid_val(current_uid()), __kgid_val(current_gid()), | ||
6871 | + __kuid_val(current_euid()), | ||
6872 | + __kgid_val(current_egid()), | ||
6873 | + __kuid_val(current_suid()), | ||
6874 | + __kgid_val(current_sgid()), | ||
6875 | + __kuid_val(current_fsuid()), | ||
6876 | + __kgid_val(current_fsgid())); | ||
6877 | +#else | ||
6878 | + snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, | ||
6879 | + "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d " | ||
6880 | + "sgid=%d fsuid=%d fsgid=%d", ccs_sys_getpid(), | ||
6881 | + current_uid(), current_gid(), current_euid(), | ||
6882 | + current_egid(), current_suid(), current_sgid(), | ||
6883 | + current_fsuid(), current_fsgid()); | ||
6884 | +#endif | ||
6885 | + retval = ccs_copy_argv(ee->tmp, bprm); | ||
6886 | + if (retval < 0) | ||
6887 | + goto out; | ||
6888 | + } | ||
6889 | + | ||
6890 | + /* Set argv[2] */ | ||
6891 | + { | ||
6892 | + char *exe = (char *) ccs_get_exe(); | ||
6893 | + if (exe) { | ||
6894 | + retval = ccs_copy_argv(exe, bprm); | ||
6895 | + kfree(exe); | ||
6896 | + } else { | ||
6897 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | ||
6898 | + retval = ccs_copy_argv("<unknown>", bprm); | ||
6899 | +#else | ||
6900 | + snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "<unknown>"); | ||
6901 | + retval = ccs_copy_argv(ee->tmp, bprm); | ||
6902 | +#endif | ||
6903 | + } | ||
6904 | + if (retval < 0) | ||
6905 | + goto out; | ||
6906 | + } | ||
6907 | + | ||
6908 | + /* Set argv[1] */ | ||
6909 | + { | ||
6910 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | ||
6911 | + retval = ccs_copy_argv(ccs_current_domain()->domainname->name, | ||
6912 | + bprm); | ||
6913 | +#else | ||
6914 | + snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s", | ||
6915 | + ccs_current_domain()->domainname->name); | ||
6916 | + retval = ccs_copy_argv(ee->tmp, bprm); | ||
6917 | +#endif | ||
6918 | + if (retval < 0) | ||
6919 | + goto out; | ||
6920 | + } | ||
6921 | + | ||
6922 | + /* Set argv[0] */ | ||
6923 | + { | ||
6924 | + struct path root; | ||
6925 | + char *cp; | ||
6926 | + int root_len; | ||
6927 | + int handler_len; | ||
6928 | + get_fs_root(current->fs, &root); | ||
6929 | + cp = ccs_realpath(&root); | ||
6930 | + path_put(&root); | ||
6931 | + if (!cp) { | ||
6932 | + retval = -ENOMEM; | ||
6933 | + goto out; | ||
6934 | + } | ||
6935 | + root_len = strlen(cp); | ||
6936 | + retval = strncmp(ee->handler->name, cp, root_len); | ||
6937 | + root_len--; | ||
6938 | + kfree(cp); | ||
6939 | + if (retval) { | ||
6940 | + retval = -ENOENT; | ||
6941 | + goto out; | ||
6942 | + } | ||
6943 | + handler_len = ee->handler->total_len + 1; | ||
6944 | + cp = kmalloc(handler_len, CCS_GFP_FLAGS); | ||
6945 | + if (!cp) { | ||
6946 | + retval = -ENOMEM; | ||
6947 | + goto out; | ||
6948 | + } | ||
6949 | + /* ee->handler_path is released by ccs_finish_execve(). */ | ||
6950 | + ee->handler_path = cp; | ||
6951 | + /* Adjust root directory for open_exec(). */ | ||
6952 | + memmove(cp, ee->handler->name + root_len, | ||
6953 | + handler_len - root_len); | ||
6954 | + ccs_unescape(cp); | ||
6955 | + retval = -ENOENT; | ||
6956 | + if (*cp != '/') | ||
6957 | + goto out; | ||
6958 | + retval = ccs_copy_argv(cp, bprm); | ||
6959 | + if (retval < 0) | ||
6960 | + goto out; | ||
6961 | + } | ||
6962 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) | ||
6963 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24) | ||
6964 | + bprm->argv_len = bprm->exec - bprm->p; | ||
6965 | +#endif | ||
6966 | +#endif | ||
6967 | + | ||
6968 | + /* | ||
6969 | + * OK, now restart the process with execute handler program's dentry. | ||
6970 | + */ | ||
6971 | + filp = open_exec(ee->handler_path); | ||
6972 | + if (IS_ERR(filp)) { | ||
6973 | + retval = PTR_ERR(filp); | ||
6974 | + goto out; | ||
6975 | + } | ||
6976 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) | ||
6977 | + ee->obj.path1 = filp->f_path; | ||
6978 | +#else | ||
6979 | + ee->obj.path1.dentry = filp->f_dentry; | ||
6980 | + ee->obj.path1.mnt = filp->f_vfsmnt; | ||
6981 | +#endif | ||
6982 | + bprm->file = filp; | ||
6983 | + bprm->filename = ee->handler_path; | ||
6984 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) | ||
6985 | + bprm->interp = bprm->filename; | ||
6986 | +#endif | ||
6987 | + retval = prepare_binprm(bprm); | ||
6988 | + if (retval < 0) | ||
6989 | + goto out; | ||
6990 | + ee->r.dont_sleep_on_enforce_error = true; | ||
6991 | + retval = ccs_find_next_domain(ee); | ||
6992 | + ee->r.dont_sleep_on_enforce_error = false; | ||
6993 | +out: | ||
6994 | + return retval; | ||
6995 | +} | ||
6996 | + | ||
6997 | +/** | ||
6998 | + * ccs_find_execute_handler - Find an execute handler. | ||
6999 | + * | ||
7000 | + * @ee: Pointer to "struct ccs_execve". | ||
7001 | + * @type: Type of execute handler. | ||
7002 | + * | ||
7003 | + * Returns true if found, false otherwise. | ||
7004 | + * | ||
7005 | + * Caller holds ccs_read_lock(). | ||
7006 | + */ | ||
7007 | +static bool ccs_find_execute_handler(struct ccs_execve *ee, const u8 type) | ||
7008 | +{ | ||
7009 | + struct ccs_request_info *r = &ee->r; | ||
7010 | + /* | ||
7011 | + * To avoid infinite execute handler loop, don't use execute handler | ||
7012 | + * if the current process is marked as execute handler. | ||
7013 | + */ | ||
7014 | + if (ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER) | ||
7015 | + return false; | ||
7016 | + r->param_type = type; | ||
7017 | + ccs_check_acl(r); | ||
7018 | + if (!r->granted) | ||
7019 | + return false; | ||
7020 | + ee->handler = container_of(r->matched_acl, struct ccs_handler_acl, | ||
7021 | + head)->handler; | ||
7022 | + ee->transition = r->matched_acl && r->matched_acl->cond && | ||
7023 | + r->matched_acl->cond->exec_transit ? | ||
7024 | + r->matched_acl->cond->transit : NULL; | ||
7025 | + return true; | ||
7026 | +} | ||
7027 | + | ||
7028 | +#endif | ||
7029 | + | ||
7030 | +#ifdef CONFIG_MMU | ||
7031 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) | ||
7032 | +#define CCS_BPRM_MMU | ||
7033 | +#elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR >= 3 | ||
7034 | +#define CCS_BPRM_MMU | ||
7035 | +#elif defined(AX_MAJOR) && AX_MAJOR == 3 && defined(AX_MINOR) && AX_MINOR >= 2 | ||
7036 | +#define CCS_BPRM_MMU | ||
7037 | +#endif | ||
7038 | +#endif | ||
7039 | + | ||
7040 | +/** | ||
7041 | + * ccs_dump_page - Dump a page to buffer. | ||
7042 | + * | ||
7043 | + * @bprm: Pointer to "struct linux_binprm". | ||
7044 | + * @pos: Location to dump. | ||
7045 | + * @dump: Poiner to "struct ccs_page_dump". | ||
7046 | + * | ||
7047 | + * Returns true on success, false otherwise. | ||
7048 | + */ | ||
7049 | +bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos, | ||
7050 | + struct ccs_page_dump *dump) | ||
7051 | +{ | ||
7052 | + struct page *page; | ||
7053 | + /* dump->data is released by ccs_start_execve(). */ | ||
7054 | + if (!dump->data) { | ||
7055 | + dump->data = kzalloc(PAGE_SIZE, CCS_GFP_FLAGS); | ||
7056 | + if (!dump->data) | ||
7057 | + return false; | ||
7058 | + } | ||
7059 | + /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */ | ||
7060 | +#ifdef CCS_BPRM_MMU | ||
7061 | + if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) | ||
7062 | + return false; | ||
7063 | +#else | ||
7064 | + page = bprm->page[pos / PAGE_SIZE]; | ||
7065 | +#endif | ||
7066 | + if (page != dump->page) { | ||
7067 | + const unsigned int offset = pos % PAGE_SIZE; | ||
7068 | + /* | ||
7069 | + * Maybe kmap()/kunmap() should be used here. | ||
7070 | + * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic(). | ||
7071 | + * So do I. | ||
7072 | + */ | ||
7073 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) | ||
7074 | + char *kaddr = kmap_atomic(page); | ||
7075 | +#else | ||
7076 | + char *kaddr = kmap_atomic(page, KM_USER0); | ||
7077 | +#endif | ||
7078 | + dump->page = page; | ||
7079 | + memcpy(dump->data + offset, kaddr + offset, | ||
7080 | + PAGE_SIZE - offset); | ||
7081 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) | ||
7082 | + kunmap_atomic(kaddr); | ||
7083 | +#else | ||
7084 | + kunmap_atomic(kaddr, KM_USER0); | ||
7085 | +#endif | ||
7086 | + } | ||
7087 | + /* Same with put_arg_page(page) in fs/exec.c */ | ||
7088 | +#ifdef CCS_BPRM_MMU | ||
7089 | + put_page(page); | ||
7090 | +#endif | ||
7091 | + return true; | ||
7092 | +} | ||
7093 | + | ||
7094 | +/** | ||
7095 | + * ccs_start_execve - Prepare for execve() operation. | ||
7096 | + * | ||
7097 | + * @bprm: Pointer to "struct linux_binprm". | ||
7098 | + * @eep: Pointer to "struct ccs_execve *". | ||
7099 | + * | ||
7100 | + * Returns 0 on success, negative value otherwise. | ||
7101 | + */ | ||
7102 | +static int ccs_start_execve(struct linux_binprm *bprm, | ||
7103 | + struct ccs_execve **eep) | ||
7104 | +{ | ||
7105 | + int retval; | ||
7106 | + struct ccs_security *task = ccs_current_security(); | ||
7107 | + struct ccs_execve *ee; | ||
7108 | + int idx; | ||
7109 | + *eep = NULL; | ||
7110 | + ee = kzalloc(sizeof(*ee), CCS_GFP_FLAGS); | ||
7111 | + if (!ee) | ||
7112 | + return -ENOMEM; | ||
7113 | + ee->tmp = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS); | ||
7114 | + if (!ee->tmp) { | ||
7115 | + kfree(ee); | ||
7116 | + return -ENOMEM; | ||
7117 | + } | ||
7118 | + idx = ccs_read_lock(); | ||
7119 | + /* ee->dump->data is allocated by ccs_dump_page(). */ | ||
7120 | + ee->previous_domain = task->ccs_domain_info; | ||
7121 | + /* Clear manager flag. */ | ||
7122 | + task->ccs_flags &= ~CCS_TASK_IS_MANAGER; | ||
7123 | + *eep = ee; | ||
7124 | + ccs_init_request_info(&ee->r, CCS_MAC_FILE_EXECUTE); | ||
7125 | + ee->r.ee = ee; | ||
7126 | + ee->bprm = bprm; | ||
7127 | + ee->r.obj = &ee->obj; | ||
7128 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) | ||
7129 | + ee->obj.path1 = bprm->file->f_path; | ||
7130 | +#else | ||
7131 | + ee->obj.path1.dentry = bprm->file->f_dentry; | ||
7132 | + ee->obj.path1.mnt = bprm->file->f_vfsmnt; | ||
7133 | +#endif | ||
7134 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
7135 | + /* | ||
7136 | + * No need to call ccs_environ() for execute handler because envp[] is | ||
7137 | + * moved to argv[]. | ||
7138 | + */ | ||
7139 | + if (ccs_find_execute_handler(ee, CCS_TYPE_AUTO_EXECUTE_HANDLER)) { | ||
7140 | + retval = ccs_try_alt_exec(ee); | ||
7141 | + goto done; | ||
7142 | + } | ||
7143 | +#endif | ||
7144 | + retval = ccs_find_next_domain(ee); | ||
7145 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
7146 | + if (retval == -EPERM && | ||
7147 | + ccs_find_execute_handler(ee, CCS_TYPE_DENIED_EXECUTE_HANDLER)) { | ||
7148 | + retval = ccs_try_alt_exec(ee); | ||
7149 | + goto done; | ||
7150 | + } | ||
7151 | +#endif | ||
7152 | +#ifdef CONFIG_CCSECURITY_MISC | ||
7153 | + if (!retval) | ||
7154 | + retval = ccs_environ(ee); | ||
7155 | +#endif | ||
7156 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
7157 | +done: | ||
7158 | +#endif | ||
7159 | + ccs_read_unlock(idx); | ||
7160 | + kfree(ee->tmp); | ||
7161 | + ee->tmp = NULL; | ||
7162 | + kfree(ee->dump.data); | ||
7163 | + ee->dump.data = NULL; | ||
7164 | + return retval; | ||
7165 | +} | ||
7166 | + | ||
7167 | +/** | ||
7168 | + * ccs_finish_execve - Clean up execve() operation. | ||
7169 | + * | ||
7170 | + * @retval: Return code of an execve() operation. | ||
7171 | + * @ee: Pointer to "struct ccs_execve". | ||
7172 | + * | ||
7173 | + * Returns nothing. | ||
7174 | + */ | ||
7175 | +static void ccs_finish_execve(int retval, struct ccs_execve *ee) | ||
7176 | +{ | ||
7177 | + struct ccs_security *task = ccs_current_security(); | ||
7178 | + if (!ee) | ||
7179 | + return; | ||
7180 | + if (retval < 0) { | ||
7181 | + task->ccs_domain_info = ee->previous_domain; | ||
7182 | + /* | ||
7183 | + * Make task->ccs_domain_info visible to GC before changing | ||
7184 | + * task->ccs_flags. | ||
7185 | + */ | ||
7186 | + smp_wmb(); | ||
7187 | + } else { | ||
7188 | + /* Mark the current process as execute handler. */ | ||
7189 | + if (ee->handler) | ||
7190 | + task->ccs_flags |= CCS_TASK_IS_EXECUTE_HANDLER; | ||
7191 | + /* Mark the current process as normal process. */ | ||
7192 | + else | ||
7193 | + task->ccs_flags &= ~CCS_TASK_IS_EXECUTE_HANDLER; | ||
7194 | + } | ||
7195 | + /* Tell GC that I finished execve(). */ | ||
7196 | + task->ccs_flags &= ~CCS_TASK_IS_IN_EXECVE; | ||
7197 | + kfree(ee->handler_path); | ||
7198 | + kfree(ee); | ||
7199 | +} | ||
7200 | + | ||
7201 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) | ||
7202 | + | ||
7203 | +/** | ||
7204 | + * __ccs_search_binary_handler - Main routine for do_execve(). | ||
7205 | + * | ||
7206 | + * @bprm: Pointer to "struct linux_binprm". | ||
7207 | + * | ||
7208 | + * Returns 0 on success, negative value otherwise. | ||
7209 | + * | ||
7210 | + * Performs permission checks for do_execve() and domain transition. | ||
7211 | + * Domain transition by "struct ccs_domain_transition_control" and | ||
7212 | + * "auto_domain_transition=" parameter of "struct ccs_condition" are reverted | ||
7213 | + * if do_execve() failed. | ||
7214 | + * Garbage collector does not remove "struct ccs_domain_info" from | ||
7215 | + * ccs_domain_list nor kfree("struct ccs_domain_info") if the current thread is | ||
7216 | + * marked as CCS_TASK_IS_IN_EXECVE. | ||
7217 | + */ | ||
7218 | +static int __ccs_search_binary_handler(struct linux_binprm *bprm) | ||
7219 | +{ | ||
7220 | + struct ccs_execve *ee; | ||
7221 | + int retval; | ||
7222 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | ||
7223 | + if (!ccs_policy_loaded) | ||
7224 | + ccsecurity_exports.load_policy(bprm->filename); | ||
7225 | +#endif | ||
7226 | + retval = ccs_start_execve(bprm, &ee); | ||
7227 | + if (!retval) | ||
7228 | + retval = search_binary_handler(bprm); | ||
7229 | + ccs_finish_execve(retval, ee); | ||
7230 | + return retval; | ||
7231 | +} | ||
7232 | + | ||
7233 | +#else | ||
7234 | + | ||
7235 | +/** | ||
7236 | + * __ccs_search_binary_handler - Main routine for do_execve(). | ||
7237 | + * | ||
7238 | + * @bprm: Pointer to "struct linux_binprm". | ||
7239 | + * @regs: Pointer to "struct pt_regs". | ||
7240 | + * | ||
7241 | + * Returns 0 on success, negative value otherwise. | ||
7242 | + * | ||
7243 | + * Performs permission checks for do_execve() and domain transition. | ||
7244 | + * Domain transition by "struct ccs_domain_transition_control" and | ||
7245 | + * "auto_domain_transition=" parameter of "struct ccs_condition" are reverted | ||
7246 | + * if do_execve() failed. | ||
7247 | + * Garbage collector does not remove "struct ccs_domain_info" from | ||
7248 | + * ccs_domain_list nor kfree("struct ccs_domain_info") if the current thread is | ||
7249 | + * marked as CCS_TASK_IS_IN_EXECVE. | ||
7250 | + */ | ||
7251 | +static int __ccs_search_binary_handler(struct linux_binprm *bprm, | ||
7252 | + struct pt_regs *regs) | ||
7253 | +{ | ||
7254 | + struct ccs_execve *ee; | ||
7255 | + int retval; | ||
7256 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | ||
7257 | + if (!ccs_policy_loaded) | ||
7258 | + ccsecurity_exports.load_policy(bprm->filename); | ||
7259 | +#endif | ||
7260 | + retval = ccs_start_execve(bprm, &ee); | ||
7261 | + if (!retval) | ||
7262 | + retval = search_binary_handler(bprm, regs); | ||
7263 | + ccs_finish_execve(retval, ee); | ||
7264 | + return retval; | ||
7265 | +} | ||
7266 | + | ||
7267 | +#endif | ||
7268 | + | ||
7269 | +/** | ||
7270 | + * ccs_permission_init - Register permission check hooks. | ||
7271 | + * | ||
7272 | + * Returns nothing. | ||
7273 | + */ | ||
7274 | +void __init ccs_permission_init(void) | ||
7275 | +{ | ||
7276 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | ||
7277 | + ccsecurity_ops.save_open_mode = __ccs_save_open_mode; | ||
7278 | + ccsecurity_ops.clear_open_mode = __ccs_clear_open_mode; | ||
7279 | + ccsecurity_ops.open_permission = __ccs_open_permission; | ||
7280 | +#else | ||
7281 | + ccsecurity_ops.open_permission = ccs_new_open_permission; | ||
7282 | +#endif | ||
7283 | + ccsecurity_ops.fcntl_permission = __ccs_fcntl_permission; | ||
7284 | + ccsecurity_ops.ioctl_permission = __ccs_ioctl_permission; | ||
7285 | + ccsecurity_ops.chmod_permission = __ccs_chmod_permission; | ||
7286 | + ccsecurity_ops.chown_permission = __ccs_chown_permission; | ||
7287 | +#ifdef CONFIG_CCSECURITY_FILE_GETATTR | ||
7288 | + ccsecurity_ops.getattr_permission = __ccs_getattr_permission; | ||
7289 | +#endif | ||
7290 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) | ||
7291 | + ccsecurity_ops.pivot_root_permission = __ccs_pivot_root_permission; | ||
7292 | + ccsecurity_ops.chroot_permission = __ccs_chroot_permission; | ||
7293 | +#else | ||
7294 | + ccsecurity_ops.pivot_root_permission = ccs_old_pivot_root_permission; | ||
7295 | + ccsecurity_ops.chroot_permission = ccs_old_chroot_permission; | ||
7296 | +#endif | ||
7297 | + ccsecurity_ops.umount_permission = __ccs_umount_permission; | ||
7298 | + ccsecurity_ops.mknod_permission = __ccs_mknod_permission; | ||
7299 | + ccsecurity_ops.mkdir_permission = __ccs_mkdir_permission; | ||
7300 | + ccsecurity_ops.rmdir_permission = __ccs_rmdir_permission; | ||
7301 | + ccsecurity_ops.unlink_permission = __ccs_unlink_permission; | ||
7302 | + ccsecurity_ops.symlink_permission = __ccs_symlink_permission; | ||
7303 | + ccsecurity_ops.truncate_permission = __ccs_truncate_permission; | ||
7304 | + ccsecurity_ops.rename_permission = __ccs_rename_permission; | ||
7305 | + ccsecurity_ops.link_permission = __ccs_link_permission; | ||
7306 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | ||
7307 | + ccsecurity_ops.open_exec_permission = __ccs_open_exec_permission; | ||
7308 | + ccsecurity_ops.uselib_permission = __ccs_uselib_permission; | ||
7309 | +#endif | ||
7310 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) || (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL)) | ||
7311 | + ccsecurity_ops.parse_table = __ccs_parse_table; | ||
7312 | +#endif | ||
7313 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) | ||
7314 | + ccsecurity_ops.mount_permission = __ccs_mount_permission; | ||
7315 | +#else | ||
7316 | + ccsecurity_ops.mount_permission = ccs_old_mount_permission; | ||
7317 | +#endif | ||
7318 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
7319 | + ccsecurity_ops.socket_create_permission = | ||
7320 | + __ccs_socket_create_permission; | ||
7321 | +#endif | ||
7322 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
7323 | + ccsecurity_ops.socket_listen_permission = | ||
7324 | + __ccs_socket_listen_permission; | ||
7325 | + ccsecurity_ops.socket_connect_permission = | ||
7326 | + __ccs_socket_connect_permission; | ||
7327 | + ccsecurity_ops.socket_bind_permission = __ccs_socket_bind_permission; | ||
7328 | + ccsecurity_ops.socket_post_accept_permission = | ||
7329 | + __ccs_socket_post_accept_permission; | ||
7330 | + ccsecurity_ops.socket_sendmsg_permission = | ||
7331 | + __ccs_socket_sendmsg_permission; | ||
7332 | +#endif | ||
7333 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
7334 | + ccsecurity_ops.socket_post_recvmsg_permission = | ||
7335 | + __ccs_socket_post_recvmsg_permission; | ||
7336 | +#endif | ||
7337 | +#ifdef CONFIG_CCSECURITY_IPC | ||
7338 | + ccsecurity_ops.kill_permission = ccs_signal_acl; | ||
7339 | + ccsecurity_ops.tgkill_permission = ccs_signal_acl0; | ||
7340 | + ccsecurity_ops.tkill_permission = ccs_signal_acl; | ||
7341 | + ccsecurity_ops.sigqueue_permission = ccs_signal_acl; | ||
7342 | + ccsecurity_ops.tgsigqueue_permission = ccs_signal_acl0; | ||
7343 | +#endif | ||
7344 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
7345 | + ccsecurity_ops.capable = __ccs_capable; | ||
7346 | + ccsecurity_ops.ptrace_permission = __ccs_ptrace_permission; | ||
7347 | +#endif | ||
7348 | + ccsecurity_ops.search_binary_handler = __ccs_search_binary_handler; | ||
7349 | +} | ||
7350 | + | ||
7351 | +/** | ||
7352 | + * ccs_kern_path - Wrapper for kern_path(). | ||
7353 | + * | ||
7354 | + * @pathname: Pathname to resolve. Maybe NULL. | ||
7355 | + * @flags: Lookup flags. | ||
7356 | + * @path: Pointer to "struct path". | ||
7357 | + * | ||
7358 | + * Returns 0 on success, negative value otherwise. | ||
7359 | + */ | ||
7360 | +static int ccs_kern_path(const char *pathname, int flags, struct path *path) | ||
7361 | +{ | ||
7362 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) | ||
7363 | + if (!pathname || kern_path(pathname, flags, path)) | ||
7364 | + return -ENOENT; | ||
7365 | +#else | ||
7366 | + struct nameidata nd; | ||
7367 | + if (!pathname || path_lookup(pathname, flags, &nd)) | ||
7368 | + return -ENOENT; | ||
7369 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) | ||
7370 | + *path = nd.path; | ||
7371 | +#else | ||
7372 | + path->dentry = nd.dentry; | ||
7373 | + path->mnt = nd.mnt; | ||
7374 | +#endif | ||
7375 | +#endif | ||
7376 | + return 0; | ||
7377 | +} | ||
7378 | + | ||
7379 | +/** | ||
7380 | + * ccs_get_path - Get dentry/vfsmmount of a pathname. | ||
7381 | + * | ||
7382 | + * @pathname: The pathname to solve. Maybe NULL. | ||
7383 | + * @path: Pointer to "struct path". | ||
7384 | + * | ||
7385 | + * Returns 0 on success, negative value otherwise. | ||
7386 | + */ | ||
7387 | +static int ccs_get_path(const char *pathname, struct path *path) | ||
7388 | +{ | ||
7389 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) | ||
7390 | + return ccs_kern_path(pathname, LOOKUP_FOLLOW, path); | ||
7391 | +#else | ||
7392 | + return ccs_kern_path(pathname, LOOKUP_FOLLOW | LOOKUP_POSITIVE, path); | ||
7393 | +#endif | ||
7394 | +} | ||
7395 | + | ||
7396 | +/** | ||
7397 | + * ccs_symlink_path - Get symlink's pathname. | ||
7398 | + * | ||
7399 | + * @pathname: The pathname to solve. Maybe NULL. | ||
7400 | + * @name: Pointer to "struct ccs_path_info". | ||
7401 | + * | ||
7402 | + * Returns 0 on success, negative value otherwise. | ||
7403 | + * | ||
7404 | + * This function uses kzalloc(), so caller must kfree() if this function | ||
7405 | + * didn't return NULL. | ||
7406 | + */ | ||
7407 | +static int ccs_symlink_path(const char *pathname, struct ccs_path_info *name) | ||
7408 | +{ | ||
7409 | + char *buf; | ||
7410 | + struct path path; | ||
7411 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) | ||
7412 | + if (ccs_kern_path(pathname, 0, &path)) | ||
7413 | + return -ENOENT; | ||
7414 | +#else | ||
7415 | + if (ccs_kern_path(pathname, LOOKUP_POSITIVE, &path)) | ||
7416 | + return -ENOENT; | ||
7417 | +#endif | ||
7418 | + buf = ccs_realpath(&path); | ||
7419 | + path_put(&path); | ||
7420 | + if (buf) { | ||
7421 | + name->name = buf; | ||
7422 | + ccs_fill_path_info(name); | ||
7423 | + return 0; | ||
7424 | + } | ||
7425 | + return -ENOMEM; | ||
7426 | +} | ||
7427 | + | ||
7428 | +/** | ||
7429 | + * ccs_check_mount_acl - Check permission for path path path number operation. | ||
7430 | + * | ||
7431 | + * @r: Pointer to "struct ccs_request_info". | ||
7432 | + * @ptr: Pointer to "struct ccs_acl_info". | ||
7433 | + * | ||
7434 | + * Returns true if granted, false otherwise. | ||
7435 | + */ | ||
7436 | +static bool ccs_check_mount_acl(struct ccs_request_info *r, | ||
7437 | + const struct ccs_acl_info *ptr) | ||
7438 | +{ | ||
7439 | + const struct ccs_mount_acl *acl = | ||
7440 | + container_of(ptr, typeof(*acl), head); | ||
7441 | + return ccs_compare_number_union(r->param.mount.flags, &acl->flags) && | ||
7442 | + ccs_compare_name_union(r->param.mount.type, &acl->fs_type) && | ||
7443 | + ccs_compare_name_union(r->param.mount.dir, &acl->dir_name) && | ||
7444 | + (!r->param.mount.need_dev || | ||
7445 | + ccs_compare_name_union(r->param.mount.dev, &acl->dev_name)); | ||
7446 | +} | ||
7447 | + | ||
7448 | +/** | ||
7449 | + * ccs_mount_acl - Check permission for mount() operation. | ||
7450 | + * | ||
7451 | + * @r: Pointer to "struct ccs_request_info". | ||
7452 | + * @dev_name: Name of device file. Maybe NULL. | ||
7453 | + * @dir: Pointer to "struct path". | ||
7454 | + * @type: Name of filesystem type. | ||
7455 | + * @flags: Mount options. | ||
7456 | + * | ||
7457 | + * Returns 0 on success, negative value otherwise. | ||
7458 | + * | ||
7459 | + * Caller holds ccs_read_lock(). | ||
7460 | + */ | ||
7461 | +static int ccs_mount_acl(struct ccs_request_info *r, const char *dev_name, | ||
7462 | + struct path *dir, const char *type, | ||
7463 | + unsigned long flags) | ||
7464 | +{ | ||
7465 | + struct ccs_obj_info obj = { }; | ||
7466 | + struct file_system_type *fstype = NULL; | ||
7467 | + const char *requested_type = NULL; | ||
7468 | + const char *requested_dir_name = NULL; | ||
7469 | + const char *requested_dev_name = NULL; | ||
7470 | + struct ccs_path_info rtype; | ||
7471 | + struct ccs_path_info rdev; | ||
7472 | + struct ccs_path_info rdir; | ||
7473 | + int need_dev = 0; | ||
7474 | + int error = -ENOMEM; | ||
7475 | + r->obj = &obj; | ||
7476 | + | ||
7477 | + /* Get fstype. */ | ||
7478 | + requested_type = ccs_encode(type); | ||
7479 | + if (!requested_type) | ||
7480 | + goto out; | ||
7481 | + rtype.name = requested_type; | ||
7482 | + ccs_fill_path_info(&rtype); | ||
7483 | + | ||
7484 | + /* Get mount point. */ | ||
7485 | + obj.path2 = *dir; | ||
7486 | + requested_dir_name = ccs_realpath(dir); | ||
7487 | + if (!requested_dir_name) { | ||
7488 | + error = -ENOMEM; | ||
7489 | + goto out; | ||
7490 | + } | ||
7491 | + rdir.name = requested_dir_name; | ||
7492 | + ccs_fill_path_info(&rdir); | ||
7493 | + | ||
7494 | + /* Compare fs name. */ | ||
7495 | + if (type == ccs_mounts[CCS_MOUNT_REMOUNT]) { | ||
7496 | + /* dev_name is ignored. */ | ||
7497 | + } else if (type == ccs_mounts[CCS_MOUNT_MAKE_UNBINDABLE] || | ||
7498 | + type == ccs_mounts[CCS_MOUNT_MAKE_PRIVATE] || | ||
7499 | + type == ccs_mounts[CCS_MOUNT_MAKE_SLAVE] || | ||
7500 | + type == ccs_mounts[CCS_MOUNT_MAKE_SHARED]) { | ||
7501 | + /* dev_name is ignored. */ | ||
7502 | + } else if (type == ccs_mounts[CCS_MOUNT_BIND] || | ||
7503 | + type == ccs_mounts[CCS_MOUNT_MOVE]) { | ||
7504 | + need_dev = -1; /* dev_name is a directory */ | ||
7505 | + } else { | ||
7506 | + fstype = get_fs_type(type); | ||
7507 | + if (!fstype) { | ||
7508 | + error = -ENODEV; | ||
7509 | + goto out; | ||
7510 | + } | ||
7511 | + if (fstype->fs_flags & FS_REQUIRES_DEV) | ||
7512 | + /* dev_name is a block device file. */ | ||
7513 | + need_dev = 1; | ||
7514 | + } | ||
7515 | + if (need_dev) { | ||
7516 | + /* Get mount point or device file. */ | ||
7517 | + if (ccs_get_path(dev_name, &obj.path1)) { | ||
7518 | + error = -ENOENT; | ||
7519 | + goto out; | ||
7520 | + } | ||
7521 | + requested_dev_name = ccs_realpath(&obj.path1); | ||
7522 | + if (!requested_dev_name) { | ||
7523 | + error = -ENOENT; | ||
7524 | + goto out; | ||
7525 | + } | ||
7526 | + } else { | ||
7527 | + /* Map dev_name to "<NULL>" if no dev_name given. */ | ||
7528 | + if (!dev_name) | ||
7529 | + dev_name = "<NULL>"; | ||
7530 | + requested_dev_name = ccs_encode(dev_name); | ||
7531 | + if (!requested_dev_name) { | ||
7532 | + error = -ENOMEM; | ||
7533 | + goto out; | ||
7534 | + } | ||
7535 | + } | ||
7536 | + rdev.name = requested_dev_name; | ||
7537 | + ccs_fill_path_info(&rdev); | ||
7538 | + r->param_type = CCS_TYPE_MOUNT_ACL; | ||
7539 | + r->param.mount.need_dev = need_dev; | ||
7540 | + r->param.mount.dev = &rdev; | ||
7541 | + r->param.mount.dir = &rdir; | ||
7542 | + r->param.mount.type = &rtype; | ||
7543 | + r->param.mount.flags = flags; | ||
7544 | + error = ccs_check_acl(r); | ||
7545 | +out: | ||
7546 | + kfree(requested_dev_name); | ||
7547 | + kfree(requested_dir_name); | ||
7548 | + if (fstype) | ||
7549 | + ccs_put_filesystem(fstype); | ||
7550 | + kfree(requested_type); | ||
7551 | + /* Drop refcount obtained by ccs_get_path(). */ | ||
7552 | + if (obj.path1.dentry) | ||
7553 | + path_put(&obj.path1); | ||
7554 | + return error; | ||
7555 | +} | ||
7556 | + | ||
7557 | +/** | ||
7558 | + * __ccs_mount_permission - Check permission for mount() operation. | ||
7559 | + * | ||
7560 | + * @dev_name: Name of device file. Maybe NULL. | ||
7561 | + * @path: Pointer to "struct path". | ||
7562 | + * @type: Name of filesystem type. Maybe NULL. | ||
7563 | + * @flags: Mount options. | ||
7564 | + * @data_page: Optional data. Maybe NULL. | ||
7565 | + * | ||
7566 | + * Returns 0 on success, negative value otherwise. | ||
7567 | + */ | ||
7568 | +static int __ccs_mount_permission(const char *dev_name, struct path *path, | ||
7569 | + const char *type, unsigned long flags, | ||
7570 | + void *data_page) | ||
7571 | +{ | ||
7572 | + struct ccs_request_info r; | ||
7573 | + int error = 0; | ||
7574 | + int idx; | ||
7575 | + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | ||
7576 | + flags &= ~MS_MGC_MSK; | ||
7577 | + if (flags & MS_REMOUNT) { | ||
7578 | + type = ccs_mounts[CCS_MOUNT_REMOUNT]; | ||
7579 | + flags &= ~MS_REMOUNT; | ||
7580 | + } else if (flags & MS_BIND) { | ||
7581 | + type = ccs_mounts[CCS_MOUNT_BIND]; | ||
7582 | + flags &= ~MS_BIND; | ||
7583 | + } else if (flags & MS_SHARED) { | ||
7584 | + if (flags & (MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) | ||
7585 | + return -EINVAL; | ||
7586 | + type = ccs_mounts[CCS_MOUNT_MAKE_SHARED]; | ||
7587 | + flags &= ~MS_SHARED; | ||
7588 | + } else if (flags & MS_PRIVATE) { | ||
7589 | + if (flags & (MS_SHARED | MS_SLAVE | MS_UNBINDABLE)) | ||
7590 | + return -EINVAL; | ||
7591 | + type = ccs_mounts[CCS_MOUNT_MAKE_PRIVATE]; | ||
7592 | + flags &= ~MS_PRIVATE; | ||
7593 | + } else if (flags & MS_SLAVE) { | ||
7594 | + if (flags & (MS_SHARED | MS_PRIVATE | MS_UNBINDABLE)) | ||
7595 | + return -EINVAL; | ||
7596 | + type = ccs_mounts[CCS_MOUNT_MAKE_SLAVE]; | ||
7597 | + flags &= ~MS_SLAVE; | ||
7598 | + } else if (flags & MS_UNBINDABLE) { | ||
7599 | + if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE)) | ||
7600 | + return -EINVAL; | ||
7601 | + type = ccs_mounts[CCS_MOUNT_MAKE_UNBINDABLE]; | ||
7602 | + flags &= ~MS_UNBINDABLE; | ||
7603 | + } else if (flags & MS_MOVE) { | ||
7604 | + type = ccs_mounts[CCS_MOUNT_MOVE]; | ||
7605 | + flags &= ~MS_MOVE; | ||
7606 | + } | ||
7607 | + if (!type) | ||
7608 | + type = "<NULL>"; | ||
7609 | + idx = ccs_read_lock(); | ||
7610 | + if (ccs_init_request_info(&r, CCS_MAC_FILE_MOUNT) | ||
7611 | + != CCS_CONFIG_DISABLED) | ||
7612 | + error = ccs_mount_acl(&r, dev_name, path, type, flags); | ||
7613 | + ccs_read_unlock(idx); | ||
7614 | + return error; | ||
7615 | +} | ||
7616 | + | ||
7617 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24) | ||
7618 | + | ||
7619 | +/** | ||
7620 | + * ccs_old_mount_permission - Check permission for mount() operation. | ||
7621 | + * | ||
7622 | + * @dev_name: Name of device file. | ||
7623 | + * @nd: Pointer to "struct nameidata". | ||
7624 | + * @type: Name of filesystem type. Maybe NULL. | ||
7625 | + * @flags: Mount options. | ||
7626 | + * @data_page: Optional data. Maybe NULL. | ||
7627 | + * | ||
7628 | + * Returns 0 on success, negative value otherwise. | ||
7629 | + */ | ||
7630 | +static int ccs_old_mount_permission(const char *dev_name, struct nameidata *nd, | ||
7631 | + const char *type, unsigned long flags, | ||
7632 | + void *data_page) | ||
7633 | +{ | ||
7634 | + struct path path = { nd->mnt, nd->dentry }; | ||
7635 | + return __ccs_mount_permission(dev_name, &path, type, flags, data_page); | ||
7636 | +} | ||
7637 | + | ||
7638 | +#endif | ||
7639 | + | ||
7640 | +/** | ||
7641 | + * ccs_compare_number_union - Check whether a value matches "struct ccs_number_union" or not. | ||
7642 | + * | ||
7643 | + * @value: Number to check. | ||
7644 | + * @ptr: Pointer to "struct ccs_number_union". | ||
7645 | + * | ||
7646 | + * Returns true if @value matches @ptr, false otherwise. | ||
7647 | + */ | ||
7648 | +static bool ccs_compare_number_union(const unsigned long value, | ||
7649 | + const struct ccs_number_union *ptr) | ||
7650 | +{ | ||
7651 | + if (ptr->group) | ||
7652 | + return ccs_number_matches_group(value, value, ptr->group); | ||
7653 | + return value >= ptr->values[0] && value <= ptr->values[1]; | ||
7654 | +} | ||
7655 | + | ||
7656 | +/** | ||
7657 | + * ccs_compare_name_union - Check whether a name matches "struct ccs_name_union" or not. | ||
7658 | + * | ||
7659 | + * @name: Pointer to "struct ccs_path_info". | ||
7660 | + * @ptr: Pointer to "struct ccs_name_union". | ||
7661 | + * | ||
7662 | + * Returns "struct ccs_path_info" if @name matches @ptr, NULL otherwise. | ||
7663 | + */ | ||
7664 | +static const struct ccs_path_info *ccs_compare_name_union | ||
7665 | +(const struct ccs_path_info *name, const struct ccs_name_union *ptr) | ||
7666 | +{ | ||
7667 | + if (ptr->group) | ||
7668 | + return ccs_path_matches_group(name, ptr->group); | ||
7669 | + if (ccs_path_matches_pattern(name, ptr->filename)) | ||
7670 | + return ptr->filename; | ||
7671 | + return NULL; | ||
7672 | +} | ||
7673 | + | ||
7674 | +/** | ||
7675 | + * ccs_add_slash - Add trailing '/' if needed. | ||
7676 | + * | ||
7677 | + * @buf: Pointer to "struct ccs_path_info". | ||
7678 | + * | ||
7679 | + * Returns nothing. | ||
7680 | + * | ||
7681 | + * @buf must be generated by ccs_encode() because this function does not | ||
7682 | + * allocate memory for adding '/'. | ||
7683 | + */ | ||
7684 | +static void ccs_add_slash(struct ccs_path_info *buf) | ||
7685 | +{ | ||
7686 | + if (buf->is_dir) | ||
7687 | + return; | ||
7688 | + /* This is OK because ccs_encode() reserves space for appending "/". */ | ||
7689 | + strcat((char *) buf->name, "/"); | ||
7690 | + ccs_fill_path_info(buf); | ||
7691 | +} | ||
7692 | + | ||
7693 | +/** | ||
7694 | + * ccs_get_realpath - Get realpath. | ||
7695 | + * | ||
7696 | + * @buf: Pointer to "struct ccs_path_info". | ||
7697 | + * @path: Pointer to "struct path". @path->mnt may be NULL. | ||
7698 | + * | ||
7699 | + * Returns true on success, false otherwise. | ||
7700 | + */ | ||
7701 | +static bool ccs_get_realpath(struct ccs_path_info *buf, struct path *path) | ||
7702 | +{ | ||
7703 | + buf->name = ccs_realpath(path); | ||
7704 | + if (buf->name) { | ||
7705 | + ccs_fill_path_info(buf); | ||
7706 | + return true; | ||
7707 | + } | ||
7708 | + return false; | ||
7709 | +} | ||
7710 | + | ||
7711 | +/** | ||
7712 | + * ccs_check_path_acl - Check permission for path operation. | ||
7713 | + * | ||
7714 | + * @r: Pointer to "struct ccs_request_info". | ||
7715 | + * @ptr: Pointer to "struct ccs_acl_info". | ||
7716 | + * | ||
7717 | + * Returns true if granted, false otherwise. | ||
7718 | + * | ||
7719 | + * To be able to use wildcard for domain transition, this function sets | ||
7720 | + * matching entry on success. Since the caller holds ccs_read_lock(), | ||
7721 | + * it is safe to set matching entry. | ||
7722 | + */ | ||
7723 | +static bool ccs_check_path_acl(struct ccs_request_info *r, | ||
7724 | + const struct ccs_acl_info *ptr) | ||
7725 | +{ | ||
7726 | + const struct ccs_path_acl *acl = container_of(ptr, typeof(*acl), head); | ||
7727 | + if (ptr->perm & (1 << r->param.path.operation)) { | ||
7728 | + r->param.path.matched_path = | ||
7729 | + ccs_compare_name_union(r->param.path.filename, | ||
7730 | + &acl->name); | ||
7731 | + return r->param.path.matched_path != NULL; | ||
7732 | + } | ||
7733 | + return false; | ||
7734 | +} | ||
7735 | + | ||
7736 | +/** | ||
7737 | + * ccs_check_path_number_acl - Check permission for path number operation. | ||
7738 | + * | ||
7739 | + * @r: Pointer to "struct ccs_request_info". | ||
7740 | + * @ptr: Pointer to "struct ccs_acl_info". | ||
7741 | + * | ||
7742 | + * Returns true if granted, false otherwise. | ||
7743 | + */ | ||
7744 | +static bool ccs_check_path_number_acl(struct ccs_request_info *r, | ||
7745 | + const struct ccs_acl_info *ptr) | ||
7746 | +{ | ||
7747 | + const struct ccs_path_number_acl *acl = | ||
7748 | + container_of(ptr, typeof(*acl), head); | ||
7749 | + return (ptr->perm & (1 << r->param.path_number.operation)) && | ||
7750 | + ccs_compare_number_union(r->param.path_number.number, | ||
7751 | + &acl->number) && | ||
7752 | + ccs_compare_name_union(r->param.path_number.filename, | ||
7753 | + &acl->name); | ||
7754 | +} | ||
7755 | + | ||
7756 | +/** | ||
7757 | + * ccs_check_path2_acl - Check permission for path path operation. | ||
7758 | + * | ||
7759 | + * @r: Pointer to "struct ccs_request_info". | ||
7760 | + * @ptr: Pointer to "struct ccs_acl_info". | ||
7761 | + * | ||
7762 | + * Returns true if granted, false otherwise. | ||
7763 | + */ | ||
7764 | +static bool ccs_check_path2_acl(struct ccs_request_info *r, | ||
7765 | + const struct ccs_acl_info *ptr) | ||
7766 | +{ | ||
7767 | + const struct ccs_path2_acl *acl = | ||
7768 | + container_of(ptr, typeof(*acl), head); | ||
7769 | + return (ptr->perm & (1 << r->param.path2.operation)) && | ||
7770 | + ccs_compare_name_union(r->param.path2.filename1, &acl->name1) | ||
7771 | + && ccs_compare_name_union(r->param.path2.filename2, | ||
7772 | + &acl->name2); | ||
7773 | +} | ||
7774 | + | ||
7775 | +/** | ||
7776 | + * ccs_check_mkdev_acl - Check permission for path number number number operation. | ||
7777 | + * | ||
7778 | + * @r: Pointer to "struct ccs_request_info". | ||
7779 | + * @ptr: Pointer to "struct ccs_acl_info". | ||
7780 | + * | ||
7781 | + * Returns true if granted, false otherwise. | ||
7782 | + */ | ||
7783 | +static bool ccs_check_mkdev_acl(struct ccs_request_info *r, | ||
7784 | + const struct ccs_acl_info *ptr) | ||
7785 | +{ | ||
7786 | + const struct ccs_mkdev_acl *acl = | ||
7787 | + container_of(ptr, typeof(*acl), head); | ||
7788 | + return (ptr->perm & (1 << r->param.mkdev.operation)) && | ||
7789 | + ccs_compare_number_union(r->param.mkdev.mode, &acl->mode) && | ||
7790 | + ccs_compare_number_union(r->param.mkdev.major, &acl->major) && | ||
7791 | + ccs_compare_number_union(r->param.mkdev.minor, &acl->minor) && | ||
7792 | + ccs_compare_name_union(r->param.mkdev.filename, &acl->name); | ||
7793 | +} | ||
7794 | + | ||
7795 | +/** | ||
7796 | + * ccs_path_permission - Check permission for path operation. | ||
7797 | + * | ||
7798 | + * @r: Pointer to "struct ccs_request_info". | ||
7799 | + * @operation: Type of operation. | ||
7800 | + * @filename: Filename to check. | ||
7801 | + * | ||
7802 | + * Returns 0 on success, negative value otherwise. | ||
7803 | + * | ||
7804 | + * Caller holds ccs_read_lock(). | ||
7805 | + */ | ||
7806 | +static int ccs_path_permission(struct ccs_request_info *r, u8 operation, | ||
7807 | + const struct ccs_path_info *filename) | ||
7808 | +{ | ||
7809 | + r->type = ccs_p2mac[operation]; | ||
7810 | + r->mode = ccs_get_mode(r->profile, r->type); | ||
7811 | + if (r->mode == CCS_CONFIG_DISABLED) | ||
7812 | + return 0; | ||
7813 | + r->param_type = CCS_TYPE_PATH_ACL; | ||
7814 | + r->param.path.filename = filename; | ||
7815 | + r->param.path.operation = operation; | ||
7816 | + return ccs_check_acl(r); | ||
7817 | +} | ||
7818 | + | ||
7819 | +/** | ||
7820 | + * ccs_execute_permission - Check permission for execute operation. | ||
7821 | + * | ||
7822 | + * @r: Pointer to "struct ccs_request_info". | ||
7823 | + * @filename: Filename to check. | ||
7824 | + * | ||
7825 | + * Returns 0 on success, CCS_RETRY_REQUEST on retry, negative value otherwise. | ||
7826 | + * | ||
7827 | + * Caller holds ccs_read_lock(). | ||
7828 | + */ | ||
7829 | +static int ccs_execute_permission(struct ccs_request_info *r, | ||
7830 | + const struct ccs_path_info *filename) | ||
7831 | +{ | ||
7832 | + int error; | ||
7833 | + /* | ||
7834 | + * Unlike other permission checks, this check is done regardless of | ||
7835 | + * profile mode settings in order to check for domain transition | ||
7836 | + * preference. | ||
7837 | + */ | ||
7838 | + r->type = CCS_MAC_FILE_EXECUTE; | ||
7839 | + r->mode = ccs_get_mode(r->profile, r->type); | ||
7840 | + r->param_type = CCS_TYPE_PATH_ACL; | ||
7841 | + r->param.path.filename = filename; | ||
7842 | + r->param.path.operation = CCS_TYPE_EXECUTE; | ||
7843 | + error = ccs_check_acl(r); | ||
7844 | + r->ee->transition = r->matched_acl && r->matched_acl->cond && | ||
7845 | + r->matched_acl->cond->exec_transit ? | ||
7846 | + r->matched_acl->cond->transit : NULL; | ||
7847 | + return error; | ||
7848 | +} | ||
7849 | + | ||
7850 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | ||
7851 | + | ||
7852 | +/** | ||
7853 | + * __ccs_save_open_mode - Remember original flags passed to sys_open(). | ||
7854 | + * | ||
7855 | + * @mode: Flags passed to sys_open(). | ||
7856 | + * | ||
7857 | + * Returns nothing. | ||
7858 | + * | ||
7859 | + * TOMOYO does not check "file write" if open(path, O_TRUNC | O_RDONLY) was | ||
7860 | + * requested because write() is not permitted. Instead, TOMOYO checks | ||
7861 | + * "file truncate" if O_TRUNC is passed. | ||
7862 | + * | ||
7863 | + * TOMOYO does not check "file read" and "file write" if open(path, 3) was | ||
7864 | + * requested because read()/write() are not permitted. Instead, TOMOYO checks | ||
7865 | + * "file ioctl" when ioctl() is requested. | ||
7866 | + */ | ||
7867 | +static void __ccs_save_open_mode(int mode) | ||
7868 | +{ | ||
7869 | + if ((mode & 3) == 3) | ||
7870 | + ccs_current_security()->ccs_flags |= CCS_OPEN_FOR_IOCTL_ONLY; | ||
7871 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 14) | ||
7872 | + /* O_TRUNC passes MAY_WRITE to ccs_open_permission(). */ | ||
7873 | + else if (!(mode & 3) && (mode & O_TRUNC)) | ||
7874 | + ccs_current_security()->ccs_flags |= | ||
7875 | + CCS_OPEN_FOR_READ_TRUNCATE; | ||
7876 | +#endif | ||
7877 | +} | ||
7878 | + | ||
7879 | +/** | ||
7880 | + * __ccs_clear_open_mode - Forget original flags passed to sys_open(). | ||
7881 | + * | ||
7882 | + * Returns nothing. | ||
7883 | + */ | ||
7884 | +static void __ccs_clear_open_mode(void) | ||
7885 | +{ | ||
7886 | + ccs_current_security()->ccs_flags &= ~(CCS_OPEN_FOR_IOCTL_ONLY | | ||
7887 | + CCS_OPEN_FOR_READ_TRUNCATE); | ||
7888 | +} | ||
7889 | + | ||
7890 | +#endif | ||
7891 | + | ||
7892 | +/** | ||
7893 | + * __ccs_open_permission - Check permission for "read" and "write". | ||
7894 | + * | ||
7895 | + * @dentry: Pointer to "struct dentry". | ||
7896 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | ||
7897 | + * @flag: Flags for open(). | ||
7898 | + * | ||
7899 | + * Returns 0 on success, negative value otherwise. | ||
7900 | + */ | ||
7901 | +static int __ccs_open_permission(struct dentry *dentry, struct vfsmount *mnt, | ||
7902 | + const int flag) | ||
7903 | +{ | ||
7904 | + struct ccs_request_info r; | ||
7905 | + struct ccs_obj_info obj = { | ||
7906 | + .path1.dentry = dentry, | ||
7907 | + .path1.mnt = mnt, | ||
7908 | + }; | ||
7909 | + const u32 ccs_flags = ccs_current_flags(); | ||
7910 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | ||
7911 | + const u8 acc_mode = (flag & 3) == 3 ? 0 : ACC_MODE(flag); | ||
7912 | +#else | ||
7913 | + const u8 acc_mode = (ccs_flags & CCS_OPEN_FOR_IOCTL_ONLY) ? 0 : | ||
7914 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 14) | ||
7915 | + (ccs_flags & CCS_OPEN_FOR_READ_TRUNCATE) ? 4 : | ||
7916 | +#endif | ||
7917 | + ACC_MODE(flag); | ||
7918 | +#endif | ||
7919 | + int error = 0; | ||
7920 | + struct ccs_path_info buf; | ||
7921 | + int idx; | ||
7922 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) | ||
7923 | + if (current->in_execve && !(ccs_flags & CCS_TASK_IS_IN_EXECVE)) | ||
7924 | + return 0; | ||
7925 | +#endif | ||
7926 | +#ifndef CONFIG_CCSECURITY_FILE_READDIR | ||
7927 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) | ||
7928 | + if (d_is_dir(dentry)) | ||
7929 | + return 0; | ||
7930 | +#else | ||
7931 | + if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) | ||
7932 | + return 0; | ||
7933 | +#endif | ||
7934 | +#endif | ||
7935 | + buf.name = NULL; | ||
7936 | + r.mode = CCS_CONFIG_DISABLED; | ||
7937 | + idx = ccs_read_lock(); | ||
7938 | + if (acc_mode && ccs_init_request_info(&r, CCS_MAC_FILE_OPEN) | ||
7939 | + != CCS_CONFIG_DISABLED) { | ||
7940 | + if (!ccs_get_realpath(&buf, &obj.path1)) { | ||
7941 | + error = -ENOMEM; | ||
7942 | + goto out; | ||
7943 | + } | ||
7944 | + r.obj = &obj; | ||
7945 | + if (acc_mode & MAY_READ) | ||
7946 | + error = ccs_path_permission(&r, CCS_TYPE_READ, &buf); | ||
7947 | + if (!error && (acc_mode & MAY_WRITE)) | ||
7948 | + error = ccs_path_permission(&r, (flag & O_APPEND) ? | ||
7949 | + CCS_TYPE_APPEND : | ||
7950 | + CCS_TYPE_WRITE, &buf); | ||
7951 | + } | ||
7952 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | ||
7953 | + if (!error && (flag & O_TRUNC) && | ||
7954 | + ccs_init_request_info(&r, CCS_MAC_FILE_TRUNCATE) | ||
7955 | + != CCS_CONFIG_DISABLED) { | ||
7956 | + if (!buf.name && !ccs_get_realpath(&buf, &obj.path1)) { | ||
7957 | + error = -ENOMEM; | ||
7958 | + goto out; | ||
7959 | + } | ||
7960 | + r.obj = &obj; | ||
7961 | + error = ccs_path_permission(&r, CCS_TYPE_TRUNCATE, &buf); | ||
7962 | + } | ||
7963 | +#endif | ||
7964 | +out: | ||
7965 | + kfree(buf.name); | ||
7966 | + ccs_read_unlock(idx); | ||
7967 | + if (r.mode != CCS_CONFIG_ENFORCING) | ||
7968 | + error = 0; | ||
7969 | + return error; | ||
7970 | +} | ||
7971 | + | ||
7972 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | ||
7973 | + | ||
7974 | +/** | ||
7975 | + * ccs_new_open_permission - Check permission for "read" and "write". | ||
7976 | + * | ||
7977 | + * @filp: Pointer to "struct file". | ||
7978 | + * | ||
7979 | + * Returns 0 on success, negative value otherwise. | ||
7980 | + */ | ||
7981 | +static int ccs_new_open_permission(struct file *filp) | ||
7982 | +{ | ||
7983 | + return __ccs_open_permission(filp->f_path.dentry, filp->f_path.mnt, | ||
7984 | + filp->f_flags); | ||
7985 | +} | ||
7986 | + | ||
7987 | +#endif | ||
7988 | + | ||
7989 | +/** | ||
7990 | + * ccs_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "append", "getattr", "chroot" and "unmount". | ||
7991 | + * | ||
7992 | + * @operation: Type of operation. | ||
7993 | + * @dentry: Pointer to "struct dentry". | ||
7994 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | ||
7995 | + * @target: Symlink's target if @operation is CCS_TYPE_SYMLINK, | ||
7996 | + * NULL otherwise. | ||
7997 | + * | ||
7998 | + * Returns 0 on success, negative value otherwise. | ||
7999 | + */ | ||
8000 | +static int ccs_path_perm(const u8 operation, struct dentry *dentry, | ||
8001 | + struct vfsmount *mnt, const char *target) | ||
8002 | +{ | ||
8003 | + struct ccs_request_info r; | ||
8004 | + struct ccs_obj_info obj = { | ||
8005 | + .path1.dentry = dentry, | ||
8006 | + .path1.mnt = mnt, | ||
8007 | + }; | ||
8008 | + int error = 0; | ||
8009 | + struct ccs_path_info buf; | ||
8010 | + bool is_enforce = false; | ||
8011 | + struct ccs_path_info symlink_target; | ||
8012 | + int idx; | ||
8013 | + buf.name = NULL; | ||
8014 | + symlink_target.name = NULL; | ||
8015 | + idx = ccs_read_lock(); | ||
8016 | + if (ccs_init_request_info(&r, ccs_p2mac[operation]) | ||
8017 | + == CCS_CONFIG_DISABLED) | ||
8018 | + goto out; | ||
8019 | + is_enforce = (r.mode == CCS_CONFIG_ENFORCING); | ||
8020 | + error = -ENOMEM; | ||
8021 | + if (!ccs_get_realpath(&buf, &obj.path1)) | ||
8022 | + goto out; | ||
8023 | + r.obj = &obj; | ||
8024 | + switch (operation) { | ||
8025 | + case CCS_TYPE_RMDIR: | ||
8026 | + case CCS_TYPE_CHROOT: | ||
8027 | + ccs_add_slash(&buf); | ||
8028 | + break; | ||
8029 | + case CCS_TYPE_SYMLINK: | ||
8030 | + symlink_target.name = ccs_encode(target); | ||
8031 | + if (!symlink_target.name) | ||
8032 | + goto out; | ||
8033 | + ccs_fill_path_info(&symlink_target); | ||
8034 | + obj.symlink_target = &symlink_target; | ||
8035 | + break; | ||
8036 | + } | ||
8037 | + error = ccs_path_permission(&r, operation, &buf); | ||
8038 | + if (operation == CCS_TYPE_SYMLINK) | ||
8039 | + kfree(symlink_target.name); | ||
8040 | +out: | ||
8041 | + kfree(buf.name); | ||
8042 | + ccs_read_unlock(idx); | ||
8043 | + if (!is_enforce) | ||
8044 | + error = 0; | ||
8045 | + return error; | ||
8046 | +} | ||
8047 | + | ||
8048 | +/** | ||
8049 | + * ccs_mkdev_perm - Check permission for "mkblock" and "mkchar". | ||
8050 | + * | ||
8051 | + * @operation: Type of operation. (CCS_TYPE_MKCHAR or CCS_TYPE_MKBLOCK) | ||
8052 | + * @dentry: Pointer to "struct dentry". | ||
8053 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8054 | + * @mode: Create mode. | ||
8055 | + * @dev: Device number. | ||
8056 | + * | ||
8057 | + * Returns 0 on success, negative value otherwise. | ||
8058 | + */ | ||
8059 | +static int ccs_mkdev_perm(const u8 operation, struct dentry *dentry, | ||
8060 | + struct vfsmount *mnt, const unsigned int mode, | ||
8061 | + unsigned int dev) | ||
8062 | +{ | ||
8063 | + struct ccs_request_info r; | ||
8064 | + struct ccs_obj_info obj = { | ||
8065 | + .path1.dentry = dentry, | ||
8066 | + .path1.mnt = mnt, | ||
8067 | + }; | ||
8068 | + int error = 0; | ||
8069 | + struct ccs_path_info buf; | ||
8070 | + bool is_enforce = false; | ||
8071 | + int idx; | ||
8072 | + idx = ccs_read_lock(); | ||
8073 | + if (ccs_init_request_info(&r, ccs_pnnn2mac[operation]) | ||
8074 | + == CCS_CONFIG_DISABLED) | ||
8075 | + goto out; | ||
8076 | + is_enforce = (r.mode == CCS_CONFIG_ENFORCING); | ||
8077 | + error = -EPERM; | ||
8078 | + if (!capable(CAP_MKNOD)) | ||
8079 | + goto out; | ||
8080 | + error = -ENOMEM; | ||
8081 | + if (!ccs_get_realpath(&buf, &obj.path1)) | ||
8082 | + goto out; | ||
8083 | + r.obj = &obj; | ||
8084 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) | ||
8085 | + dev = new_decode_dev(dev); | ||
8086 | +#endif | ||
8087 | + r.param_type = CCS_TYPE_MKDEV_ACL; | ||
8088 | + r.param.mkdev.filename = &buf; | ||
8089 | + r.param.mkdev.operation = operation; | ||
8090 | + r.param.mkdev.mode = mode; | ||
8091 | + r.param.mkdev.major = MAJOR(dev); | ||
8092 | + r.param.mkdev.minor = MINOR(dev); | ||
8093 | + error = ccs_check_acl(&r); | ||
8094 | + kfree(buf.name); | ||
8095 | +out: | ||
8096 | + ccs_read_unlock(idx); | ||
8097 | + if (!is_enforce) | ||
8098 | + error = 0; | ||
8099 | + return error; | ||
8100 | +} | ||
8101 | + | ||
8102 | +/** | ||
8103 | + * ccs_path2_perm - Check permission for "rename", "link" and "pivot_root". | ||
8104 | + * | ||
8105 | + * @operation: Type of operation. | ||
8106 | + * @dentry1: Pointer to "struct dentry". | ||
8107 | + * @mnt1: Pointer to "struct vfsmount". Maybe NULL. | ||
8108 | + * @dentry2: Pointer to "struct dentry". | ||
8109 | + * @mnt2: Pointer to "struct vfsmount". Maybe NULL. | ||
8110 | + * | ||
8111 | + * Returns 0 on success, negative value otherwise. | ||
8112 | + */ | ||
8113 | +static int ccs_path2_perm(const u8 operation, struct dentry *dentry1, | ||
8114 | + struct vfsmount *mnt1, struct dentry *dentry2, | ||
8115 | + struct vfsmount *mnt2) | ||
8116 | +{ | ||
8117 | + struct ccs_request_info r; | ||
8118 | + int error = 0; | ||
8119 | + struct ccs_path_info buf1; | ||
8120 | + struct ccs_path_info buf2; | ||
8121 | + bool is_enforce = false; | ||
8122 | + struct ccs_obj_info obj = { | ||
8123 | + .path1.dentry = dentry1, | ||
8124 | + .path1.mnt = mnt1, | ||
8125 | + .path2.dentry = dentry2, | ||
8126 | + .path2.mnt = mnt2, | ||
8127 | + }; | ||
8128 | + int idx; | ||
8129 | + buf1.name = NULL; | ||
8130 | + buf2.name = NULL; | ||
8131 | + idx = ccs_read_lock(); | ||
8132 | + if (ccs_init_request_info(&r, ccs_pp2mac[operation]) | ||
8133 | + == CCS_CONFIG_DISABLED) | ||
8134 | + goto out; | ||
8135 | + is_enforce = (r.mode == CCS_CONFIG_ENFORCING); | ||
8136 | + error = -ENOMEM; | ||
8137 | + if (!ccs_get_realpath(&buf1, &obj.path1) || | ||
8138 | + !ccs_get_realpath(&buf2, &obj.path2)) | ||
8139 | + goto out; | ||
8140 | + switch (operation) { | ||
8141 | + case CCS_TYPE_RENAME: | ||
8142 | + case CCS_TYPE_LINK: | ||
8143 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) | ||
8144 | + if (!d_is_dir(dentry1)) | ||
8145 | + break; | ||
8146 | +#else | ||
8147 | + if (!dentry1->d_inode || !S_ISDIR(dentry1->d_inode->i_mode)) | ||
8148 | + break; | ||
8149 | +#endif | ||
8150 | + /* fall through */ | ||
8151 | + case CCS_TYPE_PIVOT_ROOT: | ||
8152 | + ccs_add_slash(&buf1); | ||
8153 | + ccs_add_slash(&buf2); | ||
8154 | + break; | ||
8155 | + } | ||
8156 | + r.obj = &obj; | ||
8157 | + r.param_type = CCS_TYPE_PATH2_ACL; | ||
8158 | + r.param.path2.operation = operation; | ||
8159 | + r.param.path2.filename1 = &buf1; | ||
8160 | + r.param.path2.filename2 = &buf2; | ||
8161 | + error = ccs_check_acl(&r); | ||
8162 | +out: | ||
8163 | + kfree(buf1.name); | ||
8164 | + kfree(buf2.name); | ||
8165 | + ccs_read_unlock(idx); | ||
8166 | + if (!is_enforce) | ||
8167 | + error = 0; | ||
8168 | + return error; | ||
8169 | +} | ||
8170 | + | ||
8171 | +/** | ||
8172 | + * ccs_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp". | ||
8173 | + * | ||
8174 | + * @type: Type of operation. | ||
8175 | + * @dentry: Pointer to "struct dentry". | ||
8176 | + * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8177 | + * @number: Number. | ||
8178 | + * | ||
8179 | + * Returns 0 on success, negative value otherwise. | ||
8180 | + */ | ||
8181 | +static int ccs_path_number_perm(const u8 type, struct dentry *dentry, | ||
8182 | + struct vfsmount *vfsmnt, unsigned long number) | ||
8183 | +{ | ||
8184 | + struct ccs_request_info r; | ||
8185 | + struct ccs_obj_info obj = { | ||
8186 | + .path1.dentry = dentry, | ||
8187 | + .path1.mnt = vfsmnt, | ||
8188 | + }; | ||
8189 | + int error = 0; | ||
8190 | + struct ccs_path_info buf; | ||
8191 | + int idx; | ||
8192 | + if (!dentry) | ||
8193 | + return 0; | ||
8194 | + idx = ccs_read_lock(); | ||
8195 | + if (ccs_init_request_info(&r, ccs_pn2mac[type]) == CCS_CONFIG_DISABLED) | ||
8196 | + goto out; | ||
8197 | + error = -ENOMEM; | ||
8198 | + if (!ccs_get_realpath(&buf, &obj.path1)) | ||
8199 | + goto out; | ||
8200 | + r.obj = &obj; | ||
8201 | + if (type == CCS_TYPE_MKDIR) | ||
8202 | + ccs_add_slash(&buf); | ||
8203 | + r.param_type = CCS_TYPE_PATH_NUMBER_ACL; | ||
8204 | + r.param.path_number.operation = type; | ||
8205 | + r.param.path_number.filename = &buf; | ||
8206 | + r.param.path_number.number = number; | ||
8207 | + error = ccs_check_acl(&r); | ||
8208 | + kfree(buf.name); | ||
8209 | +out: | ||
8210 | + ccs_read_unlock(idx); | ||
8211 | + if (r.mode != CCS_CONFIG_ENFORCING) | ||
8212 | + error = 0; | ||
8213 | + return error; | ||
8214 | +} | ||
8215 | + | ||
8216 | +/** | ||
8217 | + * __ccs_ioctl_permission - Check permission for "ioctl". | ||
8218 | + * | ||
8219 | + * @filp: Pointer to "struct file". | ||
8220 | + * @cmd: Ioctl command number. | ||
8221 | + * @arg: Param for @cmd. | ||
8222 | + * | ||
8223 | + * Returns 0 on success, negative value otherwise. | ||
8224 | + */ | ||
8225 | +static int __ccs_ioctl_permission(struct file *filp, unsigned int cmd, | ||
8226 | + unsigned long arg) | ||
8227 | +{ | ||
8228 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) | ||
8229 | + return ccs_path_number_perm(CCS_TYPE_IOCTL, filp->f_path.dentry, | ||
8230 | + filp->f_path.mnt, cmd); | ||
8231 | +#else | ||
8232 | + return ccs_path_number_perm(CCS_TYPE_IOCTL, filp->f_dentry, | ||
8233 | + filp->f_vfsmnt, cmd); | ||
8234 | +#endif | ||
8235 | +} | ||
8236 | + | ||
8237 | +/** | ||
8238 | + * __ccs_chmod_permission - Check permission for "chmod". | ||
8239 | + * | ||
8240 | + * @dentry: Pointer to "struct dentry". | ||
8241 | + * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8242 | + * @mode: Mode. | ||
8243 | + * | ||
8244 | + * Returns 0 on success, negative value otherwise. | ||
8245 | + */ | ||
8246 | +static int __ccs_chmod_permission(struct dentry *dentry, | ||
8247 | + struct vfsmount *vfsmnt, mode_t mode) | ||
8248 | +{ | ||
8249 | + return ccs_path_number_perm(CCS_TYPE_CHMOD, dentry, vfsmnt, | ||
8250 | + mode & S_IALLUGO); | ||
8251 | +} | ||
8252 | + | ||
8253 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) | ||
8254 | + | ||
8255 | +/** | ||
8256 | + * __ccs_chown_permission - Check permission for "chown/chgrp". | ||
8257 | + * | ||
8258 | + * @dentry: Pointer to "struct dentry". | ||
8259 | + * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8260 | + * @user: User ID. | ||
8261 | + * @group: Group ID. | ||
8262 | + * | ||
8263 | + * Returns 0 on success, negative value otherwise. | ||
8264 | + */ | ||
8265 | +static int __ccs_chown_permission(struct dentry *dentry, | ||
8266 | + struct vfsmount *vfsmnt, kuid_t user, | ||
8267 | + kgid_t group) | ||
8268 | +{ | ||
8269 | + int error = 0; | ||
8270 | + if (uid_valid(user)) | ||
8271 | + error = ccs_path_number_perm(CCS_TYPE_CHOWN, dentry, vfsmnt, | ||
8272 | + from_kuid(&init_user_ns, user)); | ||
8273 | + if (!error && gid_valid(group)) | ||
8274 | + error = ccs_path_number_perm(CCS_TYPE_CHGRP, dentry, vfsmnt, | ||
8275 | + from_kgid(&init_user_ns, group)); | ||
8276 | + return error; | ||
8277 | +} | ||
8278 | + | ||
8279 | +#else | ||
8280 | + | ||
8281 | +/** | ||
8282 | + * __ccs_chown_permission - Check permission for "chown/chgrp". | ||
8283 | + * | ||
8284 | + * @dentry: Pointer to "struct dentry". | ||
8285 | + * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8286 | + * @user: User ID. | ||
8287 | + * @group: Group ID. | ||
8288 | + * | ||
8289 | + * Returns 0 on success, negative value otherwise. | ||
8290 | + */ | ||
8291 | +static int __ccs_chown_permission(struct dentry *dentry, | ||
8292 | + struct vfsmount *vfsmnt, uid_t user, | ||
8293 | + gid_t group) | ||
8294 | +{ | ||
8295 | + int error = 0; | ||
8296 | + if (user == (uid_t) -1 && group == (gid_t) -1) | ||
8297 | + return 0; | ||
8298 | + if (user != (uid_t) -1) | ||
8299 | + error = ccs_path_number_perm(CCS_TYPE_CHOWN, dentry, vfsmnt, | ||
8300 | + user); | ||
8301 | + if (!error && group != (gid_t) -1) | ||
8302 | + error = ccs_path_number_perm(CCS_TYPE_CHGRP, dentry, vfsmnt, | ||
8303 | + group); | ||
8304 | + return error; | ||
8305 | +} | ||
8306 | + | ||
8307 | +#endif | ||
8308 | + | ||
8309 | +/** | ||
8310 | + * __ccs_fcntl_permission - Check permission for changing O_APPEND flag. | ||
8311 | + * | ||
8312 | + * @file: Pointer to "struct file". | ||
8313 | + * @cmd: Command number. | ||
8314 | + * @arg: Value for @cmd. | ||
8315 | + * | ||
8316 | + * Returns 0 on success, negative value otherwise. | ||
8317 | + */ | ||
8318 | +static int __ccs_fcntl_permission(struct file *file, unsigned int cmd, | ||
8319 | + unsigned long arg) | ||
8320 | +{ | ||
8321 | + if (!(cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))) | ||
8322 | + return 0; | ||
8323 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | ||
8324 | + return __ccs_open_permission(file->f_path.dentry, file->f_path.mnt, | ||
8325 | + O_WRONLY | (arg & O_APPEND)); | ||
8326 | +#elif defined(RHEL_MAJOR) && RHEL_MAJOR == 6 | ||
8327 | + return __ccs_open_permission(file->f_dentry, file->f_vfsmnt, | ||
8328 | + O_WRONLY | (arg & O_APPEND)); | ||
8329 | +#else | ||
8330 | + return __ccs_open_permission(file->f_dentry, file->f_vfsmnt, | ||
8331 | + (O_WRONLY + 1) | (arg & O_APPEND)); | ||
8332 | +#endif | ||
8333 | +} | ||
8334 | + | ||
8335 | +/** | ||
8336 | + * __ccs_pivot_root_permission - Check permission for pivot_root(). | ||
8337 | + * | ||
8338 | + * @old_path: Pointer to "struct path". | ||
8339 | + * @new_path: Pointer to "struct path". | ||
8340 | + * | ||
8341 | + * Returns 0 on success, negative value otherwise. | ||
8342 | + */ | ||
8343 | +static int __ccs_pivot_root_permission(struct path *old_path, | ||
8344 | + struct path *new_path) | ||
8345 | +{ | ||
8346 | + return ccs_path2_perm(CCS_TYPE_PIVOT_ROOT, new_path->dentry, | ||
8347 | + new_path->mnt, old_path->dentry, old_path->mnt); | ||
8348 | +} | ||
8349 | + | ||
8350 | +/** | ||
8351 | + * __ccs_chroot_permission - Check permission for chroot(). | ||
8352 | + * | ||
8353 | + * @path: Pointer to "struct path". | ||
8354 | + * | ||
8355 | + * Returns 0 on success, negative value otherwise. | ||
8356 | + */ | ||
8357 | +static int __ccs_chroot_permission(struct path *path) | ||
8358 | +{ | ||
8359 | + return ccs_path_perm(CCS_TYPE_CHROOT, path->dentry, path->mnt, NULL); | ||
8360 | +} | ||
8361 | + | ||
8362 | +/** | ||
8363 | + * __ccs_umount_permission - Check permission for unmount. | ||
8364 | + * | ||
8365 | + * @mnt: Pointer to "struct vfsmount". | ||
8366 | + * @flags: Unused. | ||
8367 | + * | ||
8368 | + * Returns 0 on success, negative value otherwise. | ||
8369 | + */ | ||
8370 | +static int __ccs_umount_permission(struct vfsmount *mnt, int flags) | ||
8371 | +{ | ||
8372 | + return ccs_path_perm(CCS_TYPE_UMOUNT, mnt->mnt_root, mnt, NULL); | ||
8373 | +} | ||
8374 | + | ||
8375 | +/** | ||
8376 | + * __ccs_mknod_permission - Check permission for vfs_mknod(). | ||
8377 | + * | ||
8378 | + * @dentry: Pointer to "struct dentry". | ||
8379 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8380 | + * @mode: Device type and permission. | ||
8381 | + * @dev: Device number for block or character device. | ||
8382 | + * | ||
8383 | + * Returns 0 on success, negative value otherwise. | ||
8384 | + */ | ||
8385 | +static int __ccs_mknod_permission(struct dentry *dentry, struct vfsmount *mnt, | ||
8386 | + const unsigned int mode, unsigned int dev) | ||
8387 | +{ | ||
8388 | + int error = 0; | ||
8389 | + const unsigned int perm = mode & S_IALLUGO; | ||
8390 | + switch (mode & S_IFMT) { | ||
8391 | + case S_IFCHR: | ||
8392 | + error = ccs_mkdev_perm(CCS_TYPE_MKCHAR, dentry, mnt, perm, | ||
8393 | + dev); | ||
8394 | + break; | ||
8395 | + case S_IFBLK: | ||
8396 | + error = ccs_mkdev_perm(CCS_TYPE_MKBLOCK, dentry, mnt, perm, | ||
8397 | + dev); | ||
8398 | + break; | ||
8399 | + case S_IFIFO: | ||
8400 | + error = ccs_path_number_perm(CCS_TYPE_MKFIFO, dentry, mnt, | ||
8401 | + perm); | ||
8402 | + break; | ||
8403 | + case S_IFSOCK: | ||
8404 | + error = ccs_path_number_perm(CCS_TYPE_MKSOCK, dentry, mnt, | ||
8405 | + perm); | ||
8406 | + break; | ||
8407 | + case 0: | ||
8408 | + case S_IFREG: | ||
8409 | + error = ccs_path_number_perm(CCS_TYPE_CREATE, dentry, mnt, | ||
8410 | + perm); | ||
8411 | + break; | ||
8412 | + } | ||
8413 | + return error; | ||
8414 | +} | ||
8415 | + | ||
8416 | +/** | ||
8417 | + * __ccs_mkdir_permission - Check permission for vfs_mkdir(). | ||
8418 | + * | ||
8419 | + * @dentry: Pointer to "struct dentry". | ||
8420 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8421 | + * @mode: Create mode. | ||
8422 | + * | ||
8423 | + * Returns 0 on success, negative value otherwise. | ||
8424 | + */ | ||
8425 | +static int __ccs_mkdir_permission(struct dentry *dentry, struct vfsmount *mnt, | ||
8426 | + unsigned int mode) | ||
8427 | +{ | ||
8428 | + return ccs_path_number_perm(CCS_TYPE_MKDIR, dentry, mnt, mode); | ||
8429 | +} | ||
8430 | + | ||
8431 | +/** | ||
8432 | + * __ccs_rmdir_permission - Check permission for vfs_rmdir(). | ||
8433 | + * | ||
8434 | + * @dentry: Pointer to "struct dentry". | ||
8435 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8436 | + * | ||
8437 | + * Returns 0 on success, negative value otherwise. | ||
8438 | + */ | ||
8439 | +static int __ccs_rmdir_permission(struct dentry *dentry, struct vfsmount *mnt) | ||
8440 | +{ | ||
8441 | + return ccs_path_perm(CCS_TYPE_RMDIR, dentry, mnt, NULL); | ||
8442 | +} | ||
8443 | + | ||
8444 | +/** | ||
8445 | + * __ccs_unlink_permission - Check permission for vfs_unlink(). | ||
8446 | + * | ||
8447 | + * @dentry: Pointer to "struct dentry". | ||
8448 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8449 | + * | ||
8450 | + * Returns 0 on success, negative value otherwise. | ||
8451 | + */ | ||
8452 | +static int __ccs_unlink_permission(struct dentry *dentry, struct vfsmount *mnt) | ||
8453 | +{ | ||
8454 | + return ccs_path_perm(CCS_TYPE_UNLINK, dentry, mnt, NULL); | ||
8455 | +} | ||
8456 | + | ||
8457 | +#ifdef CONFIG_CCSECURITY_FILE_GETATTR | ||
8458 | + | ||
8459 | +/** | ||
8460 | + * __ccs_getattr_permission - Check permission for vfs_getattr(). | ||
8461 | + * | ||
8462 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8463 | + * @dentry: Pointer to "struct dentry". | ||
8464 | + * | ||
8465 | + * Returns 0 on success, negative value otherwise. | ||
8466 | + */ | ||
8467 | +static int __ccs_getattr_permission(struct vfsmount *mnt, | ||
8468 | + struct dentry *dentry) | ||
8469 | +{ | ||
8470 | + return ccs_path_perm(CCS_TYPE_GETATTR, dentry, mnt, NULL); | ||
8471 | +} | ||
8472 | + | ||
8473 | +#endif | ||
8474 | + | ||
8475 | +/** | ||
8476 | + * __ccs_symlink_permission - Check permission for vfs_symlink(). | ||
8477 | + * | ||
8478 | + * @dentry: Pointer to "struct dentry". | ||
8479 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8480 | + * @from: Content of symlink. | ||
8481 | + * | ||
8482 | + * Returns 0 on success, negative value otherwise. | ||
8483 | + */ | ||
8484 | +static int __ccs_symlink_permission(struct dentry *dentry, | ||
8485 | + struct vfsmount *mnt, const char *from) | ||
8486 | +{ | ||
8487 | + return ccs_path_perm(CCS_TYPE_SYMLINK, dentry, mnt, from); | ||
8488 | +} | ||
8489 | + | ||
8490 | +/** | ||
8491 | + * __ccs_truncate_permission - Check permission for notify_change(). | ||
8492 | + * | ||
8493 | + * @dentry: Pointer to "struct dentry". | ||
8494 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8495 | + * | ||
8496 | + * Returns 0 on success, negative value otherwise. | ||
8497 | + */ | ||
8498 | +static int __ccs_truncate_permission(struct dentry *dentry, | ||
8499 | + struct vfsmount *mnt) | ||
8500 | +{ | ||
8501 | + return ccs_path_perm(CCS_TYPE_TRUNCATE, dentry, mnt, NULL); | ||
8502 | +} | ||
8503 | + | ||
8504 | +/** | ||
8505 | + * __ccs_rename_permission - Check permission for vfs_rename(). | ||
8506 | + * | ||
8507 | + * @old_dentry: Pointer to "struct dentry". | ||
8508 | + * @new_dentry: Pointer to "struct dentry". | ||
8509 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8510 | + * | ||
8511 | + * Returns 0 on success, negative value otherwise. | ||
8512 | + */ | ||
8513 | +static int __ccs_rename_permission(struct dentry *old_dentry, | ||
8514 | + struct dentry *new_dentry, | ||
8515 | + struct vfsmount *mnt) | ||
8516 | +{ | ||
8517 | + return ccs_path2_perm(CCS_TYPE_RENAME, old_dentry, mnt, new_dentry, | ||
8518 | + mnt); | ||
8519 | +} | ||
8520 | + | ||
8521 | +/** | ||
8522 | + * __ccs_link_permission - Check permission for vfs_link(). | ||
8523 | + * | ||
8524 | + * @old_dentry: Pointer to "struct dentry". | ||
8525 | + * @new_dentry: Pointer to "struct dentry". | ||
8526 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | ||
8527 | + * | ||
8528 | + * Returns 0 on success, negative value otherwise. | ||
8529 | + */ | ||
8530 | +static int __ccs_link_permission(struct dentry *old_dentry, | ||
8531 | + struct dentry *new_dentry, | ||
8532 | + struct vfsmount *mnt) | ||
8533 | +{ | ||
8534 | + return ccs_path2_perm(CCS_TYPE_LINK, old_dentry, mnt, new_dentry, mnt); | ||
8535 | +} | ||
8536 | + | ||
8537 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | ||
8538 | + | ||
8539 | +/** | ||
8540 | + * __ccs_open_exec_permission - Check permission for open_exec(). | ||
8541 | + * | ||
8542 | + * @dentry: Pointer to "struct dentry". | ||
8543 | + * @mnt: Pointer to "struct vfsmount". | ||
8544 | + * | ||
8545 | + * Returns 0 on success, negative value otherwise. | ||
8546 | + */ | ||
8547 | +static int __ccs_open_exec_permission(struct dentry *dentry, | ||
8548 | + struct vfsmount *mnt) | ||
8549 | +{ | ||
8550 | + return (ccs_current_flags() & CCS_TASK_IS_IN_EXECVE) ? | ||
8551 | + __ccs_open_permission(dentry, mnt, O_RDONLY + 1) : 0; | ||
8552 | +} | ||
8553 | + | ||
8554 | +/** | ||
8555 | + * __ccs_uselib_permission - Check permission for sys_uselib(). | ||
8556 | + * | ||
8557 | + * @dentry: Pointer to "struct dentry". | ||
8558 | + * @mnt: Pointer to "struct vfsmount". | ||
8559 | + * | ||
8560 | + * Returns 0 on success, negative value otherwise. | ||
8561 | + */ | ||
8562 | +static int __ccs_uselib_permission(struct dentry *dentry, struct vfsmount *mnt) | ||
8563 | +{ | ||
8564 | + return __ccs_open_permission(dentry, mnt, O_RDONLY + 1); | ||
8565 | +} | ||
8566 | + | ||
8567 | +#endif | ||
8568 | + | ||
8569 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) || (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL)) | ||
8570 | + | ||
8571 | +/** | ||
8572 | + * __ccs_parse_table - Check permission for parse_table(). | ||
8573 | + * | ||
8574 | + * @name: Pointer to "int __user". | ||
8575 | + * @nlen: Number of elements in @name. | ||
8576 | + * @oldval: Pointer to "void __user". | ||
8577 | + * @newval: Pointer to "void __user". | ||
8578 | + * @table: Pointer to "struct ctl_table". | ||
8579 | + * | ||
8580 | + * Returns 0 on success, negative value otherwise. | ||
8581 | + * | ||
8582 | + * Note that this function is racy because this function checks values in | ||
8583 | + * userspace memory which could be changed after permission check. | ||
8584 | + */ | ||
8585 | +static int __ccs_parse_table(int __user *name, int nlen, void __user *oldval, | ||
8586 | + void __user *newval, struct ctl_table *table) | ||
8587 | +{ | ||
8588 | + int n; | ||
8589 | + int error = -ENOMEM; | ||
8590 | + int op = 0; | ||
8591 | + struct ccs_path_info buf; | ||
8592 | + char *buffer = NULL; | ||
8593 | + struct ccs_request_info r; | ||
8594 | + int idx; | ||
8595 | + if (oldval) | ||
8596 | + op |= 004; | ||
8597 | + if (newval) | ||
8598 | + op |= 002; | ||
8599 | + if (!op) /* Neither read nor write */ | ||
8600 | + return 0; | ||
8601 | + idx = ccs_read_lock(); | ||
8602 | + if (ccs_init_request_info(&r, CCS_MAC_FILE_OPEN) | ||
8603 | + == CCS_CONFIG_DISABLED) { | ||
8604 | + error = 0; | ||
8605 | + goto out; | ||
8606 | + } | ||
8607 | + buffer = kmalloc(PAGE_SIZE, CCS_GFP_FLAGS); | ||
8608 | + if (!buffer) | ||
8609 | + goto out; | ||
8610 | + snprintf(buffer, PAGE_SIZE - 1, "proc:/sys"); | ||
8611 | +repeat: | ||
8612 | + if (!nlen) { | ||
8613 | + error = -ENOTDIR; | ||
8614 | + goto out; | ||
8615 | + } | ||
8616 | + if (get_user(n, name)) { | ||
8617 | + error = -EFAULT; | ||
8618 | + goto out; | ||
8619 | + } | ||
8620 | + for ( ; table->ctl_name | ||
8621 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21) | ||
8622 | + || table->procname | ||
8623 | +#endif | ||
8624 | + ; table++) { | ||
8625 | + int pos; | ||
8626 | + const char *cp; | ||
8627 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21) | ||
8628 | + if (n != table->ctl_name && table->ctl_name != CTL_ANY) | ||
8629 | + continue; | ||
8630 | +#else | ||
8631 | + if (!n || n != table->ctl_name) | ||
8632 | + continue; | ||
8633 | +#endif | ||
8634 | + pos = strlen(buffer); | ||
8635 | + cp = table->procname; | ||
8636 | + error = -ENOMEM; | ||
8637 | + if (cp) { | ||
8638 | + int len = strlen(cp); | ||
8639 | + if (len + 2 > PAGE_SIZE - 1) | ||
8640 | + goto out; | ||
8641 | + buffer[pos++] = '/'; | ||
8642 | + memmove(buffer + pos, cp, len + 1); | ||
8643 | + } else { | ||
8644 | + /* Assume nobody assigns "=\$=" for procname. */ | ||
8645 | + snprintf(buffer + pos, PAGE_SIZE - pos - 1, | ||
8646 | + "/=%d=", table->ctl_name); | ||
8647 | + if (!memchr(buffer, '\0', PAGE_SIZE - 2)) | ||
8648 | + goto out; | ||
8649 | + } | ||
8650 | + if (!table->child) | ||
8651 | + goto no_child; | ||
8652 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21) | ||
8653 | + if (!table->strategy) | ||
8654 | + goto no_strategy; | ||
8655 | + /* printk("sysctl='%s'\n", buffer); */ | ||
8656 | + buf.name = ccs_encode(buffer); | ||
8657 | + if (!buf.name) | ||
8658 | + goto out; | ||
8659 | + ccs_fill_path_info(&buf); | ||
8660 | + if (op & MAY_READ) | ||
8661 | + error = ccs_path_permission(&r, CCS_TYPE_READ, &buf); | ||
8662 | + else | ||
8663 | + error = 0; | ||
8664 | + if (!error && (op & MAY_WRITE)) | ||
8665 | + error = ccs_path_permission(&r, CCS_TYPE_WRITE, &buf); | ||
8666 | + kfree(buf.name); | ||
8667 | + if (error) | ||
8668 | + goto out; | ||
8669 | +no_strategy: | ||
8670 | +#endif | ||
8671 | + name++; | ||
8672 | + nlen--; | ||
8673 | + table = table->child; | ||
8674 | + goto repeat; | ||
8675 | +no_child: | ||
8676 | + /* printk("sysctl='%s'\n", buffer); */ | ||
8677 | + buf.name = ccs_encode(buffer); | ||
8678 | + if (!buf.name) | ||
8679 | + goto out; | ||
8680 | + ccs_fill_path_info(&buf); | ||
8681 | + if (op & MAY_READ) | ||
8682 | + error = ccs_path_permission(&r, CCS_TYPE_READ, &buf); | ||
8683 | + else | ||
8684 | + error = 0; | ||
8685 | + if (!error && (op & MAY_WRITE)) | ||
8686 | + error = ccs_path_permission(&r, CCS_TYPE_WRITE, &buf); | ||
8687 | + kfree(buf.name); | ||
8688 | + goto out; | ||
8689 | + } | ||
8690 | + error = -ENOTDIR; | ||
8691 | +out: | ||
8692 | + ccs_read_unlock(idx); | ||
8693 | + kfree(buffer); | ||
8694 | + return error; | ||
8695 | +} | ||
8696 | + | ||
8697 | +#endif | ||
8698 | + | ||
8699 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24) | ||
8700 | + | ||
8701 | +/** | ||
8702 | + * ccs_old_pivot_root_permission - Check permission for pivot_root(). | ||
8703 | + * | ||
8704 | + * @old_nd: Pointer to "struct nameidata". | ||
8705 | + * @new_nd: Pointer to "struct nameidata". | ||
8706 | + * | ||
8707 | + * Returns 0 on success, negative value otherwise. | ||
8708 | + */ | ||
8709 | +static int ccs_old_pivot_root_permission(struct nameidata *old_nd, | ||
8710 | + struct nameidata *new_nd) | ||
8711 | +{ | ||
8712 | + struct path old_path = { old_nd->mnt, old_nd->dentry }; | ||
8713 | + struct path new_path = { new_nd->mnt, new_nd->dentry }; | ||
8714 | + return __ccs_pivot_root_permission(&old_path, &new_path); | ||
8715 | +} | ||
8716 | + | ||
8717 | +/** | ||
8718 | + * ccs_old_chroot_permission - Check permission for chroot(). | ||
8719 | + * | ||
8720 | + * @nd: Pointer to "struct nameidata". | ||
8721 | + * | ||
8722 | + * Returns 0 on success, negative value otherwise. | ||
8723 | + */ | ||
8724 | +static int ccs_old_chroot_permission(struct nameidata *nd) | ||
8725 | +{ | ||
8726 | + struct path path = { nd->mnt, nd->dentry }; | ||
8727 | + return __ccs_chroot_permission(&path); | ||
8728 | +} | ||
8729 | + | ||
8730 | +#endif | ||
8731 | + | ||
8732 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
8733 | + | ||
8734 | +/** | ||
8735 | + * ccs_address_matches_group - Check whether the given address matches members of the given address group. | ||
8736 | + * | ||
8737 | + * @is_ipv6: True if @address is an IPv6 address. | ||
8738 | + * @address: An IPv4 or IPv6 address. | ||
8739 | + * @group: Pointer to "struct ccs_address_group". | ||
8740 | + * | ||
8741 | + * Returns true if @address matches addresses in @group group, false otherwise. | ||
8742 | + * | ||
8743 | + * Caller holds ccs_read_lock(). | ||
8744 | + */ | ||
8745 | +static bool ccs_address_matches_group(const bool is_ipv6, const u32 *address, | ||
8746 | + const struct ccs_group *group) | ||
8747 | +{ | ||
8748 | + struct ccs_address_group *member; | ||
8749 | + bool matched = false; | ||
8750 | + const u8 size = is_ipv6 ? 16 : 4; | ||
8751 | + list_for_each_entry_srcu(member, &group->member_list, head.list, | ||
8752 | + &ccs_ss) { | ||
8753 | + if (member->head.is_deleted) | ||
8754 | + continue; | ||
8755 | + if (member->address.is_ipv6 != is_ipv6) | ||
8756 | + continue; | ||
8757 | + if (memcmp(&member->address.ip[0], address, size) > 0 || | ||
8758 | + memcmp(address, &member->address.ip[1], size) > 0) | ||
8759 | + continue; | ||
8760 | + matched = true; | ||
8761 | + break; | ||
8762 | + } | ||
8763 | + return matched; | ||
8764 | +} | ||
8765 | + | ||
8766 | +/** | ||
8767 | + * ccs_check_inet_acl - Check permission for inet domain socket operation. | ||
8768 | + * | ||
8769 | + * @r: Pointer to "struct ccs_request_info". | ||
8770 | + * @ptr: Pointer to "struct ccs_acl_info". | ||
8771 | + * | ||
8772 | + * Returns true if granted, false otherwise. | ||
8773 | + */ | ||
8774 | +static bool ccs_check_inet_acl(struct ccs_request_info *r, | ||
8775 | + const struct ccs_acl_info *ptr) | ||
8776 | +{ | ||
8777 | + const struct ccs_inet_acl *acl = container_of(ptr, typeof(*acl), head); | ||
8778 | + const u8 size = r->param.inet_network.is_ipv6 ? 16 : 4; | ||
8779 | + if (!(ptr->perm & (1 << r->param.inet_network.operation)) || | ||
8780 | + !ccs_compare_number_union(r->param.inet_network.port, &acl->port)) | ||
8781 | + return false; | ||
8782 | + if (acl->address.group) | ||
8783 | + return ccs_address_matches_group(r->param.inet_network.is_ipv6, | ||
8784 | + r->param.inet_network.address, | ||
8785 | + acl->address.group); | ||
8786 | + return acl->address.is_ipv6 == r->param.inet_network.is_ipv6 && | ||
8787 | + memcmp(&acl->address.ip[0], | ||
8788 | + r->param.inet_network.address, size) <= 0 && | ||
8789 | + memcmp(r->param.inet_network.address, | ||
8790 | + &acl->address.ip[1], size) <= 0; | ||
8791 | +} | ||
8792 | + | ||
8793 | +/** | ||
8794 | + * ccs_check_unix_acl - Check permission for unix domain socket operation. | ||
8795 | + * | ||
8796 | + * @r: Pointer to "struct ccs_request_info". | ||
8797 | + * @ptr: Pointer to "struct ccs_acl_info". | ||
8798 | + * | ||
8799 | + * Returns true if granted, false otherwise. | ||
8800 | + */ | ||
8801 | +static bool ccs_check_unix_acl(struct ccs_request_info *r, | ||
8802 | + const struct ccs_acl_info *ptr) | ||
8803 | +{ | ||
8804 | + const struct ccs_unix_acl *acl = container_of(ptr, typeof(*acl), head); | ||
8805 | + return (ptr->perm & (1 << r->param.unix_network.operation)) && | ||
8806 | + ccs_compare_name_union(r->param.unix_network.address, | ||
8807 | + &acl->name); | ||
8808 | +} | ||
8809 | + | ||
8810 | +/** | ||
8811 | + * ccs_inet_entry - Check permission for INET network operation. | ||
8812 | + * | ||
8813 | + * @address: Pointer to "struct ccs_addr_info". | ||
8814 | + * | ||
8815 | + * Returns 0 on success, negative value otherwise. | ||
8816 | + */ | ||
8817 | +static int ccs_inet_entry(const struct ccs_addr_info *address) | ||
8818 | +{ | ||
8819 | + const int idx = ccs_read_lock(); | ||
8820 | + struct ccs_request_info r; | ||
8821 | + int error = 0; | ||
8822 | + const u8 type = ccs_inet2mac[address->protocol][address->operation]; | ||
8823 | + if (type && ccs_init_request_info(&r, type) != CCS_CONFIG_DISABLED) { | ||
8824 | + r.param_type = CCS_TYPE_INET_ACL; | ||
8825 | + r.param.inet_network.protocol = address->protocol; | ||
8826 | + r.param.inet_network.operation = address->operation; | ||
8827 | + r.param.inet_network.is_ipv6 = address->inet.is_ipv6; | ||
8828 | + r.param.inet_network.address = address->inet.address; | ||
8829 | + r.param.inet_network.port = ntohs(address->inet.port); | ||
8830 | + r.dont_sleep_on_enforce_error = | ||
8831 | + address->operation == CCS_NETWORK_ACCEPT | ||
8832 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
8833 | + || address->operation == CCS_NETWORK_RECV | ||
8834 | +#endif | ||
8835 | + ; | ||
8836 | + error = ccs_check_acl(&r); | ||
8837 | + } | ||
8838 | + ccs_read_unlock(idx); | ||
8839 | + return error; | ||
8840 | +} | ||
8841 | + | ||
8842 | +/** | ||
8843 | + * ccs_check_inet_address - Check permission for inet domain socket's operation. | ||
8844 | + * | ||
8845 | + * @addr: Pointer to "struct sockaddr". | ||
8846 | + * @addr_len: Size of @addr. | ||
8847 | + * @port: Port number. | ||
8848 | + * @address: Pointer to "struct ccs_addr_info". | ||
8849 | + * | ||
8850 | + * Returns 0 on success, negative value otherwise. | ||
8851 | + */ | ||
8852 | +static int ccs_check_inet_address(const struct sockaddr *addr, | ||
8853 | + const unsigned int addr_len, const u16 port, | ||
8854 | + struct ccs_addr_info *address) | ||
8855 | +{ | ||
8856 | + struct ccs_inet_addr_info *i = &address->inet; | ||
8857 | + switch (addr->sa_family) { | ||
8858 | + case AF_INET6: | ||
8859 | + if (addr_len < SIN6_LEN_RFC2133) | ||
8860 | + goto skip; | ||
8861 | + i->is_ipv6 = true; | ||
8862 | + i->address = (u32 *) | ||
8863 | + ((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr; | ||
8864 | + i->port = ((struct sockaddr_in6 *) addr)->sin6_port; | ||
8865 | + break; | ||
8866 | + case AF_INET: | ||
8867 | + if (addr_len < sizeof(struct sockaddr_in)) | ||
8868 | + goto skip; | ||
8869 | + i->is_ipv6 = false; | ||
8870 | + i->address = (u32 *) &((struct sockaddr_in *) addr)->sin_addr; | ||
8871 | + i->port = ((struct sockaddr_in *) addr)->sin_port; | ||
8872 | + break; | ||
8873 | + default: | ||
8874 | + goto skip; | ||
8875 | + } | ||
8876 | + if (address->protocol == SOCK_RAW) | ||
8877 | + i->port = htons(port); | ||
8878 | + return ccs_inet_entry(address); | ||
8879 | +skip: | ||
8880 | + return 0; | ||
8881 | +} | ||
8882 | + | ||
8883 | +/** | ||
8884 | + * ccs_unix_entry - Check permission for UNIX network operation. | ||
8885 | + * | ||
8886 | + * @address: Pointer to "struct ccs_addr_info". | ||
8887 | + * | ||
8888 | + * Returns 0 on success, negative value otherwise. | ||
8889 | + */ | ||
8890 | +static int ccs_unix_entry(const struct ccs_addr_info *address) | ||
8891 | +{ | ||
8892 | + const int idx = ccs_read_lock(); | ||
8893 | + struct ccs_request_info r; | ||
8894 | + int error = 0; | ||
8895 | + const u8 type = ccs_unix2mac[address->protocol][address->operation]; | ||
8896 | + if (type && ccs_init_request_info(&r, type) != CCS_CONFIG_DISABLED) { | ||
8897 | + char *buf = address->unix0.addr; | ||
8898 | + int len = address->unix0.addr_len - sizeof(sa_family_t); | ||
8899 | + if (len <= 0) { | ||
8900 | + buf = "anonymous"; | ||
8901 | + len = 9; | ||
8902 | + } else if (buf[0]) { | ||
8903 | + len = strnlen(buf, len); | ||
8904 | + } | ||
8905 | + buf = ccs_encode2(buf, len); | ||
8906 | + if (buf) { | ||
8907 | + struct ccs_path_info addr; | ||
8908 | + addr.name = buf; | ||
8909 | + ccs_fill_path_info(&addr); | ||
8910 | + r.param_type = CCS_TYPE_UNIX_ACL; | ||
8911 | + r.param.unix_network.protocol = address->protocol; | ||
8912 | + r.param.unix_network.operation = address->operation; | ||
8913 | + r.param.unix_network.address = &addr; | ||
8914 | + r.dont_sleep_on_enforce_error = | ||
8915 | + address->operation == CCS_NETWORK_ACCEPT | ||
8916 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
8917 | + || address->operation == CCS_NETWORK_RECV | ||
8918 | +#endif | ||
8919 | + ; | ||
8920 | + error = ccs_check_acl(&r); | ||
8921 | + kfree(buf); | ||
8922 | + } else | ||
8923 | + error = -ENOMEM; | ||
8924 | + } | ||
8925 | + ccs_read_unlock(idx); | ||
8926 | + return error; | ||
8927 | +} | ||
8928 | + | ||
8929 | +/** | ||
8930 | + * ccs_check_unix_address - Check permission for unix domain socket's operation. | ||
8931 | + * | ||
8932 | + * @addr: Pointer to "struct sockaddr". | ||
8933 | + * @addr_len: Size of @addr. | ||
8934 | + * @address: Pointer to "struct ccs_addr_info". | ||
8935 | + * | ||
8936 | + * Returns 0 on success, negative value otherwise. | ||
8937 | + */ | ||
8938 | +static int ccs_check_unix_address(struct sockaddr *addr, | ||
8939 | + const unsigned int addr_len, | ||
8940 | + struct ccs_addr_info *address) | ||
8941 | +{ | ||
8942 | + struct ccs_unix_addr_info *u = &address->unix0; | ||
8943 | + if (addr->sa_family != AF_UNIX) | ||
8944 | + return 0; | ||
8945 | + u->addr = ((struct sockaddr_un *) addr)->sun_path; | ||
8946 | + u->addr_len = addr_len; | ||
8947 | + return ccs_unix_entry(address); | ||
8948 | +} | ||
8949 | + | ||
8950 | +/** | ||
8951 | + * ccs_sock_family - Get socket's family. | ||
8952 | + * | ||
8953 | + * @sk: Pointer to "struct sock". | ||
8954 | + * | ||
8955 | + * Returns one of PF_INET, PF_INET6, PF_UNIX or 0. | ||
8956 | + */ | ||
8957 | +static u8 ccs_sock_family(struct sock *sk) | ||
8958 | +{ | ||
8959 | + u8 family; | ||
8960 | + if (ccs_kernel_service()) | ||
8961 | + return 0; | ||
8962 | + family = sk->sk_family; | ||
8963 | + switch (family) { | ||
8964 | + case PF_INET: | ||
8965 | + case PF_INET6: | ||
8966 | + case PF_UNIX: | ||
8967 | + return family; | ||
8968 | + default: | ||
8969 | + return 0; | ||
8970 | + } | ||
8971 | +} | ||
8972 | + | ||
8973 | +/** | ||
8974 | + * __ccs_socket_listen_permission - Check permission for listening a socket. | ||
8975 | + * | ||
8976 | + * @sock: Pointer to "struct socket". | ||
8977 | + * | ||
8978 | + * Returns 0 on success, negative value otherwise. | ||
8979 | + */ | ||
8980 | +static int __ccs_socket_listen_permission(struct socket *sock) | ||
8981 | +{ | ||
8982 | + struct ccs_addr_info address; | ||
8983 | + const u8 family = ccs_sock_family(sock->sk); | ||
8984 | + const unsigned int type = sock->type; | ||
8985 | + struct sockaddr_storage addr; | ||
8986 | + int addr_len; | ||
8987 | + if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET)) | ||
8988 | + return 0; | ||
8989 | + { | ||
8990 | + const int error = sock->ops->getname(sock, (struct sockaddr *) | ||
8991 | + &addr, &addr_len, 0); | ||
8992 | + if (error) | ||
8993 | + return error; | ||
8994 | + } | ||
8995 | + address.protocol = type; | ||
8996 | + address.operation = CCS_NETWORK_LISTEN; | ||
8997 | + if (family == PF_UNIX) | ||
8998 | + return ccs_check_unix_address((struct sockaddr *) &addr, | ||
8999 | + addr_len, &address); | ||
9000 | + return ccs_check_inet_address((struct sockaddr *) &addr, addr_len, 0, | ||
9001 | + &address); | ||
9002 | +} | ||
9003 | + | ||
9004 | +/** | ||
9005 | + * __ccs_socket_connect_permission - Check permission for setting the remote address of a socket. | ||
9006 | + * | ||
9007 | + * @sock: Pointer to "struct socket". | ||
9008 | + * @addr: Pointer to "struct sockaddr". | ||
9009 | + * @addr_len: Size of @addr. | ||
9010 | + * | ||
9011 | + * Returns 0 on success, negative value otherwise. | ||
9012 | + */ | ||
9013 | +static int __ccs_socket_connect_permission(struct socket *sock, | ||
9014 | + struct sockaddr *addr, int addr_len) | ||
9015 | +{ | ||
9016 | + struct ccs_addr_info address; | ||
9017 | + const u8 family = ccs_sock_family(sock->sk); | ||
9018 | + const unsigned int type = sock->type; | ||
9019 | + if (!family) | ||
9020 | + return 0; | ||
9021 | + address.protocol = type; | ||
9022 | + switch (type) { | ||
9023 | + case SOCK_DGRAM: | ||
9024 | + case SOCK_RAW: | ||
9025 | + address.operation = CCS_NETWORK_SEND; | ||
9026 | + break; | ||
9027 | + case SOCK_STREAM: | ||
9028 | + case SOCK_SEQPACKET: | ||
9029 | + address.operation = CCS_NETWORK_CONNECT; | ||
9030 | + break; | ||
9031 | + default: | ||
9032 | + return 0; | ||
9033 | + } | ||
9034 | + if (family == PF_UNIX) | ||
9035 | + return ccs_check_unix_address(addr, addr_len, &address); | ||
9036 | + return ccs_check_inet_address(addr, addr_len, sock->sk->sk_protocol, | ||
9037 | + &address); | ||
9038 | +} | ||
9039 | + | ||
9040 | +/** | ||
9041 | + * __ccs_socket_bind_permission - Check permission for setting the local address of a socket. | ||
9042 | + * | ||
9043 | + * @sock: Pointer to "struct socket". | ||
9044 | + * @addr: Pointer to "struct sockaddr". | ||
9045 | + * @addr_len: Size of @addr. | ||
9046 | + * | ||
9047 | + * Returns 0 on success, negative value otherwise. | ||
9048 | + */ | ||
9049 | +static int __ccs_socket_bind_permission(struct socket *sock, | ||
9050 | + struct sockaddr *addr, int addr_len) | ||
9051 | +{ | ||
9052 | + struct ccs_addr_info address; | ||
9053 | + const u8 family = ccs_sock_family(sock->sk); | ||
9054 | + const unsigned int type = sock->type; | ||
9055 | + if (!family) | ||
9056 | + return 0; | ||
9057 | + switch (type) { | ||
9058 | + case SOCK_STREAM: | ||
9059 | + case SOCK_DGRAM: | ||
9060 | + case SOCK_RAW: | ||
9061 | + case SOCK_SEQPACKET: | ||
9062 | + address.protocol = type; | ||
9063 | + address.operation = CCS_NETWORK_BIND; | ||
9064 | + break; | ||
9065 | + default: | ||
9066 | + return 0; | ||
9067 | + } | ||
9068 | + if (family == PF_UNIX) | ||
9069 | + return ccs_check_unix_address(addr, addr_len, &address); | ||
9070 | + return ccs_check_inet_address(addr, addr_len, sock->sk->sk_protocol, | ||
9071 | + &address); | ||
9072 | +} | ||
9073 | + | ||
9074 | +/** | ||
9075 | + * __ccs_socket_sendmsg_permission - Check permission for sending a datagram. | ||
9076 | + * | ||
9077 | + * @sock: Pointer to "struct socket". | ||
9078 | + * @msg: Pointer to "struct msghdr". | ||
9079 | + * @size: Unused. | ||
9080 | + * | ||
9081 | + * Returns 0 on success, negative value otherwise. | ||
9082 | + */ | ||
9083 | +static int __ccs_socket_sendmsg_permission(struct socket *sock, | ||
9084 | + struct msghdr *msg, int size) | ||
9085 | +{ | ||
9086 | + struct ccs_addr_info address; | ||
9087 | + const u8 family = ccs_sock_family(sock->sk); | ||
9088 | + const unsigned int type = sock->type; | ||
9089 | + if (!msg->msg_name || !family || | ||
9090 | + (type != SOCK_DGRAM && type != SOCK_RAW)) | ||
9091 | + return 0; | ||
9092 | + address.protocol = type; | ||
9093 | + address.operation = CCS_NETWORK_SEND; | ||
9094 | + if (family == PF_UNIX) | ||
9095 | + return ccs_check_unix_address((struct sockaddr *) | ||
9096 | + msg->msg_name, msg->msg_namelen, | ||
9097 | + &address); | ||
9098 | + return ccs_check_inet_address((struct sockaddr *) msg->msg_name, | ||
9099 | + msg->msg_namelen, sock->sk->sk_protocol, | ||
9100 | + &address); | ||
9101 | +} | ||
9102 | + | ||
9103 | +/** | ||
9104 | + * __ccs_socket_post_accept_permission - Check permission for accepting a socket. | ||
9105 | + * | ||
9106 | + * @sock: Pointer to "struct socket". | ||
9107 | + * @newsock: Pointer to "struct socket". | ||
9108 | + * | ||
9109 | + * Returns 0 on success, negative value otherwise. | ||
9110 | + */ | ||
9111 | +static int __ccs_socket_post_accept_permission(struct socket *sock, | ||
9112 | + struct socket *newsock) | ||
9113 | +{ | ||
9114 | + struct ccs_addr_info address; | ||
9115 | + const u8 family = ccs_sock_family(sock->sk); | ||
9116 | + const unsigned int type = sock->type; | ||
9117 | + struct sockaddr_storage addr; | ||
9118 | + int addr_len; | ||
9119 | + if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET)) | ||
9120 | + return 0; | ||
9121 | + { | ||
9122 | + const int error = newsock->ops->getname(newsock, | ||
9123 | + (struct sockaddr *) | ||
9124 | + &addr, &addr_len, 2); | ||
9125 | + if (error) | ||
9126 | + return error; | ||
9127 | + } | ||
9128 | + address.protocol = type; | ||
9129 | + address.operation = CCS_NETWORK_ACCEPT; | ||
9130 | + if (family == PF_UNIX) | ||
9131 | + return ccs_check_unix_address((struct sockaddr *) &addr, | ||
9132 | + addr_len, &address); | ||
9133 | + return ccs_check_inet_address((struct sockaddr *) &addr, addr_len, 0, | ||
9134 | + &address); | ||
9135 | +} | ||
9136 | + | ||
9137 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
9138 | + | ||
9139 | +/** | ||
9140 | + * __ccs_socket_post_recvmsg_permission - Check permission for receiving a datagram. | ||
9141 | + * | ||
9142 | + * @sk: Pointer to "struct sock". | ||
9143 | + * @skb: Pointer to "struct sk_buff". | ||
9144 | + * @flags: Flags passed to skb_recv_datagram(). | ||
9145 | + * | ||
9146 | + * Returns 0 on success, negative value otherwise. | ||
9147 | + */ | ||
9148 | +static int __ccs_socket_post_recvmsg_permission(struct sock *sk, | ||
9149 | + struct sk_buff *skb, int flags) | ||
9150 | +{ | ||
9151 | + struct ccs_addr_info address; | ||
9152 | + const u8 family = ccs_sock_family(sk); | ||
9153 | + const unsigned int type = sk->sk_type; | ||
9154 | + struct sockaddr_storage addr; | ||
9155 | + if (!family) | ||
9156 | + return 0; | ||
9157 | + switch (type) { | ||
9158 | + case SOCK_DGRAM: | ||
9159 | + case SOCK_RAW: | ||
9160 | + address.protocol = type; | ||
9161 | + break; | ||
9162 | + default: | ||
9163 | + return 0; | ||
9164 | + } | ||
9165 | + address.operation = CCS_NETWORK_RECV; | ||
9166 | + switch (family) { | ||
9167 | + case PF_INET6: | ||
9168 | + { | ||
9169 | + struct in6_addr *sin6 = (struct in6_addr *) &addr; | ||
9170 | + address.inet.is_ipv6 = true; | ||
9171 | + if (type == SOCK_DGRAM && | ||
9172 | + skb->protocol == htons(ETH_P_IP)) | ||
9173 | + ipv6_addr_set(sin6, 0, 0, htonl(0xffff), | ||
9174 | + ip_hdr(skb)->saddr); | ||
9175 | + else | ||
9176 | + *sin6 = ipv6_hdr(skb)->saddr; | ||
9177 | + break; | ||
9178 | + } | ||
9179 | + case PF_INET: | ||
9180 | + { | ||
9181 | + struct in_addr *sin4 = (struct in_addr *) &addr; | ||
9182 | + address.inet.is_ipv6 = false; | ||
9183 | + sin4->s_addr = ip_hdr(skb)->saddr; | ||
9184 | + break; | ||
9185 | + } | ||
9186 | + default: /* == PF_UNIX */ | ||
9187 | + { | ||
9188 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) | ||
9189 | + struct unix_address *u = unix_sk(skb->sk)->addr; | ||
9190 | +#else | ||
9191 | + struct unix_address *u = | ||
9192 | + skb->sk->protinfo.af_unix.addr; | ||
9193 | +#endif | ||
9194 | + unsigned int addr_len; | ||
9195 | + if (u && u->len <= sizeof(addr)) { | ||
9196 | + addr_len = u->len; | ||
9197 | + memcpy(&addr, u->name, addr_len); | ||
9198 | + } else { | ||
9199 | + addr_len = 0; | ||
9200 | + addr.ss_family = AF_UNIX; | ||
9201 | + } | ||
9202 | + if (ccs_check_unix_address((struct sockaddr *) &addr, | ||
9203 | + addr_len, &address)) | ||
9204 | + goto out; | ||
9205 | + return 0; | ||
9206 | + } | ||
9207 | + } | ||
9208 | + address.inet.address = (u32 *) &addr; | ||
9209 | + if (type == SOCK_DGRAM) | ||
9210 | + address.inet.port = udp_hdr(skb)->source; | ||
9211 | + else | ||
9212 | + address.inet.port = htons(sk->sk_protocol); | ||
9213 | + if (ccs_inet_entry(&address)) | ||
9214 | + goto out; | ||
9215 | + return 0; | ||
9216 | +out: | ||
9217 | + /* | ||
9218 | + * Remove from queue if MSG_PEEK is used so that | ||
9219 | + * the head message from unwanted source in receive queue will not | ||
9220 | + * prevent the caller from picking up next message from wanted source | ||
9221 | + * when the caller is using MSG_PEEK flag for picking up. | ||
9222 | + */ | ||
9223 | + { | ||
9224 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) | ||
9225 | + bool slow = false; | ||
9226 | + if (type == SOCK_DGRAM && family != PF_UNIX) | ||
9227 | + slow = lock_sock_fast(sk); | ||
9228 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) | ||
9229 | + if (type == SOCK_DGRAM && family != PF_UNIX) | ||
9230 | + lock_sock(sk); | ||
9231 | +#elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR >= 2 | ||
9232 | + if (type == SOCK_DGRAM && family != PF_UNIX) | ||
9233 | + lock_sock(sk); | ||
9234 | +#endif | ||
9235 | + skb_kill_datagram(sk, skb, flags); | ||
9236 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) | ||
9237 | + if (type == SOCK_DGRAM && family != PF_UNIX) | ||
9238 | + unlock_sock_fast(sk, slow); | ||
9239 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) | ||
9240 | + if (type == SOCK_DGRAM && family != PF_UNIX) | ||
9241 | + release_sock(sk); | ||
9242 | +#elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR >= 2 | ||
9243 | + if (type == SOCK_DGRAM && family != PF_UNIX) | ||
9244 | + release_sock(sk); | ||
9245 | +#endif | ||
9246 | + } | ||
9247 | + return -EPERM; | ||
9248 | +} | ||
9249 | + | ||
9250 | +#endif | ||
9251 | + | ||
9252 | +#endif | ||
9253 | + | ||
9254 | +#if defined(CONFIG_CCSECURITY_CAPABILITY) || defined(CONFIG_CCSECURITY_NETWORK) | ||
9255 | + | ||
9256 | +/** | ||
9257 | + * ccs_kernel_service - Check whether I'm kernel service or not. | ||
9258 | + * | ||
9259 | + * Returns true if I'm kernel service, false otherwise. | ||
9260 | + */ | ||
9261 | +static bool ccs_kernel_service(void) | ||
9262 | +{ | ||
9263 | + /* Nothing to do if I am a kernel service. */ | ||
9264 | + return segment_eq(get_fs(), KERNEL_DS); | ||
9265 | +} | ||
9266 | + | ||
9267 | +#endif | ||
9268 | + | ||
9269 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
9270 | + | ||
9271 | +/** | ||
9272 | + * ccs_check_capability_acl - Check permission for capability operation. | ||
9273 | + * | ||
9274 | + * @r: Pointer to "struct ccs_request_info". | ||
9275 | + * @ptr: Pointer to "struct ccs_acl_info". | ||
9276 | + * | ||
9277 | + * Returns true if granted, false otherwise. | ||
9278 | + */ | ||
9279 | +static bool ccs_check_capability_acl(struct ccs_request_info *r, | ||
9280 | + const struct ccs_acl_info *ptr) | ||
9281 | +{ | ||
9282 | + const struct ccs_capability_acl *acl = | ||
9283 | + container_of(ptr, typeof(*acl), head); | ||
9284 | + return acl->operation == r->param.capability.operation; | ||
9285 | +} | ||
9286 | + | ||
9287 | +/** | ||
9288 | + * ccs_capable - Check permission for capability. | ||
9289 | + * | ||
9290 | + * @operation: Type of operation. | ||
9291 | + * | ||
9292 | + * Returns true on success, false otherwise. | ||
9293 | + */ | ||
9294 | +static bool __ccs_capable(const u8 operation) | ||
9295 | +{ | ||
9296 | + struct ccs_request_info r; | ||
9297 | + int error = 0; | ||
9298 | + const int idx = ccs_read_lock(); | ||
9299 | + if (ccs_init_request_info(&r, ccs_c2mac[operation]) | ||
9300 | + != CCS_CONFIG_DISABLED) { | ||
9301 | + r.param_type = CCS_TYPE_CAPABILITY_ACL; | ||
9302 | + r.param.capability.operation = operation; | ||
9303 | + error = ccs_check_acl(&r); | ||
9304 | + } | ||
9305 | + ccs_read_unlock(idx); | ||
9306 | + return !error; | ||
9307 | +} | ||
9308 | + | ||
9309 | +/** | ||
9310 | + * __ccs_socket_create_permission - Check permission for creating a socket. | ||
9311 | + * | ||
9312 | + * @family: Protocol family. | ||
9313 | + * @type: Unused. | ||
9314 | + * @protocol: Unused. | ||
9315 | + * | ||
9316 | + * Returns 0 on success, negative value otherwise. | ||
9317 | + */ | ||
9318 | +static int __ccs_socket_create_permission(int family, int type, int protocol) | ||
9319 | +{ | ||
9320 | + if (ccs_kernel_service()) | ||
9321 | + return 0; | ||
9322 | + if (family == PF_PACKET && !ccs_capable(CCS_USE_PACKET_SOCKET)) | ||
9323 | + return -EPERM; | ||
9324 | + if (family == PF_ROUTE && !ccs_capable(CCS_USE_ROUTE_SOCKET)) | ||
9325 | + return -EPERM; | ||
9326 | + return 0; | ||
9327 | +} | ||
9328 | + | ||
9329 | +/** | ||
9330 | + * __ccs_ptrace_permission - Check permission for ptrace(). | ||
9331 | + * | ||
9332 | + * @request: Unused. | ||
9333 | + * @pid: Unused. | ||
9334 | + * | ||
9335 | + * Returns 0 on success, negative value otherwise. | ||
9336 | + * | ||
9337 | + * Since this function is called from location where it is permitted to sleep, | ||
9338 | + * it is racy to check target process's domainname anyway. Therefore, we don't | ||
9339 | + * use target process's domainname. | ||
9340 | + */ | ||
9341 | +static int __ccs_ptrace_permission(long request, long pid) | ||
9342 | +{ | ||
9343 | + return __ccs_capable(CCS_SYS_PTRACE) ? 0 : -EPERM; | ||
9344 | +} | ||
9345 | + | ||
9346 | +#endif | ||
9347 | + | ||
9348 | +#ifdef CONFIG_CCSECURITY_IPC | ||
9349 | + | ||
9350 | +/** | ||
9351 | + * ccs_check_signal_acl - Check permission for signal operation. | ||
9352 | + * | ||
9353 | + * @r: Pointer to "struct ccs_request_info". | ||
9354 | + * @ptr: Pointer to "struct ccs_acl_info". | ||
9355 | + * | ||
9356 | + * Returns true if granted, false otherwise. | ||
9357 | + */ | ||
9358 | +static bool ccs_check_signal_acl(struct ccs_request_info *r, | ||
9359 | + const struct ccs_acl_info *ptr) | ||
9360 | +{ | ||
9361 | + const struct ccs_signal_acl *acl = | ||
9362 | + container_of(ptr, typeof(*acl), head); | ||
9363 | + if (ccs_compare_number_union(r->param.signal.sig, &acl->sig)) { | ||
9364 | + const int len = acl->domainname->total_len; | ||
9365 | + if (!strncmp(acl->domainname->name, | ||
9366 | + r->param.signal.dest_pattern, len)) { | ||
9367 | + switch (r->param.signal.dest_pattern[len]) { | ||
9368 | + case ' ': | ||
9369 | + case '\0': | ||
9370 | + return true; | ||
9371 | + } | ||
9372 | + } | ||
9373 | + } | ||
9374 | + return false; | ||
9375 | +} | ||
9376 | + | ||
9377 | +/** | ||
9378 | + * ccs_signal_acl2 - Check permission for signal. | ||
9379 | + * | ||
9380 | + * @sig: Signal number. | ||
9381 | + * @pid: Target's PID. | ||
9382 | + * | ||
9383 | + * Returns 0 on success, negative value otherwise. | ||
9384 | + * | ||
9385 | + * Caller holds ccs_read_lock(). | ||
9386 | + */ | ||
9387 | +static int ccs_signal_acl2(const int sig, const int pid) | ||
9388 | +{ | ||
9389 | + struct ccs_request_info r; | ||
9390 | + struct ccs_domain_info *dest = NULL; | ||
9391 | + const struct ccs_domain_info * const domain = ccs_current_domain(); | ||
9392 | + if (ccs_init_request_info(&r, CCS_MAC_SIGNAL) == CCS_CONFIG_DISABLED) | ||
9393 | + return 0; | ||
9394 | + if (!sig) | ||
9395 | + return 0; /* No check for NULL signal. */ | ||
9396 | + r.param_type = CCS_TYPE_SIGNAL_ACL; | ||
9397 | + r.param.signal.sig = sig; | ||
9398 | + r.param.signal.dest_pattern = domain->domainname->name; | ||
9399 | + r.granted = true; | ||
9400 | + if (ccs_sys_getpid() == pid) { | ||
9401 | + ccs_audit_log(&r); | ||
9402 | + return 0; /* No check for self process. */ | ||
9403 | + } | ||
9404 | + { /* Simplified checking. */ | ||
9405 | + struct task_struct *p = NULL; | ||
9406 | + ccs_tasklist_lock(); | ||
9407 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | ||
9408 | + if (pid > 0) | ||
9409 | + p = ccsecurity_exports.find_task_by_vpid((pid_t) pid); | ||
9410 | + else if (pid == 0) | ||
9411 | + p = current; | ||
9412 | + else if (pid == -1) | ||
9413 | + dest = &ccs_kernel_domain; | ||
9414 | + else | ||
9415 | + p = ccsecurity_exports.find_task_by_vpid((pid_t) -pid); | ||
9416 | +#else | ||
9417 | + if (pid > 0) | ||
9418 | + p = find_task_by_pid((pid_t) pid); | ||
9419 | + else if (pid == 0) | ||
9420 | + p = current; | ||
9421 | + else if (pid == -1) | ||
9422 | + dest = &ccs_kernel_domain; | ||
9423 | + else | ||
9424 | + p = find_task_by_pid((pid_t) -pid); | ||
9425 | +#endif | ||
9426 | + if (p) | ||
9427 | + dest = ccs_task_domain(p); | ||
9428 | + ccs_tasklist_unlock(); | ||
9429 | + } | ||
9430 | + if (!dest) | ||
9431 | + return 0; /* I can't find destinatioin. */ | ||
9432 | + if (domain == dest) { | ||
9433 | + ccs_audit_log(&r); | ||
9434 | + return 0; /* No check for self domain. */ | ||
9435 | + } | ||
9436 | + r.param.signal.dest_pattern = dest->domainname->name; | ||
9437 | + return ccs_check_acl(&r); | ||
9438 | +} | ||
9439 | + | ||
9440 | +/** | ||
9441 | + * ccs_signal_acl - Check permission for signal. | ||
9442 | + * | ||
9443 | + * @pid: Target's PID. | ||
9444 | + * @sig: Signal number. | ||
9445 | + * | ||
9446 | + * Returns 0 on success, negative value otherwise. | ||
9447 | + */ | ||
9448 | +static int ccs_signal_acl(const int pid, const int sig) | ||
9449 | +{ | ||
9450 | + int error; | ||
9451 | + if (!sig) | ||
9452 | + error = 0; | ||
9453 | + else { | ||
9454 | + const int idx = ccs_read_lock(); | ||
9455 | + error = ccs_signal_acl2(sig, pid); | ||
9456 | + ccs_read_unlock(idx); | ||
9457 | + } | ||
9458 | + return error; | ||
9459 | +} | ||
9460 | + | ||
9461 | +/** | ||
9462 | + * ccs_signal_acl0 - Permission check for signal(). | ||
9463 | + * | ||
9464 | + * @tgid: Unused. | ||
9465 | + * @pid: Target's PID. | ||
9466 | + * @sig: Signal number. | ||
9467 | + * | ||
9468 | + * Returns 0 on success, negative value otherwise. | ||
9469 | + */ | ||
9470 | +static int ccs_signal_acl0(pid_t tgid, pid_t pid, int sig) | ||
9471 | +{ | ||
9472 | + return ccs_signal_acl(pid, sig); | ||
9473 | +} | ||
9474 | + | ||
9475 | +#endif | ||
9476 | + | ||
9477 | +#ifdef CONFIG_CCSECURITY_MISC | ||
9478 | + | ||
9479 | +/** | ||
9480 | + * ccs_check_env_acl - Check permission for environment variable's name. | ||
9481 | + * | ||
9482 | + * @r: Pointer to "struct ccs_request_info". | ||
9483 | + * @ptr: Pointer to "struct ccs_acl_info". | ||
9484 | + * | ||
9485 | + * Returns true if granted, false otherwise. | ||
9486 | + */ | ||
9487 | +static bool ccs_check_env_acl(struct ccs_request_info *r, | ||
9488 | + const struct ccs_acl_info *ptr) | ||
9489 | +{ | ||
9490 | + const struct ccs_env_acl *acl = container_of(ptr, typeof(*acl), head); | ||
9491 | + return ccs_path_matches_pattern(r->param.environ.name, acl->env); | ||
9492 | +} | ||
9493 | + | ||
9494 | +/** | ||
9495 | + * ccs_env_perm - Check permission for environment variable's name. | ||
9496 | + * | ||
9497 | + * @r: Pointer to "struct ccs_request_info". | ||
9498 | + * @env: The name of environment variable. | ||
9499 | + * | ||
9500 | + * Returns 0 on success, negative value otherwise. | ||
9501 | + * | ||
9502 | + * Caller holds ccs_read_lock(). | ||
9503 | + */ | ||
9504 | +static int ccs_env_perm(struct ccs_request_info *r, const char *env) | ||
9505 | +{ | ||
9506 | + struct ccs_path_info environ; | ||
9507 | + if (!env || !*env) | ||
9508 | + return 0; | ||
9509 | + environ.name = env; | ||
9510 | + ccs_fill_path_info(&environ); | ||
9511 | + r->param_type = CCS_TYPE_ENV_ACL; | ||
9512 | + r->param.environ.name = &environ; | ||
9513 | + return ccs_check_acl(r); | ||
9514 | +} | ||
9515 | + | ||
9516 | +/** | ||
9517 | + * ccs_environ - Check permission for environment variable names. | ||
9518 | + * | ||
9519 | + * @ee: Pointer to "struct ccs_execve". | ||
9520 | + * | ||
9521 | + * Returns 0 on success, negative value otherwise. | ||
9522 | + */ | ||
9523 | +static int ccs_environ(struct ccs_execve *ee) | ||
9524 | +{ | ||
9525 | + struct ccs_request_info *r = &ee->r; | ||
9526 | + struct linux_binprm *bprm = ee->bprm; | ||
9527 | + /* env_page.data is allocated by ccs_dump_page(). */ | ||
9528 | + struct ccs_page_dump env_page = { }; | ||
9529 | + char *arg_ptr; /* Size is CCS_EXEC_TMPSIZE bytes */ | ||
9530 | + int arg_len = 0; | ||
9531 | + unsigned long pos = bprm->p; | ||
9532 | + int offset = pos % PAGE_SIZE; | ||
9533 | + int argv_count = bprm->argc; | ||
9534 | + int envp_count = bprm->envc; | ||
9535 | + /* printk(KERN_DEBUG "start %d %d\n", argv_count, envp_count); */ | ||
9536 | + int error = -ENOMEM; | ||
9537 | + ee->r.type = CCS_MAC_ENVIRON; | ||
9538 | + ee->r.profile = ccs_current_domain()->profile; | ||
9539 | + ee->r.mode = ccs_get_mode(ee->r.profile, CCS_MAC_ENVIRON); | ||
9540 | + if (!r->mode || !envp_count) | ||
9541 | + return 0; | ||
9542 | + arg_ptr = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS); | ||
9543 | + if (!arg_ptr) | ||
9544 | + goto out; | ||
9545 | + while (error == -ENOMEM) { | ||
9546 | + if (!ccs_dump_page(bprm, pos, &env_page)) | ||
9547 | + goto out; | ||
9548 | + pos += PAGE_SIZE - offset; | ||
9549 | + /* Read. */ | ||
9550 | + while (argv_count && offset < PAGE_SIZE) { | ||
9551 | + if (!env_page.data[offset++]) | ||
9552 | + argv_count--; | ||
9553 | + } | ||
9554 | + if (argv_count) { | ||
9555 | + offset = 0; | ||
9556 | + continue; | ||
9557 | + } | ||
9558 | + while (offset < PAGE_SIZE) { | ||
9559 | + const unsigned char c = env_page.data[offset++]; | ||
9560 | + if (c && arg_len < CCS_EXEC_TMPSIZE - 10) { | ||
9561 | + if (c == '=') { | ||
9562 | + arg_ptr[arg_len++] = '\0'; | ||
9563 | + } else if (c == '\\') { | ||
9564 | + arg_ptr[arg_len++] = '\\'; | ||
9565 | + arg_ptr[arg_len++] = '\\'; | ||
9566 | + } else if (c > ' ' && c < 127) { | ||
9567 | + arg_ptr[arg_len++] = c; | ||
9568 | + } else { | ||
9569 | + arg_ptr[arg_len++] = '\\'; | ||
9570 | + arg_ptr[arg_len++] = (c >> 6) + '0'; | ||
9571 | + arg_ptr[arg_len++] | ||
9572 | + = ((c >> 3) & 7) + '0'; | ||
9573 | + arg_ptr[arg_len++] = (c & 7) + '0'; | ||
9574 | + } | ||
9575 | + } else { | ||
9576 | + arg_ptr[arg_len] = '\0'; | ||
9577 | + } | ||
9578 | + if (c) | ||
9579 | + continue; | ||
9580 | + if (ccs_env_perm(r, arg_ptr)) { | ||
9581 | + error = -EPERM; | ||
9582 | + break; | ||
9583 | + } | ||
9584 | + if (!--envp_count) { | ||
9585 | + error = 0; | ||
9586 | + break; | ||
9587 | + } | ||
9588 | + arg_len = 0; | ||
9589 | + } | ||
9590 | + offset = 0; | ||
9591 | + } | ||
9592 | +out: | ||
9593 | + if (r->mode != CCS_CONFIG_ENFORCING) | ||
9594 | + error = 0; | ||
9595 | + kfree(env_page.data); | ||
9596 | + kfree(arg_ptr); | ||
9597 | + return error; | ||
9598 | +} | ||
9599 | + | ||
9600 | +#endif | ||
9601 | + | ||
9602 | +/** | ||
9603 | + * ccs_argv - Check argv[] in "struct linux_binbrm". | ||
9604 | + * | ||
9605 | + * @index: Index number of @arg_ptr. | ||
9606 | + * @arg_ptr: Contents of argv[@index]. | ||
9607 | + * @argc: Length of @argv. | ||
9608 | + * @argv: Pointer to "struct ccs_argv". | ||
9609 | + * @checked: Set to true if @argv[@index] was found. | ||
9610 | + * | ||
9611 | + * Returns true on success, false otherwise. | ||
9612 | + */ | ||
9613 | +static bool ccs_argv(const unsigned int index, const char *arg_ptr, | ||
9614 | + const int argc, const struct ccs_argv *argv, | ||
9615 | + u8 *checked) | ||
9616 | +{ | ||
9617 | + int i; | ||
9618 | + struct ccs_path_info arg; | ||
9619 | + arg.name = arg_ptr; | ||
9620 | + for (i = 0; i < argc; argv++, checked++, i++) { | ||
9621 | + bool result; | ||
9622 | + if (index != argv->index) | ||
9623 | + continue; | ||
9624 | + *checked = 1; | ||
9625 | + ccs_fill_path_info(&arg); | ||
9626 | + result = ccs_path_matches_pattern(&arg, argv->value); | ||
9627 | + if (argv->is_not) | ||
9628 | + result = !result; | ||
9629 | + if (!result) | ||
9630 | + return false; | ||
9631 | + } | ||
9632 | + return true; | ||
9633 | +} | ||
9634 | + | ||
9635 | +/** | ||
9636 | + * ccs_envp - Check envp[] in "struct linux_binbrm". | ||
9637 | + * | ||
9638 | + * @env_name: The name of environment variable. | ||
9639 | + * @env_value: The value of environment variable. | ||
9640 | + * @envc: Length of @envp. | ||
9641 | + * @envp: Pointer to "struct ccs_envp". | ||
9642 | + * @checked: Set to true if @envp[@env_name] was found. | ||
9643 | + * | ||
9644 | + * Returns true on success, false otherwise. | ||
9645 | + */ | ||
9646 | +static bool ccs_envp(const char *env_name, const char *env_value, | ||
9647 | + const int envc, const struct ccs_envp *envp, | ||
9648 | + u8 *checked) | ||
9649 | +{ | ||
9650 | + int i; | ||
9651 | + struct ccs_path_info name; | ||
9652 | + struct ccs_path_info value; | ||
9653 | + name.name = env_name; | ||
9654 | + ccs_fill_path_info(&name); | ||
9655 | + value.name = env_value; | ||
9656 | + ccs_fill_path_info(&value); | ||
9657 | + for (i = 0; i < envc; envp++, checked++, i++) { | ||
9658 | + bool result; | ||
9659 | + if (!ccs_path_matches_pattern(&name, envp->name)) | ||
9660 | + continue; | ||
9661 | + *checked = 1; | ||
9662 | + if (envp->value) { | ||
9663 | + result = ccs_path_matches_pattern(&value, envp->value); | ||
9664 | + if (envp->is_not) | ||
9665 | + result = !result; | ||
9666 | + } else { | ||
9667 | + result = true; | ||
9668 | + if (!envp->is_not) | ||
9669 | + result = !result; | ||
9670 | + } | ||
9671 | + if (!result) | ||
9672 | + return false; | ||
9673 | + } | ||
9674 | + return true; | ||
9675 | +} | ||
9676 | + | ||
9677 | +/** | ||
9678 | + * ccs_scan_bprm - Scan "struct linux_binprm". | ||
9679 | + * | ||
9680 | + * @ee: Pointer to "struct ccs_execve". | ||
9681 | + * @argc: Length of @argc. | ||
9682 | + * @argv: Pointer to "struct ccs_argv". | ||
9683 | + * @envc: Length of @envp. | ||
9684 | + * @envp: Poiner to "struct ccs_envp". | ||
9685 | + * | ||
9686 | + * Returns true on success, false otherwise. | ||
9687 | + */ | ||
9688 | +static bool ccs_scan_bprm(struct ccs_execve *ee, | ||
9689 | + const u16 argc, const struct ccs_argv *argv, | ||
9690 | + const u16 envc, const struct ccs_envp *envp) | ||
9691 | +{ | ||
9692 | + struct linux_binprm *bprm = ee->bprm; | ||
9693 | + struct ccs_page_dump *dump = &ee->dump; | ||
9694 | + char *arg_ptr = ee->tmp; | ||
9695 | + int arg_len = 0; | ||
9696 | + unsigned long pos = bprm->p; | ||
9697 | + int offset = pos % PAGE_SIZE; | ||
9698 | + int argv_count = bprm->argc; | ||
9699 | + int envp_count = bprm->envc; | ||
9700 | + bool result = true; | ||
9701 | + u8 local_checked[32]; | ||
9702 | + u8 *checked; | ||
9703 | + if (argc + envc <= sizeof(local_checked)) { | ||
9704 | + checked = local_checked; | ||
9705 | + memset(local_checked, 0, sizeof(local_checked)); | ||
9706 | + } else { | ||
9707 | + checked = kzalloc(argc + envc, CCS_GFP_FLAGS); | ||
9708 | + if (!checked) | ||
9709 | + return false; | ||
9710 | + } | ||
9711 | + while (argv_count || envp_count) { | ||
9712 | + if (!ccs_dump_page(bprm, pos, dump)) { | ||
9713 | + result = false; | ||
9714 | + goto out; | ||
9715 | + } | ||
9716 | + pos += PAGE_SIZE - offset; | ||
9717 | + while (offset < PAGE_SIZE) { | ||
9718 | + /* Read. */ | ||
9719 | + const char *kaddr = dump->data; | ||
9720 | + const unsigned char c = kaddr[offset++]; | ||
9721 | + if (c && arg_len < CCS_EXEC_TMPSIZE - 10) { | ||
9722 | + if (c == '\\') { | ||
9723 | + arg_ptr[arg_len++] = '\\'; | ||
9724 | + arg_ptr[arg_len++] = '\\'; | ||
9725 | + } else if (c > ' ' && c < 127) { | ||
9726 | + arg_ptr[arg_len++] = c; | ||
9727 | + } else { | ||
9728 | + arg_ptr[arg_len++] = '\\'; | ||
9729 | + arg_ptr[arg_len++] = (c >> 6) + '0'; | ||
9730 | + arg_ptr[arg_len++] = | ||
9731 | + ((c >> 3) & 7) + '0'; | ||
9732 | + arg_ptr[arg_len++] = (c & 7) + '0'; | ||
9733 | + } | ||
9734 | + } else { | ||
9735 | + arg_ptr[arg_len] = '\0'; | ||
9736 | + } | ||
9737 | + if (c) | ||
9738 | + continue; | ||
9739 | + /* Check. */ | ||
9740 | + if (argv_count) { | ||
9741 | + if (!ccs_argv(bprm->argc - argv_count, | ||
9742 | + arg_ptr, argc, argv, | ||
9743 | + checked)) { | ||
9744 | + result = false; | ||
9745 | + break; | ||
9746 | + } | ||
9747 | + argv_count--; | ||
9748 | + } else if (envp_count) { | ||
9749 | + char *cp = strchr(arg_ptr, '='); | ||
9750 | + if (cp) { | ||
9751 | + *cp = '\0'; | ||
9752 | + if (!ccs_envp(arg_ptr, cp + 1, | ||
9753 | + envc, envp, | ||
9754 | + checked + argc)) { | ||
9755 | + result = false; | ||
9756 | + break; | ||
9757 | + } | ||
9758 | + } | ||
9759 | + envp_count--; | ||
9760 | + } else { | ||
9761 | + break; | ||
9762 | + } | ||
9763 | + arg_len = 0; | ||
9764 | + } | ||
9765 | + offset = 0; | ||
9766 | + if (!result) | ||
9767 | + break; | ||
9768 | + } | ||
9769 | +out: | ||
9770 | + if (result) { | ||
9771 | + int i; | ||
9772 | + /* Check not-yet-checked entries. */ | ||
9773 | + for (i = 0; i < argc; i++) { | ||
9774 | + if (checked[i]) | ||
9775 | + continue; | ||
9776 | + /* | ||
9777 | + * Return true only if all unchecked indexes in | ||
9778 | + * bprm->argv[] are not matched. | ||
9779 | + */ | ||
9780 | + if (argv[i].is_not) | ||
9781 | + continue; | ||
9782 | + result = false; | ||
9783 | + break; | ||
9784 | + } | ||
9785 | + for (i = 0; i < envc; envp++, i++) { | ||
9786 | + if (checked[argc + i]) | ||
9787 | + continue; | ||
9788 | + /* | ||
9789 | + * Return true only if all unchecked environ variables | ||
9790 | + * in bprm->envp[] are either undefined or not matched. | ||
9791 | + */ | ||
9792 | + if ((!envp->value && !envp->is_not) || | ||
9793 | + (envp->value && envp->is_not)) | ||
9794 | + continue; | ||
9795 | + result = false; | ||
9796 | + break; | ||
9797 | + } | ||
9798 | + } | ||
9799 | + if (checked != local_checked) | ||
9800 | + kfree(checked); | ||
9801 | + return result; | ||
9802 | +} | ||
9803 | + | ||
9804 | +/** | ||
9805 | + * ccs_scan_exec_realpath - Check "exec.realpath" parameter of "struct ccs_condition". | ||
9806 | + * | ||
9807 | + * @file: Pointer to "struct file". | ||
9808 | + * @ptr: Pointer to "struct ccs_name_union". | ||
9809 | + * @match: True if "exec.realpath=", false if "exec.realpath!=". | ||
9810 | + * | ||
9811 | + * Returns true on success, false otherwise. | ||
9812 | + */ | ||
9813 | +static bool ccs_scan_exec_realpath(struct file *file, | ||
9814 | + const struct ccs_name_union *ptr, | ||
9815 | + const bool match) | ||
9816 | +{ | ||
9817 | + bool result; | ||
9818 | + struct ccs_path_info exe; | ||
9819 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) | ||
9820 | + struct path path; | ||
9821 | +#endif | ||
9822 | + if (!file) | ||
9823 | + return false; | ||
9824 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) | ||
9825 | + exe.name = ccs_realpath(&file->f_path); | ||
9826 | +#else | ||
9827 | + path.mnt = file->f_vfsmnt; | ||
9828 | + path.dentry = file->f_dentry; | ||
9829 | + exe.name = ccs_realpath(&path); | ||
9830 | +#endif | ||
9831 | + if (!exe.name) | ||
9832 | + return false; | ||
9833 | + ccs_fill_path_info(&exe); | ||
9834 | + result = ccs_compare_name_union(&exe, ptr); | ||
9835 | + kfree(exe.name); | ||
9836 | + return result == match; | ||
9837 | +} | ||
9838 | + | ||
9839 | +/** | ||
9840 | + * ccs_get_attributes - Revalidate "struct inode". | ||
9841 | + * | ||
9842 | + * @obj: Pointer to "struct ccs_obj_info". | ||
9843 | + * | ||
9844 | + * Returns nothing. | ||
9845 | + */ | ||
9846 | +void ccs_get_attributes(struct ccs_obj_info *obj) | ||
9847 | +{ | ||
9848 | + u8 i; | ||
9849 | + struct dentry *dentry = NULL; | ||
9850 | + | ||
9851 | + for (i = 0; i < CCS_MAX_PATH_STAT; i++) { | ||
9852 | + struct inode *inode; | ||
9853 | + switch (i) { | ||
9854 | + case CCS_PATH1: | ||
9855 | + dentry = obj->path1.dentry; | ||
9856 | + if (!dentry) | ||
9857 | + continue; | ||
9858 | + break; | ||
9859 | + case CCS_PATH2: | ||
9860 | + dentry = obj->path2.dentry; | ||
9861 | + if (!dentry) | ||
9862 | + continue; | ||
9863 | + break; | ||
9864 | + default: | ||
9865 | + if (!dentry) | ||
9866 | + continue; | ||
9867 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
9868 | + spin_lock(&dcache_lock); | ||
9869 | + dentry = dget(dentry->d_parent); | ||
9870 | + spin_unlock(&dcache_lock); | ||
9871 | +#else | ||
9872 | + dentry = dget_parent(dentry); | ||
9873 | +#endif | ||
9874 | + break; | ||
9875 | + } | ||
9876 | + inode = dentry->d_inode; | ||
9877 | + if (inode) { | ||
9878 | + struct ccs_mini_stat *stat = &obj->stat[i]; | ||
9879 | + stat->uid = inode->i_uid; | ||
9880 | + stat->gid = inode->i_gid; | ||
9881 | + stat->ino = inode->i_ino; | ||
9882 | + stat->mode = inode->i_mode; | ||
9883 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
9884 | + stat->dev = inode->i_dev; | ||
9885 | +#else | ||
9886 | + stat->dev = inode->i_sb->s_dev; | ||
9887 | +#endif | ||
9888 | + stat->rdev = inode->i_rdev; | ||
9889 | + obj->stat_valid[i] = true; | ||
9890 | + } | ||
9891 | + if (i & 1) /* i == CCS_PATH1_PARENT || i == CCS_PATH2_PARENT */ | ||
9892 | + dput(dentry); | ||
9893 | + } | ||
9894 | +} | ||
9895 | + | ||
9896 | +/** | ||
9897 | + * ccs_condition - Check condition part. | ||
9898 | + * | ||
9899 | + * @r: Pointer to "struct ccs_request_info". | ||
9900 | + * @cond: Pointer to "struct ccs_condition". Maybe NULL. | ||
9901 | + * | ||
9902 | + * Returns true on success, false otherwise. | ||
9903 | + * | ||
9904 | + * Caller holds ccs_read_lock(). | ||
9905 | + */ | ||
9906 | +static bool ccs_condition(struct ccs_request_info *r, | ||
9907 | + const struct ccs_condition *cond) | ||
9908 | +{ | ||
9909 | + const u32 ccs_flags = ccs_current_flags(); | ||
9910 | + u32 i; | ||
9911 | + unsigned long min_v[2] = { 0, 0 }; | ||
9912 | + unsigned long max_v[2] = { 0, 0 }; | ||
9913 | + const struct ccs_condition_element *condp; | ||
9914 | + const struct ccs_number_union *numbers_p; | ||
9915 | + const struct ccs_name_union *names_p; | ||
9916 | + const struct ccs_argv *argv; | ||
9917 | + const struct ccs_envp *envp; | ||
9918 | + struct ccs_obj_info *obj; | ||
9919 | + u16 condc; | ||
9920 | + u16 argc; | ||
9921 | + u16 envc; | ||
9922 | + struct linux_binprm *bprm = NULL; | ||
9923 | + if (!cond) | ||
9924 | + return true; | ||
9925 | + condc = cond->condc; | ||
9926 | + argc = cond->argc; | ||
9927 | + envc = cond->envc; | ||
9928 | + obj = r->obj; | ||
9929 | + if (r->ee) | ||
9930 | + bprm = r->ee->bprm; | ||
9931 | + if (!bprm && (argc || envc)) | ||
9932 | + return false; | ||
9933 | + condp = (struct ccs_condition_element *) (cond + 1); | ||
9934 | + numbers_p = (const struct ccs_number_union *) (condp + condc); | ||
9935 | + names_p = (const struct ccs_name_union *) | ||
9936 | + (numbers_p + cond->numbers_count); | ||
9937 | + argv = (const struct ccs_argv *) (names_p + cond->names_count); | ||
9938 | + envp = (const struct ccs_envp *) (argv + argc); | ||
9939 | + for (i = 0; i < condc; i++) { | ||
9940 | + const bool match = condp->equals; | ||
9941 | + const u8 left = condp->left; | ||
9942 | + const u8 right = condp->right; | ||
9943 | + bool is_bitop[2] = { false, false }; | ||
9944 | + u8 j; | ||
9945 | + condp++; | ||
9946 | + /* Check argv[] and envp[] later. */ | ||
9947 | + if (left == CCS_ARGV_ENTRY || left == CCS_ENVP_ENTRY) | ||
9948 | + continue; | ||
9949 | + /* Check string expressions. */ | ||
9950 | + if (right == CCS_NAME_UNION) { | ||
9951 | + const struct ccs_name_union *ptr = names_p++; | ||
9952 | + switch (left) { | ||
9953 | + struct ccs_path_info *symlink; | ||
9954 | + struct ccs_execve *ee; | ||
9955 | + struct file *file; | ||
9956 | + case CCS_SYMLINK_TARGET: | ||
9957 | + symlink = obj ? obj->symlink_target : NULL; | ||
9958 | + if (!symlink || | ||
9959 | + !ccs_compare_name_union(symlink, ptr) | ||
9960 | + == match) | ||
9961 | + goto out; | ||
9962 | + break; | ||
9963 | + case CCS_EXEC_REALPATH: | ||
9964 | + ee = r->ee; | ||
9965 | + file = ee ? ee->bprm->file : NULL; | ||
9966 | + if (!ccs_scan_exec_realpath(file, ptr, match)) | ||
9967 | + goto out; | ||
9968 | + break; | ||
9969 | + } | ||
9970 | + continue; | ||
9971 | + } | ||
9972 | + /* Check numeric or bit-op expressions. */ | ||
9973 | + for (j = 0; j < 2; j++) { | ||
9974 | + const u8 index = j ? right : left; | ||
9975 | + unsigned long value = 0; | ||
9976 | + switch (index) { | ||
9977 | + case CCS_TASK_UID: | ||
9978 | + value = from_kuid(&init_user_ns, | ||
9979 | + current_uid()); | ||
9980 | + break; | ||
9981 | + case CCS_TASK_EUID: | ||
9982 | + value = from_kuid(&init_user_ns, | ||
9983 | + current_euid()); | ||
9984 | + break; | ||
9985 | + case CCS_TASK_SUID: | ||
9986 | + value = from_kuid(&init_user_ns, | ||
9987 | + current_suid()); | ||
9988 | + break; | ||
9989 | + case CCS_TASK_FSUID: | ||
9990 | + value = from_kuid(&init_user_ns, | ||
9991 | + current_fsuid()); | ||
9992 | + break; | ||
9993 | + case CCS_TASK_GID: | ||
9994 | + value = from_kgid(&init_user_ns, | ||
9995 | + current_gid()); | ||
9996 | + break; | ||
9997 | + case CCS_TASK_EGID: | ||
9998 | + value = from_kgid(&init_user_ns, | ||
9999 | + current_egid()); | ||
10000 | + break; | ||
10001 | + case CCS_TASK_SGID: | ||
10002 | + value = from_kgid(&init_user_ns, | ||
10003 | + current_sgid()); | ||
10004 | + break; | ||
10005 | + case CCS_TASK_FSGID: | ||
10006 | + value = from_kgid(&init_user_ns, | ||
10007 | + current_fsgid()); | ||
10008 | + break; | ||
10009 | + case CCS_TASK_PID: | ||
10010 | + value = ccs_sys_getpid(); | ||
10011 | + break; | ||
10012 | + case CCS_TASK_PPID: | ||
10013 | + value = ccs_sys_getppid(); | ||
10014 | + break; | ||
10015 | + case CCS_TYPE_IS_SOCKET: | ||
10016 | + value = S_IFSOCK; | ||
10017 | + break; | ||
10018 | + case CCS_TYPE_IS_SYMLINK: | ||
10019 | + value = S_IFLNK; | ||
10020 | + break; | ||
10021 | + case CCS_TYPE_IS_FILE: | ||
10022 | + value = S_IFREG; | ||
10023 | + break; | ||
10024 | + case CCS_TYPE_IS_BLOCK_DEV: | ||
10025 | + value = S_IFBLK; | ||
10026 | + break; | ||
10027 | + case CCS_TYPE_IS_DIRECTORY: | ||
10028 | + value = S_IFDIR; | ||
10029 | + break; | ||
10030 | + case CCS_TYPE_IS_CHAR_DEV: | ||
10031 | + value = S_IFCHR; | ||
10032 | + break; | ||
10033 | + case CCS_TYPE_IS_FIFO: | ||
10034 | + value = S_IFIFO; | ||
10035 | + break; | ||
10036 | + case CCS_MODE_SETUID: | ||
10037 | + value = S_ISUID; | ||
10038 | + break; | ||
10039 | + case CCS_MODE_SETGID: | ||
10040 | + value = S_ISGID; | ||
10041 | + break; | ||
10042 | + case CCS_MODE_STICKY: | ||
10043 | + value = S_ISVTX; | ||
10044 | + break; | ||
10045 | + case CCS_MODE_OWNER_READ: | ||
10046 | + value = S_IRUSR; | ||
10047 | + break; | ||
10048 | + case CCS_MODE_OWNER_WRITE: | ||
10049 | + value = S_IWUSR; | ||
10050 | + break; | ||
10051 | + case CCS_MODE_OWNER_EXECUTE: | ||
10052 | + value = S_IXUSR; | ||
10053 | + break; | ||
10054 | + case CCS_MODE_GROUP_READ: | ||
10055 | + value = S_IRGRP; | ||
10056 | + break; | ||
10057 | + case CCS_MODE_GROUP_WRITE: | ||
10058 | + value = S_IWGRP; | ||
10059 | + break; | ||
10060 | + case CCS_MODE_GROUP_EXECUTE: | ||
10061 | + value = S_IXGRP; | ||
10062 | + break; | ||
10063 | + case CCS_MODE_OTHERS_READ: | ||
10064 | + value = S_IROTH; | ||
10065 | + break; | ||
10066 | + case CCS_MODE_OTHERS_WRITE: | ||
10067 | + value = S_IWOTH; | ||
10068 | + break; | ||
10069 | + case CCS_MODE_OTHERS_EXECUTE: | ||
10070 | + value = S_IXOTH; | ||
10071 | + break; | ||
10072 | + case CCS_EXEC_ARGC: | ||
10073 | + if (!bprm) | ||
10074 | + goto out; | ||
10075 | + value = bprm->argc; | ||
10076 | + break; | ||
10077 | + case CCS_EXEC_ENVC: | ||
10078 | + if (!bprm) | ||
10079 | + goto out; | ||
10080 | + value = bprm->envc; | ||
10081 | + break; | ||
10082 | + case CCS_TASK_TYPE: | ||
10083 | + value = ((u8) ccs_flags) | ||
10084 | + & CCS_TASK_IS_EXECUTE_HANDLER; | ||
10085 | + break; | ||
10086 | + case CCS_TASK_EXECUTE_HANDLER: | ||
10087 | + value = CCS_TASK_IS_EXECUTE_HANDLER; | ||
10088 | + break; | ||
10089 | + case CCS_NUMBER_UNION: | ||
10090 | + /* Fetch values later. */ | ||
10091 | + break; | ||
10092 | + default: | ||
10093 | + if (!obj) | ||
10094 | + goto out; | ||
10095 | + if (!obj->validate_done) { | ||
10096 | + ccs_get_attributes(obj); | ||
10097 | + obj->validate_done = true; | ||
10098 | + } | ||
10099 | + { | ||
10100 | + u8 stat_index; | ||
10101 | + struct ccs_mini_stat *stat; | ||
10102 | + switch (index) { | ||
10103 | + case CCS_PATH1_UID: | ||
10104 | + case CCS_PATH1_GID: | ||
10105 | + case CCS_PATH1_INO: | ||
10106 | + case CCS_PATH1_MAJOR: | ||
10107 | + case CCS_PATH1_MINOR: | ||
10108 | + case CCS_PATH1_TYPE: | ||
10109 | + case CCS_PATH1_DEV_MAJOR: | ||
10110 | + case CCS_PATH1_DEV_MINOR: | ||
10111 | + case CCS_PATH1_PERM: | ||
10112 | + stat_index = CCS_PATH1; | ||
10113 | + break; | ||
10114 | + case CCS_PATH2_UID: | ||
10115 | + case CCS_PATH2_GID: | ||
10116 | + case CCS_PATH2_INO: | ||
10117 | + case CCS_PATH2_MAJOR: | ||
10118 | + case CCS_PATH2_MINOR: | ||
10119 | + case CCS_PATH2_TYPE: | ||
10120 | + case CCS_PATH2_DEV_MAJOR: | ||
10121 | + case CCS_PATH2_DEV_MINOR: | ||
10122 | + case CCS_PATH2_PERM: | ||
10123 | + stat_index = CCS_PATH2; | ||
10124 | + break; | ||
10125 | + case CCS_PATH1_PARENT_UID: | ||
10126 | + case CCS_PATH1_PARENT_GID: | ||
10127 | + case CCS_PATH1_PARENT_INO: | ||
10128 | + case CCS_PATH1_PARENT_PERM: | ||
10129 | + stat_index = CCS_PATH1_PARENT; | ||
10130 | + break; | ||
10131 | + case CCS_PATH2_PARENT_UID: | ||
10132 | + case CCS_PATH2_PARENT_GID: | ||
10133 | + case CCS_PATH2_PARENT_INO: | ||
10134 | + case CCS_PATH2_PARENT_PERM: | ||
10135 | + stat_index = CCS_PATH2_PARENT; | ||
10136 | + break; | ||
10137 | + default: | ||
10138 | + goto out; | ||
10139 | + } | ||
10140 | + if (!obj->stat_valid[stat_index]) | ||
10141 | + goto out; | ||
10142 | + stat = &obj->stat[stat_index]; | ||
10143 | + switch (index) { | ||
10144 | + case CCS_PATH1_UID: | ||
10145 | + case CCS_PATH2_UID: | ||
10146 | + case CCS_PATH1_PARENT_UID: | ||
10147 | + case CCS_PATH2_PARENT_UID: | ||
10148 | + value = from_kuid | ||
10149 | + (&init_user_ns, | ||
10150 | + stat->uid); | ||
10151 | + break; | ||
10152 | + case CCS_PATH1_GID: | ||
10153 | + case CCS_PATH2_GID: | ||
10154 | + case CCS_PATH1_PARENT_GID: | ||
10155 | + case CCS_PATH2_PARENT_GID: | ||
10156 | + value = from_kgid | ||
10157 | + (&init_user_ns, | ||
10158 | + stat->gid); | ||
10159 | + break; | ||
10160 | + case CCS_PATH1_INO: | ||
10161 | + case CCS_PATH2_INO: | ||
10162 | + case CCS_PATH1_PARENT_INO: | ||
10163 | + case CCS_PATH2_PARENT_INO: | ||
10164 | + value = stat->ino; | ||
10165 | + break; | ||
10166 | + case CCS_PATH1_MAJOR: | ||
10167 | + case CCS_PATH2_MAJOR: | ||
10168 | + value = MAJOR(stat->dev); | ||
10169 | + break; | ||
10170 | + case CCS_PATH1_MINOR: | ||
10171 | + case CCS_PATH2_MINOR: | ||
10172 | + value = MINOR(stat->dev); | ||
10173 | + break; | ||
10174 | + case CCS_PATH1_TYPE: | ||
10175 | + case CCS_PATH2_TYPE: | ||
10176 | + value = stat->mode & S_IFMT; | ||
10177 | + break; | ||
10178 | + case CCS_PATH1_DEV_MAJOR: | ||
10179 | + case CCS_PATH2_DEV_MAJOR: | ||
10180 | + value = MAJOR(stat->rdev); | ||
10181 | + break; | ||
10182 | + case CCS_PATH1_DEV_MINOR: | ||
10183 | + case CCS_PATH2_DEV_MINOR: | ||
10184 | + value = MINOR(stat->rdev); | ||
10185 | + break; | ||
10186 | + case CCS_PATH1_PERM: | ||
10187 | + case CCS_PATH2_PERM: | ||
10188 | + case CCS_PATH1_PARENT_PERM: | ||
10189 | + case CCS_PATH2_PARENT_PERM: | ||
10190 | + value = stat->mode & S_IALLUGO; | ||
10191 | + break; | ||
10192 | + } | ||
10193 | + } | ||
10194 | + break; | ||
10195 | + } | ||
10196 | + max_v[j] = value; | ||
10197 | + min_v[j] = value; | ||
10198 | + switch (index) { | ||
10199 | + case CCS_MODE_SETUID: | ||
10200 | + case CCS_MODE_SETGID: | ||
10201 | + case CCS_MODE_STICKY: | ||
10202 | + case CCS_MODE_OWNER_READ: | ||
10203 | + case CCS_MODE_OWNER_WRITE: | ||
10204 | + case CCS_MODE_OWNER_EXECUTE: | ||
10205 | + case CCS_MODE_GROUP_READ: | ||
10206 | + case CCS_MODE_GROUP_WRITE: | ||
10207 | + case CCS_MODE_GROUP_EXECUTE: | ||
10208 | + case CCS_MODE_OTHERS_READ: | ||
10209 | + case CCS_MODE_OTHERS_WRITE: | ||
10210 | + case CCS_MODE_OTHERS_EXECUTE: | ||
10211 | + is_bitop[j] = true; | ||
10212 | + } | ||
10213 | + } | ||
10214 | + if (left == CCS_NUMBER_UNION) { | ||
10215 | + /* Fetch values now. */ | ||
10216 | + const struct ccs_number_union *ptr = numbers_p++; | ||
10217 | + min_v[0] = ptr->values[0]; | ||
10218 | + max_v[0] = ptr->values[1]; | ||
10219 | + } | ||
10220 | + if (right == CCS_NUMBER_UNION) { | ||
10221 | + /* Fetch values now. */ | ||
10222 | + const struct ccs_number_union *ptr = numbers_p++; | ||
10223 | + if (ptr->group) { | ||
10224 | + if (ccs_number_matches_group(min_v[0], | ||
10225 | + max_v[0], | ||
10226 | + ptr->group) | ||
10227 | + == match) | ||
10228 | + continue; | ||
10229 | + } else { | ||
10230 | + if ((min_v[0] <= ptr->values[1] && | ||
10231 | + max_v[0] >= ptr->values[0]) == match) | ||
10232 | + continue; | ||
10233 | + } | ||
10234 | + goto out; | ||
10235 | + } | ||
10236 | + /* | ||
10237 | + * Bit operation is valid only when counterpart value | ||
10238 | + * represents permission. | ||
10239 | + */ | ||
10240 | + if (is_bitop[0] && is_bitop[1]) { | ||
10241 | + goto out; | ||
10242 | + } else if (is_bitop[0]) { | ||
10243 | + switch (right) { | ||
10244 | + case CCS_PATH1_PERM: | ||
10245 | + case CCS_PATH1_PARENT_PERM: | ||
10246 | + case CCS_PATH2_PERM: | ||
10247 | + case CCS_PATH2_PARENT_PERM: | ||
10248 | + if (!(max_v[0] & max_v[1]) == !match) | ||
10249 | + continue; | ||
10250 | + } | ||
10251 | + goto out; | ||
10252 | + } else if (is_bitop[1]) { | ||
10253 | + switch (left) { | ||
10254 | + case CCS_PATH1_PERM: | ||
10255 | + case CCS_PATH1_PARENT_PERM: | ||
10256 | + case CCS_PATH2_PERM: | ||
10257 | + case CCS_PATH2_PARENT_PERM: | ||
10258 | + if (!(max_v[0] & max_v[1]) == !match) | ||
10259 | + continue; | ||
10260 | + } | ||
10261 | + goto out; | ||
10262 | + } | ||
10263 | + /* Normal value range comparison. */ | ||
10264 | + if ((min_v[0] <= max_v[1] && max_v[0] >= min_v[1]) == match) | ||
10265 | + continue; | ||
10266 | +out: | ||
10267 | + return false; | ||
10268 | + } | ||
10269 | + /* Check argv[] and envp[] now. */ | ||
10270 | + if (r->ee && (argc || envc)) | ||
10271 | + return ccs_scan_bprm(r->ee, argc, argv, envc, envp); | ||
10272 | + return true; | ||
10273 | +} | ||
10274 | + | ||
10275 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
10276 | + | ||
10277 | +/** | ||
10278 | + * ccs_check_task_acl - Check permission for task operation. | ||
10279 | + * | ||
10280 | + * @r: Pointer to "struct ccs_request_info". | ||
10281 | + * @ptr: Pointer to "struct ccs_acl_info". | ||
10282 | + * | ||
10283 | + * Returns true if granted, false otherwise. | ||
10284 | + */ | ||
10285 | +static bool ccs_check_task_acl(struct ccs_request_info *r, | ||
10286 | + const struct ccs_acl_info *ptr) | ||
10287 | +{ | ||
10288 | + const struct ccs_task_acl *acl = container_of(ptr, typeof(*acl), head); | ||
10289 | + return !ccs_pathcmp(r->param.task.domainname, acl->domainname); | ||
10290 | +} | ||
10291 | + | ||
10292 | +#endif | ||
10293 | + | ||
10294 | +/** | ||
10295 | + * ccs_init_request_info - Initialize "struct ccs_request_info" members. | ||
10296 | + * | ||
10297 | + * @r: Pointer to "struct ccs_request_info" to initialize. | ||
10298 | + * @index: Index number of functionality. | ||
10299 | + * | ||
10300 | + * Returns mode. | ||
10301 | + * | ||
10302 | + * "task auto_domain_transition" keyword is evaluated before returning mode for | ||
10303 | + * @index. If "task auto_domain_transition" keyword was specified and | ||
10304 | + * transition to that domain failed, the current thread will be killed by | ||
10305 | + * SIGKILL. Note that if current->pid == 1, sending SIGKILL won't work. | ||
10306 | + */ | ||
10307 | +int ccs_init_request_info(struct ccs_request_info *r, const u8 index) | ||
10308 | +{ | ||
10309 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
10310 | + u8 i; | ||
10311 | + const char *buf; | ||
10312 | + for (i = 0; i < 255; i++) { | ||
10313 | + const u8 profile = ccs_current_domain()->profile; | ||
10314 | + memset(r, 0, sizeof(*r)); | ||
10315 | + r->profile = profile; | ||
10316 | + r->type = index; | ||
10317 | + r->mode = ccs_get_mode(profile, index); | ||
10318 | + r->param_type = CCS_TYPE_AUTO_TASK_ACL; | ||
10319 | + ccs_check_acl(r); | ||
10320 | + if (!r->granted) | ||
10321 | + return r->mode; | ||
10322 | + buf = container_of(r->matched_acl, typeof(struct ccs_task_acl), | ||
10323 | + head)->domainname->name; | ||
10324 | + if (!ccs_assign_domain(buf, true)) | ||
10325 | + break; | ||
10326 | + } | ||
10327 | + ccs_transition_failed(buf); | ||
10328 | + return CCS_CONFIG_DISABLED; | ||
10329 | +#else | ||
10330 | + const u8 profile = ccs_current_domain()->profile; | ||
10331 | + memset(r, 0, sizeof(*r)); | ||
10332 | + r->profile = profile; | ||
10333 | + r->type = index; | ||
10334 | + r->mode = ccs_get_mode(profile, index); | ||
10335 | + return r->mode; | ||
10336 | +#endif | ||
10337 | +} | ||
10338 | + | ||
10339 | +/** | ||
10340 | + * ccs_byte_range - Check whether the string is a \ooo style octal value. | ||
10341 | + * | ||
10342 | + * @str: Pointer to the string. | ||
10343 | + * | ||
10344 | + * Returns true if @str is a \ooo style octal value, false otherwise. | ||
10345 | + */ | ||
10346 | +static bool ccs_byte_range(const char *str) | ||
10347 | +{ | ||
10348 | + return *str >= '0' && *str++ <= '3' && | ||
10349 | + *str >= '0' && *str++ <= '7' && | ||
10350 | + *str >= '0' && *str <= '7'; | ||
10351 | +} | ||
10352 | + | ||
10353 | +/** | ||
10354 | + * ccs_decimal - Check whether the character is a decimal character. | ||
10355 | + * | ||
10356 | + * @c: The character to check. | ||
10357 | + * | ||
10358 | + * Returns true if @c is a decimal character, false otherwise. | ||
10359 | + */ | ||
10360 | +static bool ccs_decimal(const char c) | ||
10361 | +{ | ||
10362 | + return c >= '0' && c <= '9'; | ||
10363 | +} | ||
10364 | + | ||
10365 | +/** | ||
10366 | + * ccs_hexadecimal - Check whether the character is a hexadecimal character. | ||
10367 | + * | ||
10368 | + * @c: The character to check. | ||
10369 | + * | ||
10370 | + * Returns true if @c is a hexadecimal character, false otherwise. | ||
10371 | + */ | ||
10372 | +static bool ccs_hexadecimal(const char c) | ||
10373 | +{ | ||
10374 | + return (c >= '0' && c <= '9') || | ||
10375 | + (c >= 'A' && c <= 'F') || | ||
10376 | + (c >= 'a' && c <= 'f'); | ||
10377 | +} | ||
10378 | + | ||
10379 | +/** | ||
10380 | + * ccs_alphabet_char - Check whether the character is an alphabet. | ||
10381 | + * | ||
10382 | + * @c: The character to check. | ||
10383 | + * | ||
10384 | + * Returns true if @c is an alphabet character, false otherwise. | ||
10385 | + */ | ||
10386 | +static bool ccs_alphabet_char(const char c) | ||
10387 | +{ | ||
10388 | + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); | ||
10389 | +} | ||
10390 | + | ||
10391 | +/** | ||
10392 | + * ccs_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern. | ||
10393 | + * | ||
10394 | + * @filename: The start of string to check. | ||
10395 | + * @filename_end: The end of string to check. | ||
10396 | + * @pattern: The start of pattern to compare. | ||
10397 | + * @pattern_end: The end of pattern to compare. | ||
10398 | + * | ||
10399 | + * Returns true if @filename matches @pattern, false otherwise. | ||
10400 | + */ | ||
10401 | +static bool ccs_file_matches_pattern2(const char *filename, | ||
10402 | + const char *filename_end, | ||
10403 | + const char *pattern, | ||
10404 | + const char *pattern_end) | ||
10405 | +{ | ||
10406 | + while (filename < filename_end && pattern < pattern_end) { | ||
10407 | + char c; | ||
10408 | + if (*pattern != '\\') { | ||
10409 | + if (*filename++ != *pattern++) | ||
10410 | + return false; | ||
10411 | + continue; | ||
10412 | + } | ||
10413 | + c = *filename; | ||
10414 | + pattern++; | ||
10415 | + switch (*pattern) { | ||
10416 | + int i; | ||
10417 | + int j; | ||
10418 | + case '?': | ||
10419 | + if (c == '/') { | ||
10420 | + return false; | ||
10421 | + } else if (c == '\\') { | ||
10422 | + if (filename[1] == '\\') | ||
10423 | + filename++; | ||
10424 | + else if (ccs_byte_range(filename + 1)) | ||
10425 | + filename += 3; | ||
10426 | + else | ||
10427 | + return false; | ||
10428 | + } | ||
10429 | + break; | ||
10430 | + case '\\': | ||
10431 | + if (c != '\\') | ||
10432 | + return false; | ||
10433 | + if (*++filename != '\\') | ||
10434 | + return false; | ||
10435 | + break; | ||
10436 | + case '+': | ||
10437 | + if (!ccs_decimal(c)) | ||
10438 | + return false; | ||
10439 | + break; | ||
10440 | + case 'x': | ||
10441 | + if (!ccs_hexadecimal(c)) | ||
10442 | + return false; | ||
10443 | + break; | ||
10444 | + case 'a': | ||
10445 | + if (!ccs_alphabet_char(c)) | ||
10446 | + return false; | ||
10447 | + break; | ||
10448 | + case '0': | ||
10449 | + case '1': | ||
10450 | + case '2': | ||
10451 | + case '3': | ||
10452 | + if (c == '\\' && ccs_byte_range(filename + 1) | ||
10453 | + && !strncmp(filename + 1, pattern, 3)) { | ||
10454 | + filename += 3; | ||
10455 | + pattern += 2; | ||
10456 | + break; | ||
10457 | + } | ||
10458 | + return false; /* Not matched. */ | ||
10459 | + case '*': | ||
10460 | + case '@': | ||
10461 | + for (i = 0; i <= filename_end - filename; i++) { | ||
10462 | + if (ccs_file_matches_pattern2(filename + i, | ||
10463 | + filename_end, | ||
10464 | + pattern + 1, | ||
10465 | + pattern_end)) | ||
10466 | + return true; | ||
10467 | + c = filename[i]; | ||
10468 | + if (c == '.' && *pattern == '@') | ||
10469 | + break; | ||
10470 | + if (c != '\\') | ||
10471 | + continue; | ||
10472 | + if (filename[i + 1] == '\\') | ||
10473 | + i++; | ||
10474 | + else if (ccs_byte_range(filename + i + 1)) | ||
10475 | + i += 3; | ||
10476 | + else | ||
10477 | + break; /* Bad pattern. */ | ||
10478 | + } | ||
10479 | + return false; /* Not matched. */ | ||
10480 | + default: | ||
10481 | + j = 0; | ||
10482 | + c = *pattern; | ||
10483 | + if (c == '$') { | ||
10484 | + while (ccs_decimal(filename[j])) | ||
10485 | + j++; | ||
10486 | + } else if (c == 'X') { | ||
10487 | + while (ccs_hexadecimal(filename[j])) | ||
10488 | + j++; | ||
10489 | + } else if (c == 'A') { | ||
10490 | + while (ccs_alphabet_char(filename[j])) | ||
10491 | + j++; | ||
10492 | + } | ||
10493 | + for (i = 1; i <= j; i++) { | ||
10494 | + if (ccs_file_matches_pattern2(filename + i, | ||
10495 | + filename_end, | ||
10496 | + pattern + 1, | ||
10497 | + pattern_end)) | ||
10498 | + return true; | ||
10499 | + } | ||
10500 | + return false; /* Not matched or bad pattern. */ | ||
10501 | + } | ||
10502 | + filename++; | ||
10503 | + pattern++; | ||
10504 | + } | ||
10505 | + while (*pattern == '\\' && | ||
10506 | + (*(pattern + 1) == '*' || *(pattern + 1) == '@')) | ||
10507 | + pattern += 2; | ||
10508 | + return filename == filename_end && pattern == pattern_end; | ||
10509 | +} | ||
10510 | + | ||
10511 | +/** | ||
10512 | + * ccs_file_matches_pattern - Pattern matching without '/' character. | ||
10513 | + * | ||
10514 | + * @filename: The start of string to check. | ||
10515 | + * @filename_end: The end of string to check. | ||
10516 | + * @pattern: The start of pattern to compare. | ||
10517 | + * @pattern_end: The end of pattern to compare. | ||
10518 | + * | ||
10519 | + * Returns true if @filename matches @pattern, false otherwise. | ||
10520 | + */ | ||
10521 | +static bool ccs_file_matches_pattern(const char *filename, | ||
10522 | + const char *filename_end, | ||
10523 | + const char *pattern, | ||
10524 | + const char *pattern_end) | ||
10525 | +{ | ||
10526 | + const char *pattern_start = pattern; | ||
10527 | + bool first = true; | ||
10528 | + bool result; | ||
10529 | + while (pattern < pattern_end - 1) { | ||
10530 | + /* Split at "\-" pattern. */ | ||
10531 | + if (*pattern++ != '\\' || *pattern++ != '-') | ||
10532 | + continue; | ||
10533 | + result = ccs_file_matches_pattern2(filename, filename_end, | ||
10534 | + pattern_start, pattern - 2); | ||
10535 | + if (first) | ||
10536 | + result = !result; | ||
10537 | + if (result) | ||
10538 | + return false; | ||
10539 | + first = false; | ||
10540 | + pattern_start = pattern; | ||
10541 | + } | ||
10542 | + result = ccs_file_matches_pattern2(filename, filename_end, | ||
10543 | + pattern_start, pattern_end); | ||
10544 | + return first ? result : !result; | ||
10545 | +} | ||
10546 | + | ||
10547 | +/** | ||
10548 | + * ccs_path_matches_pattern2 - Do pathname pattern matching. | ||
10549 | + * | ||
10550 | + * @f: The start of string to check. | ||
10551 | + * @p: The start of pattern to compare. | ||
10552 | + * | ||
10553 | + * Returns true if @f matches @p, false otherwise. | ||
10554 | + */ | ||
10555 | +static bool ccs_path_matches_pattern2(const char *f, const char *p) | ||
10556 | +{ | ||
10557 | + const char *f_delimiter; | ||
10558 | + const char *p_delimiter; | ||
10559 | + while (*f && *p) { | ||
10560 | + f_delimiter = strchr(f, '/'); | ||
10561 | + if (!f_delimiter) | ||
10562 | + f_delimiter = f + strlen(f); | ||
10563 | + p_delimiter = strchr(p, '/'); | ||
10564 | + if (!p_delimiter) | ||
10565 | + p_delimiter = p + strlen(p); | ||
10566 | + if (*p == '\\' && *(p + 1) == '{') | ||
10567 | + goto recursive; | ||
10568 | + if (!ccs_file_matches_pattern(f, f_delimiter, p, p_delimiter)) | ||
10569 | + return false; | ||
10570 | + f = f_delimiter; | ||
10571 | + if (*f) | ||
10572 | + f++; | ||
10573 | + p = p_delimiter; | ||
10574 | + if (*p) | ||
10575 | + p++; | ||
10576 | + } | ||
10577 | + /* Ignore trailing "\*" and "\@" in @pattern. */ | ||
10578 | + while (*p == '\\' && | ||
10579 | + (*(p + 1) == '*' || *(p + 1) == '@')) | ||
10580 | + p += 2; | ||
10581 | + return !*f && !*p; | ||
10582 | +recursive: | ||
10583 | + /* | ||
10584 | + * The "\{" pattern is permitted only after '/' character. | ||
10585 | + * This guarantees that below "*(p - 1)" is safe. | ||
10586 | + * Also, the "\}" pattern is permitted only before '/' character | ||
10587 | + * so that "\{" + "\}" pair will not break the "\-" operator. | ||
10588 | + */ | ||
10589 | + if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || | ||
10590 | + *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') | ||
10591 | + return false; /* Bad pattern. */ | ||
10592 | + do { | ||
10593 | + /* Compare current component with pattern. */ | ||
10594 | + if (!ccs_file_matches_pattern(f, f_delimiter, p + 2, | ||
10595 | + p_delimiter - 2)) | ||
10596 | + break; | ||
10597 | + /* Proceed to next component. */ | ||
10598 | + f = f_delimiter; | ||
10599 | + if (!*f) | ||
10600 | + break; | ||
10601 | + f++; | ||
10602 | + /* Continue comparison. */ | ||
10603 | + if (ccs_path_matches_pattern2(f, p_delimiter + 1)) | ||
10604 | + return true; | ||
10605 | + f_delimiter = strchr(f, '/'); | ||
10606 | + } while (f_delimiter); | ||
10607 | + return false; /* Not matched. */ | ||
10608 | +} | ||
10609 | + | ||
10610 | +/** | ||
10611 | + * ccs_path_matches_pattern - Check whether the given filename matches the given pattern. | ||
10612 | + * | ||
10613 | + * @filename: The filename to check. | ||
10614 | + * @pattern: The pattern to compare. | ||
10615 | + * | ||
10616 | + * Returns true if matches, false otherwise. | ||
10617 | + * | ||
10618 | + * The following patterns are available. | ||
10619 | + * \\ \ itself. | ||
10620 | + * \ooo Octal representation of a byte. | ||
10621 | + * \* Zero or more repetitions of characters other than '/'. | ||
10622 | + * \@ Zero or more repetitions of characters other than '/' or '.'. | ||
10623 | + * \? 1 byte character other than '/'. | ||
10624 | + * \$ One or more repetitions of decimal digits. | ||
10625 | + * \+ 1 decimal digit. | ||
10626 | + * \X One or more repetitions of hexadecimal digits. | ||
10627 | + * \x 1 hexadecimal digit. | ||
10628 | + * \A One or more repetitions of alphabet characters. | ||
10629 | + * \a 1 alphabet character. | ||
10630 | + * | ||
10631 | + * \- Subtraction operator. | ||
10632 | + * | ||
10633 | + * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ | ||
10634 | + * /dir/dir/dir/ ). | ||
10635 | + */ | ||
10636 | +static bool ccs_path_matches_pattern(const struct ccs_path_info *filename, | ||
10637 | + const struct ccs_path_info *pattern) | ||
10638 | +{ | ||
10639 | + const char *f = filename->name; | ||
10640 | + const char *p = pattern->name; | ||
10641 | + const int len = pattern->const_len; | ||
10642 | + /* If @pattern doesn't contain pattern, I can use strcmp(). */ | ||
10643 | + if (!pattern->is_patterned) | ||
10644 | + return !ccs_pathcmp(filename, pattern); | ||
10645 | + /* Don't compare directory and non-directory. */ | ||
10646 | + if (filename->is_dir != pattern->is_dir) | ||
10647 | + return false; | ||
10648 | + /* Compare the initial length without patterns. */ | ||
10649 | + if (strncmp(f, p, len)) | ||
10650 | + return false; | ||
10651 | + f += len; | ||
10652 | + p += len; | ||
10653 | + return ccs_path_matches_pattern2(f, p); | ||
10654 | +} | ||
10655 | diff --git a/security/ccsecurity/policy_io.c b/security/ccsecurity/policy_io.c | ||
10656 | new file mode 100644 | ||
10657 | index 0000000..67adb50 | ||
10658 | --- /dev/null | ||
10659 | +++ b/security/ccsecurity/policy_io.c | ||
10660 | @@ -0,0 +1,6484 @@ | ||
10661 | +/* | ||
10662 | + * security/ccsecurity/policy_io.c | ||
10663 | + * | ||
10664 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | ||
10665 | + * | ||
10666 | + * Version: 1.8.4 2015/05/05 | ||
10667 | + */ | ||
10668 | + | ||
10669 | +#include "internal.h" | ||
10670 | + | ||
10671 | +/***** SECTION1: Constants definition *****/ | ||
10672 | + | ||
10673 | +/* Define this to enable debug mode. */ | ||
10674 | +/* #define DEBUG_CONDITION */ | ||
10675 | + | ||
10676 | +#ifdef DEBUG_CONDITION | ||
10677 | +#define dprintk printk | ||
10678 | +#else | ||
10679 | +#define dprintk(...) do { } while (0) | ||
10680 | +#endif | ||
10681 | + | ||
10682 | +/* Mapping table from "enum ccs_mac_index" to "enum ccs_mac_category_index". */ | ||
10683 | +static const u8 ccs_index2category[CCS_MAX_MAC_INDEX] = { | ||
10684 | + /* CONFIG::file group */ | ||
10685 | + [CCS_MAC_FILE_EXECUTE] = CCS_MAC_CATEGORY_FILE, | ||
10686 | + [CCS_MAC_FILE_OPEN] = CCS_MAC_CATEGORY_FILE, | ||
10687 | + [CCS_MAC_FILE_CREATE] = CCS_MAC_CATEGORY_FILE, | ||
10688 | + [CCS_MAC_FILE_UNLINK] = CCS_MAC_CATEGORY_FILE, | ||
10689 | +#ifdef CONFIG_CCSECURITY_FILE_GETATTR | ||
10690 | + [CCS_MAC_FILE_GETATTR] = CCS_MAC_CATEGORY_FILE, | ||
10691 | +#endif | ||
10692 | + [CCS_MAC_FILE_MKDIR] = CCS_MAC_CATEGORY_FILE, | ||
10693 | + [CCS_MAC_FILE_RMDIR] = CCS_MAC_CATEGORY_FILE, | ||
10694 | + [CCS_MAC_FILE_MKFIFO] = CCS_MAC_CATEGORY_FILE, | ||
10695 | + [CCS_MAC_FILE_MKSOCK] = CCS_MAC_CATEGORY_FILE, | ||
10696 | + [CCS_MAC_FILE_TRUNCATE] = CCS_MAC_CATEGORY_FILE, | ||
10697 | + [CCS_MAC_FILE_SYMLINK] = CCS_MAC_CATEGORY_FILE, | ||
10698 | + [CCS_MAC_FILE_MKBLOCK] = CCS_MAC_CATEGORY_FILE, | ||
10699 | + [CCS_MAC_FILE_MKCHAR] = CCS_MAC_CATEGORY_FILE, | ||
10700 | + [CCS_MAC_FILE_LINK] = CCS_MAC_CATEGORY_FILE, | ||
10701 | + [CCS_MAC_FILE_RENAME] = CCS_MAC_CATEGORY_FILE, | ||
10702 | + [CCS_MAC_FILE_CHMOD] = CCS_MAC_CATEGORY_FILE, | ||
10703 | + [CCS_MAC_FILE_CHOWN] = CCS_MAC_CATEGORY_FILE, | ||
10704 | + [CCS_MAC_FILE_CHGRP] = CCS_MAC_CATEGORY_FILE, | ||
10705 | + [CCS_MAC_FILE_IOCTL] = CCS_MAC_CATEGORY_FILE, | ||
10706 | + [CCS_MAC_FILE_CHROOT] = CCS_MAC_CATEGORY_FILE, | ||
10707 | + [CCS_MAC_FILE_MOUNT] = CCS_MAC_CATEGORY_FILE, | ||
10708 | + [CCS_MAC_FILE_UMOUNT] = CCS_MAC_CATEGORY_FILE, | ||
10709 | + [CCS_MAC_FILE_PIVOT_ROOT] = CCS_MAC_CATEGORY_FILE, | ||
10710 | +#ifdef CONFIG_CCSECURITY_MISC | ||
10711 | + /* CONFIG::misc group */ | ||
10712 | + [CCS_MAC_ENVIRON] = CCS_MAC_CATEGORY_MISC, | ||
10713 | +#endif | ||
10714 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
10715 | + /* CONFIG::network group */ | ||
10716 | + [CCS_MAC_NETWORK_INET_STREAM_BIND] = CCS_MAC_CATEGORY_NETWORK, | ||
10717 | + [CCS_MAC_NETWORK_INET_STREAM_LISTEN] = CCS_MAC_CATEGORY_NETWORK, | ||
10718 | + [CCS_MAC_NETWORK_INET_STREAM_CONNECT] = CCS_MAC_CATEGORY_NETWORK, | ||
10719 | + [CCS_MAC_NETWORK_INET_STREAM_ACCEPT] = CCS_MAC_CATEGORY_NETWORK, | ||
10720 | + [CCS_MAC_NETWORK_INET_DGRAM_BIND] = CCS_MAC_CATEGORY_NETWORK, | ||
10721 | + [CCS_MAC_NETWORK_INET_DGRAM_SEND] = CCS_MAC_CATEGORY_NETWORK, | ||
10722 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
10723 | + [CCS_MAC_NETWORK_INET_DGRAM_RECV] = CCS_MAC_CATEGORY_NETWORK, | ||
10724 | +#endif | ||
10725 | + [CCS_MAC_NETWORK_INET_RAW_BIND] = CCS_MAC_CATEGORY_NETWORK, | ||
10726 | + [CCS_MAC_NETWORK_INET_RAW_SEND] = CCS_MAC_CATEGORY_NETWORK, | ||
10727 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
10728 | + [CCS_MAC_NETWORK_INET_RAW_RECV] = CCS_MAC_CATEGORY_NETWORK, | ||
10729 | +#endif | ||
10730 | + [CCS_MAC_NETWORK_UNIX_STREAM_BIND] = CCS_MAC_CATEGORY_NETWORK, | ||
10731 | + [CCS_MAC_NETWORK_UNIX_STREAM_LISTEN] = CCS_MAC_CATEGORY_NETWORK, | ||
10732 | + [CCS_MAC_NETWORK_UNIX_STREAM_CONNECT] = CCS_MAC_CATEGORY_NETWORK, | ||
10733 | + [CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT] = CCS_MAC_CATEGORY_NETWORK, | ||
10734 | + [CCS_MAC_NETWORK_UNIX_DGRAM_BIND] = CCS_MAC_CATEGORY_NETWORK, | ||
10735 | + [CCS_MAC_NETWORK_UNIX_DGRAM_SEND] = CCS_MAC_CATEGORY_NETWORK, | ||
10736 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
10737 | + [CCS_MAC_NETWORK_UNIX_DGRAM_RECV] = CCS_MAC_CATEGORY_NETWORK, | ||
10738 | +#endif | ||
10739 | + [CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND] = CCS_MAC_CATEGORY_NETWORK, | ||
10740 | + [CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = CCS_MAC_CATEGORY_NETWORK, | ||
10741 | + [CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = CCS_MAC_CATEGORY_NETWORK, | ||
10742 | + [CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT] = CCS_MAC_CATEGORY_NETWORK, | ||
10743 | +#endif | ||
10744 | +#ifdef CONFIG_CCSECURITY_IPC | ||
10745 | + /* CONFIG::ipc group */ | ||
10746 | + [CCS_MAC_SIGNAL] = CCS_MAC_CATEGORY_IPC, | ||
10747 | +#endif | ||
10748 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
10749 | + /* CONFIG::capability group */ | ||
10750 | + [CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET] = CCS_MAC_CATEGORY_CAPABILITY, | ||
10751 | + [CCS_MAC_CAPABILITY_USE_PACKET_SOCKET] = CCS_MAC_CATEGORY_CAPABILITY, | ||
10752 | + [CCS_MAC_CAPABILITY_SYS_REBOOT] = CCS_MAC_CATEGORY_CAPABILITY, | ||
10753 | + [CCS_MAC_CAPABILITY_SYS_VHANGUP] = CCS_MAC_CATEGORY_CAPABILITY, | ||
10754 | + [CCS_MAC_CAPABILITY_SYS_SETTIME] = CCS_MAC_CATEGORY_CAPABILITY, | ||
10755 | + [CCS_MAC_CAPABILITY_SYS_NICE] = CCS_MAC_CATEGORY_CAPABILITY, | ||
10756 | + [CCS_MAC_CAPABILITY_SYS_SETHOSTNAME] = CCS_MAC_CATEGORY_CAPABILITY, | ||
10757 | + [CCS_MAC_CAPABILITY_USE_KERNEL_MODULE] = CCS_MAC_CATEGORY_CAPABILITY, | ||
10758 | + [CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD] = CCS_MAC_CATEGORY_CAPABILITY, | ||
10759 | + [CCS_MAC_CAPABILITY_SYS_PTRACE] = CCS_MAC_CATEGORY_CAPABILITY, | ||
10760 | +#endif | ||
10761 | +}; | ||
10762 | + | ||
10763 | +/* String table for operation mode. */ | ||
10764 | +static const char * const ccs_mode[CCS_CONFIG_MAX_MODE] = { | ||
10765 | + [CCS_CONFIG_DISABLED] = "disabled", | ||
10766 | + [CCS_CONFIG_LEARNING] = "learning", | ||
10767 | + [CCS_CONFIG_PERMISSIVE] = "permissive", | ||
10768 | + [CCS_CONFIG_ENFORCING] = "enforcing" | ||
10769 | +}; | ||
10770 | + | ||
10771 | +/* String table for /proc/ccs/profile interface. */ | ||
10772 | +static const char * const ccs_mac_keywords[CCS_MAX_MAC_INDEX | ||
10773 | + + CCS_MAX_MAC_CATEGORY_INDEX] = { | ||
10774 | + /* CONFIG::file group */ | ||
10775 | + [CCS_MAC_FILE_EXECUTE] = "execute", | ||
10776 | + [CCS_MAC_FILE_OPEN] = "open", | ||
10777 | + [CCS_MAC_FILE_CREATE] = "create", | ||
10778 | + [CCS_MAC_FILE_UNLINK] = "unlink", | ||
10779 | +#ifdef CONFIG_CCSECURITY_FILE_GETATTR | ||
10780 | + [CCS_MAC_FILE_GETATTR] = "getattr", | ||
10781 | +#endif | ||
10782 | + [CCS_MAC_FILE_MKDIR] = "mkdir", | ||
10783 | + [CCS_MAC_FILE_RMDIR] = "rmdir", | ||
10784 | + [CCS_MAC_FILE_MKFIFO] = "mkfifo", | ||
10785 | + [CCS_MAC_FILE_MKSOCK] = "mksock", | ||
10786 | + [CCS_MAC_FILE_TRUNCATE] = "truncate", | ||
10787 | + [CCS_MAC_FILE_SYMLINK] = "symlink", | ||
10788 | + [CCS_MAC_FILE_MKBLOCK] = "mkblock", | ||
10789 | + [CCS_MAC_FILE_MKCHAR] = "mkchar", | ||
10790 | + [CCS_MAC_FILE_LINK] = "link", | ||
10791 | + [CCS_MAC_FILE_RENAME] = "rename", | ||
10792 | + [CCS_MAC_FILE_CHMOD] = "chmod", | ||
10793 | + [CCS_MAC_FILE_CHOWN] = "chown", | ||
10794 | + [CCS_MAC_FILE_CHGRP] = "chgrp", | ||
10795 | + [CCS_MAC_FILE_IOCTL] = "ioctl", | ||
10796 | + [CCS_MAC_FILE_CHROOT] = "chroot", | ||
10797 | + [CCS_MAC_FILE_MOUNT] = "mount", | ||
10798 | + [CCS_MAC_FILE_UMOUNT] = "unmount", | ||
10799 | + [CCS_MAC_FILE_PIVOT_ROOT] = "pivot_root", | ||
10800 | +#ifdef CONFIG_CCSECURITY_MISC | ||
10801 | + /* CONFIG::misc group */ | ||
10802 | + [CCS_MAC_ENVIRON] = "env", | ||
10803 | +#endif | ||
10804 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
10805 | + /* CONFIG::network group */ | ||
10806 | + [CCS_MAC_NETWORK_INET_STREAM_BIND] = "inet_stream_bind", | ||
10807 | + [CCS_MAC_NETWORK_INET_STREAM_LISTEN] = "inet_stream_listen", | ||
10808 | + [CCS_MAC_NETWORK_INET_STREAM_CONNECT] = "inet_stream_connect", | ||
10809 | + [CCS_MAC_NETWORK_INET_STREAM_ACCEPT] = "inet_stream_accept", | ||
10810 | + [CCS_MAC_NETWORK_INET_DGRAM_BIND] = "inet_dgram_bind", | ||
10811 | + [CCS_MAC_NETWORK_INET_DGRAM_SEND] = "inet_dgram_send", | ||
10812 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
10813 | + [CCS_MAC_NETWORK_INET_DGRAM_RECV] = "inet_dgram_recv", | ||
10814 | +#endif | ||
10815 | + [CCS_MAC_NETWORK_INET_RAW_BIND] = "inet_raw_bind", | ||
10816 | + [CCS_MAC_NETWORK_INET_RAW_SEND] = "inet_raw_send", | ||
10817 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
10818 | + [CCS_MAC_NETWORK_INET_RAW_RECV] = "inet_raw_recv", | ||
10819 | +#endif | ||
10820 | + [CCS_MAC_NETWORK_UNIX_STREAM_BIND] = "unix_stream_bind", | ||
10821 | + [CCS_MAC_NETWORK_UNIX_STREAM_LISTEN] = "unix_stream_listen", | ||
10822 | + [CCS_MAC_NETWORK_UNIX_STREAM_CONNECT] = "unix_stream_connect", | ||
10823 | + [CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT] = "unix_stream_accept", | ||
10824 | + [CCS_MAC_NETWORK_UNIX_DGRAM_BIND] = "unix_dgram_bind", | ||
10825 | + [CCS_MAC_NETWORK_UNIX_DGRAM_SEND] = "unix_dgram_send", | ||
10826 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
10827 | + [CCS_MAC_NETWORK_UNIX_DGRAM_RECV] = "unix_dgram_recv", | ||
10828 | +#endif | ||
10829 | + [CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind", | ||
10830 | + [CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen", | ||
10831 | + [CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect", | ||
10832 | + [CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT] = "unix_seqpacket_accept", | ||
10833 | +#endif | ||
10834 | +#ifdef CONFIG_CCSECURITY_IPC | ||
10835 | + /* CONFIG::ipc group */ | ||
10836 | + [CCS_MAC_SIGNAL] = "signal", | ||
10837 | +#endif | ||
10838 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
10839 | + /* CONFIG::capability group */ | ||
10840 | + [CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET] = "use_route", | ||
10841 | + [CCS_MAC_CAPABILITY_USE_PACKET_SOCKET] = "use_packet", | ||
10842 | + [CCS_MAC_CAPABILITY_SYS_REBOOT] = "SYS_REBOOT", | ||
10843 | + [CCS_MAC_CAPABILITY_SYS_VHANGUP] = "SYS_VHANGUP", | ||
10844 | + [CCS_MAC_CAPABILITY_SYS_SETTIME] = "SYS_TIME", | ||
10845 | + [CCS_MAC_CAPABILITY_SYS_NICE] = "SYS_NICE", | ||
10846 | + [CCS_MAC_CAPABILITY_SYS_SETHOSTNAME] = "SYS_SETHOSTNAME", | ||
10847 | + [CCS_MAC_CAPABILITY_USE_KERNEL_MODULE] = "use_kernel_module", | ||
10848 | + [CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD] = "SYS_KEXEC_LOAD", | ||
10849 | + [CCS_MAC_CAPABILITY_SYS_PTRACE] = "SYS_PTRACE", | ||
10850 | +#endif | ||
10851 | + /* CONFIG group */ | ||
10852 | + [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_FILE] = "file", | ||
10853 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
10854 | + [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_NETWORK] = "network", | ||
10855 | +#endif | ||
10856 | +#ifdef CONFIG_CCSECURITY_MISC | ||
10857 | + [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_MISC] = "misc", | ||
10858 | +#endif | ||
10859 | +#ifdef CONFIG_CCSECURITY_IPC | ||
10860 | + [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_IPC] = "ipc", | ||
10861 | +#endif | ||
10862 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
10863 | + [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_CAPABILITY] = "capability", | ||
10864 | +#endif | ||
10865 | +}; | ||
10866 | + | ||
10867 | +/* String table for path operation. */ | ||
10868 | +static const char * const ccs_path_keyword[CCS_MAX_PATH_OPERATION] = { | ||
10869 | + [CCS_TYPE_EXECUTE] = "execute", | ||
10870 | + [CCS_TYPE_READ] = "read", | ||
10871 | + [CCS_TYPE_WRITE] = "write", | ||
10872 | + [CCS_TYPE_APPEND] = "append", | ||
10873 | + [CCS_TYPE_UNLINK] = "unlink", | ||
10874 | +#ifdef CONFIG_CCSECURITY_FILE_GETATTR | ||
10875 | + [CCS_TYPE_GETATTR] = "getattr", | ||
10876 | +#endif | ||
10877 | + [CCS_TYPE_RMDIR] = "rmdir", | ||
10878 | + [CCS_TYPE_TRUNCATE] = "truncate", | ||
10879 | + [CCS_TYPE_SYMLINK] = "symlink", | ||
10880 | + [CCS_TYPE_CHROOT] = "chroot", | ||
10881 | + [CCS_TYPE_UMOUNT] = "unmount", | ||
10882 | +}; | ||
10883 | + | ||
10884 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
10885 | + | ||
10886 | +/* String table for socket's operation. */ | ||
10887 | +static const char * const ccs_socket_keyword[CCS_MAX_NETWORK_OPERATION] = { | ||
10888 | + [CCS_NETWORK_BIND] = "bind", | ||
10889 | + [CCS_NETWORK_LISTEN] = "listen", | ||
10890 | + [CCS_NETWORK_CONNECT] = "connect", | ||
10891 | + [CCS_NETWORK_ACCEPT] = "accept", | ||
10892 | + [CCS_NETWORK_SEND] = "send", | ||
10893 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | ||
10894 | + [CCS_NETWORK_RECV] = "recv", | ||
10895 | +#endif | ||
10896 | +}; | ||
10897 | + | ||
10898 | +/* String table for socket's protocols. */ | ||
10899 | +static const char * const ccs_proto_keyword[CCS_SOCK_MAX] = { | ||
10900 | + [SOCK_STREAM] = "stream", | ||
10901 | + [SOCK_DGRAM] = "dgram", | ||
10902 | + [SOCK_RAW] = "raw", | ||
10903 | + [SOCK_SEQPACKET] = "seqpacket", | ||
10904 | + [0] = " ", /* Dummy for avoiding NULL pointer dereference. */ | ||
10905 | + [4] = " ", /* Dummy for avoiding NULL pointer dereference. */ | ||
10906 | +}; | ||
10907 | + | ||
10908 | +#endif | ||
10909 | + | ||
10910 | +/* String table for categories. */ | ||
10911 | +static const char * const ccs_category_keywords[CCS_MAX_MAC_CATEGORY_INDEX] = { | ||
10912 | + [CCS_MAC_CATEGORY_FILE] = "file", | ||
10913 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
10914 | + [CCS_MAC_CATEGORY_NETWORK] = "network", | ||
10915 | +#endif | ||
10916 | +#ifdef CONFIG_CCSECURITY_MISC | ||
10917 | + [CCS_MAC_CATEGORY_MISC] = "misc", | ||
10918 | +#endif | ||
10919 | +#ifdef CONFIG_CCSECURITY_IPC | ||
10920 | + [CCS_MAC_CATEGORY_IPC] = "ipc", | ||
10921 | +#endif | ||
10922 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
10923 | + [CCS_MAC_CATEGORY_CAPABILITY] = "capability", | ||
10924 | +#endif | ||
10925 | +}; | ||
10926 | + | ||
10927 | +/* String table for conditions. */ | ||
10928 | +static const char * const ccs_condition_keyword[CCS_MAX_CONDITION_KEYWORD] = { | ||
10929 | + [CCS_TASK_UID] = "task.uid", | ||
10930 | + [CCS_TASK_EUID] = "task.euid", | ||
10931 | + [CCS_TASK_SUID] = "task.suid", | ||
10932 | + [CCS_TASK_FSUID] = "task.fsuid", | ||
10933 | + [CCS_TASK_GID] = "task.gid", | ||
10934 | + [CCS_TASK_EGID] = "task.egid", | ||
10935 | + [CCS_TASK_SGID] = "task.sgid", | ||
10936 | + [CCS_TASK_FSGID] = "task.fsgid", | ||
10937 | + [CCS_TASK_PID] = "task.pid", | ||
10938 | + [CCS_TASK_PPID] = "task.ppid", | ||
10939 | + [CCS_EXEC_ARGC] = "exec.argc", | ||
10940 | + [CCS_EXEC_ENVC] = "exec.envc", | ||
10941 | + [CCS_TYPE_IS_SOCKET] = "socket", | ||
10942 | + [CCS_TYPE_IS_SYMLINK] = "symlink", | ||
10943 | + [CCS_TYPE_IS_FILE] = "file", | ||
10944 | + [CCS_TYPE_IS_BLOCK_DEV] = "block", | ||
10945 | + [CCS_TYPE_IS_DIRECTORY] = "directory", | ||
10946 | + [CCS_TYPE_IS_CHAR_DEV] = "char", | ||
10947 | + [CCS_TYPE_IS_FIFO] = "fifo", | ||
10948 | + [CCS_MODE_SETUID] = "setuid", | ||
10949 | + [CCS_MODE_SETGID] = "setgid", | ||
10950 | + [CCS_MODE_STICKY] = "sticky", | ||
10951 | + [CCS_MODE_OWNER_READ] = "owner_read", | ||
10952 | + [CCS_MODE_OWNER_WRITE] = "owner_write", | ||
10953 | + [CCS_MODE_OWNER_EXECUTE] = "owner_execute", | ||
10954 | + [CCS_MODE_GROUP_READ] = "group_read", | ||
10955 | + [CCS_MODE_GROUP_WRITE] = "group_write", | ||
10956 | + [CCS_MODE_GROUP_EXECUTE] = "group_execute", | ||
10957 | + [CCS_MODE_OTHERS_READ] = "others_read", | ||
10958 | + [CCS_MODE_OTHERS_WRITE] = "others_write", | ||
10959 | + [CCS_MODE_OTHERS_EXECUTE] = "others_execute", | ||
10960 | + [CCS_TASK_TYPE] = "task.type", | ||
10961 | + [CCS_TASK_EXECUTE_HANDLER] = "execute_handler", | ||
10962 | + [CCS_EXEC_REALPATH] = "exec.realpath", | ||
10963 | + [CCS_SYMLINK_TARGET] = "symlink.target", | ||
10964 | + [CCS_PATH1_UID] = "path1.uid", | ||
10965 | + [CCS_PATH1_GID] = "path1.gid", | ||
10966 | + [CCS_PATH1_INO] = "path1.ino", | ||
10967 | + [CCS_PATH1_MAJOR] = "path1.major", | ||
10968 | + [CCS_PATH1_MINOR] = "path1.minor", | ||
10969 | + [CCS_PATH1_PERM] = "path1.perm", | ||
10970 | + [CCS_PATH1_TYPE] = "path1.type", | ||
10971 | + [CCS_PATH1_DEV_MAJOR] = "path1.dev_major", | ||
10972 | + [CCS_PATH1_DEV_MINOR] = "path1.dev_minor", | ||
10973 | + [CCS_PATH2_UID] = "path2.uid", | ||
10974 | + [CCS_PATH2_GID] = "path2.gid", | ||
10975 | + [CCS_PATH2_INO] = "path2.ino", | ||
10976 | + [CCS_PATH2_MAJOR] = "path2.major", | ||
10977 | + [CCS_PATH2_MINOR] = "path2.minor", | ||
10978 | + [CCS_PATH2_PERM] = "path2.perm", | ||
10979 | + [CCS_PATH2_TYPE] = "path2.type", | ||
10980 | + [CCS_PATH2_DEV_MAJOR] = "path2.dev_major", | ||
10981 | + [CCS_PATH2_DEV_MINOR] = "path2.dev_minor", | ||
10982 | + [CCS_PATH1_PARENT_UID] = "path1.parent.uid", | ||
10983 | + [CCS_PATH1_PARENT_GID] = "path1.parent.gid", | ||
10984 | + [CCS_PATH1_PARENT_INO] = "path1.parent.ino", | ||
10985 | + [CCS_PATH1_PARENT_PERM] = "path1.parent.perm", | ||
10986 | + [CCS_PATH2_PARENT_UID] = "path2.parent.uid", | ||
10987 | + [CCS_PATH2_PARENT_GID] = "path2.parent.gid", | ||
10988 | + [CCS_PATH2_PARENT_INO] = "path2.parent.ino", | ||
10989 | + [CCS_PATH2_PARENT_PERM] = "path2.parent.perm", | ||
10990 | +}; | ||
10991 | + | ||
10992 | +/* String table for PREFERENCE keyword. */ | ||
10993 | +static const char * const ccs_pref_keywords[CCS_MAX_PREF] = { | ||
10994 | + [CCS_PREF_MAX_AUDIT_LOG] = "max_audit_log", | ||
10995 | + [CCS_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry", | ||
10996 | + [CCS_PREF_ENFORCING_PENALTY] = "enforcing_penalty", | ||
10997 | +}; | ||
10998 | + | ||
10999 | +/* String table for domain flags. */ | ||
11000 | +const char * const ccs_dif[CCS_MAX_DOMAIN_INFO_FLAGS] = { | ||
11001 | + [CCS_DIF_QUOTA_WARNED] = "quota_exceeded\n", | ||
11002 | + [CCS_DIF_TRANSITION_FAILED] = "transition_failed\n", | ||
11003 | +}; | ||
11004 | + | ||
11005 | +/* String table for domain transition control keywords. */ | ||
11006 | +static const char * const ccs_transition_type[CCS_MAX_TRANSITION_TYPE] = { | ||
11007 | + [CCS_TRANSITION_CONTROL_NO_RESET] = "no_reset_domain ", | ||
11008 | + [CCS_TRANSITION_CONTROL_RESET] = "reset_domain ", | ||
11009 | + [CCS_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ", | ||
11010 | + [CCS_TRANSITION_CONTROL_INITIALIZE] = "initialize_domain ", | ||
11011 | + [CCS_TRANSITION_CONTROL_NO_KEEP] = "no_keep_domain ", | ||
11012 | + [CCS_TRANSITION_CONTROL_KEEP] = "keep_domain ", | ||
11013 | +}; | ||
11014 | + | ||
11015 | +/* String table for grouping keywords. */ | ||
11016 | +static const char * const ccs_group_name[CCS_MAX_GROUP] = { | ||
11017 | + [CCS_PATH_GROUP] = "path_group ", | ||
11018 | + [CCS_NUMBER_GROUP] = "number_group ", | ||
11019 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
11020 | + [CCS_ADDRESS_GROUP] = "address_group ", | ||
11021 | +#endif | ||
11022 | +}; | ||
11023 | + | ||
11024 | +/* String table for /proc/ccs/stat interface. */ | ||
11025 | +static const char * const ccs_policy_headers[CCS_MAX_POLICY_STAT] = { | ||
11026 | + [CCS_STAT_POLICY_UPDATES] = "update:", | ||
11027 | + [CCS_STAT_POLICY_LEARNING] = "violation in learning mode:", | ||
11028 | + [CCS_STAT_POLICY_PERMISSIVE] = "violation in permissive mode:", | ||
11029 | + [CCS_STAT_POLICY_ENFORCING] = "violation in enforcing mode:", | ||
11030 | +}; | ||
11031 | + | ||
11032 | +/* String table for /proc/ccs/stat interface. */ | ||
11033 | +static const char * const ccs_memory_headers[CCS_MAX_MEMORY_STAT] = { | ||
11034 | + [CCS_MEMORY_POLICY] = "policy:", | ||
11035 | + [CCS_MEMORY_AUDIT] = "audit log:", | ||
11036 | + [CCS_MEMORY_QUERY] = "query message:", | ||
11037 | +}; | ||
11038 | + | ||
11039 | +/***** SECTION2: Structure definition *****/ | ||
11040 | + | ||
11041 | +struct iattr; | ||
11042 | + | ||
11043 | +/* Structure for query. */ | ||
11044 | +struct ccs_query { | ||
11045 | + struct list_head list; | ||
11046 | + struct ccs_domain_info *domain; | ||
11047 | + char *query; | ||
11048 | + size_t query_len; | ||
11049 | + unsigned int serial; | ||
11050 | + u8 timer; | ||
11051 | + u8 answer; | ||
11052 | + u8 retry; | ||
11053 | +}; | ||
11054 | + | ||
11055 | +/* Structure for audit log. */ | ||
11056 | +struct ccs_log { | ||
11057 | + struct list_head list; | ||
11058 | + char *log; | ||
11059 | + int size; | ||
11060 | +}; | ||
11061 | + | ||
11062 | +/***** SECTION3: Prototype definition section *****/ | ||
11063 | + | ||
11064 | +int ccs_audit_log(struct ccs_request_info *r); | ||
11065 | +struct ccs_domain_info *ccs_assign_domain(const char *domainname, | ||
11066 | + const bool transit); | ||
11067 | +u8 ccs_get_config(const u8 profile, const u8 index); | ||
11068 | +void ccs_transition_failed(const char *domainname); | ||
11069 | +void ccs_write_log(struct ccs_request_info *r, const char *fmt, ...); | ||
11070 | + | ||
11071 | +static bool ccs_correct_domain(const unsigned char *domainname); | ||
11072 | +static bool ccs_correct_path(const char *filename); | ||
11073 | +static bool ccs_correct_word(const char *string); | ||
11074 | +static bool ccs_correct_word2(const char *string, size_t len); | ||
11075 | +static bool ccs_domain_def(const unsigned char *buffer); | ||
11076 | +static bool ccs_domain_quota_ok(struct ccs_request_info *r); | ||
11077 | +static bool ccs_flush(struct ccs_io_buffer *head); | ||
11078 | +static bool ccs_get_audit(const struct ccs_request_info *r); | ||
11079 | +static bool ccs_has_more_namespace(struct ccs_io_buffer *head); | ||
11080 | +static bool ccs_manager(void); | ||
11081 | +static bool ccs_namespace_jump(const char *domainname); | ||
11082 | +static bool ccs_parse_argv(char *left, char *right, struct ccs_argv *argv); | ||
11083 | +static bool ccs_parse_envp(char *left, char *right, struct ccs_envp *envp); | ||
11084 | +static bool ccs_parse_name_union(struct ccs_acl_param *param, | ||
11085 | + struct ccs_name_union *ptr); | ||
11086 | +static bool ccs_parse_name_union_quoted(struct ccs_acl_param *param, | ||
11087 | + struct ccs_name_union *ptr); | ||
11088 | +static bool ccs_parse_number_union(struct ccs_acl_param *param, | ||
11089 | + struct ccs_number_union *ptr); | ||
11090 | +static bool ccs_permstr(const char *string, const char *keyword); | ||
11091 | +static bool ccs_print_condition(struct ccs_io_buffer *head, | ||
11092 | + const struct ccs_condition *cond); | ||
11093 | +static bool ccs_print_entry(struct ccs_io_buffer *head, | ||
11094 | + const struct ccs_acl_info *acl); | ||
11095 | +static bool ccs_print_group(struct ccs_io_buffer *head, | ||
11096 | + const struct ccs_group *group); | ||
11097 | +static bool ccs_read_acl(struct ccs_io_buffer *head, struct list_head *list); | ||
11098 | +static bool ccs_read_group(struct ccs_io_buffer *head, const int idx); | ||
11099 | +static bool ccs_read_policy(struct ccs_io_buffer *head, const int idx); | ||
11100 | +static bool ccs_same_condition(const struct ccs_condition *a, | ||
11101 | + const struct ccs_condition *b); | ||
11102 | +static bool ccs_select_domain(struct ccs_io_buffer *head, const char *data); | ||
11103 | +static bool ccs_set_lf(struct ccs_io_buffer *head); | ||
11104 | +static bool ccs_str_starts(char **src, const char *find); | ||
11105 | +static char *ccs_get_transit_preference(struct ccs_acl_param *param, | ||
11106 | + struct ccs_condition *e); | ||
11107 | +static char *ccs_init_log(struct ccs_request_info *r, int len, const char *fmt, | ||
11108 | + va_list args); | ||
11109 | +static char *ccs_print_bprm(struct linux_binprm *bprm, | ||
11110 | + struct ccs_page_dump *dump); | ||
11111 | +static char *ccs_print_header(struct ccs_request_info *r); | ||
11112 | +static char *ccs_read_token(struct ccs_acl_param *param); | ||
11113 | +static const char *ccs_yesno(const unsigned int value); | ||
11114 | +static const struct ccs_path_info *ccs_get_domainname | ||
11115 | +(struct ccs_acl_param *param); | ||
11116 | +static const struct ccs_path_info *ccs_get_dqword(char *start); | ||
11117 | +static int __init ccs_init_module(void); | ||
11118 | +static int ccs_delete_domain(char *domainname); | ||
11119 | +static int ccs_open(struct inode *inode, struct file *file); | ||
11120 | +static int ccs_parse_policy(struct ccs_io_buffer *head, char *line); | ||
11121 | +static int ccs_release(struct inode *inode, struct file *file); | ||
11122 | +static int ccs_set_mode(char *name, const char *value, | ||
11123 | + struct ccs_profile *profile); | ||
11124 | +static int ccs_supervisor(struct ccs_request_info *r, const char *fmt, ...) | ||
11125 | + __printf(2, 3); | ||
11126 | +static int ccs_truncate(char *str); | ||
11127 | +static int ccs_update_acl(const int size, struct ccs_acl_param *param); | ||
11128 | +static int ccs_update_manager_entry(const char *manager, const bool is_delete); | ||
11129 | +static int ccs_update_policy(const int size, struct ccs_acl_param *param); | ||
11130 | +static int ccs_write_acl(struct ccs_policy_namespace *ns, | ||
11131 | + struct list_head *list, char *data, | ||
11132 | + const bool is_delete); | ||
11133 | +static int ccs_write_aggregator(struct ccs_acl_param *param); | ||
11134 | +static int ccs_write_answer(struct ccs_io_buffer *head); | ||
11135 | +static int ccs_write_domain(struct ccs_io_buffer *head); | ||
11136 | +static int ccs_write_exception(struct ccs_io_buffer *head); | ||
11137 | +static int ccs_write_file(struct ccs_acl_param *param); | ||
11138 | +static int ccs_write_group(struct ccs_acl_param *param, const u8 type); | ||
11139 | +static int ccs_write_manager(struct ccs_io_buffer *head); | ||
11140 | +static int ccs_write_pid(struct ccs_io_buffer *head); | ||
11141 | +static int ccs_write_profile(struct ccs_io_buffer *head); | ||
11142 | +static int ccs_write_stat(struct ccs_io_buffer *head); | ||
11143 | +static int ccs_write_task(struct ccs_acl_param *param); | ||
11144 | +static int ccs_write_transition_control(struct ccs_acl_param *param, | ||
11145 | + const u8 type); | ||
11146 | +static s8 ccs_find_yesno(const char *string, const char *find); | ||
11147 | +static ssize_t ccs_read(struct file *file, char __user *buf, size_t count, | ||
11148 | + loff_t *ppos); | ||
11149 | +static ssize_t ccs_read_self(struct file *file, char __user *buf, size_t count, | ||
11150 | + loff_t *ppos); | ||
11151 | +static ssize_t ccs_write(struct file *file, const char __user *buf, | ||
11152 | + size_t count, loff_t *ppos); | ||
11153 | +static struct ccs_condition *ccs_commit_condition(struct ccs_condition *entry); | ||
11154 | +static struct ccs_condition *ccs_get_condition(struct ccs_acl_param *param); | ||
11155 | +static struct ccs_domain_info *ccs_find_domain(const char *domainname); | ||
11156 | +static struct ccs_domain_info *ccs_find_domain_by_qid(unsigned int serial); | ||
11157 | +static struct ccs_group *ccs_get_group(struct ccs_acl_param *param, | ||
11158 | + const u8 idx); | ||
11159 | +static struct ccs_policy_namespace *ccs_assign_namespace | ||
11160 | +(const char *domainname); | ||
11161 | +static struct ccs_policy_namespace *ccs_find_namespace(const char *name, | ||
11162 | + const unsigned int len); | ||
11163 | +static struct ccs_profile *ccs_assign_profile(struct ccs_policy_namespace *ns, | ||
11164 | + const unsigned int profile); | ||
11165 | +static struct ccs_profile *ccs_profile(const u8 profile); | ||
11166 | +static u8 ccs_condition_type(const char *word); | ||
11167 | +static u8 ccs_make_byte(const u8 c1, const u8 c2, const u8 c3); | ||
11168 | +static u8 ccs_parse_ulong(unsigned long *result, char **str); | ||
11169 | +static unsigned int ccs_poll(struct file *file, poll_table *wait); | ||
11170 | +static void __init ccs_create_entry(const char *name, const umode_t mode, | ||
11171 | + struct proc_dir_entry *parent, | ||
11172 | + const u8 key); | ||
11173 | +static void __init ccs_load_builtin_policy(void); | ||
11174 | +static void __init ccs_policy_io_init(void); | ||
11175 | +static void __init ccs_proc_init(void); | ||
11176 | +static void ccs_add_entry(char *header); | ||
11177 | +static void ccs_addprintf(char *buffer, int len, const char *fmt, ...) | ||
11178 | + __printf(3, 4); | ||
11179 | +static void ccs_addprintf(char *buffer, int len, const char *fmt, ...); | ||
11180 | +static void ccs_check_profile(void); | ||
11181 | +static void ccs_convert_time(time_t time, struct ccs_time *stamp); | ||
11182 | +static void ccs_init_policy_namespace(struct ccs_policy_namespace *ns); | ||
11183 | +static void ccs_io_printf(struct ccs_io_buffer *head, const char *fmt, ...) | ||
11184 | + __printf(2, 3); | ||
11185 | +static void ccs_normalize_line(unsigned char *buffer); | ||
11186 | +static void ccs_print_config(struct ccs_io_buffer *head, const u8 config); | ||
11187 | +static void ccs_print_name_union(struct ccs_io_buffer *head, | ||
11188 | + const struct ccs_name_union *ptr); | ||
11189 | +static void ccs_print_name_union_quoted(struct ccs_io_buffer *head, | ||
11190 | + const struct ccs_name_union *ptr); | ||
11191 | +static void ccs_print_namespace(struct ccs_io_buffer *head); | ||
11192 | +static void ccs_print_number_union(struct ccs_io_buffer *head, | ||
11193 | + const struct ccs_number_union *ptr); | ||
11194 | +static void ccs_print_number_union_nospace(struct ccs_io_buffer *head, | ||
11195 | + const struct ccs_number_union *ptr); | ||
11196 | +static void ccs_read_domain(struct ccs_io_buffer *head); | ||
11197 | +static void ccs_read_exception(struct ccs_io_buffer *head); | ||
11198 | +static void ccs_read_log(struct ccs_io_buffer *head); | ||
11199 | +static void ccs_read_manager(struct ccs_io_buffer *head); | ||
11200 | +static void ccs_read_pid(struct ccs_io_buffer *head); | ||
11201 | +static void ccs_read_profile(struct ccs_io_buffer *head); | ||
11202 | +static void ccs_read_query(struct ccs_io_buffer *head); | ||
11203 | +static void ccs_read_stat(struct ccs_io_buffer *head); | ||
11204 | +static void ccs_read_version(struct ccs_io_buffer *head); | ||
11205 | +static void ccs_set_group(struct ccs_io_buffer *head, const char *category); | ||
11206 | +static void ccs_set_namespace_cursor(struct ccs_io_buffer *head); | ||
11207 | +static void ccs_set_slash(struct ccs_io_buffer *head); | ||
11208 | +static void ccs_set_space(struct ccs_io_buffer *head); | ||
11209 | +static void ccs_set_string(struct ccs_io_buffer *head, const char *string); | ||
11210 | +static void ccs_set_uint(unsigned int *i, const char *string, | ||
11211 | + const char *find); | ||
11212 | +static void ccs_update_stat(const u8 index); | ||
11213 | +static void ccs_update_task_domain(struct ccs_request_info *r); | ||
11214 | +static void ccs_write_log2(struct ccs_request_info *r, int len, | ||
11215 | + const char *fmt, va_list args); | ||
11216 | + | ||
11217 | +#ifdef CONFIG_CCSECURITY_PORTRESERVE | ||
11218 | +static bool __ccs_lport_reserved(const u16 port); | ||
11219 | +static int ccs_write_reserved_port(struct ccs_acl_param *param); | ||
11220 | +#endif | ||
11221 | + | ||
11222 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
11223 | +static bool ccs_parse_ipaddr_union(struct ccs_acl_param *param, | ||
11224 | + struct ccs_ipaddr_union *ptr); | ||
11225 | +static int ccs_print_ipv4(char *buffer, const unsigned int buffer_len, | ||
11226 | + const u32 *ip); | ||
11227 | +static int ccs_print_ipv6(char *buffer, const unsigned int buffer_len, | ||
11228 | + const struct in6_addr *ip); | ||
11229 | +static int ccs_write_inet_network(struct ccs_acl_param *param); | ||
11230 | +static int ccs_write_unix_network(struct ccs_acl_param *param); | ||
11231 | +static void ccs_print_ip(char *buf, const unsigned int size, | ||
11232 | + const struct ccs_ipaddr_union *ptr); | ||
11233 | +#endif | ||
11234 | + | ||
11235 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
11236 | +static int ccs_write_capability(struct ccs_acl_param *param); | ||
11237 | +#endif | ||
11238 | + | ||
11239 | +#ifdef CONFIG_CCSECURITY_MISC | ||
11240 | +static int ccs_write_misc(struct ccs_acl_param *param); | ||
11241 | +#endif | ||
11242 | + | ||
11243 | +#ifdef CONFIG_CCSECURITY_IPC | ||
11244 | +static int ccs_write_ipc(struct ccs_acl_param *param); | ||
11245 | +#endif | ||
11246 | + | ||
11247 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
11248 | +static ssize_t ccs_write_self(struct file *file, const char __user *buf, | ||
11249 | + size_t count, loff_t *ppos); | ||
11250 | +#endif | ||
11251 | + | ||
11252 | +/***** SECTION4: Standalone functions section *****/ | ||
11253 | + | ||
11254 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) | ||
11255 | + | ||
11256 | +/** | ||
11257 | + * fatal_signal_pending - Check whether SIGKILL is pending or not. | ||
11258 | + * | ||
11259 | + * @p: Pointer to "struct task_struct". | ||
11260 | + * | ||
11261 | + * Returns true if SIGKILL is pending on @p, false otherwise. | ||
11262 | + * | ||
11263 | + * This is for compatibility with older kernels. | ||
11264 | + */ | ||
11265 | +#define fatal_signal_pending(p) (signal_pending(p) && \ | ||
11266 | + sigismember(&p->pending.signal, SIGKILL)) | ||
11267 | + | ||
11268 | +#endif | ||
11269 | + | ||
11270 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
11271 | + | ||
11272 | +/** | ||
11273 | + * __wait_event_interruptible_timeout - Sleep until a condition gets true or a timeout elapses. | ||
11274 | + * | ||
11275 | + * @wq: The waitqueue to wait on. | ||
11276 | + * @condition: A C expression for the event to wait for. | ||
11277 | + * @ret: Timeout, in jiffies. | ||
11278 | + * | ||
11279 | + * Returns 0 if the @timeout elapsed, -ERESTARTSYS if it was interrupted by a | ||
11280 | + * signal, and the remaining jiffies otherwise if the condition evaluated to | ||
11281 | + * true before the timeout elapsed. | ||
11282 | + * | ||
11283 | + * This is for compatibility with older kernels. | ||
11284 | + */ | ||
11285 | +#define __wait_event_interruptible_timeout(wq, condition, ret) \ | ||
11286 | +do { \ | ||
11287 | + wait_queue_t __wait; \ | ||
11288 | + init_waitqueue_entry(&__wait, current); \ | ||
11289 | + \ | ||
11290 | + add_wait_queue(&wq, &__wait); \ | ||
11291 | + for (;;) { \ | ||
11292 | + set_current_state(TASK_INTERRUPTIBLE); \ | ||
11293 | + if (condition) \ | ||
11294 | + break; \ | ||
11295 | + if (!signal_pending(current)) { \ | ||
11296 | + ret = schedule_timeout(ret); \ | ||
11297 | + if (!ret) \ | ||
11298 | + break; \ | ||
11299 | + continue; \ | ||
11300 | + } \ | ||
11301 | + ret = -ERESTARTSYS; \ | ||
11302 | + break; \ | ||
11303 | + } \ | ||
11304 | + current->state = TASK_RUNNING; \ | ||
11305 | + remove_wait_queue(&wq, &__wait); \ | ||
11306 | +} while (0) | ||
11307 | + | ||
11308 | +/** | ||
11309 | + * wait_event_interruptible_timeout - Sleep until a condition gets true or a timeout elapses. | ||
11310 | + * | ||
11311 | + * @wq: The waitqueue to wait on. | ||
11312 | + * @condition: A C expression for the event to wait for. | ||
11313 | + * @timeout: Timeout, in jiffies. | ||
11314 | + * | ||
11315 | + * Returns 0 if the @timeout elapsed, -ERESTARTSYS if it was interrupted by a | ||
11316 | + * signal, and the remaining jiffies otherwise if the condition evaluated to | ||
11317 | + * true before the timeout elapsed. | ||
11318 | + * | ||
11319 | + * This is for compatibility with older kernels. | ||
11320 | + */ | ||
11321 | +#define wait_event_interruptible_timeout(wq, condition, timeout) \ | ||
11322 | +({ \ | ||
11323 | + long __ret = timeout; \ | ||
11324 | + if (!(condition)) \ | ||
11325 | + __wait_event_interruptible_timeout(wq, condition, __ret); \ | ||
11326 | + __ret; \ | ||
11327 | +}) | ||
11328 | + | ||
11329 | +#endif | ||
11330 | + | ||
11331 | +/** | ||
11332 | + * ccs_convert_time - Convert time_t to YYYY/MM/DD hh/mm/ss. | ||
11333 | + * | ||
11334 | + * @time: Seconds since 1970/01/01 00:00:00. | ||
11335 | + * @stamp: Pointer to "struct ccs_time". | ||
11336 | + * | ||
11337 | + * Returns nothing. | ||
11338 | + * | ||
11339 | + * This function does not handle Y2038 problem. | ||
11340 | + */ | ||
11341 | +static void ccs_convert_time(time_t time, struct ccs_time *stamp) | ||
11342 | +{ | ||
11343 | + static const u16 ccs_eom[2][12] = { | ||
11344 | + { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, | ||
11345 | + { 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } | ||
11346 | + }; | ||
11347 | + u16 y; | ||
11348 | + u8 m; | ||
11349 | + bool r; | ||
11350 | + stamp->sec = time % 60; | ||
11351 | + time /= 60; | ||
11352 | + stamp->min = time % 60; | ||
11353 | + time /= 60; | ||
11354 | + stamp->hour = time % 24; | ||
11355 | + time /= 24; | ||
11356 | + for (y = 1970; ; y++) { | ||
11357 | + const unsigned short days = (y & 3) ? 365 : 366; | ||
11358 | + if (time < days) | ||
11359 | + break; | ||
11360 | + time -= days; | ||
11361 | + } | ||
11362 | + r = (y & 3) == 0; | ||
11363 | + for (m = 0; m < 11 && time >= ccs_eom[r][m]; m++); | ||
11364 | + if (m) | ||
11365 | + time -= ccs_eom[r][m - 1]; | ||
11366 | + stamp->year = y; | ||
11367 | + stamp->month = ++m; | ||
11368 | + stamp->day = ++time; | ||
11369 | +} | ||
11370 | + | ||
11371 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 23) | ||
11372 | +#if !defined(RHEL_VERSION) || RHEL_VERSION != 3 | ||
11373 | + | ||
11374 | +/** | ||
11375 | + * PDE - Get "struct proc_dir_entry". | ||
11376 | + * | ||
11377 | + * @inode: Pointer to "struct inode". | ||
11378 | + * | ||
11379 | + * Returns pointer to "struct proc_dir_entry". | ||
11380 | + * | ||
11381 | + * This is for compatibility with older kernels. | ||
11382 | + */ | ||
11383 | +static inline struct proc_dir_entry *PDE(const struct inode *inode) | ||
11384 | +{ | ||
11385 | + return (struct proc_dir_entry *) inode->u.generic_ip; | ||
11386 | +} | ||
11387 | + | ||
11388 | +#endif | ||
11389 | +#endif | ||
11390 | + | ||
11391 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
11392 | + | ||
11393 | +/** | ||
11394 | + * proc_notify_change - Update inode's attributes and reflect to the dentry. | ||
11395 | + * | ||
11396 | + * @dentry: Pointer to "struct dentry". | ||
11397 | + * @iattr: Pointer to "struct iattr". | ||
11398 | + * | ||
11399 | + * Returns 0 on success, negative value otherwise. | ||
11400 | + * | ||
11401 | + * The 2.4 kernels don't allow chmod()/chown() for files in /proc, | ||
11402 | + * while the 2.6 kernels allow. | ||
11403 | + * To permit management of /proc/ccs/ interface by non-root user, | ||
11404 | + * I modified to allow chmod()/chown() of /proc/ccs/ interface like 2.6 kernels | ||
11405 | + * by adding "struct inode_operations"->setattr hook. | ||
11406 | + */ | ||
11407 | +static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) | ||
11408 | +{ | ||
11409 | + struct inode *inode = dentry->d_inode; | ||
11410 | + struct proc_dir_entry *de = PDE(inode); | ||
11411 | + int error; | ||
11412 | + | ||
11413 | + error = inode_change_ok(inode, iattr); | ||
11414 | + if (error) | ||
11415 | + goto out; | ||
11416 | + | ||
11417 | + error = inode_setattr(inode, iattr); | ||
11418 | + if (error) | ||
11419 | + goto out; | ||
11420 | + | ||
11421 | + de->uid = inode->i_uid; | ||
11422 | + de->gid = inode->i_gid; | ||
11423 | + de->mode = inode->i_mode; | ||
11424 | +out: | ||
11425 | + return error; | ||
11426 | +} | ||
11427 | + | ||
11428 | +#endif | ||
11429 | + | ||
11430 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
11431 | + | ||
11432 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) && defined(CONFIG_NET) | ||
11433 | +#define ccs_in4_pton in4_pton | ||
11434 | +#define ccs_in6_pton in6_pton | ||
11435 | +#else | ||
11436 | +/* | ||
11437 | + * Routines for parsing IPv4 or IPv6 address. | ||
11438 | + * These are copied from lib/hexdump.c net/core/utils.c . | ||
11439 | + */ | ||
11440 | +#include <linux/ctype.h> | ||
11441 | + | ||
11442 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) | ||
11443 | +static int hex_to_bin(char ch) | ||
11444 | +{ | ||
11445 | + if ((ch >= '0') && (ch <= '9')) | ||
11446 | + return ch - '0'; | ||
11447 | + ch = tolower(ch); | ||
11448 | + if ((ch >= 'a') && (ch <= 'f')) | ||
11449 | + return ch - 'a' + 10; | ||
11450 | + return -1; | ||
11451 | +} | ||
11452 | +#endif | ||
11453 | + | ||
11454 | +#define IN6PTON_XDIGIT 0x00010000 | ||
11455 | +#define IN6PTON_DIGIT 0x00020000 | ||
11456 | +#define IN6PTON_COLON_MASK 0x00700000 | ||
11457 | +#define IN6PTON_COLON_1 0x00100000 /* single : requested */ | ||
11458 | +#define IN6PTON_COLON_2 0x00200000 /* second : requested */ | ||
11459 | +#define IN6PTON_COLON_1_2 0x00400000 /* :: requested */ | ||
11460 | +#define IN6PTON_DOT 0x00800000 /* . */ | ||
11461 | +#define IN6PTON_DELIM 0x10000000 | ||
11462 | +#define IN6PTON_NULL 0x20000000 /* first/tail */ | ||
11463 | +#define IN6PTON_UNKNOWN 0x40000000 | ||
11464 | + | ||
11465 | +static inline int xdigit2bin(char c, int delim) | ||
11466 | +{ | ||
11467 | + int val; | ||
11468 | + | ||
11469 | + if (c == delim || c == '\0') | ||
11470 | + return IN6PTON_DELIM; | ||
11471 | + if (c == ':') | ||
11472 | + return IN6PTON_COLON_MASK; | ||
11473 | + if (c == '.') | ||
11474 | + return IN6PTON_DOT; | ||
11475 | + | ||
11476 | + val = hex_to_bin(c); | ||
11477 | + if (val >= 0) | ||
11478 | + return val | IN6PTON_XDIGIT | (val < 10 ? IN6PTON_DIGIT : 0); | ||
11479 | + | ||
11480 | + if (delim == -1) | ||
11481 | + return IN6PTON_DELIM; | ||
11482 | + return IN6PTON_UNKNOWN; | ||
11483 | +} | ||
11484 | + | ||
11485 | +static int ccs_in4_pton(const char *src, int srclen, u8 *dst, int delim, | ||
11486 | + const char **end) | ||
11487 | +{ | ||
11488 | + const char *s; | ||
11489 | + u8 *d; | ||
11490 | + u8 dbuf[4]; | ||
11491 | + int ret = 0; | ||
11492 | + int i; | ||
11493 | + int w = 0; | ||
11494 | + | ||
11495 | + if (srclen < 0) | ||
11496 | + srclen = strlen(src); | ||
11497 | + s = src; | ||
11498 | + d = dbuf; | ||
11499 | + i = 0; | ||
11500 | + while (1) { | ||
11501 | + int c; | ||
11502 | + c = xdigit2bin(srclen > 0 ? *s : '\0', delim); | ||
11503 | + if (!(c & (IN6PTON_DIGIT | IN6PTON_DOT | IN6PTON_DELIM | | ||
11504 | + IN6PTON_COLON_MASK))) | ||
11505 | + goto out; | ||
11506 | + if (c & (IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK)) { | ||
11507 | + if (w == 0) | ||
11508 | + goto out; | ||
11509 | + *d++ = w & 0xff; | ||
11510 | + w = 0; | ||
11511 | + i++; | ||
11512 | + if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { | ||
11513 | + if (i != 4) | ||
11514 | + goto out; | ||
11515 | + break; | ||
11516 | + } | ||
11517 | + goto cont; | ||
11518 | + } | ||
11519 | + w = (w * 10) + c; | ||
11520 | + if ((w & 0xffff) > 255) | ||
11521 | + goto out; | ||
11522 | +cont: | ||
11523 | + if (i >= 4) | ||
11524 | + goto out; | ||
11525 | + s++; | ||
11526 | + srclen--; | ||
11527 | + } | ||
11528 | + ret = 1; | ||
11529 | + memcpy(dst, dbuf, sizeof(dbuf)); | ||
11530 | +out: | ||
11531 | + if (end) | ||
11532 | + *end = s; | ||
11533 | + return ret; | ||
11534 | +} | ||
11535 | + | ||
11536 | +static int ccs_in6_pton(const char *src, int srclen, u8 *dst, int delim, | ||
11537 | + const char **end) | ||
11538 | +{ | ||
11539 | + const char *s, *tok = NULL; | ||
11540 | + u8 *d, *dc = NULL; | ||
11541 | + u8 dbuf[16]; | ||
11542 | + int ret = 0; | ||
11543 | + int i; | ||
11544 | + int state = IN6PTON_COLON_1_2 | IN6PTON_XDIGIT | IN6PTON_NULL; | ||
11545 | + int w = 0; | ||
11546 | + | ||
11547 | + memset(dbuf, 0, sizeof(dbuf)); | ||
11548 | + | ||
11549 | + s = src; | ||
11550 | + d = dbuf; | ||
11551 | + if (srclen < 0) | ||
11552 | + srclen = strlen(src); | ||
11553 | + | ||
11554 | + while (1) { | ||
11555 | + int c; | ||
11556 | + | ||
11557 | + c = xdigit2bin(srclen > 0 ? *s : '\0', delim); | ||
11558 | + if (!(c & state)) | ||
11559 | + goto out; | ||
11560 | + if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { | ||
11561 | + /* process one 16-bit word */ | ||
11562 | + if (!(state & IN6PTON_NULL)) { | ||
11563 | + *d++ = (w >> 8) & 0xff; | ||
11564 | + *d++ = w & 0xff; | ||
11565 | + } | ||
11566 | + w = 0; | ||
11567 | + if (c & IN6PTON_DELIM) { | ||
11568 | + /* We've processed last word */ | ||
11569 | + break; | ||
11570 | + } | ||
11571 | + /* | ||
11572 | + * COLON_1 => XDIGIT | ||
11573 | + * COLON_2 => XDIGIT|DELIM | ||
11574 | + * COLON_1_2 => COLON_2 | ||
11575 | + */ | ||
11576 | + switch (state & IN6PTON_COLON_MASK) { | ||
11577 | + case IN6PTON_COLON_2: | ||
11578 | + dc = d; | ||
11579 | + state = IN6PTON_XDIGIT | IN6PTON_DELIM; | ||
11580 | + if (dc - dbuf >= sizeof(dbuf)) | ||
11581 | + state |= IN6PTON_NULL; | ||
11582 | + break; | ||
11583 | + case IN6PTON_COLON_1|IN6PTON_COLON_1_2: | ||
11584 | + state = IN6PTON_XDIGIT | IN6PTON_COLON_2; | ||
11585 | + break; | ||
11586 | + case IN6PTON_COLON_1: | ||
11587 | + state = IN6PTON_XDIGIT; | ||
11588 | + break; | ||
11589 | + case IN6PTON_COLON_1_2: | ||
11590 | + state = IN6PTON_COLON_2; | ||
11591 | + break; | ||
11592 | + default: | ||
11593 | + state = 0; | ||
11594 | + } | ||
11595 | + tok = s + 1; | ||
11596 | + goto cont; | ||
11597 | + } | ||
11598 | + | ||
11599 | + if (c & IN6PTON_DOT) { | ||
11600 | + ret = ccs_in4_pton(tok ? tok : s, srclen + | ||
11601 | + (int)(s - tok), d, delim, &s); | ||
11602 | + if (ret > 0) { | ||
11603 | + d += 4; | ||
11604 | + break; | ||
11605 | + } | ||
11606 | + goto out; | ||
11607 | + } | ||
11608 | + | ||
11609 | + w = (w << 4) | (0xff & c); | ||
11610 | + state = IN6PTON_COLON_1 | IN6PTON_DELIM; | ||
11611 | + if (!(w & 0xf000)) | ||
11612 | + state |= IN6PTON_XDIGIT; | ||
11613 | + if (!dc && d + 2 < dbuf + sizeof(dbuf)) { | ||
11614 | + state |= IN6PTON_COLON_1_2; | ||
11615 | + state &= ~IN6PTON_DELIM; | ||
11616 | + } | ||
11617 | + if (d + 2 >= dbuf + sizeof(dbuf)) | ||
11618 | + state &= ~(IN6PTON_COLON_1|IN6PTON_COLON_1_2); | ||
11619 | +cont: | ||
11620 | + if ((dc && d + 4 < dbuf + sizeof(dbuf)) || | ||
11621 | + d + 4 == dbuf + sizeof(dbuf)) | ||
11622 | + state |= IN6PTON_DOT; | ||
11623 | + if (d >= dbuf + sizeof(dbuf)) | ||
11624 | + state &= ~(IN6PTON_XDIGIT|IN6PTON_COLON_MASK); | ||
11625 | + s++; | ||
11626 | + srclen--; | ||
11627 | + } | ||
11628 | + | ||
11629 | + i = 15; d--; | ||
11630 | + | ||
11631 | + if (dc) { | ||
11632 | + while (d >= dc) | ||
11633 | + dst[i--] = *d--; | ||
11634 | + while (i >= dc - dbuf) | ||
11635 | + dst[i--] = 0; | ||
11636 | + while (i >= 0) | ||
11637 | + dst[i--] = *d--; | ||
11638 | + } else | ||
11639 | + memcpy(dst, dbuf, sizeof(dbuf)); | ||
11640 | + | ||
11641 | + ret = 1; | ||
11642 | +out: | ||
11643 | + if (end) | ||
11644 | + *end = s; | ||
11645 | + return ret; | ||
11646 | +} | ||
11647 | +#endif | ||
11648 | + | ||
11649 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) | ||
11650 | + | ||
11651 | +/* | ||
11652 | + * Routines for printing IPv4 or IPv6 address. | ||
11653 | + * These are copied from include/linux/kernel.h include/net/ipv6.h | ||
11654 | + * include/net/addrconf.h lib/hexdump.c lib/vsprintf.c and simplified. | ||
11655 | + */ | ||
11656 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) | ||
11657 | +#if !defined(RHEL_MAJOR) || RHEL_MAJOR != 5 || !defined(RHEL_MINOR) || RHEL_MINOR < 9 | ||
11658 | +static const char hex_asc[] = "0123456789abcdef"; | ||
11659 | +#define hex_asc_lo(x) hex_asc[((x) & 0x0f)] | ||
11660 | +#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4] | ||
11661 | + | ||
11662 | +static inline char *pack_hex_byte(char *buf, u8 byte) | ||
11663 | +{ | ||
11664 | + *buf++ = hex_asc_hi(byte); | ||
11665 | + *buf++ = hex_asc_lo(byte); | ||
11666 | + return buf; | ||
11667 | +} | ||
11668 | +#endif | ||
11669 | +#endif | ||
11670 | + | ||
11671 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) | ||
11672 | +static inline int ipv6_addr_v4mapped(const struct in6_addr *a) | ||
11673 | +{ | ||
11674 | + return (a->s6_addr32[0] | a->s6_addr32[1] | | ||
11675 | + (a->s6_addr32[2] ^ htonl(0x0000ffff))) == 0; | ||
11676 | +} | ||
11677 | +#endif | ||
11678 | + | ||
11679 | +static inline int ipv6_addr_is_isatap(const struct in6_addr *addr) | ||
11680 | +{ | ||
11681 | + return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE); | ||
11682 | +} | ||
11683 | + | ||
11684 | +static char *ip4_string(char *p, const u8 *addr) | ||
11685 | +{ | ||
11686 | + /* | ||
11687 | + * Since this function is called outside vsnprintf(), I can use | ||
11688 | + * sprintf() here. | ||
11689 | + */ | ||
11690 | + return p + | ||
11691 | + sprintf(p, "%u.%u.%u.%u", addr[0], addr[1], addr[2], addr[3]); | ||
11692 | +} | ||
11693 | + | ||
11694 | +static char *ip6_compressed_string(char *p, const char *addr) | ||
11695 | +{ | ||
11696 | + int i, j, range; | ||
11697 | + unsigned char zerolength[8]; | ||
11698 | + int longest = 1; | ||
11699 | + int colonpos = -1; | ||
11700 | + u16 word; | ||
11701 | + u8 hi, lo; | ||
11702 | + bool needcolon = false; | ||
11703 | + bool useIPv4; | ||
11704 | + struct in6_addr in6; | ||
11705 | + | ||
11706 | + memcpy(&in6, addr, sizeof(struct in6_addr)); | ||
11707 | + | ||
11708 | + useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6); | ||
11709 | + | ||
11710 | + memset(zerolength, 0, sizeof(zerolength)); | ||
11711 | + | ||
11712 | + if (useIPv4) | ||
11713 | + range = 6; | ||
11714 | + else | ||
11715 | + range = 8; | ||
11716 | + | ||
11717 | + /* find position of longest 0 run */ | ||
11718 | + for (i = 0; i < range; i++) { | ||
11719 | + for (j = i; j < range; j++) { | ||
11720 | + if (in6.s6_addr16[j] != 0) | ||
11721 | + break; | ||
11722 | + zerolength[i]++; | ||
11723 | + } | ||
11724 | + } | ||
11725 | + for (i = 0; i < range; i++) { | ||
11726 | + if (zerolength[i] > longest) { | ||
11727 | + longest = zerolength[i]; | ||
11728 | + colonpos = i; | ||
11729 | + } | ||
11730 | + } | ||
11731 | + if (longest == 1) /* don't compress a single 0 */ | ||
11732 | + colonpos = -1; | ||
11733 | + | ||
11734 | + /* emit address */ | ||
11735 | + for (i = 0; i < range; i++) { | ||
11736 | + if (i == colonpos) { | ||
11737 | + if (needcolon || i == 0) | ||
11738 | + *p++ = ':'; | ||
11739 | + *p++ = ':'; | ||
11740 | + needcolon = false; | ||
11741 | + i += longest - 1; | ||
11742 | + continue; | ||
11743 | + } | ||
11744 | + if (needcolon) { | ||
11745 | + *p++ = ':'; | ||
11746 | + needcolon = false; | ||
11747 | + } | ||
11748 | + /* hex u16 without leading 0s */ | ||
11749 | + word = ntohs(in6.s6_addr16[i]); | ||
11750 | + hi = word >> 8; | ||
11751 | + lo = word & 0xff; | ||
11752 | + if (hi) { | ||
11753 | + if (hi > 0x0f) | ||
11754 | + p = pack_hex_byte(p, hi); | ||
11755 | + else | ||
11756 | + *p++ = hex_asc_lo(hi); | ||
11757 | + p = pack_hex_byte(p, lo); | ||
11758 | + } else if (lo > 0x0f) | ||
11759 | + p = pack_hex_byte(p, lo); | ||
11760 | + else | ||
11761 | + *p++ = hex_asc_lo(lo); | ||
11762 | + needcolon = true; | ||
11763 | + } | ||
11764 | + | ||
11765 | + if (useIPv4) { | ||
11766 | + if (needcolon) | ||
11767 | + *p++ = ':'; | ||
11768 | + p = ip4_string(p, &in6.s6_addr[12]); | ||
11769 | + } | ||
11770 | + *p = '\0'; | ||
11771 | + | ||
11772 | + return p; | ||
11773 | +} | ||
11774 | +#endif | ||
11775 | + | ||
11776 | +/** | ||
11777 | + * ccs_print_ipv4 - Print an IPv4 address. | ||
11778 | + * | ||
11779 | + * @buffer: Buffer to write to. | ||
11780 | + * @buffer_len: Size of @buffer. | ||
11781 | + * @ip: Pointer to "u32 in network byte order". | ||
11782 | + * | ||
11783 | + * Returns written length. | ||
11784 | + */ | ||
11785 | +static int ccs_print_ipv4(char *buffer, const unsigned int buffer_len, | ||
11786 | + const u32 *ip) | ||
11787 | +{ | ||
11788 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | ||
11789 | + return snprintf(buffer, buffer_len, "%pI4", ip); | ||
11790 | +#else | ||
11791 | + char addr[sizeof("255.255.255.255")]; | ||
11792 | + ip4_string(addr, (const u8 *) ip); | ||
11793 | + return snprintf(buffer, buffer_len, "%s", addr); | ||
11794 | +#endif | ||
11795 | +} | ||
11796 | + | ||
11797 | +/** | ||
11798 | + * ccs_print_ipv6 - Print an IPv6 address. | ||
11799 | + * | ||
11800 | + * @buffer: Buffer to write to. | ||
11801 | + * @buffer_len: Size of @buffer. | ||
11802 | + * @ip: Pointer to "struct in6_addr". | ||
11803 | + * | ||
11804 | + * Returns written length. | ||
11805 | + */ | ||
11806 | +static int ccs_print_ipv6(char *buffer, const unsigned int buffer_len, | ||
11807 | + const struct in6_addr *ip) | ||
11808 | +{ | ||
11809 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | ||
11810 | + return snprintf(buffer, buffer_len, "%pI6c", ip); | ||
11811 | +#else | ||
11812 | + char addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")]; | ||
11813 | + ip6_compressed_string(addr, (const u8 *) ip); | ||
11814 | + return snprintf(buffer, buffer_len, "%s", addr); | ||
11815 | +#endif | ||
11816 | +} | ||
11817 | + | ||
11818 | +/** | ||
11819 | + * ccs_print_ip - Print an IP address. | ||
11820 | + * | ||
11821 | + * @buf: Buffer to write to. | ||
11822 | + * @size: Size of @buf. | ||
11823 | + * @ptr: Pointer to "struct ipaddr_union". | ||
11824 | + * | ||
11825 | + * Returns nothing. | ||
11826 | + */ | ||
11827 | +static void ccs_print_ip(char *buf, const unsigned int size, | ||
11828 | + const struct ccs_ipaddr_union *ptr) | ||
11829 | +{ | ||
11830 | + int len; | ||
11831 | + if (ptr->is_ipv6) | ||
11832 | + len = ccs_print_ipv6(buf, size, &ptr->ip[0]); | ||
11833 | + else | ||
11834 | + len = ccs_print_ipv4(buf, size, &ptr->ip[0].s6_addr32[0]); | ||
11835 | + if (!memcmp(&ptr->ip[0], &ptr->ip[1], 16) || len >= size / 2) | ||
11836 | + return; | ||
11837 | + buf[len++] = '-'; | ||
11838 | + if (ptr->is_ipv6) | ||
11839 | + ccs_print_ipv6(buf + len, size - len, &ptr->ip[1]); | ||
11840 | + else | ||
11841 | + ccs_print_ipv4(buf + len, size - len, | ||
11842 | + &ptr->ip[1].s6_addr32[0]); | ||
11843 | +} | ||
11844 | + | ||
11845 | +#endif | ||
11846 | + | ||
11847 | +/***** SECTION5: Variables definition section *****/ | ||
11848 | + | ||
11849 | +/* Permit policy management by non-root user? */ | ||
11850 | +static bool ccs_manage_by_non_root; | ||
11851 | + | ||
11852 | +/* Lock for protecting policy. */ | ||
11853 | +DEFINE_MUTEX(ccs_policy_lock); | ||
11854 | + | ||
11855 | +/* Has /sbin/init started? */ | ||
11856 | +bool ccs_policy_loaded; | ||
11857 | + | ||
11858 | +/* List of namespaces. */ | ||
11859 | +LIST_HEAD(ccs_namespace_list); | ||
11860 | +/* True if namespace other than ccs_kernel_namespace is defined. */ | ||
11861 | +static bool ccs_namespace_enabled; | ||
11862 | + | ||
11863 | +/* Initial namespace.*/ | ||
11864 | +static struct ccs_policy_namespace ccs_kernel_namespace; | ||
11865 | + | ||
11866 | +/* List of "struct ccs_condition". */ | ||
11867 | +LIST_HEAD(ccs_condition_list); | ||
11868 | + | ||
11869 | +#ifdef CONFIG_CCSECURITY_PORTRESERVE | ||
11870 | +/* Bitmap for reserved local port numbers.*/ | ||
11871 | +static u8 ccs_reserved_port_map[8192]; | ||
11872 | +#endif | ||
11873 | + | ||
11874 | +/* Wait queue for kernel -> userspace notification. */ | ||
11875 | +static DECLARE_WAIT_QUEUE_HEAD(ccs_query_wait); | ||
11876 | +/* Wait queue for userspace -> kernel notification. */ | ||
11877 | +static DECLARE_WAIT_QUEUE_HEAD(ccs_answer_wait); | ||
11878 | + | ||
11879 | +/* The list for "struct ccs_query". */ | ||
11880 | +static LIST_HEAD(ccs_query_list); | ||
11881 | + | ||
11882 | +/* Lock for manipulating ccs_query_list. */ | ||
11883 | +static DEFINE_SPINLOCK(ccs_query_list_lock); | ||
11884 | + | ||
11885 | +/* Number of "struct file" referring /proc/ccs/query interface. */ | ||
11886 | +static atomic_t ccs_query_observers = ATOMIC_INIT(0); | ||
11887 | + | ||
11888 | +/* Wait queue for /proc/ccs/audit. */ | ||
11889 | +static DECLARE_WAIT_QUEUE_HEAD(ccs_log_wait); | ||
11890 | + | ||
11891 | +/* The list for "struct ccs_log". */ | ||
11892 | +static LIST_HEAD(ccs_log); | ||
11893 | + | ||
11894 | +/* Lock for "struct list_head ccs_log". */ | ||
11895 | +static DEFINE_SPINLOCK(ccs_log_lock); | ||
11896 | + | ||
11897 | +/* Length of "stuct list_head ccs_log". */ | ||
11898 | +static unsigned int ccs_log_count; | ||
11899 | + | ||
11900 | +/* Timestamp counter for last updated. */ | ||
11901 | +static unsigned int ccs_stat_updated[CCS_MAX_POLICY_STAT]; | ||
11902 | + | ||
11903 | +/* Counter for number of updates. */ | ||
11904 | +static unsigned int ccs_stat_modified[CCS_MAX_POLICY_STAT]; | ||
11905 | + | ||
11906 | +/* Operations for /proc/ccs/self_domain interface. */ | ||
11907 | +static | ||
11908 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) | ||
11909 | +const | ||
11910 | +#endif | ||
11911 | +struct file_operations ccs_self_operations = { | ||
11912 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
11913 | + .write = ccs_write_self, | ||
11914 | +#endif | ||
11915 | + .read = ccs_read_self, | ||
11916 | +}; | ||
11917 | + | ||
11918 | +/* Operations for /proc/ccs/ interface. */ | ||
11919 | +static | ||
11920 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) | ||
11921 | +const | ||
11922 | +#endif | ||
11923 | +struct file_operations ccs_operations = { | ||
11924 | + .open = ccs_open, | ||
11925 | + .release = ccs_release, | ||
11926 | + .poll = ccs_poll, | ||
11927 | + .read = ccs_read, | ||
11928 | + .write = ccs_write, | ||
11929 | +}; | ||
11930 | + | ||
11931 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
11932 | + | ||
11933 | +/* The inode operations for /proc/ccs/ directory. */ | ||
11934 | +static struct inode_operations ccs_dir_inode_operations; | ||
11935 | + | ||
11936 | +/* The inode operations for files under /proc/ccs/ directory. */ | ||
11937 | +static struct inode_operations ccs_file_inode_operations; | ||
11938 | + | ||
11939 | +#endif | ||
11940 | + | ||
11941 | +/***** SECTION6: Dependent functions section *****/ | ||
11942 | + | ||
11943 | +/** | ||
11944 | + * list_for_each_cookie - iterate over a list with cookie. | ||
11945 | + * | ||
11946 | + * @pos: Pointer to "struct list_head". | ||
11947 | + * @head: Pointer to "struct list_head". | ||
11948 | + */ | ||
11949 | +#define list_for_each_cookie(pos, head) \ | ||
11950 | + for (pos = pos ? pos : srcu_dereference((head)->next, &ccs_ss); \ | ||
11951 | + pos != (head); pos = srcu_dereference(pos->next, &ccs_ss)) | ||
11952 | + | ||
11953 | +/** | ||
11954 | + * ccs_read_token - Read a word from a line. | ||
11955 | + * | ||
11956 | + * @param: Pointer to "struct ccs_acl_param". | ||
11957 | + * | ||
11958 | + * Returns a word on success, "" otherwise. | ||
11959 | + * | ||
11960 | + * To allow the caller to skip NULL check, this function returns "" rather than | ||
11961 | + * NULL if there is no more words to read. | ||
11962 | + */ | ||
11963 | +static char *ccs_read_token(struct ccs_acl_param *param) | ||
11964 | +{ | ||
11965 | + char *pos = param->data; | ||
11966 | + char *del = strchr(pos, ' '); | ||
11967 | + if (del) | ||
11968 | + *del++ = '\0'; | ||
11969 | + else | ||
11970 | + del = pos + strlen(pos); | ||
11971 | + param->data = del; | ||
11972 | + return pos; | ||
11973 | +} | ||
11974 | + | ||
11975 | +/** | ||
11976 | + * ccs_make_byte - Make byte value from three octal characters. | ||
11977 | + * | ||
11978 | + * @c1: The first character. | ||
11979 | + * @c2: The second character. | ||
11980 | + * @c3: The third character. | ||
11981 | + * | ||
11982 | + * Returns byte value. | ||
11983 | + */ | ||
11984 | +static u8 ccs_make_byte(const u8 c1, const u8 c2, const u8 c3) | ||
11985 | +{ | ||
11986 | + return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); | ||
11987 | +} | ||
11988 | + | ||
11989 | +/** | ||
11990 | + * ccs_correct_word2 - Check whether the given string follows the naming rules. | ||
11991 | + * | ||
11992 | + * @string: The byte sequence to check. Not '\0'-terminated. | ||
11993 | + * @len: Length of @string. | ||
11994 | + * | ||
11995 | + * Returns true if @string follows the naming rules, false otherwise. | ||
11996 | + */ | ||
11997 | +static bool ccs_correct_word2(const char *string, size_t len) | ||
11998 | +{ | ||
11999 | + const char *const start = string; | ||
12000 | + bool in_repetition = false; | ||
12001 | + unsigned char c; | ||
12002 | + unsigned char d; | ||
12003 | + unsigned char e; | ||
12004 | + if (!len) | ||
12005 | + goto out; | ||
12006 | + while (len--) { | ||
12007 | + c = *string++; | ||
12008 | + if (c == '\\') { | ||
12009 | + if (!len--) | ||
12010 | + goto out; | ||
12011 | + c = *string++; | ||
12012 | + switch (c) { | ||
12013 | + case '\\': /* "\\" */ | ||
12014 | + continue; | ||
12015 | + case '$': /* "\$" */ | ||
12016 | + case '+': /* "\+" */ | ||
12017 | + case '?': /* "\?" */ | ||
12018 | + case '*': /* "\*" */ | ||
12019 | + case '@': /* "\@" */ | ||
12020 | + case 'x': /* "\x" */ | ||
12021 | + case 'X': /* "\X" */ | ||
12022 | + case 'a': /* "\a" */ | ||
12023 | + case 'A': /* "\A" */ | ||
12024 | + case '-': /* "\-" */ | ||
12025 | + continue; | ||
12026 | + case '{': /* "/\{" */ | ||
12027 | + if (string - 3 < start || *(string - 3) != '/') | ||
12028 | + break; | ||
12029 | + in_repetition = true; | ||
12030 | + continue; | ||
12031 | + case '}': /* "\}/" */ | ||
12032 | + if (*string != '/') | ||
12033 | + break; | ||
12034 | + if (!in_repetition) | ||
12035 | + break; | ||
12036 | + in_repetition = false; | ||
12037 | + continue; | ||
12038 | + case '0': /* "\ooo" */ | ||
12039 | + case '1': | ||
12040 | + case '2': | ||
12041 | + case '3': | ||
12042 | + if (!len-- || !len--) | ||
12043 | + break; | ||
12044 | + d = *string++; | ||
12045 | + e = *string++; | ||
12046 | + if (d < '0' || d > '7' || e < '0' || e > '7') | ||
12047 | + break; | ||
12048 | + c = ccs_make_byte(c, d, e); | ||
12049 | + if (c <= ' ' || c >= 127) | ||
12050 | + continue; | ||
12051 | + } | ||
12052 | + goto out; | ||
12053 | + } else if (in_repetition && c == '/') { | ||
12054 | + goto out; | ||
12055 | + } else if (c <= ' ' || c >= 127) { | ||
12056 | + goto out; | ||
12057 | + } | ||
12058 | + } | ||
12059 | + if (in_repetition) | ||
12060 | + goto out; | ||
12061 | + return true; | ||
12062 | +out: | ||
12063 | + return false; | ||
12064 | +} | ||
12065 | + | ||
12066 | +/** | ||
12067 | + * ccs_correct_word - Check whether the given string follows the naming rules. | ||
12068 | + * | ||
12069 | + * @string: The string to check. | ||
12070 | + * | ||
12071 | + * Returns true if @string follows the naming rules, false otherwise. | ||
12072 | + */ | ||
12073 | +static bool ccs_correct_word(const char *string) | ||
12074 | +{ | ||
12075 | + return ccs_correct_word2(string, strlen(string)); | ||
12076 | +} | ||
12077 | + | ||
12078 | +/** | ||
12079 | + * ccs_get_group - Allocate memory for "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group". | ||
12080 | + * | ||
12081 | + * @param: Pointer to "struct ccs_acl_param". | ||
12082 | + * @idx: Index number. | ||
12083 | + * | ||
12084 | + * Returns pointer to "struct ccs_group" on success, NULL otherwise. | ||
12085 | + */ | ||
12086 | +static struct ccs_group *ccs_get_group(struct ccs_acl_param *param, | ||
12087 | + const u8 idx) | ||
12088 | +{ | ||
12089 | + struct ccs_group e = { }; | ||
12090 | + struct ccs_group *group = NULL; | ||
12091 | + struct list_head *list; | ||
12092 | + const char *group_name = ccs_read_token(param); | ||
12093 | + bool found = false; | ||
12094 | + if (!ccs_correct_word(group_name) || idx >= CCS_MAX_GROUP) | ||
12095 | + return NULL; | ||
12096 | + e.group_name = ccs_get_name(group_name); | ||
12097 | + if (!e.group_name) | ||
12098 | + return NULL; | ||
12099 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | ||
12100 | + goto out; | ||
12101 | + list = ¶m->ns->group_list[idx]; | ||
12102 | + list_for_each_entry(group, list, head.list) { | ||
12103 | + if (e.group_name != group->group_name || | ||
12104 | + atomic_read(&group->head.users) == CCS_GC_IN_PROGRESS) | ||
12105 | + continue; | ||
12106 | + atomic_inc(&group->head.users); | ||
12107 | + found = true; | ||
12108 | + break; | ||
12109 | + } | ||
12110 | + if (!found) { | ||
12111 | + struct ccs_group *entry = ccs_commit_ok(&e, sizeof(e)); | ||
12112 | + if (entry) { | ||
12113 | + INIT_LIST_HEAD(&entry->member_list); | ||
12114 | + atomic_set(&entry->head.users, 1); | ||
12115 | + list_add_tail_rcu(&entry->head.list, list); | ||
12116 | + group = entry; | ||
12117 | + found = true; | ||
12118 | + } | ||
12119 | + } | ||
12120 | + mutex_unlock(&ccs_policy_lock); | ||
12121 | +out: | ||
12122 | + ccs_put_name(e.group_name); | ||
12123 | + return found ? group : NULL; | ||
12124 | +} | ||
12125 | + | ||
12126 | +/** | ||
12127 | + * ccs_parse_name_union - Parse a ccs_name_union. | ||
12128 | + * | ||
12129 | + * @param: Pointer to "struct ccs_acl_param". | ||
12130 | + * @ptr: Pointer to "struct ccs_name_union". | ||
12131 | + * | ||
12132 | + * Returns true on success, false otherwise. | ||
12133 | + */ | ||
12134 | +static bool ccs_parse_name_union(struct ccs_acl_param *param, | ||
12135 | + struct ccs_name_union *ptr) | ||
12136 | +{ | ||
12137 | + char *filename; | ||
12138 | + if (param->data[0] == '@') { | ||
12139 | + param->data++; | ||
12140 | + ptr->group = ccs_get_group(param, CCS_PATH_GROUP); | ||
12141 | + return ptr->group != NULL; | ||
12142 | + } | ||
12143 | + filename = ccs_read_token(param); | ||
12144 | + if (!ccs_correct_word(filename)) | ||
12145 | + return false; | ||
12146 | + ptr->filename = ccs_get_name(filename); | ||
12147 | + return ptr->filename != NULL; | ||
12148 | +} | ||
12149 | + | ||
12150 | +/** | ||
12151 | + * ccs_parse_ulong - Parse an "unsigned long" value. | ||
12152 | + * | ||
12153 | + * @result: Pointer to "unsigned long". | ||
12154 | + * @str: Pointer to string to parse. | ||
12155 | + * | ||
12156 | + * Returns one of values in "enum ccs_value_type". | ||
12157 | + * | ||
12158 | + * The @src is updated to point the first character after the value | ||
12159 | + * on success. | ||
12160 | + */ | ||
12161 | +static u8 ccs_parse_ulong(unsigned long *result, char **str) | ||
12162 | +{ | ||
12163 | + const char *cp = *str; | ||
12164 | + char *ep; | ||
12165 | + int base = 10; | ||
12166 | + if (*cp == '0') { | ||
12167 | + char c = *(cp + 1); | ||
12168 | + if (c == 'x' || c == 'X') { | ||
12169 | + base = 16; | ||
12170 | + cp += 2; | ||
12171 | + } else if (c >= '0' && c <= '7') { | ||
12172 | + base = 8; | ||
12173 | + cp++; | ||
12174 | + } | ||
12175 | + } | ||
12176 | + *result = simple_strtoul(cp, &ep, base); | ||
12177 | + if (cp == ep) | ||
12178 | + return CCS_VALUE_TYPE_INVALID; | ||
12179 | + *str = ep; | ||
12180 | + switch (base) { | ||
12181 | + case 16: | ||
12182 | + return CCS_VALUE_TYPE_HEXADECIMAL; | ||
12183 | + case 8: | ||
12184 | + return CCS_VALUE_TYPE_OCTAL; | ||
12185 | + default: | ||
12186 | + return CCS_VALUE_TYPE_DECIMAL; | ||
12187 | + } | ||
12188 | +} | ||
12189 | + | ||
12190 | +/** | ||
12191 | + * ccs_parse_number_union - Parse a ccs_number_union. | ||
12192 | + * | ||
12193 | + * @param: Pointer to "struct ccs_acl_param". | ||
12194 | + * @ptr: Pointer to "struct ccs_number_union". | ||
12195 | + * | ||
12196 | + * Returns true on success, false otherwise. | ||
12197 | + */ | ||
12198 | +static bool ccs_parse_number_union(struct ccs_acl_param *param, | ||
12199 | + struct ccs_number_union *ptr) | ||
12200 | +{ | ||
12201 | + char *data; | ||
12202 | + u8 type; | ||
12203 | + unsigned long v; | ||
12204 | + memset(ptr, 0, sizeof(*ptr)); | ||
12205 | + if (param->data[0] == '@') { | ||
12206 | + param->data++; | ||
12207 | + ptr->group = ccs_get_group(param, CCS_NUMBER_GROUP); | ||
12208 | + return ptr->group != NULL; | ||
12209 | + } | ||
12210 | + data = ccs_read_token(param); | ||
12211 | + type = ccs_parse_ulong(&v, &data); | ||
12212 | + if (type == CCS_VALUE_TYPE_INVALID) | ||
12213 | + return false; | ||
12214 | + ptr->values[0] = v; | ||
12215 | + ptr->value_type[0] = type; | ||
12216 | + if (!*data) { | ||
12217 | + ptr->values[1] = v; | ||
12218 | + ptr->value_type[1] = type; | ||
12219 | + return true; | ||
12220 | + } | ||
12221 | + if (*data++ != '-') | ||
12222 | + return false; | ||
12223 | + type = ccs_parse_ulong(&v, &data); | ||
12224 | + if (type == CCS_VALUE_TYPE_INVALID || *data || ptr->values[0] > v) | ||
12225 | + return false; | ||
12226 | + ptr->values[1] = v; | ||
12227 | + ptr->value_type[1] = type; | ||
12228 | + return true; | ||
12229 | +} | ||
12230 | + | ||
12231 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
12232 | + | ||
12233 | +/** | ||
12234 | + * ccs_parse_ipaddr_union - Parse an IP address. | ||
12235 | + * | ||
12236 | + * @param: Pointer to "struct ccs_acl_param". | ||
12237 | + * @ptr: Pointer to "struct ccs_ipaddr_union". | ||
12238 | + * | ||
12239 | + * Returns true on success, false otherwise. | ||
12240 | + */ | ||
12241 | +static bool ccs_parse_ipaddr_union(struct ccs_acl_param *param, | ||
12242 | + struct ccs_ipaddr_union *ptr) | ||
12243 | +{ | ||
12244 | + u8 * const min = ptr->ip[0].in6_u.u6_addr8; | ||
12245 | + u8 * const max = ptr->ip[1].in6_u.u6_addr8; | ||
12246 | + char *address = ccs_read_token(param); | ||
12247 | + const char *end; | ||
12248 | + if (!strchr(address, ':') && | ||
12249 | + ccs_in4_pton(address, -1, min, '-', &end) > 0) { | ||
12250 | + ptr->is_ipv6 = false; | ||
12251 | + if (!*end) | ||
12252 | + ptr->ip[1].s6_addr32[0] = ptr->ip[0].s6_addr32[0]; | ||
12253 | + else if (*end++ != '-' || | ||
12254 | + ccs_in4_pton(end, -1, max, '\0', &end) <= 0 || *end) | ||
12255 | + return false; | ||
12256 | + return true; | ||
12257 | + } | ||
12258 | + if (ccs_in6_pton(address, -1, min, '-', &end) > 0) { | ||
12259 | + ptr->is_ipv6 = true; | ||
12260 | + if (!*end) | ||
12261 | + memmove(max, min, sizeof(u16) * 8); | ||
12262 | + else if (*end++ != '-' || | ||
12263 | + ccs_in6_pton(end, -1, max, '\0', &end) <= 0 || *end) | ||
12264 | + return false; | ||
12265 | + return true; | ||
12266 | + } | ||
12267 | + return false; | ||
12268 | +} | ||
12269 | + | ||
12270 | +#endif | ||
12271 | + | ||
12272 | +/** | ||
12273 | + * ccs_get_dqword - ccs_get_name() for a quoted string. | ||
12274 | + * | ||
12275 | + * @start: String to save. | ||
12276 | + * | ||
12277 | + * Returns pointer to "struct ccs_path_info" on success, NULL otherwise. | ||
12278 | + */ | ||
12279 | +static const struct ccs_path_info *ccs_get_dqword(char *start) | ||
12280 | +{ | ||
12281 | + char *cp = start + strlen(start) - 1; | ||
12282 | + if (cp == start || *start++ != '"' || *cp != '"') | ||
12283 | + return NULL; | ||
12284 | + *cp = '\0'; | ||
12285 | + if (*start && !ccs_correct_word(start)) | ||
12286 | + return NULL; | ||
12287 | + return ccs_get_name(start); | ||
12288 | +} | ||
12289 | + | ||
12290 | +/** | ||
12291 | + * ccs_parse_name_union_quoted - Parse a quoted word. | ||
12292 | + * | ||
12293 | + * @param: Pointer to "struct ccs_acl_param". | ||
12294 | + * @ptr: Pointer to "struct ccs_name_union". | ||
12295 | + * | ||
12296 | + * Returns true on success, false otherwise. | ||
12297 | + */ | ||
12298 | +static bool ccs_parse_name_union_quoted(struct ccs_acl_param *param, | ||
12299 | + struct ccs_name_union *ptr) | ||
12300 | +{ | ||
12301 | + char *filename = param->data; | ||
12302 | + if (*filename == '@') | ||
12303 | + return ccs_parse_name_union(param, ptr); | ||
12304 | + ptr->filename = ccs_get_dqword(filename); | ||
12305 | + return ptr->filename != NULL; | ||
12306 | +} | ||
12307 | + | ||
12308 | +/** | ||
12309 | + * ccs_parse_argv - Parse an argv[] condition part. | ||
12310 | + * | ||
12311 | + * @left: Lefthand value. | ||
12312 | + * @right: Righthand value. | ||
12313 | + * @argv: Pointer to "struct ccs_argv". | ||
12314 | + * | ||
12315 | + * Returns true on success, false otherwise. | ||
12316 | + */ | ||
12317 | +static bool ccs_parse_argv(char *left, char *right, struct ccs_argv *argv) | ||
12318 | +{ | ||
12319 | + if (ccs_parse_ulong(&argv->index, &left) != CCS_VALUE_TYPE_DECIMAL || | ||
12320 | + *left++ != ']' || *left) | ||
12321 | + return false; | ||
12322 | + argv->value = ccs_get_dqword(right); | ||
12323 | + return argv->value != NULL; | ||
12324 | +} | ||
12325 | + | ||
12326 | +/** | ||
12327 | + * ccs_parse_envp - Parse an envp[] condition part. | ||
12328 | + * | ||
12329 | + * @left: Lefthand value. | ||
12330 | + * @right: Righthand value. | ||
12331 | + * @envp: Pointer to "struct ccs_envp". | ||
12332 | + * | ||
12333 | + * Returns true on success, false otherwise. | ||
12334 | + */ | ||
12335 | +static bool ccs_parse_envp(char *left, char *right, struct ccs_envp *envp) | ||
12336 | +{ | ||
12337 | + const struct ccs_path_info *name; | ||
12338 | + const struct ccs_path_info *value; | ||
12339 | + char *cp = left + strlen(left) - 1; | ||
12340 | + if (*cp-- != ']' || *cp != '"') | ||
12341 | + goto out; | ||
12342 | + *cp = '\0'; | ||
12343 | + if (!ccs_correct_word(left)) | ||
12344 | + goto out; | ||
12345 | + name = ccs_get_name(left); | ||
12346 | + if (!name) | ||
12347 | + goto out; | ||
12348 | + if (!strcmp(right, "NULL")) { | ||
12349 | + value = NULL; | ||
12350 | + } else { | ||
12351 | + value = ccs_get_dqword(right); | ||
12352 | + if (!value) { | ||
12353 | + ccs_put_name(name); | ||
12354 | + goto out; | ||
12355 | + } | ||
12356 | + } | ||
12357 | + envp->name = name; | ||
12358 | + envp->value = value; | ||
12359 | + return true; | ||
12360 | +out: | ||
12361 | + return false; | ||
12362 | +} | ||
12363 | + | ||
12364 | +/** | ||
12365 | + * ccs_same_condition - Check for duplicated "struct ccs_condition" entry. | ||
12366 | + * | ||
12367 | + * @a: Pointer to "struct ccs_condition". | ||
12368 | + * @b: Pointer to "struct ccs_condition". | ||
12369 | + * | ||
12370 | + * Returns true if @a == @b, false otherwise. | ||
12371 | + */ | ||
12372 | +static bool ccs_same_condition(const struct ccs_condition *a, | ||
12373 | + const struct ccs_condition *b) | ||
12374 | +{ | ||
12375 | + return a->size == b->size && a->condc == b->condc && | ||
12376 | + a->numbers_count == b->numbers_count && | ||
12377 | + a->names_count == b->names_count && | ||
12378 | + a->argc == b->argc && a->envc == b->envc && | ||
12379 | + a->grant_log == b->grant_log && | ||
12380 | + a->exec_transit == b->exec_transit && a->transit == b->transit | ||
12381 | + && !memcmp(a + 1, b + 1, a->size - sizeof(*a)); | ||
12382 | +} | ||
12383 | + | ||
12384 | +/** | ||
12385 | + * ccs_condition_type - Get condition type. | ||
12386 | + * | ||
12387 | + * @word: Keyword string. | ||
12388 | + * | ||
12389 | + * Returns one of values in "enum ccs_conditions_index" on success, | ||
12390 | + * CCS_MAX_CONDITION_KEYWORD otherwise. | ||
12391 | + */ | ||
12392 | +static u8 ccs_condition_type(const char *word) | ||
12393 | +{ | ||
12394 | + u8 i; | ||
12395 | + for (i = 0; i < CCS_MAX_CONDITION_KEYWORD; i++) { | ||
12396 | + if (!strcmp(word, ccs_condition_keyword[i])) | ||
12397 | + break; | ||
12398 | + } | ||
12399 | + return i; | ||
12400 | +} | ||
12401 | + | ||
12402 | +/** | ||
12403 | + * ccs_commit_condition - Commit "struct ccs_condition". | ||
12404 | + * | ||
12405 | + * @entry: Pointer to "struct ccs_condition". | ||
12406 | + * | ||
12407 | + * Returns pointer to "struct ccs_condition" on success, NULL otherwise. | ||
12408 | + * | ||
12409 | + * This function merges duplicated entries. This function returns NULL if | ||
12410 | + * @entry is not duplicated but memory quota for policy has exceeded. | ||
12411 | + */ | ||
12412 | +static struct ccs_condition *ccs_commit_condition(struct ccs_condition *entry) | ||
12413 | +{ | ||
12414 | + struct ccs_condition *ptr; | ||
12415 | + bool found = false; | ||
12416 | + if (mutex_lock_interruptible(&ccs_policy_lock)) { | ||
12417 | + dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__); | ||
12418 | + ptr = NULL; | ||
12419 | + found = true; | ||
12420 | + goto out; | ||
12421 | + } | ||
12422 | + list_for_each_entry(ptr, &ccs_condition_list, head.list) { | ||
12423 | + if (!ccs_same_condition(ptr, entry) || | ||
12424 | + atomic_read(&ptr->head.users) == CCS_GC_IN_PROGRESS) | ||
12425 | + continue; | ||
12426 | + /* Same entry found. Share this entry. */ | ||
12427 | + atomic_inc(&ptr->head.users); | ||
12428 | + found = true; | ||
12429 | + break; | ||
12430 | + } | ||
12431 | + if (!found) { | ||
12432 | + if (ccs_memory_ok(entry, entry->size)) { | ||
12433 | + atomic_set(&entry->head.users, 1); | ||
12434 | + list_add(&entry->head.list, &ccs_condition_list); | ||
12435 | + } else { | ||
12436 | + found = true; | ||
12437 | + ptr = NULL; | ||
12438 | + } | ||
12439 | + } | ||
12440 | + mutex_unlock(&ccs_policy_lock); | ||
12441 | +out: | ||
12442 | + if (found) { | ||
12443 | + ccs_del_condition(&entry->head.list); | ||
12444 | + kfree(entry); | ||
12445 | + entry = ptr; | ||
12446 | + } | ||
12447 | + return entry; | ||
12448 | +} | ||
12449 | + | ||
12450 | +/** | ||
12451 | + * ccs_correct_path - Check whether the given pathname follows the naming rules. | ||
12452 | + * | ||
12453 | + * @filename: The pathname to check. | ||
12454 | + * | ||
12455 | + * Returns true if @filename follows the naming rules, false otherwise. | ||
12456 | + */ | ||
12457 | +static bool ccs_correct_path(const char *filename) | ||
12458 | +{ | ||
12459 | + return *filename == '/' && ccs_correct_word(filename); | ||
12460 | +} | ||
12461 | + | ||
12462 | +/** | ||
12463 | + * ccs_domain_def - Check whether the given token can be a domainname. | ||
12464 | + * | ||
12465 | + * @buffer: The token to check. | ||
12466 | + * | ||
12467 | + * Returns true if @buffer possibly be a domainname, false otherwise. | ||
12468 | + */ | ||
12469 | +static bool ccs_domain_def(const unsigned char *buffer) | ||
12470 | +{ | ||
12471 | + const unsigned char *cp; | ||
12472 | + int len; | ||
12473 | + if (*buffer != '<') | ||
12474 | + return false; | ||
12475 | + cp = strchr(buffer, ' '); | ||
12476 | + if (!cp) | ||
12477 | + len = strlen(buffer); | ||
12478 | + else | ||
12479 | + len = cp - buffer; | ||
12480 | + if (buffer[len - 1] != '>' || !ccs_correct_word2(buffer + 1, len - 2)) | ||
12481 | + return false; | ||
12482 | + return true; | ||
12483 | +} | ||
12484 | + | ||
12485 | +/** | ||
12486 | + * ccs_correct_domain - Check whether the given domainname follows the naming rules. | ||
12487 | + * | ||
12488 | + * @domainname: The domainname to check. | ||
12489 | + * | ||
12490 | + * Returns true if @domainname follows the naming rules, false otherwise. | ||
12491 | + */ | ||
12492 | +static bool ccs_correct_domain(const unsigned char *domainname) | ||
12493 | +{ | ||
12494 | + if (!domainname || !ccs_domain_def(domainname)) | ||
12495 | + return false; | ||
12496 | + domainname = strchr(domainname, ' '); | ||
12497 | + if (!domainname++) | ||
12498 | + return true; | ||
12499 | + while (1) { | ||
12500 | + const unsigned char *cp = strchr(domainname, ' '); | ||
12501 | + if (!cp) | ||
12502 | + break; | ||
12503 | + if (*domainname != '/' || | ||
12504 | + !ccs_correct_word2(domainname, cp - domainname)) | ||
12505 | + return false; | ||
12506 | + domainname = cp + 1; | ||
12507 | + } | ||
12508 | + return ccs_correct_path(domainname); | ||
12509 | +} | ||
12510 | + | ||
12511 | +/** | ||
12512 | + * ccs_normalize_line - Format string. | ||
12513 | + * | ||
12514 | + * @buffer: The line to normalize. | ||
12515 | + * | ||
12516 | + * Returns nothing. | ||
12517 | + * | ||
12518 | + * Leading and trailing whitespaces are removed. | ||
12519 | + * Multiple whitespaces are packed into single space. | ||
12520 | + */ | ||
12521 | +static void ccs_normalize_line(unsigned char *buffer) | ||
12522 | +{ | ||
12523 | + unsigned char *sp = buffer; | ||
12524 | + unsigned char *dp = buffer; | ||
12525 | + bool first = true; | ||
12526 | + while (*sp && (*sp <= ' ' || *sp >= 127)) | ||
12527 | + sp++; | ||
12528 | + while (*sp) { | ||
12529 | + if (!first) | ||
12530 | + *dp++ = ' '; | ||
12531 | + first = false; | ||
12532 | + while (*sp > ' ' && *sp < 127) | ||
12533 | + *dp++ = *sp++; | ||
12534 | + while (*sp && (*sp <= ' ' || *sp >= 127)) | ||
12535 | + sp++; | ||
12536 | + } | ||
12537 | + *dp = '\0'; | ||
12538 | +} | ||
12539 | + | ||
12540 | +/** | ||
12541 | + * ccs_get_domainname - Read a domainname from a line. | ||
12542 | + * | ||
12543 | + * @param: Pointer to "struct ccs_acl_param". | ||
12544 | + * | ||
12545 | + * Returns a domainname on success, NULL otherwise. | ||
12546 | + */ | ||
12547 | +static const struct ccs_path_info *ccs_get_domainname | ||
12548 | +(struct ccs_acl_param *param) | ||
12549 | +{ | ||
12550 | + char *start = param->data; | ||
12551 | + char *pos = start; | ||
12552 | + while (*pos) { | ||
12553 | + if (*pos++ != ' ' || *pos++ == '/') | ||
12554 | + continue; | ||
12555 | + pos -= 2; | ||
12556 | + *pos++ = '\0'; | ||
12557 | + break; | ||
12558 | + } | ||
12559 | + param->data = pos; | ||
12560 | + if (ccs_correct_domain(start)) | ||
12561 | + return ccs_get_name(start); | ||
12562 | + return NULL; | ||
12563 | +} | ||
12564 | + | ||
12565 | +/** | ||
12566 | + * ccs_get_transit_preference - Parse domain transition preference for execve(). | ||
12567 | + * | ||
12568 | + * @param: Pointer to "struct ccs_acl_param". | ||
12569 | + * @e: Pointer to "struct ccs_condition". | ||
12570 | + * | ||
12571 | + * Returns the condition string part. | ||
12572 | + */ | ||
12573 | +static char *ccs_get_transit_preference(struct ccs_acl_param *param, | ||
12574 | + struct ccs_condition *e) | ||
12575 | +{ | ||
12576 | + char * const pos = param->data; | ||
12577 | + bool flag; | ||
12578 | + if (*pos == '<') { | ||
12579 | + e->transit = ccs_get_domainname(param); | ||
12580 | + goto done; | ||
12581 | + } | ||
12582 | + { | ||
12583 | + char *cp = strchr(pos, ' '); | ||
12584 | + if (cp) | ||
12585 | + *cp = '\0'; | ||
12586 | + flag = ccs_correct_path(pos) || !strcmp(pos, "keep") || | ||
12587 | + !strcmp(pos, "initialize") || !strcmp(pos, "reset") || | ||
12588 | + !strcmp(pos, "child") || !strcmp(pos, "parent"); | ||
12589 | + if (cp) | ||
12590 | + *cp = ' '; | ||
12591 | + } | ||
12592 | + if (!flag) | ||
12593 | + return pos; | ||
12594 | + e->transit = ccs_get_name(ccs_read_token(param)); | ||
12595 | +done: | ||
12596 | + if (e->transit) { | ||
12597 | + e->exec_transit = true; | ||
12598 | + return param->data; | ||
12599 | + } | ||
12600 | + /* | ||
12601 | + * Return a bad read-only condition string that will let | ||
12602 | + * ccs_get_condition() return NULL. | ||
12603 | + */ | ||
12604 | + return "/"; | ||
12605 | +} | ||
12606 | + | ||
12607 | +/** | ||
12608 | + * ccs_get_condition - Parse condition part. | ||
12609 | + * | ||
12610 | + * @param: Pointer to "struct ccs_acl_param". | ||
12611 | + * | ||
12612 | + * Returns pointer to "struct ccs_condition" on success, NULL otherwise. | ||
12613 | + */ | ||
12614 | +struct ccs_condition *ccs_get_condition(struct ccs_acl_param *param) | ||
12615 | +{ | ||
12616 | + struct ccs_condition *entry = NULL; | ||
12617 | + struct ccs_condition_element *condp = NULL; | ||
12618 | + struct ccs_number_union *numbers_p = NULL; | ||
12619 | + struct ccs_name_union *names_p = NULL; | ||
12620 | + struct ccs_argv *argv = NULL; | ||
12621 | + struct ccs_envp *envp = NULL; | ||
12622 | + struct ccs_condition e = { }; | ||
12623 | + char * const start_of_string = ccs_get_transit_preference(param, &e); | ||
12624 | + char * const end_of_string = start_of_string + strlen(start_of_string); | ||
12625 | + char *pos; | ||
12626 | +rerun: | ||
12627 | + pos = start_of_string; | ||
12628 | + while (1) { | ||
12629 | + u8 left = -1; | ||
12630 | + u8 right = -1; | ||
12631 | + char *left_word = pos; | ||
12632 | + char *cp; | ||
12633 | + char *right_word; | ||
12634 | + bool is_not; | ||
12635 | + if (!*left_word) | ||
12636 | + break; | ||
12637 | + /* | ||
12638 | + * Since left-hand condition does not allow use of "path_group" | ||
12639 | + * or "number_group" and environment variable's names do not | ||
12640 | + * accept '=', it is guaranteed that the original line consists | ||
12641 | + * of one or more repetition of $left$operator$right blocks | ||
12642 | + * where "$left is free from '=' and ' '" and "$operator is | ||
12643 | + * either '=' or '!='" and "$right is free from ' '". | ||
12644 | + * Therefore, we can reconstruct the original line at the end | ||
12645 | + * of dry run even if we overwrite $operator with '\0'. | ||
12646 | + */ | ||
12647 | + cp = strchr(pos, ' '); | ||
12648 | + if (cp) { | ||
12649 | + *cp = '\0'; /* Will restore later. */ | ||
12650 | + pos = cp + 1; | ||
12651 | + } else { | ||
12652 | + pos = ""; | ||
12653 | + } | ||
12654 | + right_word = strchr(left_word, '='); | ||
12655 | + if (!right_word || right_word == left_word) | ||
12656 | + goto out; | ||
12657 | + is_not = *(right_word - 1) == '!'; | ||
12658 | + if (is_not) | ||
12659 | + *(right_word++ - 1) = '\0'; /* Will restore later. */ | ||
12660 | + else if (*(right_word + 1) != '=') | ||
12661 | + *right_word++ = '\0'; /* Will restore later. */ | ||
12662 | + else | ||
12663 | + goto out; | ||
12664 | + dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word, | ||
12665 | + is_not ? "!" : "", right_word); | ||
12666 | + if (!strcmp(left_word, "grant_log")) { | ||
12667 | + if (entry) { | ||
12668 | + if (is_not || | ||
12669 | + entry->grant_log != CCS_GRANTLOG_AUTO) | ||
12670 | + goto out; | ||
12671 | + else if (!strcmp(right_word, "yes")) | ||
12672 | + entry->grant_log = CCS_GRANTLOG_YES; | ||
12673 | + else if (!strcmp(right_word, "no")) | ||
12674 | + entry->grant_log = CCS_GRANTLOG_NO; | ||
12675 | + else | ||
12676 | + goto out; | ||
12677 | + } | ||
12678 | + continue; | ||
12679 | + } | ||
12680 | + if (!strcmp(left_word, "auto_domain_transition")) { | ||
12681 | + if (entry) { | ||
12682 | + if (is_not || entry->transit) | ||
12683 | + goto out; | ||
12684 | + entry->transit = ccs_get_dqword(right_word); | ||
12685 | + if (!entry->transit || | ||
12686 | + (entry->transit->name[0] != '/' && | ||
12687 | + !ccs_domain_def(entry->transit->name))) | ||
12688 | + goto out; | ||
12689 | + } | ||
12690 | + continue; | ||
12691 | + } | ||
12692 | + if (!strncmp(left_word, "exec.argv[", 10)) { | ||
12693 | + if (!argv) { | ||
12694 | + e.argc++; | ||
12695 | + e.condc++; | ||
12696 | + } else { | ||
12697 | + e.argc--; | ||
12698 | + e.condc--; | ||
12699 | + left = CCS_ARGV_ENTRY; | ||
12700 | + argv->is_not = is_not; | ||
12701 | + if (!ccs_parse_argv(left_word + 10, | ||
12702 | + right_word, argv++)) | ||
12703 | + goto out; | ||
12704 | + } | ||
12705 | + goto store_value; | ||
12706 | + } | ||
12707 | + if (!strncmp(left_word, "exec.envp[\"", 11)) { | ||
12708 | + if (!envp) { | ||
12709 | + e.envc++; | ||
12710 | + e.condc++; | ||
12711 | + } else { | ||
12712 | + e.envc--; | ||
12713 | + e.condc--; | ||
12714 | + left = CCS_ENVP_ENTRY; | ||
12715 | + envp->is_not = is_not; | ||
12716 | + if (!ccs_parse_envp(left_word + 11, | ||
12717 | + right_word, envp++)) | ||
12718 | + goto out; | ||
12719 | + } | ||
12720 | + goto store_value; | ||
12721 | + } | ||
12722 | + left = ccs_condition_type(left_word); | ||
12723 | + dprintk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__, left_word, | ||
12724 | + left); | ||
12725 | + if (left == CCS_MAX_CONDITION_KEYWORD) { | ||
12726 | + if (!numbers_p) { | ||
12727 | + e.numbers_count++; | ||
12728 | + } else { | ||
12729 | + e.numbers_count--; | ||
12730 | + left = CCS_NUMBER_UNION; | ||
12731 | + param->data = left_word; | ||
12732 | + if (*left_word == '@' || | ||
12733 | + !ccs_parse_number_union(param, | ||
12734 | + numbers_p++)) | ||
12735 | + goto out; | ||
12736 | + } | ||
12737 | + } | ||
12738 | + if (!condp) | ||
12739 | + e.condc++; | ||
12740 | + else | ||
12741 | + e.condc--; | ||
12742 | + if (left == CCS_EXEC_REALPATH || left == CCS_SYMLINK_TARGET) { | ||
12743 | + if (!names_p) { | ||
12744 | + e.names_count++; | ||
12745 | + } else { | ||
12746 | + e.names_count--; | ||
12747 | + right = CCS_NAME_UNION; | ||
12748 | + param->data = right_word; | ||
12749 | + if (!ccs_parse_name_union_quoted(param, | ||
12750 | + names_p++)) | ||
12751 | + goto out; | ||
12752 | + } | ||
12753 | + goto store_value; | ||
12754 | + } | ||
12755 | + right = ccs_condition_type(right_word); | ||
12756 | + if (right == CCS_MAX_CONDITION_KEYWORD) { | ||
12757 | + if (!numbers_p) { | ||
12758 | + e.numbers_count++; | ||
12759 | + } else { | ||
12760 | + e.numbers_count--; | ||
12761 | + right = CCS_NUMBER_UNION; | ||
12762 | + param->data = right_word; | ||
12763 | + if (!ccs_parse_number_union(param, | ||
12764 | + numbers_p++)) | ||
12765 | + goto out; | ||
12766 | + } | ||
12767 | + } | ||
12768 | +store_value: | ||
12769 | + if (!condp) { | ||
12770 | + dprintk(KERN_WARNING "%u: dry_run left=%u right=%u " | ||
12771 | + "match=%u\n", __LINE__, left, right, !is_not); | ||
12772 | + continue; | ||
12773 | + } | ||
12774 | + condp->left = left; | ||
12775 | + condp->right = right; | ||
12776 | + condp->equals = !is_not; | ||
12777 | + dprintk(KERN_WARNING "%u: left=%u right=%u match=%u\n", | ||
12778 | + __LINE__, condp->left, condp->right, | ||
12779 | + condp->equals); | ||
12780 | + condp++; | ||
12781 | + } | ||
12782 | + dprintk(KERN_INFO "%u: cond=%u numbers=%u names=%u ac=%u ec=%u\n", | ||
12783 | + __LINE__, e.condc, e.numbers_count, e.names_count, e.argc, | ||
12784 | + e.envc); | ||
12785 | + if (entry) { | ||
12786 | + BUG_ON(e.names_count | e.numbers_count | e.argc | e.envc | | ||
12787 | + e.condc); | ||
12788 | + return ccs_commit_condition(entry); | ||
12789 | + } | ||
12790 | + e.size = sizeof(*entry) | ||
12791 | + + e.condc * sizeof(struct ccs_condition_element) | ||
12792 | + + e.numbers_count * sizeof(struct ccs_number_union) | ||
12793 | + + e.names_count * sizeof(struct ccs_name_union) | ||
12794 | + + e.argc * sizeof(struct ccs_argv) | ||
12795 | + + e.envc * sizeof(struct ccs_envp); | ||
12796 | + entry = kzalloc(e.size, CCS_GFP_FLAGS); | ||
12797 | + if (!entry) | ||
12798 | + goto out2; | ||
12799 | + *entry = e; | ||
12800 | + e.transit = NULL; | ||
12801 | + condp = (struct ccs_condition_element *) (entry + 1); | ||
12802 | + numbers_p = (struct ccs_number_union *) (condp + e.condc); | ||
12803 | + names_p = (struct ccs_name_union *) (numbers_p + e.numbers_count); | ||
12804 | + argv = (struct ccs_argv *) (names_p + e.names_count); | ||
12805 | + envp = (struct ccs_envp *) (argv + e.argc); | ||
12806 | + { | ||
12807 | + bool flag = false; | ||
12808 | + for (pos = start_of_string; pos < end_of_string; pos++) { | ||
12809 | + if (*pos) | ||
12810 | + continue; | ||
12811 | + if (flag) /* Restore " ". */ | ||
12812 | + *pos = ' '; | ||
12813 | + else if (*(pos + 1) == '=') /* Restore "!=". */ | ||
12814 | + *pos = '!'; | ||
12815 | + else /* Restore "=". */ | ||
12816 | + *pos = '='; | ||
12817 | + flag = !flag; | ||
12818 | + } | ||
12819 | + } | ||
12820 | + goto rerun; | ||
12821 | +out: | ||
12822 | + dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__); | ||
12823 | + if (entry) { | ||
12824 | + ccs_del_condition(&entry->head.list); | ||
12825 | + kfree(entry); | ||
12826 | + } | ||
12827 | +out2: | ||
12828 | + ccs_put_name(e.transit); | ||
12829 | + return NULL; | ||
12830 | +} | ||
12831 | + | ||
12832 | +/** | ||
12833 | + * ccs_yesno - Return "yes" or "no". | ||
12834 | + * | ||
12835 | + * @value: Bool value. | ||
12836 | + * | ||
12837 | + * Returns "yes" if @value is not 0, "no" otherwise. | ||
12838 | + */ | ||
12839 | +static const char *ccs_yesno(const unsigned int value) | ||
12840 | +{ | ||
12841 | + return value ? "yes" : "no"; | ||
12842 | +} | ||
12843 | + | ||
12844 | +/** | ||
12845 | + * ccs_addprintf - strncat()-like-snprintf(). | ||
12846 | + * | ||
12847 | + * @buffer: Buffer to write to. Must be '\0'-terminated. | ||
12848 | + * @len: Size of @buffer. | ||
12849 | + * @fmt: The printf()'s format string, followed by parameters. | ||
12850 | + * | ||
12851 | + * Returns nothing. | ||
12852 | + */ | ||
12853 | +static void ccs_addprintf(char *buffer, int len, const char *fmt, ...) | ||
12854 | +{ | ||
12855 | + va_list args; | ||
12856 | + const int pos = strlen(buffer); | ||
12857 | + va_start(args, fmt); | ||
12858 | + vsnprintf(buffer + pos, len - pos - 1, fmt, args); | ||
12859 | + va_end(args); | ||
12860 | +} | ||
12861 | + | ||
12862 | +/** | ||
12863 | + * ccs_flush - Flush queued string to userspace's buffer. | ||
12864 | + * | ||
12865 | + * @head: Pointer to "struct ccs_io_buffer". | ||
12866 | + * | ||
12867 | + * Returns true if all data was flushed, false otherwise. | ||
12868 | + */ | ||
12869 | +static bool ccs_flush(struct ccs_io_buffer *head) | ||
12870 | +{ | ||
12871 | + while (head->r.w_pos) { | ||
12872 | + const char *w = head->r.w[0]; | ||
12873 | + size_t len = strlen(w); | ||
12874 | + if (len) { | ||
12875 | + if (len > head->read_user_buf_avail) | ||
12876 | + len = head->read_user_buf_avail; | ||
12877 | + if (!len) | ||
12878 | + return false; | ||
12879 | + if (copy_to_user(head->read_user_buf, w, len)) | ||
12880 | + return false; | ||
12881 | + head->read_user_buf_avail -= len; | ||
12882 | + head->read_user_buf += len; | ||
12883 | + w += len; | ||
12884 | + } | ||
12885 | + head->r.w[0] = w; | ||
12886 | + if (*w) | ||
12887 | + return false; | ||
12888 | + /* Add '\0' for audit logs and query. */ | ||
12889 | + if (head->type == CCS_AUDIT || head->type == CCS_QUERY) { | ||
12890 | + if (!head->read_user_buf_avail || | ||
12891 | + copy_to_user(head->read_user_buf, "", 1)) | ||
12892 | + return false; | ||
12893 | + head->read_user_buf_avail--; | ||
12894 | + head->read_user_buf++; | ||
12895 | + } | ||
12896 | + head->r.w_pos--; | ||
12897 | + for (len = 0; len < head->r.w_pos; len++) | ||
12898 | + head->r.w[len] = head->r.w[len + 1]; | ||
12899 | + } | ||
12900 | + head->r.avail = 0; | ||
12901 | + return true; | ||
12902 | +} | ||
12903 | + | ||
12904 | +/** | ||
12905 | + * ccs_set_string - Queue string to "struct ccs_io_buffer" structure. | ||
12906 | + * | ||
12907 | + * @head: Pointer to "struct ccs_io_buffer". | ||
12908 | + * @string: String to print. | ||
12909 | + * | ||
12910 | + * Returns nothing. | ||
12911 | + * | ||
12912 | + * Note that @string has to be kept valid until @head is kfree()d. | ||
12913 | + * This means that char[] allocated on stack memory cannot be passed to | ||
12914 | + * this function. Use ccs_io_printf() for char[] allocated on stack memory. | ||
12915 | + */ | ||
12916 | +static void ccs_set_string(struct ccs_io_buffer *head, const char *string) | ||
12917 | +{ | ||
12918 | + if (head->r.w_pos < CCS_MAX_IO_READ_QUEUE) { | ||
12919 | + head->r.w[head->r.w_pos++] = string; | ||
12920 | + ccs_flush(head); | ||
12921 | + } else | ||
12922 | + printk(KERN_WARNING "Too many words in a line.\n"); | ||
12923 | +} | ||
12924 | + | ||
12925 | +/** | ||
12926 | + * ccs_io_printf - printf() to "struct ccs_io_buffer" structure. | ||
12927 | + * | ||
12928 | + * @head: Pointer to "struct ccs_io_buffer". | ||
12929 | + * @fmt: The printf()'s format string, followed by parameters. | ||
12930 | + * | ||
12931 | + * Returns nothing. | ||
12932 | + */ | ||
12933 | +static void ccs_io_printf(struct ccs_io_buffer *head, const char *fmt, ...) | ||
12934 | +{ | ||
12935 | + va_list args; | ||
12936 | + size_t len; | ||
12937 | + size_t pos = head->r.avail; | ||
12938 | + int size = head->readbuf_size - pos; | ||
12939 | + if (size <= 0) | ||
12940 | + return; | ||
12941 | + va_start(args, fmt); | ||
12942 | + len = vsnprintf(head->read_buf + pos, size, fmt, args) + 1; | ||
12943 | + va_end(args); | ||
12944 | + if (pos + len >= head->readbuf_size) { | ||
12945 | + printk(KERN_WARNING "Too many words in a line.\n"); | ||
12946 | + return; | ||
12947 | + } | ||
12948 | + head->r.avail += len; | ||
12949 | + ccs_set_string(head, head->read_buf + pos); | ||
12950 | +} | ||
12951 | + | ||
12952 | +/** | ||
12953 | + * ccs_set_space - Put a space to "struct ccs_io_buffer" structure. | ||
12954 | + * | ||
12955 | + * @head: Pointer to "struct ccs_io_buffer". | ||
12956 | + * | ||
12957 | + * Returns nothing. | ||
12958 | + */ | ||
12959 | +static void ccs_set_space(struct ccs_io_buffer *head) | ||
12960 | +{ | ||
12961 | + ccs_set_string(head, " "); | ||
12962 | +} | ||
12963 | + | ||
12964 | +/** | ||
12965 | + * ccs_set_lf - Put a line feed to "struct ccs_io_buffer" structure. | ||
12966 | + * | ||
12967 | + * @head: Pointer to "struct ccs_io_buffer". | ||
12968 | + * | ||
12969 | + * Returns true if all data was flushed, false otherwise. | ||
12970 | + */ | ||
12971 | +static bool ccs_set_lf(struct ccs_io_buffer *head) | ||
12972 | +{ | ||
12973 | + ccs_set_string(head, "\n"); | ||
12974 | + return !head->r.w_pos; | ||
12975 | +} | ||
12976 | + | ||
12977 | +/** | ||
12978 | + * ccs_set_slash - Put a shash to "struct ccs_io_buffer" structure. | ||
12979 | + * | ||
12980 | + * @head: Pointer to "struct ccs_io_buffer". | ||
12981 | + * | ||
12982 | + * Returns nothing. | ||
12983 | + */ | ||
12984 | +static void ccs_set_slash(struct ccs_io_buffer *head) | ||
12985 | +{ | ||
12986 | + ccs_set_string(head, "/"); | ||
12987 | +} | ||
12988 | + | ||
12989 | +/** | ||
12990 | + * ccs_init_policy_namespace - Initialize namespace. | ||
12991 | + * | ||
12992 | + * @ns: Pointer to "struct ccs_policy_namespace". | ||
12993 | + * | ||
12994 | + * Returns nothing. | ||
12995 | + */ | ||
12996 | +static void ccs_init_policy_namespace(struct ccs_policy_namespace *ns) | ||
12997 | +{ | ||
12998 | + unsigned int idx; | ||
12999 | + for (idx = 0; idx < CCS_MAX_ACL_GROUPS; idx++) | ||
13000 | + INIT_LIST_HEAD(&ns->acl_group[idx]); | ||
13001 | + for (idx = 0; idx < CCS_MAX_GROUP; idx++) | ||
13002 | + INIT_LIST_HEAD(&ns->group_list[idx]); | ||
13003 | + for (idx = 0; idx < CCS_MAX_POLICY; idx++) | ||
13004 | + INIT_LIST_HEAD(&ns->policy_list[idx]); | ||
13005 | + ns->profile_version = 20150505; | ||
13006 | + ccs_namespace_enabled = !list_empty(&ccs_namespace_list); | ||
13007 | + list_add_tail_rcu(&ns->namespace_list, &ccs_namespace_list); | ||
13008 | +} | ||
13009 | + | ||
13010 | +/** | ||
13011 | + * ccs_print_namespace - Print namespace header. | ||
13012 | + * | ||
13013 | + * @head: Pointer to "struct ccs_io_buffer". | ||
13014 | + * | ||
13015 | + * Returns nothing. | ||
13016 | + */ | ||
13017 | +static void ccs_print_namespace(struct ccs_io_buffer *head) | ||
13018 | +{ | ||
13019 | + if (!ccs_namespace_enabled) | ||
13020 | + return; | ||
13021 | + ccs_set_string(head, | ||
13022 | + container_of(head->r.ns, struct ccs_policy_namespace, | ||
13023 | + namespace_list)->name); | ||
13024 | + ccs_set_space(head); | ||
13025 | +} | ||
13026 | + | ||
13027 | +/** | ||
13028 | + * ccs_assign_profile - Create a new profile. | ||
13029 | + * | ||
13030 | + * @ns: Pointer to "struct ccs_policy_namespace". | ||
13031 | + * @profile: Profile number to create. | ||
13032 | + * | ||
13033 | + * Returns pointer to "struct ccs_profile" on success, NULL otherwise. | ||
13034 | + */ | ||
13035 | +static struct ccs_profile *ccs_assign_profile(struct ccs_policy_namespace *ns, | ||
13036 | + const unsigned int profile) | ||
13037 | +{ | ||
13038 | + struct ccs_profile *ptr; | ||
13039 | + struct ccs_profile *entry; | ||
13040 | + if (profile >= CCS_MAX_PROFILES) | ||
13041 | + return NULL; | ||
13042 | + ptr = ns->profile_ptr[profile]; | ||
13043 | + if (ptr) | ||
13044 | + return ptr; | ||
13045 | + entry = kzalloc(sizeof(*entry), CCS_GFP_FLAGS); | ||
13046 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | ||
13047 | + goto out; | ||
13048 | + ptr = ns->profile_ptr[profile]; | ||
13049 | + if (!ptr && ccs_memory_ok(entry, sizeof(*entry))) { | ||
13050 | + ptr = entry; | ||
13051 | + ptr->default_config = CCS_CONFIG_DISABLED | | ||
13052 | + CCS_CONFIG_WANT_GRANT_LOG | CCS_CONFIG_WANT_REJECT_LOG; | ||
13053 | + memset(ptr->config, CCS_CONFIG_USE_DEFAULT, | ||
13054 | + sizeof(ptr->config)); | ||
13055 | + ptr->pref[CCS_PREF_MAX_AUDIT_LOG] = | ||
13056 | + CONFIG_CCSECURITY_MAX_AUDIT_LOG; | ||
13057 | + ptr->pref[CCS_PREF_MAX_LEARNING_ENTRY] = | ||
13058 | + CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY; | ||
13059 | + mb(); /* Avoid out-of-order execution. */ | ||
13060 | + ns->profile_ptr[profile] = ptr; | ||
13061 | + entry = NULL; | ||
13062 | + } | ||
13063 | + mutex_unlock(&ccs_policy_lock); | ||
13064 | +out: | ||
13065 | + kfree(entry); | ||
13066 | + return ptr; | ||
13067 | +} | ||
13068 | + | ||
13069 | +/** | ||
13070 | + * ccs_check_profile - Check all profiles currently assigned to domains are defined. | ||
13071 | + * | ||
13072 | + * Returns nothing. | ||
13073 | + */ | ||
13074 | +static void ccs_check_profile(void) | ||
13075 | +{ | ||
13076 | + struct ccs_domain_info *domain; | ||
13077 | + const int idx = ccs_read_lock(); | ||
13078 | + ccs_policy_loaded = true; | ||
13079 | + printk(KERN_INFO "CCSecurity: 1.8.4 2015/05/05\n"); | ||
13080 | + list_for_each_entry_srcu(domain, &ccs_domain_list, list, &ccs_ss) { | ||
13081 | + const u8 profile = domain->profile; | ||
13082 | + struct ccs_policy_namespace *ns = domain->ns; | ||
13083 | + if (ns->profile_version == 20100903) { | ||
13084 | + static bool done; | ||
13085 | + if (!done) | ||
13086 | + printk(KERN_INFO "Converting profile version " | ||
13087 | + "from %u to %u.\n", 20100903, 20150505); | ||
13088 | + done = true; | ||
13089 | + ns->profile_version = 20150505; | ||
13090 | + } | ||
13091 | + if (ns->profile_version != 20150505) | ||
13092 | + printk(KERN_ERR | ||
13093 | + "Profile version %u is not supported.\n", | ||
13094 | + ns->profile_version); | ||
13095 | + else if (!ns->profile_ptr[profile]) | ||
13096 | + printk(KERN_ERR | ||
13097 | + "Profile %u (used by '%s') is not defined.\n", | ||
13098 | + profile, domain->domainname->name); | ||
13099 | + else | ||
13100 | + continue; | ||
13101 | + printk(KERN_ERR | ||
13102 | + "Userland tools for TOMOYO 1.8 must be installed and " | ||
13103 | + "policy must be initialized.\n"); | ||
13104 | + printk(KERN_ERR "Please see http://tomoyo.osdn.jp/1.8/ " | ||
13105 | + "for more information.\n"); | ||
13106 | + panic("STOP!"); | ||
13107 | + } | ||
13108 | + ccs_read_unlock(idx); | ||
13109 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) && defined(CONFIG_SECURITY) | ||
13110 | + ccsecurity_exports.add_hooks(); | ||
13111 | +#endif | ||
13112 | + printk(KERN_INFO "Mandatory Access Control activated.\n"); | ||
13113 | +} | ||
13114 | + | ||
13115 | +/** | ||
13116 | + * ccs_profile - Find a profile. | ||
13117 | + * | ||
13118 | + * @profile: Profile number to find. | ||
13119 | + * | ||
13120 | + * Returns pointer to "struct ccs_profile". | ||
13121 | + */ | ||
13122 | +static struct ccs_profile *ccs_profile(const u8 profile) | ||
13123 | +{ | ||
13124 | + static struct ccs_profile ccs_null_profile; | ||
13125 | + struct ccs_profile *ptr = ccs_current_namespace()-> | ||
13126 | + profile_ptr[profile]; | ||
13127 | + if (!ptr) | ||
13128 | + ptr = &ccs_null_profile; | ||
13129 | + return ptr; | ||
13130 | +} | ||
13131 | + | ||
13132 | +/** | ||
13133 | + * ccs_get_config - Get config for specified profile's specified functionality. | ||
13134 | + * | ||
13135 | + * @profile: Profile number. | ||
13136 | + * @index: Index number of functionality. | ||
13137 | + * | ||
13138 | + * Returns config. | ||
13139 | + * | ||
13140 | + * First, check for CONFIG::category::functionality. | ||
13141 | + * If CONFIG::category::functionality is set to use default, then check | ||
13142 | + * CONFIG::category. If CONFIG::category is set to use default, then use | ||
13143 | + * CONFIG. CONFIG cannot be set to use default. | ||
13144 | + */ | ||
13145 | +u8 ccs_get_config(const u8 profile, const u8 index) | ||
13146 | +{ | ||
13147 | + u8 config; | ||
13148 | + const struct ccs_profile *p; | ||
13149 | + if (!ccs_policy_loaded) | ||
13150 | + return CCS_CONFIG_DISABLED; | ||
13151 | + p = ccs_profile(profile); | ||
13152 | + config = p->config[index]; | ||
13153 | + if (config == CCS_CONFIG_USE_DEFAULT) | ||
13154 | + config = p->config[ccs_index2category[index] | ||
13155 | + + CCS_MAX_MAC_INDEX]; | ||
13156 | + if (config == CCS_CONFIG_USE_DEFAULT) | ||
13157 | + config = p->default_config; | ||
13158 | + return config; | ||
13159 | +} | ||
13160 | + | ||
13161 | +/** | ||
13162 | + * ccs_find_yesno - Find values for specified keyword. | ||
13163 | + * | ||
13164 | + * @string: String to check. | ||
13165 | + * @find: Name of keyword. | ||
13166 | + * | ||
13167 | + * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise. | ||
13168 | + */ | ||
13169 | +static s8 ccs_find_yesno(const char *string, const char *find) | ||
13170 | +{ | ||
13171 | + const char *cp = strstr(string, find); | ||
13172 | + if (cp) { | ||
13173 | + cp += strlen(find); | ||
13174 | + if (!strncmp(cp, "=yes", 4)) | ||
13175 | + return 1; | ||
13176 | + else if (!strncmp(cp, "=no", 3)) | ||
13177 | + return 0; | ||
13178 | + } | ||
13179 | + return -1; | ||
13180 | +} | ||
13181 | + | ||
13182 | +/** | ||
13183 | + * ccs_set_uint - Set value for specified preference. | ||
13184 | + * | ||
13185 | + * @i: Pointer to "unsigned int". | ||
13186 | + * @string: String to check. | ||
13187 | + * @find: Name of keyword. | ||
13188 | + * | ||
13189 | + * Returns nothing. | ||
13190 | + */ | ||
13191 | +static void ccs_set_uint(unsigned int *i, const char *string, const char *find) | ||
13192 | +{ | ||
13193 | + const char *cp = strstr(string, find); | ||
13194 | + if (cp) | ||
13195 | + sscanf(cp + strlen(find), "=%u", i); | ||
13196 | +} | ||
13197 | + | ||
13198 | +/** | ||
13199 | + * ccs_str_starts - Check whether the given string starts with the given keyword. | ||
13200 | + * | ||
13201 | + * @src: Pointer to pointer to the string. | ||
13202 | + * @find: Pointer to the keyword. | ||
13203 | + * | ||
13204 | + * Returns true if @src starts with @find, false otherwise. | ||
13205 | + * | ||
13206 | + * The @src is updated to point the first character after the @find | ||
13207 | + * if @src starts with @find. | ||
13208 | + */ | ||
13209 | +static bool ccs_str_starts(char **src, const char *find) | ||
13210 | +{ | ||
13211 | + const int len = strlen(find); | ||
13212 | + char *tmp = *src; | ||
13213 | + if (strncmp(tmp, find, len)) | ||
13214 | + return false; | ||
13215 | + tmp += len; | ||
13216 | + *src = tmp; | ||
13217 | + return true; | ||
13218 | +} | ||
13219 | + | ||
13220 | +/** | ||
13221 | + * ccs_print_group - Print group's name. | ||
13222 | + * | ||
13223 | + * @head: Pointer to "struct ccs_io_buffer". | ||
13224 | + * @group: Pointer to "struct ccsgroup". Maybe NULL. | ||
13225 | + * | ||
13226 | + * Returns true if @group is not NULL. false otherwise. | ||
13227 | + */ | ||
13228 | +static bool ccs_print_group(struct ccs_io_buffer *head, | ||
13229 | + const struct ccs_group *group) | ||
13230 | +{ | ||
13231 | + if (group) { | ||
13232 | + ccs_set_string(head, "@"); | ||
13233 | + ccs_set_string(head, group->group_name->name); | ||
13234 | + return true; | ||
13235 | + } | ||
13236 | + return false; | ||
13237 | +} | ||
13238 | + | ||
13239 | +/** | ||
13240 | + * ccs_set_mode - Set mode for specified profile. | ||
13241 | + * | ||
13242 | + * @name: Name of functionality. | ||
13243 | + * @value: Mode for @name. | ||
13244 | + * @profile: Pointer to "struct ccs_profile". | ||
13245 | + * | ||
13246 | + * Returns 0 on success, negative value otherwise. | ||
13247 | + */ | ||
13248 | +static int ccs_set_mode(char *name, const char *value, | ||
13249 | + struct ccs_profile *profile) | ||
13250 | +{ | ||
13251 | + u8 i; | ||
13252 | + u8 config; | ||
13253 | + if (!strcmp(name, "CONFIG")) { | ||
13254 | + i = CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX; | ||
13255 | + config = profile->default_config; | ||
13256 | + } else if (ccs_str_starts(&name, "CONFIG::")) { | ||
13257 | + config = 0; | ||
13258 | + for (i = 0; i < CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX; | ||
13259 | + i++) { | ||
13260 | + int len = 0; | ||
13261 | + if (i < CCS_MAX_MAC_INDEX) { | ||
13262 | + const u8 c = ccs_index2category[i]; | ||
13263 | + const char *category = | ||
13264 | + ccs_category_keywords[c]; | ||
13265 | + len = strlen(category); | ||
13266 | + if (strncmp(name, category, len) || | ||
13267 | + name[len++] != ':' || name[len++] != ':') | ||
13268 | + continue; | ||
13269 | + } | ||
13270 | + if (strcmp(name + len, ccs_mac_keywords[i])) | ||
13271 | + continue; | ||
13272 | + config = profile->config[i]; | ||
13273 | + break; | ||
13274 | + } | ||
13275 | + if (i == CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX) | ||
13276 | + return -EINVAL; | ||
13277 | + } else { | ||
13278 | + return -EINVAL; | ||
13279 | + } | ||
13280 | + if (strstr(value, "use_default")) { | ||
13281 | + config = CCS_CONFIG_USE_DEFAULT; | ||
13282 | + } else { | ||
13283 | + u8 mode; | ||
13284 | + for (mode = 0; mode < CCS_CONFIG_MAX_MODE; mode++) | ||
13285 | + if (strstr(value, ccs_mode[mode])) | ||
13286 | + /* | ||
13287 | + * Update lower 3 bits in order to distinguish | ||
13288 | + * 'config' from 'CCS_CONFIG_USE_DEAFULT'. | ||
13289 | + */ | ||
13290 | + config = (config & ~7) | mode; | ||
13291 | + if (config != CCS_CONFIG_USE_DEFAULT) { | ||
13292 | + switch (ccs_find_yesno(value, "grant_log")) { | ||
13293 | + case 1: | ||
13294 | + config |= CCS_CONFIG_WANT_GRANT_LOG; | ||
13295 | + break; | ||
13296 | + case 0: | ||
13297 | + config &= ~CCS_CONFIG_WANT_GRANT_LOG; | ||
13298 | + break; | ||
13299 | + } | ||
13300 | + switch (ccs_find_yesno(value, "reject_log")) { | ||
13301 | + case 1: | ||
13302 | + config |= CCS_CONFIG_WANT_REJECT_LOG; | ||
13303 | + break; | ||
13304 | + case 0: | ||
13305 | + config &= ~CCS_CONFIG_WANT_REJECT_LOG; | ||
13306 | + break; | ||
13307 | + } | ||
13308 | + } | ||
13309 | + } | ||
13310 | + if (i < CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX) | ||
13311 | + profile->config[i] = config; | ||
13312 | + else if (config != CCS_CONFIG_USE_DEFAULT) | ||
13313 | + profile->default_config = config; | ||
13314 | + return 0; | ||
13315 | +} | ||
13316 | + | ||
13317 | +/** | ||
13318 | + * ccs_write_profile - Write profile table. | ||
13319 | + * | ||
13320 | + * @head: Pointer to "struct ccs_io_buffer". | ||
13321 | + * | ||
13322 | + * Returns 0 on success, negative value otherwise. | ||
13323 | + */ | ||
13324 | +static int ccs_write_profile(struct ccs_io_buffer *head) | ||
13325 | +{ | ||
13326 | + char *data = head->write_buf; | ||
13327 | + unsigned int i; | ||
13328 | + char *cp; | ||
13329 | + struct ccs_profile *profile; | ||
13330 | + if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version) | ||
13331 | + == 1) | ||
13332 | + return 0; | ||
13333 | + i = simple_strtoul(data, &cp, 10); | ||
13334 | + if (*cp != '-') | ||
13335 | + return -EINVAL; | ||
13336 | + data = cp + 1; | ||
13337 | + profile = ccs_assign_profile(head->w.ns, i); | ||
13338 | + if (!profile) | ||
13339 | + return -EINVAL; | ||
13340 | + cp = strchr(data, '='); | ||
13341 | + if (!cp) | ||
13342 | + return -EINVAL; | ||
13343 | + *cp++ = '\0'; | ||
13344 | + if (!strcmp(data, "COMMENT")) { | ||
13345 | + static DEFINE_SPINLOCK(lock); | ||
13346 | + const struct ccs_path_info *new_comment = ccs_get_name(cp); | ||
13347 | + const struct ccs_path_info *old_comment; | ||
13348 | + if (!new_comment) | ||
13349 | + return -ENOMEM; | ||
13350 | + spin_lock(&lock); | ||
13351 | + old_comment = profile->comment; | ||
13352 | + profile->comment = new_comment; | ||
13353 | + spin_unlock(&lock); | ||
13354 | + ccs_put_name(old_comment); | ||
13355 | + return 0; | ||
13356 | + } | ||
13357 | + if (!strcmp(data, "PREFERENCE")) { | ||
13358 | + for (i = 0; i < CCS_MAX_PREF; i++) | ||
13359 | + ccs_set_uint(&profile->pref[i], cp, | ||
13360 | + ccs_pref_keywords[i]); | ||
13361 | + return 0; | ||
13362 | + } | ||
13363 | + return ccs_set_mode(data, cp, profile); | ||
13364 | +} | ||
13365 | + | ||
13366 | +/** | ||
13367 | + * ccs_print_config - Print mode for specified functionality. | ||
13368 | + * | ||
13369 | + * @head: Pointer to "struct ccs_io_buffer". | ||
13370 | + * @config: Mode for that functionality. | ||
13371 | + * | ||
13372 | + * Returns nothing. | ||
13373 | + * | ||
13374 | + * Caller prints functionality's name. | ||
13375 | + */ | ||
13376 | +static void ccs_print_config(struct ccs_io_buffer *head, const u8 config) | ||
13377 | +{ | ||
13378 | + ccs_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n", | ||
13379 | + ccs_mode[config & 3], | ||
13380 | + ccs_yesno(config & CCS_CONFIG_WANT_GRANT_LOG), | ||
13381 | + ccs_yesno(config & CCS_CONFIG_WANT_REJECT_LOG)); | ||
13382 | +} | ||
13383 | + | ||
13384 | +/** | ||
13385 | + * ccs_read_profile - Read profile table. | ||
13386 | + * | ||
13387 | + * @head: Pointer to "struct ccs_io_buffer". | ||
13388 | + * | ||
13389 | + * Returns nothing. | ||
13390 | + */ | ||
13391 | +static void ccs_read_profile(struct ccs_io_buffer *head) | ||
13392 | +{ | ||
13393 | + u8 index; | ||
13394 | + struct ccs_policy_namespace *ns = container_of(head->r.ns, typeof(*ns), | ||
13395 | + namespace_list); | ||
13396 | + const struct ccs_profile *profile; | ||
13397 | + if (head->r.eof) | ||
13398 | + return; | ||
13399 | +next: | ||
13400 | + index = head->r.index; | ||
13401 | + profile = ns->profile_ptr[index]; | ||
13402 | + switch (head->r.step) { | ||
13403 | + case 0: | ||
13404 | + ccs_print_namespace(head); | ||
13405 | + ccs_io_printf(head, "PROFILE_VERSION=%u\n", | ||
13406 | + ns->profile_version); | ||
13407 | + head->r.step++; | ||
13408 | + break; | ||
13409 | + case 1: | ||
13410 | + for ( ; head->r.index < CCS_MAX_PROFILES; head->r.index++) | ||
13411 | + if (ns->profile_ptr[head->r.index]) | ||
13412 | + break; | ||
13413 | + if (head->r.index == CCS_MAX_PROFILES) { | ||
13414 | + head->r.eof = true; | ||
13415 | + return; | ||
13416 | + } | ||
13417 | + head->r.step++; | ||
13418 | + break; | ||
13419 | + case 2: | ||
13420 | + { | ||
13421 | + u8 i; | ||
13422 | + const struct ccs_path_info *comment = profile->comment; | ||
13423 | + ccs_print_namespace(head); | ||
13424 | + ccs_io_printf(head, "%u-COMMENT=", index); | ||
13425 | + ccs_set_string(head, comment ? comment->name : ""); | ||
13426 | + ccs_set_lf(head); | ||
13427 | + ccs_print_namespace(head); | ||
13428 | + ccs_io_printf(head, "%u-PREFERENCE={ ", index); | ||
13429 | + for (i = 0; i < CCS_MAX_PREF; i++) | ||
13430 | + ccs_io_printf(head, "%s=%u ", | ||
13431 | + ccs_pref_keywords[i], | ||
13432 | + profile->pref[i]); | ||
13433 | + ccs_set_string(head, "}\n"); | ||
13434 | + head->r.step++; | ||
13435 | + } | ||
13436 | + break; | ||
13437 | + case 3: | ||
13438 | + { | ||
13439 | + ccs_print_namespace(head); | ||
13440 | + ccs_io_printf(head, "%u-%s", index, "CONFIG"); | ||
13441 | + ccs_print_config(head, profile->default_config); | ||
13442 | + head->r.bit = 0; | ||
13443 | + head->r.step++; | ||
13444 | + } | ||
13445 | + break; | ||
13446 | + case 4: | ||
13447 | + for ( ; head->r.bit < CCS_MAX_MAC_INDEX | ||
13448 | + + CCS_MAX_MAC_CATEGORY_INDEX; head->r.bit++) { | ||
13449 | + const u8 i = head->r.bit; | ||
13450 | + const u8 config = profile->config[i]; | ||
13451 | + if (config == CCS_CONFIG_USE_DEFAULT) | ||
13452 | + continue; | ||
13453 | + ccs_print_namespace(head); | ||
13454 | + if (i < CCS_MAX_MAC_INDEX) | ||
13455 | + ccs_io_printf(head, "%u-CONFIG::%s::%s", index, | ||
13456 | + ccs_category_keywords | ||
13457 | + [ccs_index2category[i]], | ||
13458 | + ccs_mac_keywords[i]); | ||
13459 | + else | ||
13460 | + ccs_io_printf(head, "%u-CONFIG::%s", index, | ||
13461 | + ccs_mac_keywords[i]); | ||
13462 | + ccs_print_config(head, config); | ||
13463 | + head->r.bit++; | ||
13464 | + break; | ||
13465 | + } | ||
13466 | + if (head->r.bit == CCS_MAX_MAC_INDEX | ||
13467 | + + CCS_MAX_MAC_CATEGORY_INDEX) { | ||
13468 | + head->r.index++; | ||
13469 | + head->r.step = 1; | ||
13470 | + } | ||
13471 | + break; | ||
13472 | + } | ||
13473 | + if (ccs_flush(head)) | ||
13474 | + goto next; | ||
13475 | +} | ||
13476 | + | ||
13477 | +/** | ||
13478 | + * ccs_update_policy - Update an entry for exception policy. | ||
13479 | + * | ||
13480 | + * @size: Size of new entry in bytes. | ||
13481 | + * @param: Pointer to "struct ccs_acl_param". | ||
13482 | + * | ||
13483 | + * Returns 0 on success, negative value otherwise. | ||
13484 | + * | ||
13485 | + * Caller holds ccs_read_lock(). | ||
13486 | + */ | ||
13487 | +static int ccs_update_policy(const int size, struct ccs_acl_param *param) | ||
13488 | +{ | ||
13489 | + struct ccs_acl_head *new_entry = ¶m->e.acl_head; | ||
13490 | + int error = param->is_delete ? -ENOENT : -ENOMEM; | ||
13491 | + struct ccs_acl_head *entry; | ||
13492 | + struct list_head *list = param->list; | ||
13493 | + BUG_ON(size < sizeof(*entry)); | ||
13494 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | ||
13495 | + return -ENOMEM; | ||
13496 | + list_for_each_entry_srcu(entry, list, list, &ccs_ss) { | ||
13497 | + if (entry->is_deleted == CCS_GC_IN_PROGRESS) | ||
13498 | + continue; | ||
13499 | + if (memcmp(entry + 1, new_entry + 1, size - sizeof(*entry))) | ||
13500 | + continue; | ||
13501 | + entry->is_deleted = param->is_delete; | ||
13502 | + error = 0; | ||
13503 | + break; | ||
13504 | + } | ||
13505 | + if (error && !param->is_delete) { | ||
13506 | + entry = ccs_commit_ok(new_entry, size); | ||
13507 | + if (entry) { | ||
13508 | + list_add_tail_rcu(&entry->list, list); | ||
13509 | + error = 0; | ||
13510 | + } | ||
13511 | + } | ||
13512 | + mutex_unlock(&ccs_policy_lock); | ||
13513 | + return error; | ||
13514 | +} | ||
13515 | + | ||
13516 | +/** | ||
13517 | + * ccs_update_manager_entry - Add a manager entry. | ||
13518 | + * | ||
13519 | + * @manager: The path to manager or the domainnamme. | ||
13520 | + * @is_delete: True if it is a delete request. | ||
13521 | + * | ||
13522 | + * Returns 0 on success, negative value otherwise. | ||
13523 | + */ | ||
13524 | +static int ccs_update_manager_entry(const char *manager, | ||
13525 | + const bool is_delete) | ||
13526 | +{ | ||
13527 | + struct ccs_acl_param param = { | ||
13528 | + /* .ns = &ccs_kernel_namespace, */ | ||
13529 | + .is_delete = is_delete, | ||
13530 | + .list = &ccs_kernel_namespace.policy_list[CCS_ID_MANAGER], | ||
13531 | + }; | ||
13532 | + struct ccs_manager *e = ¶m.e.manager; | ||
13533 | + int error = is_delete ? -ENOENT : -ENOMEM; | ||
13534 | + /* Forced zero clear for using memcmp() at ccs_update_policy(). */ | ||
13535 | + memset(¶m.e, 0, sizeof(param.e)); | ||
13536 | + if (!ccs_correct_domain(manager) && !ccs_correct_word(manager)) | ||
13537 | + return -EINVAL; | ||
13538 | + e->manager = ccs_get_name(manager); | ||
13539 | + if (e->manager) { | ||
13540 | + error = ccs_update_policy(sizeof(*e), ¶m); | ||
13541 | + ccs_put_name(e->manager); | ||
13542 | + } | ||
13543 | + return error; | ||
13544 | +} | ||
13545 | + | ||
13546 | +/** | ||
13547 | + * ccs_write_manager - Write manager policy. | ||
13548 | + * | ||
13549 | + * @head: Pointer to "struct ccs_io_buffer". | ||
13550 | + * | ||
13551 | + * Returns 0 on success, negative value otherwise. | ||
13552 | + */ | ||
13553 | +static int ccs_write_manager(struct ccs_io_buffer *head) | ||
13554 | +{ | ||
13555 | + const char *data = head->write_buf; | ||
13556 | + if (!strcmp(data, "manage_by_non_root")) { | ||
13557 | + ccs_manage_by_non_root = !head->w.is_delete; | ||
13558 | + return 0; | ||
13559 | + } | ||
13560 | + return ccs_update_manager_entry(data, head->w.is_delete); | ||
13561 | +} | ||
13562 | + | ||
13563 | +/** | ||
13564 | + * ccs_read_manager - Read manager policy. | ||
13565 | + * | ||
13566 | + * @head: Pointer to "struct ccs_io_buffer". | ||
13567 | + * | ||
13568 | + * Returns nothing. | ||
13569 | + * | ||
13570 | + * Caller holds ccs_read_lock(). | ||
13571 | + */ | ||
13572 | +static void ccs_read_manager(struct ccs_io_buffer *head) | ||
13573 | +{ | ||
13574 | + if (head->r.eof) | ||
13575 | + return; | ||
13576 | + list_for_each_cookie(head->r.acl, &ccs_kernel_namespace. | ||
13577 | + policy_list[CCS_ID_MANAGER]) { | ||
13578 | + struct ccs_manager *ptr = | ||
13579 | + list_entry(head->r.acl, typeof(*ptr), head.list); | ||
13580 | + if (ptr->head.is_deleted) | ||
13581 | + continue; | ||
13582 | + if (!ccs_flush(head)) | ||
13583 | + return; | ||
13584 | + ccs_set_string(head, ptr->manager->name); | ||
13585 | + ccs_set_lf(head); | ||
13586 | + } | ||
13587 | + head->r.eof = true; | ||
13588 | +} | ||
13589 | + | ||
13590 | +/** | ||
13591 | + * ccs_manager - Check whether the current process is a policy manager. | ||
13592 | + * | ||
13593 | + * Returns true if the current process is permitted to modify policy | ||
13594 | + * via /proc/ccs/ interface. | ||
13595 | + * | ||
13596 | + * Caller holds ccs_read_lock(). | ||
13597 | + */ | ||
13598 | +static bool ccs_manager(void) | ||
13599 | +{ | ||
13600 | + struct ccs_manager *ptr; | ||
13601 | + struct ccs_path_info exe; | ||
13602 | + struct ccs_security *task = ccs_current_security(); | ||
13603 | + const struct ccs_path_info *domainname | ||
13604 | + = ccs_current_domain()->domainname; | ||
13605 | + bool found = false; | ||
13606 | + if (!ccs_policy_loaded) | ||
13607 | + return true; | ||
13608 | + if (task->ccs_flags & CCS_TASK_IS_MANAGER) | ||
13609 | + return true; | ||
13610 | + if (!ccs_manage_by_non_root && | ||
13611 | + (!uid_eq(current_uid(), GLOBAL_ROOT_UID) || | ||
13612 | + !uid_eq(current_euid(), GLOBAL_ROOT_UID))) | ||
13613 | + return false; | ||
13614 | + exe.name = ccs_get_exe(); | ||
13615 | + if (!exe.name) | ||
13616 | + return false; | ||
13617 | + ccs_fill_path_info(&exe); | ||
13618 | + list_for_each_entry_srcu(ptr, &ccs_kernel_namespace. | ||
13619 | + policy_list[CCS_ID_MANAGER], head.list, | ||
13620 | + &ccs_ss) { | ||
13621 | + if (ptr->head.is_deleted) | ||
13622 | + continue; | ||
13623 | + if (ccs_pathcmp(domainname, ptr->manager) && | ||
13624 | + ccs_pathcmp(&exe, ptr->manager)) | ||
13625 | + continue; | ||
13626 | + /* Set manager flag. */ | ||
13627 | + task->ccs_flags |= CCS_TASK_IS_MANAGER; | ||
13628 | + found = true; | ||
13629 | + break; | ||
13630 | + } | ||
13631 | + if (!found) { /* Reduce error messages. */ | ||
13632 | + static pid_t ccs_last_pid; | ||
13633 | + const pid_t pid = current->pid; | ||
13634 | + if (ccs_last_pid != pid) { | ||
13635 | + printk(KERN_WARNING "%s ( %s ) is not permitted to " | ||
13636 | + "update policies.\n", domainname->name, | ||
13637 | + exe.name); | ||
13638 | + ccs_last_pid = pid; | ||
13639 | + } | ||
13640 | + } | ||
13641 | + kfree(exe.name); | ||
13642 | + return found; | ||
13643 | +} | ||
13644 | + | ||
13645 | +/** | ||
13646 | + * ccs_find_domain - Find a domain by the given name. | ||
13647 | + * | ||
13648 | + * @domainname: The domainname to find. | ||
13649 | + * | ||
13650 | + * Returns pointer to "struct ccs_domain_info" if found, NULL otherwise. | ||
13651 | + * | ||
13652 | + * Caller holds ccs_read_lock(). | ||
13653 | + */ | ||
13654 | +static struct ccs_domain_info *ccs_find_domain(const char *domainname) | ||
13655 | +{ | ||
13656 | + struct ccs_domain_info *domain; | ||
13657 | + struct ccs_path_info name; | ||
13658 | + name.name = domainname; | ||
13659 | + ccs_fill_path_info(&name); | ||
13660 | + list_for_each_entry_srcu(domain, &ccs_domain_list, list, &ccs_ss) { | ||
13661 | + if (!domain->is_deleted && | ||
13662 | + !ccs_pathcmp(&name, domain->domainname)) | ||
13663 | + return domain; | ||
13664 | + } | ||
13665 | + return NULL; | ||
13666 | +} | ||
13667 | + | ||
13668 | +/** | ||
13669 | + * ccs_select_domain - Parse select command. | ||
13670 | + * | ||
13671 | + * @head: Pointer to "struct ccs_io_buffer". | ||
13672 | + * @data: String to parse. | ||
13673 | + * | ||
13674 | + * Returns true on success, false otherwise. | ||
13675 | + * | ||
13676 | + * Caller holds ccs_read_lock(). | ||
13677 | + */ | ||
13678 | +static bool ccs_select_domain(struct ccs_io_buffer *head, const char *data) | ||
13679 | +{ | ||
13680 | + unsigned int pid; | ||
13681 | + struct ccs_domain_info *domain = NULL; | ||
13682 | + bool global_pid = false; | ||
13683 | + if (strncmp(data, "select ", 7)) | ||
13684 | + return false; | ||
13685 | + data += 7; | ||
13686 | + if (sscanf(data, "pid=%u", &pid) == 1 || | ||
13687 | + (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { | ||
13688 | + struct task_struct *p; | ||
13689 | + ccs_tasklist_lock(); | ||
13690 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | ||
13691 | + if (global_pid) | ||
13692 | + p = ccsecurity_exports.find_task_by_pid_ns(pid, | ||
13693 | + &init_pid_ns); | ||
13694 | + else | ||
13695 | + p = ccsecurity_exports.find_task_by_vpid(pid); | ||
13696 | +#else | ||
13697 | + p = find_task_by_pid(pid); | ||
13698 | +#endif | ||
13699 | + if (p) | ||
13700 | + domain = ccs_task_domain(p); | ||
13701 | + ccs_tasklist_unlock(); | ||
13702 | + } else if (!strncmp(data, "domain=", 7)) { | ||
13703 | + if (*(data + 7) == '<') | ||
13704 | + domain = ccs_find_domain(data + 7); | ||
13705 | + } else if (sscanf(data, "Q=%u", &pid) == 1) { | ||
13706 | + domain = ccs_find_domain_by_qid(pid); | ||
13707 | + } else | ||
13708 | + return false; | ||
13709 | + head->w.domain = domain; | ||
13710 | + /* Accessing read_buf is safe because head->io_sem is held. */ | ||
13711 | + if (!head->read_buf) | ||
13712 | + return true; /* Do nothing if open(O_WRONLY). */ | ||
13713 | + memset(&head->r, 0, sizeof(head->r)); | ||
13714 | + head->r.print_this_domain_only = true; | ||
13715 | + if (domain) | ||
13716 | + head->r.domain = &domain->list; | ||
13717 | + else | ||
13718 | + head->r.eof = true; | ||
13719 | + ccs_io_printf(head, "# select %s\n", data); | ||
13720 | + if (domain && domain->is_deleted) | ||
13721 | + ccs_set_string(head, "# This is a deleted domain.\n"); | ||
13722 | + return true; | ||
13723 | +} | ||
13724 | + | ||
13725 | +/** | ||
13726 | + * ccs_update_acl - Update "struct ccs_acl_info" entry. | ||
13727 | + * | ||
13728 | + * @size: Size of new entry in bytes. | ||
13729 | + * @param: Pointer to "struct ccs_acl_param". | ||
13730 | + * | ||
13731 | + * Returns 0 on success, negative value otherwise. | ||
13732 | + * | ||
13733 | + * Caller holds ccs_read_lock(). | ||
13734 | + */ | ||
13735 | +static int ccs_update_acl(const int size, struct ccs_acl_param *param) | ||
13736 | +{ | ||
13737 | + struct ccs_acl_info *new_entry = ¶m->e.acl_info; | ||
13738 | + const bool is_delete = param->is_delete; | ||
13739 | + int error = is_delete ? -ENOENT : -ENOMEM; | ||
13740 | + struct ccs_acl_info *entry; | ||
13741 | + struct list_head * const list = param->list; | ||
13742 | + BUG_ON(size < sizeof(*entry)); | ||
13743 | + if (param->data[0]) { | ||
13744 | + new_entry->cond = ccs_get_condition(param); | ||
13745 | + if (!new_entry->cond) | ||
13746 | + return -EINVAL; | ||
13747 | + /* | ||
13748 | + * Domain transition preference is allowed for only | ||
13749 | + * "file execute"/"task auto_execute_handler"/ | ||
13750 | + * "task denied_auto_execute_handler" entries. | ||
13751 | + */ | ||
13752 | + if (new_entry->cond->exec_transit && | ||
13753 | + !(new_entry->type == CCS_TYPE_PATH_ACL && | ||
13754 | + new_entry->perm == 1 << CCS_TYPE_EXECUTE) | ||
13755 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
13756 | + && new_entry->type != CCS_TYPE_AUTO_EXECUTE_HANDLER && | ||
13757 | + new_entry->type != CCS_TYPE_DENIED_EXECUTE_HANDLER | ||
13758 | +#endif | ||
13759 | + ) | ||
13760 | + return -EINVAL; | ||
13761 | + } | ||
13762 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | ||
13763 | + return -ENOMEM; | ||
13764 | + list_for_each_entry_srcu(entry, list, list, &ccs_ss) { | ||
13765 | + if (entry->is_deleted == CCS_GC_IN_PROGRESS) | ||
13766 | + continue; | ||
13767 | + if (entry->type != new_entry->type || | ||
13768 | + entry->cond != new_entry->cond || | ||
13769 | + memcmp(entry + 1, new_entry + 1, size - sizeof(*entry))) | ||
13770 | + continue; | ||
13771 | + if (is_delete) | ||
13772 | + entry->perm &= ~new_entry->perm; | ||
13773 | + else | ||
13774 | + entry->perm |= new_entry->perm; | ||
13775 | + entry->is_deleted = !entry->perm; | ||
13776 | + error = 0; | ||
13777 | + break; | ||
13778 | + } | ||
13779 | + if (error && !is_delete) { | ||
13780 | + entry = ccs_commit_ok(new_entry, size); | ||
13781 | + if (entry) { | ||
13782 | + list_add_tail_rcu(&entry->list, list); | ||
13783 | + error = 0; | ||
13784 | + } | ||
13785 | + } | ||
13786 | + mutex_unlock(&ccs_policy_lock); | ||
13787 | + return error; | ||
13788 | +} | ||
13789 | + | ||
13790 | +/** | ||
13791 | + * ccs_permstr - Find permission keywords. | ||
13792 | + * | ||
13793 | + * @string: String representation for permissions in foo/bar/buz format. | ||
13794 | + * @keyword: Keyword to find from @string/ | ||
13795 | + * | ||
13796 | + * Returns ture if @keyword was found in @string, false otherwise. | ||
13797 | + * | ||
13798 | + * This function assumes that strncmp(w1, w2, strlen(w1)) != 0 if w1 != w2. | ||
13799 | + */ | ||
13800 | +static bool ccs_permstr(const char *string, const char *keyword) | ||
13801 | +{ | ||
13802 | + const char *cp = strstr(string, keyword); | ||
13803 | + if (cp) | ||
13804 | + return cp == string || *(cp - 1) == '/'; | ||
13805 | + return false; | ||
13806 | +} | ||
13807 | + | ||
13808 | +/** | ||
13809 | + * ccs_write_task - Update task related list. | ||
13810 | + * | ||
13811 | + * @param: Pointer to "struct ccs_acl_param". | ||
13812 | + * | ||
13813 | + * Returns 0 on success, negative value otherwise. | ||
13814 | + * | ||
13815 | + * Caller holds ccs_read_lock(). | ||
13816 | + */ | ||
13817 | +static int ccs_write_task(struct ccs_acl_param *param) | ||
13818 | +{ | ||
13819 | + int error; | ||
13820 | + const bool is_auto = ccs_str_starts(¶m->data, | ||
13821 | + "auto_domain_transition "); | ||
13822 | + if (!is_auto && !ccs_str_starts(¶m->data, | ||
13823 | + "manual_domain_transition ")) { | ||
13824 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
13825 | + struct ccs_handler_acl *e = ¶m->e.handler_acl; | ||
13826 | + char *handler; | ||
13827 | + if (ccs_str_starts(¶m->data, "auto_execute_handler ")) | ||
13828 | + e->head.type = CCS_TYPE_AUTO_EXECUTE_HANDLER; | ||
13829 | + else if (ccs_str_starts(¶m->data, | ||
13830 | + "denied_execute_handler ")) | ||
13831 | + e->head.type = CCS_TYPE_DENIED_EXECUTE_HANDLER; | ||
13832 | + else | ||
13833 | + return -EINVAL; | ||
13834 | + handler = ccs_read_token(param); | ||
13835 | + if (!ccs_correct_path(handler)) | ||
13836 | + return -EINVAL; | ||
13837 | + e->handler = ccs_get_name(handler); | ||
13838 | + if (!e->handler) | ||
13839 | + return -ENOMEM; | ||
13840 | + if (e->handler->is_patterned) | ||
13841 | + return -EINVAL; /* No patterns allowed. */ | ||
13842 | + return ccs_update_acl(sizeof(*e), param); | ||
13843 | +#else | ||
13844 | + error = -EINVAL; | ||
13845 | +#endif | ||
13846 | + } else { | ||
13847 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
13848 | + struct ccs_task_acl *e = ¶m->e.task_acl; | ||
13849 | + e->head.type = is_auto ? | ||
13850 | + CCS_TYPE_AUTO_TASK_ACL : CCS_TYPE_MANUAL_TASK_ACL; | ||
13851 | + e->domainname = ccs_get_domainname(param); | ||
13852 | + if (!e->domainname) | ||
13853 | + return -EINVAL; | ||
13854 | + return ccs_update_acl(sizeof(*e), param); | ||
13855 | +#else | ||
13856 | + error = -EINVAL; | ||
13857 | +#endif | ||
13858 | + } | ||
13859 | + return error; | ||
13860 | +} | ||
13861 | + | ||
13862 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
13863 | + | ||
13864 | +/** | ||
13865 | + * ccs_write_inet_network - Write "struct ccs_inet_acl" list. | ||
13866 | + * | ||
13867 | + * @param: Pointer to "struct ccs_acl_param". | ||
13868 | + * | ||
13869 | + * Returns 0 on success, negative value otherwise. | ||
13870 | + * | ||
13871 | + * Caller holds ccs_read_lock(). | ||
13872 | + */ | ||
13873 | +static int ccs_write_inet_network(struct ccs_acl_param *param) | ||
13874 | +{ | ||
13875 | + struct ccs_inet_acl *e = ¶m->e.inet_acl; | ||
13876 | + u8 type; | ||
13877 | + const char *protocol = ccs_read_token(param); | ||
13878 | + const char *operation = ccs_read_token(param); | ||
13879 | + e->head.type = CCS_TYPE_INET_ACL; | ||
13880 | + for (type = 0; type < CCS_SOCK_MAX; type++) | ||
13881 | + if (!strcmp(protocol, ccs_proto_keyword[type])) | ||
13882 | + break; | ||
13883 | + if (type == CCS_SOCK_MAX) | ||
13884 | + return -EINVAL; | ||
13885 | + e->protocol = type; | ||
13886 | + e->head.perm = 0; | ||
13887 | + for (type = 0; type < CCS_MAX_NETWORK_OPERATION; type++) | ||
13888 | + if (ccs_permstr(operation, ccs_socket_keyword[type])) | ||
13889 | + e->head.perm |= 1 << type; | ||
13890 | + if (!e->head.perm) | ||
13891 | + return -EINVAL; | ||
13892 | + if (param->data[0] == '@') { | ||
13893 | + param->data++; | ||
13894 | + e->address.group = ccs_get_group(param, CCS_ADDRESS_GROUP); | ||
13895 | + if (!e->address.group) | ||
13896 | + return -ENOMEM; | ||
13897 | + } else { | ||
13898 | + if (!ccs_parse_ipaddr_union(param, &e->address)) | ||
13899 | + return -EINVAL; | ||
13900 | + } | ||
13901 | + if (!ccs_parse_number_union(param, &e->port) || | ||
13902 | + e->port.values[1] > 65535) | ||
13903 | + return -EINVAL; | ||
13904 | + return ccs_update_acl(sizeof(*e), param); | ||
13905 | +} | ||
13906 | + | ||
13907 | +/** | ||
13908 | + * ccs_write_unix_network - Write "struct ccs_unix_acl" list. | ||
13909 | + * | ||
13910 | + * @param: Pointer to "struct ccs_acl_param". | ||
13911 | + * | ||
13912 | + * Returns 0 on success, negative value otherwise. | ||
13913 | + */ | ||
13914 | +static int ccs_write_unix_network(struct ccs_acl_param *param) | ||
13915 | +{ | ||
13916 | + struct ccs_unix_acl *e = ¶m->e.unix_acl; | ||
13917 | + u8 type; | ||
13918 | + const char *protocol = ccs_read_token(param); | ||
13919 | + const char *operation = ccs_read_token(param); | ||
13920 | + e->head.type = CCS_TYPE_UNIX_ACL; | ||
13921 | + for (type = 0; type < CCS_SOCK_MAX; type++) | ||
13922 | + if (!strcmp(protocol, ccs_proto_keyword[type])) | ||
13923 | + break; | ||
13924 | + if (type == CCS_SOCK_MAX) | ||
13925 | + return -EINVAL; | ||
13926 | + e->protocol = type; | ||
13927 | + e->head.perm = 0; | ||
13928 | + for (type = 0; type < CCS_MAX_NETWORK_OPERATION; type++) | ||
13929 | + if (ccs_permstr(operation, ccs_socket_keyword[type])) | ||
13930 | + e->head.perm |= 1 << type; | ||
13931 | + if (!e->head.perm) | ||
13932 | + return -EINVAL; | ||
13933 | + if (!ccs_parse_name_union(param, &e->name)) | ||
13934 | + return -EINVAL; | ||
13935 | + return ccs_update_acl(sizeof(*e), param); | ||
13936 | +} | ||
13937 | + | ||
13938 | +#endif | ||
13939 | + | ||
13940 | +/** | ||
13941 | + * ccs_write_file - Update file related list. | ||
13942 | + * | ||
13943 | + * @param: Pointer to "struct ccs_acl_param". | ||
13944 | + * | ||
13945 | + * Returns 0 on success, negative value otherwise. | ||
13946 | + * | ||
13947 | + * Caller holds ccs_read_lock(). | ||
13948 | + */ | ||
13949 | +static int ccs_write_file(struct ccs_acl_param *param) | ||
13950 | +{ | ||
13951 | + u16 perm = 0; | ||
13952 | + u8 type; | ||
13953 | + const char *operation = ccs_read_token(param); | ||
13954 | + for (type = 0; type < CCS_MAX_PATH_OPERATION; type++) | ||
13955 | + if (ccs_permstr(operation, ccs_path_keyword[type])) | ||
13956 | + perm |= 1 << type; | ||
13957 | + if (perm) { | ||
13958 | + struct ccs_path_acl *e = ¶m->e.path_acl; | ||
13959 | + e->head.type = CCS_TYPE_PATH_ACL; | ||
13960 | + e->head.perm = perm; | ||
13961 | + if (!ccs_parse_name_union(param, &e->name)) | ||
13962 | + return -EINVAL; | ||
13963 | + return ccs_update_acl(sizeof(*e), param); | ||
13964 | + } | ||
13965 | + for (type = 0; type < CCS_MAX_PATH2_OPERATION; type++) | ||
13966 | + if (ccs_permstr(operation, ccs_mac_keywords[ccs_pp2mac[type]])) | ||
13967 | + perm |= 1 << type; | ||
13968 | + if (perm) { | ||
13969 | + struct ccs_path2_acl *e = ¶m->e.path2_acl; | ||
13970 | + e->head.type = CCS_TYPE_PATH2_ACL; | ||
13971 | + e->head.perm = perm; | ||
13972 | + if (!ccs_parse_name_union(param, &e->name1) || | ||
13973 | + !ccs_parse_name_union(param, &e->name2)) | ||
13974 | + return -EINVAL; | ||
13975 | + return ccs_update_acl(sizeof(*e), param); | ||
13976 | + } | ||
13977 | + for (type = 0; type < CCS_MAX_PATH_NUMBER_OPERATION; type++) | ||
13978 | + if (ccs_permstr(operation, ccs_mac_keywords[ccs_pn2mac[type]])) | ||
13979 | + perm |= 1 << type; | ||
13980 | + if (perm) { | ||
13981 | + struct ccs_path_number_acl *e = ¶m->e.path_number_acl; | ||
13982 | + e->head.type = CCS_TYPE_PATH_NUMBER_ACL; | ||
13983 | + e->head.perm = perm; | ||
13984 | + if (!ccs_parse_name_union(param, &e->name) || | ||
13985 | + !ccs_parse_number_union(param, &e->number)) | ||
13986 | + return -EINVAL; | ||
13987 | + return ccs_update_acl(sizeof(*e), param); | ||
13988 | + } | ||
13989 | + for (type = 0; type < CCS_MAX_MKDEV_OPERATION; type++) | ||
13990 | + if (ccs_permstr(operation, | ||
13991 | + ccs_mac_keywords[ccs_pnnn2mac[type]])) | ||
13992 | + perm |= 1 << type; | ||
13993 | + if (perm) { | ||
13994 | + struct ccs_mkdev_acl *e = ¶m->e.mkdev_acl; | ||
13995 | + e->head.type = CCS_TYPE_MKDEV_ACL; | ||
13996 | + e->head.perm = perm; | ||
13997 | + if (!ccs_parse_name_union(param, &e->name) || | ||
13998 | + !ccs_parse_number_union(param, &e->mode) || | ||
13999 | + !ccs_parse_number_union(param, &e->major) || | ||
14000 | + !ccs_parse_number_union(param, &e->minor)) | ||
14001 | + return -EINVAL; | ||
14002 | + return ccs_update_acl(sizeof(*e), param); | ||
14003 | + } | ||
14004 | + if (ccs_permstr(operation, ccs_mac_keywords[CCS_MAC_FILE_MOUNT])) { | ||
14005 | + struct ccs_mount_acl *e = ¶m->e.mount_acl; | ||
14006 | + e->head.type = CCS_TYPE_MOUNT_ACL; | ||
14007 | + if (!ccs_parse_name_union(param, &e->dev_name) || | ||
14008 | + !ccs_parse_name_union(param, &e->dir_name) || | ||
14009 | + !ccs_parse_name_union(param, &e->fs_type) || | ||
14010 | + !ccs_parse_number_union(param, &e->flags)) | ||
14011 | + return -EINVAL; | ||
14012 | + return ccs_update_acl(sizeof(*e), param); | ||
14013 | + } | ||
14014 | + return -EINVAL; | ||
14015 | +} | ||
14016 | + | ||
14017 | +#ifdef CONFIG_CCSECURITY_MISC | ||
14018 | + | ||
14019 | +/** | ||
14020 | + * ccs_write_misc - Update environment variable list. | ||
14021 | + * | ||
14022 | + * @param: Pointer to "struct ccs_acl_param". | ||
14023 | + * | ||
14024 | + * Returns 0 on success, negative value otherwise. | ||
14025 | + */ | ||
14026 | +static int ccs_write_misc(struct ccs_acl_param *param) | ||
14027 | +{ | ||
14028 | + if (ccs_str_starts(¶m->data, "env ")) { | ||
14029 | + struct ccs_env_acl *e = ¶m->e.env_acl; | ||
14030 | + const char *data = ccs_read_token(param); | ||
14031 | + e->head.type = CCS_TYPE_ENV_ACL; | ||
14032 | + if (!ccs_correct_word(data) || strchr(data, '=')) | ||
14033 | + return -EINVAL; | ||
14034 | + e->env = ccs_get_name(data); | ||
14035 | + if (!e->env) | ||
14036 | + return -ENOMEM; | ||
14037 | + return ccs_update_acl(sizeof(*e), param); | ||
14038 | + } | ||
14039 | + return -EINVAL; | ||
14040 | +} | ||
14041 | + | ||
14042 | +#endif | ||
14043 | + | ||
14044 | +#ifdef CONFIG_CCSECURITY_IPC | ||
14045 | + | ||
14046 | +/** | ||
14047 | + * ccs_write_ipc - Update "struct ccs_signal_acl" list. | ||
14048 | + * | ||
14049 | + * @param: Pointer to "struct ccs_acl_param". | ||
14050 | + * | ||
14051 | + * Returns 0 on success, negative value otherwise. | ||
14052 | + */ | ||
14053 | +static int ccs_write_ipc(struct ccs_acl_param *param) | ||
14054 | +{ | ||
14055 | + struct ccs_signal_acl *e = ¶m->e.signal_acl; | ||
14056 | + e->head.type = CCS_TYPE_SIGNAL_ACL; | ||
14057 | + if (!ccs_parse_number_union(param, &e->sig)) | ||
14058 | + return -EINVAL; | ||
14059 | + e->domainname = ccs_get_domainname(param); | ||
14060 | + if (!e->domainname) | ||
14061 | + return -EINVAL; | ||
14062 | + return ccs_update_acl(sizeof(*e), param); | ||
14063 | +} | ||
14064 | + | ||
14065 | +#endif | ||
14066 | + | ||
14067 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
14068 | + | ||
14069 | +/** | ||
14070 | + * ccs_write_capability - Write "struct ccs_capability_acl" list. | ||
14071 | + * | ||
14072 | + * @param: Pointer to "struct ccs_acl_param". | ||
14073 | + * | ||
14074 | + * Returns 0 on success, negative value otherwise. | ||
14075 | + * | ||
14076 | + * Caller holds ccs_read_lock(). | ||
14077 | + */ | ||
14078 | +static int ccs_write_capability(struct ccs_acl_param *param) | ||
14079 | +{ | ||
14080 | + struct ccs_capability_acl *e = ¶m->e.capability_acl; | ||
14081 | + const char *operation = ccs_read_token(param); | ||
14082 | + u8 type; | ||
14083 | + e->head.type = CCS_TYPE_CAPABILITY_ACL; | ||
14084 | + for (type = 0; type < CCS_MAX_CAPABILITY_INDEX; type++) { | ||
14085 | + if (strcmp(operation, ccs_mac_keywords[ccs_c2mac[type]])) | ||
14086 | + continue; | ||
14087 | + e->operation = type; | ||
14088 | + return ccs_update_acl(sizeof(*e), param); | ||
14089 | + } | ||
14090 | + return -EINVAL; | ||
14091 | +} | ||
14092 | + | ||
14093 | +#endif | ||
14094 | + | ||
14095 | +/** | ||
14096 | + * ccs_write_acl - Write "struct ccs_acl_info" list. | ||
14097 | + * | ||
14098 | + * @ns: Pointer to "struct ccs_policy_namespace". | ||
14099 | + * @list: Pointer to "struct list_head". | ||
14100 | + * @data: Policy to be interpreted. | ||
14101 | + * @is_delete: True if it is a delete request. | ||
14102 | + * | ||
14103 | + * Returns 0 on success, negative value otherwise. | ||
14104 | + * | ||
14105 | + * Caller holds ccs_read_lock(). | ||
14106 | + */ | ||
14107 | +static int ccs_write_acl(struct ccs_policy_namespace *ns, | ||
14108 | + struct list_head *list, char *data, | ||
14109 | + const bool is_delete) | ||
14110 | +{ | ||
14111 | + struct ccs_acl_param param = { | ||
14112 | + .ns = ns, | ||
14113 | + .list = list, | ||
14114 | + .data = data, | ||
14115 | + .is_delete = is_delete, | ||
14116 | + }; | ||
14117 | + static const struct { | ||
14118 | + const char *keyword; | ||
14119 | + int (*write) (struct ccs_acl_param *); | ||
14120 | + } ccs_callback[] = { | ||
14121 | + { "file ", ccs_write_file }, | ||
14122 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
14123 | + { "network inet ", ccs_write_inet_network }, | ||
14124 | + { "network unix ", ccs_write_unix_network }, | ||
14125 | +#endif | ||
14126 | +#ifdef CONFIG_CCSECURITY_MISC | ||
14127 | + { "misc ", ccs_write_misc }, | ||
14128 | +#endif | ||
14129 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
14130 | + { "capability ", ccs_write_capability }, | ||
14131 | +#endif | ||
14132 | +#ifdef CONFIG_CCSECURITY_IPC | ||
14133 | + { "ipc signal ", ccs_write_ipc }, | ||
14134 | +#endif | ||
14135 | + { "task ", ccs_write_task }, | ||
14136 | + }; | ||
14137 | + u8 i; | ||
14138 | + /* Forced zero clear for using memcmp() at ccs_update_acl(). */ | ||
14139 | + memset(¶m.e, 0, sizeof(param.e)); | ||
14140 | + param.e.acl_info.perm = 1; | ||
14141 | + for (i = 0; i < ARRAY_SIZE(ccs_callback); i++) { | ||
14142 | + int error; | ||
14143 | + if (!ccs_str_starts(¶m.data, ccs_callback[i].keyword)) | ||
14144 | + continue; | ||
14145 | + error = ccs_callback[i].write(¶m); | ||
14146 | + ccs_del_acl(¶m.e.acl_info.list); | ||
14147 | + return error; | ||
14148 | + } | ||
14149 | + return -EINVAL; | ||
14150 | +} | ||
14151 | + | ||
14152 | +/** | ||
14153 | + * ccs_delete_domain - Delete a domain. | ||
14154 | + * | ||
14155 | + * @domainname: The name of domain. | ||
14156 | + * | ||
14157 | + * Returns 0. | ||
14158 | + */ | ||
14159 | +static int ccs_delete_domain(char *domainname) | ||
14160 | +{ | ||
14161 | + struct ccs_domain_info *domain; | ||
14162 | + struct ccs_path_info name; | ||
14163 | + name.name = domainname; | ||
14164 | + ccs_fill_path_info(&name); | ||
14165 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | ||
14166 | + return 0; | ||
14167 | + /* Is there an active domain? */ | ||
14168 | + list_for_each_entry_srcu(domain, &ccs_domain_list, list, &ccs_ss) { | ||
14169 | + /* Never delete ccs_kernel_domain. */ | ||
14170 | + if (domain == &ccs_kernel_domain) | ||
14171 | + continue; | ||
14172 | + if (domain->is_deleted || | ||
14173 | + ccs_pathcmp(domain->domainname, &name)) | ||
14174 | + continue; | ||
14175 | + domain->is_deleted = true; | ||
14176 | + break; | ||
14177 | + } | ||
14178 | + mutex_unlock(&ccs_policy_lock); | ||
14179 | + return 0; | ||
14180 | +} | ||
14181 | + | ||
14182 | +/** | ||
14183 | + * ccs_write_domain - Write domain policy. | ||
14184 | + * | ||
14185 | + * @head: Pointer to "struct ccs_io_buffer". | ||
14186 | + * | ||
14187 | + * Returns 0 on success, negative value otherwise. | ||
14188 | + * | ||
14189 | + * Caller holds ccs_read_lock(). | ||
14190 | + */ | ||
14191 | +static int ccs_write_domain(struct ccs_io_buffer *head) | ||
14192 | +{ | ||
14193 | + char *data = head->write_buf; | ||
14194 | + struct ccs_policy_namespace *ns; | ||
14195 | + struct ccs_domain_info *domain = head->w.domain; | ||
14196 | + const bool is_delete = head->w.is_delete; | ||
14197 | + const bool is_select = !is_delete && ccs_str_starts(&data, "select "); | ||
14198 | + unsigned int idx; | ||
14199 | + if (*data == '<') { | ||
14200 | + domain = NULL; | ||
14201 | + if (is_delete) | ||
14202 | + ccs_delete_domain(data); | ||
14203 | + else if (is_select) | ||
14204 | + domain = ccs_find_domain(data); | ||
14205 | + else | ||
14206 | + domain = ccs_assign_domain(data, false); | ||
14207 | + head->w.domain = domain; | ||
14208 | + return 0; | ||
14209 | + } | ||
14210 | + if (!domain) | ||
14211 | + return -EINVAL; | ||
14212 | + ns = domain->ns; | ||
14213 | + if (sscanf(data, "use_profile %u\n", &idx) == 1 && | ||
14214 | + idx < CCS_MAX_PROFILES) { | ||
14215 | + if (!ccs_policy_loaded || ns->profile_ptr[(u8) idx]) | ||
14216 | + if (!is_delete) | ||
14217 | + domain->profile = (u8) idx; | ||
14218 | + return 0; | ||
14219 | + } | ||
14220 | + if (sscanf(data, "use_group %u\n", &idx) == 1 && | ||
14221 | + idx < CCS_MAX_ACL_GROUPS) { | ||
14222 | + if (!is_delete) | ||
14223 | + set_bit(idx, domain->group); | ||
14224 | + else | ||
14225 | + clear_bit(idx, domain->group); | ||
14226 | + return 0; | ||
14227 | + } | ||
14228 | + for (idx = 0; idx < CCS_MAX_DOMAIN_INFO_FLAGS; idx++) { | ||
14229 | + const char *cp = ccs_dif[idx]; | ||
14230 | + if (strncmp(data, cp, strlen(cp) - 1)) | ||
14231 | + continue; | ||
14232 | + domain->flags[idx] = !is_delete; | ||
14233 | + return 0; | ||
14234 | + } | ||
14235 | + return ccs_write_acl(ns, &domain->acl_info_list, data, is_delete); | ||
14236 | +} | ||
14237 | + | ||
14238 | +/** | ||
14239 | + * ccs_print_name_union - Print a ccs_name_union. | ||
14240 | + * | ||
14241 | + * @head: Pointer to "struct ccs_io_buffer". | ||
14242 | + * @ptr: Pointer to "struct ccs_name_union". | ||
14243 | + * | ||
14244 | + * Returns nothing. | ||
14245 | + */ | ||
14246 | +static void ccs_print_name_union(struct ccs_io_buffer *head, | ||
14247 | + const struct ccs_name_union *ptr) | ||
14248 | +{ | ||
14249 | + ccs_set_space(head); | ||
14250 | + if (!ccs_print_group(head, ptr->group)) | ||
14251 | + ccs_set_string(head, ptr->filename->name); | ||
14252 | +} | ||
14253 | + | ||
14254 | +/** | ||
14255 | + * ccs_print_name_union_quoted - Print a ccs_name_union with a quote. | ||
14256 | + * | ||
14257 | + * @head: Pointer to "struct ccs_io_buffer". | ||
14258 | + * @ptr: Pointer to "struct ccs_name_union". | ||
14259 | + * | ||
14260 | + * Returns nothing. | ||
14261 | + */ | ||
14262 | +static void ccs_print_name_union_quoted(struct ccs_io_buffer *head, | ||
14263 | + const struct ccs_name_union *ptr) | ||
14264 | +{ | ||
14265 | + if (!ccs_print_group(head, ptr->group)) { | ||
14266 | + ccs_set_string(head, "\""); | ||
14267 | + ccs_set_string(head, ptr->filename->name); | ||
14268 | + ccs_set_string(head, "\""); | ||
14269 | + } | ||
14270 | +} | ||
14271 | + | ||
14272 | +/** | ||
14273 | + * ccs_print_number_union_nospace - Print a ccs_number_union without a space. | ||
14274 | + * | ||
14275 | + * @head: Pointer to "struct ccs_io_buffer". | ||
14276 | + * @ptr: Pointer to "struct ccs_number_union". | ||
14277 | + * | ||
14278 | + * Returns nothing. | ||
14279 | + */ | ||
14280 | +static void ccs_print_number_union_nospace(struct ccs_io_buffer *head, | ||
14281 | + const struct ccs_number_union *ptr) | ||
14282 | +{ | ||
14283 | + if (!ccs_print_group(head, ptr->group)) { | ||
14284 | + int i; | ||
14285 | + unsigned long min = ptr->values[0]; | ||
14286 | + const unsigned long max = ptr->values[1]; | ||
14287 | + u8 min_type = ptr->value_type[0]; | ||
14288 | + const u8 max_type = ptr->value_type[1]; | ||
14289 | + char buffer[128]; | ||
14290 | + buffer[0] = '\0'; | ||
14291 | + for (i = 0; i < 2; i++) { | ||
14292 | + switch (min_type) { | ||
14293 | + case CCS_VALUE_TYPE_HEXADECIMAL: | ||
14294 | + ccs_addprintf(buffer, sizeof(buffer), "0x%lX", | ||
14295 | + min); | ||
14296 | + break; | ||
14297 | + case CCS_VALUE_TYPE_OCTAL: | ||
14298 | + ccs_addprintf(buffer, sizeof(buffer), "0%lo", | ||
14299 | + min); | ||
14300 | + break; | ||
14301 | + default: | ||
14302 | + ccs_addprintf(buffer, sizeof(buffer), "%lu", | ||
14303 | + min); | ||
14304 | + break; | ||
14305 | + } | ||
14306 | + if (min == max && min_type == max_type) | ||
14307 | + break; | ||
14308 | + ccs_addprintf(buffer, sizeof(buffer), "-"); | ||
14309 | + min_type = max_type; | ||
14310 | + min = max; | ||
14311 | + } | ||
14312 | + ccs_io_printf(head, "%s", buffer); | ||
14313 | + } | ||
14314 | +} | ||
14315 | + | ||
14316 | +/** | ||
14317 | + * ccs_print_number_union - Print a ccs_number_union. | ||
14318 | + * | ||
14319 | + * @head: Pointer to "struct ccs_io_buffer". | ||
14320 | + * @ptr: Pointer to "struct ccs_number_union". | ||
14321 | + * | ||
14322 | + * Returns nothing. | ||
14323 | + */ | ||
14324 | +static void ccs_print_number_union(struct ccs_io_buffer *head, | ||
14325 | + const struct ccs_number_union *ptr) | ||
14326 | +{ | ||
14327 | + ccs_set_space(head); | ||
14328 | + ccs_print_number_union_nospace(head, ptr); | ||
14329 | +} | ||
14330 | + | ||
14331 | +/** | ||
14332 | + * ccs_print_condition - Print condition part. | ||
14333 | + * | ||
14334 | + * @head: Pointer to "struct ccs_io_buffer". | ||
14335 | + * @cond: Pointer to "struct ccs_condition". | ||
14336 | + * | ||
14337 | + * Returns true on success, false otherwise. | ||
14338 | + */ | ||
14339 | +static bool ccs_print_condition(struct ccs_io_buffer *head, | ||
14340 | + const struct ccs_condition *cond) | ||
14341 | +{ | ||
14342 | + switch (head->r.cond_step) { | ||
14343 | + case 0: | ||
14344 | + head->r.cond_index = 0; | ||
14345 | + head->r.cond_step++; | ||
14346 | + if (cond->transit && cond->exec_transit) { | ||
14347 | + ccs_set_space(head); | ||
14348 | + ccs_set_string(head, cond->transit->name); | ||
14349 | + } | ||
14350 | + /* fall through */ | ||
14351 | + case 1: | ||
14352 | + { | ||
14353 | + const u16 condc = cond->condc; | ||
14354 | + const struct ccs_condition_element *condp = | ||
14355 | + (typeof(condp)) (cond + 1); | ||
14356 | + const struct ccs_number_union *numbers_p = | ||
14357 | + (typeof(numbers_p)) (condp + condc); | ||
14358 | + const struct ccs_name_union *names_p = | ||
14359 | + (typeof(names_p)) | ||
14360 | + (numbers_p + cond->numbers_count); | ||
14361 | + const struct ccs_argv *argv = | ||
14362 | + (typeof(argv)) (names_p + cond->names_count); | ||
14363 | + const struct ccs_envp *envp = | ||
14364 | + (typeof(envp)) (argv + cond->argc); | ||
14365 | + u16 skip; | ||
14366 | + for (skip = 0; skip < head->r.cond_index; skip++) { | ||
14367 | + const u8 left = condp->left; | ||
14368 | + const u8 right = condp->right; | ||
14369 | + condp++; | ||
14370 | + switch (left) { | ||
14371 | + case CCS_ARGV_ENTRY: | ||
14372 | + argv++; | ||
14373 | + continue; | ||
14374 | + case CCS_ENVP_ENTRY: | ||
14375 | + envp++; | ||
14376 | + continue; | ||
14377 | + case CCS_NUMBER_UNION: | ||
14378 | + numbers_p++; | ||
14379 | + break; | ||
14380 | + } | ||
14381 | + switch (right) { | ||
14382 | + case CCS_NAME_UNION: | ||
14383 | + names_p++; | ||
14384 | + break; | ||
14385 | + case CCS_NUMBER_UNION: | ||
14386 | + numbers_p++; | ||
14387 | + break; | ||
14388 | + } | ||
14389 | + } | ||
14390 | + while (head->r.cond_index < condc) { | ||
14391 | + const u8 match = condp->equals; | ||
14392 | + const u8 left = condp->left; | ||
14393 | + const u8 right = condp->right; | ||
14394 | + if (!ccs_flush(head)) | ||
14395 | + return false; | ||
14396 | + condp++; | ||
14397 | + head->r.cond_index++; | ||
14398 | + ccs_set_space(head); | ||
14399 | + switch (left) { | ||
14400 | + case CCS_ARGV_ENTRY: | ||
14401 | + ccs_io_printf(head, | ||
14402 | + "exec.argv[%lu]%s=\"", | ||
14403 | + argv->index, | ||
14404 | + argv->is_not ? "!" : ""); | ||
14405 | + ccs_set_string(head, | ||
14406 | + argv->value->name); | ||
14407 | + ccs_set_string(head, "\""); | ||
14408 | + argv++; | ||
14409 | + continue; | ||
14410 | + case CCS_ENVP_ENTRY: | ||
14411 | + ccs_set_string(head, "exec.envp[\""); | ||
14412 | + ccs_set_string(head, envp->name->name); | ||
14413 | + ccs_io_printf(head, "\"]%s=", | ||
14414 | + envp->is_not ? "!" : ""); | ||
14415 | + if (envp->value) { | ||
14416 | + ccs_set_string(head, "\""); | ||
14417 | + ccs_set_string(head, envp-> | ||
14418 | + value->name); | ||
14419 | + ccs_set_string(head, "\""); | ||
14420 | + } else { | ||
14421 | + ccs_set_string(head, "NULL"); | ||
14422 | + } | ||
14423 | + envp++; | ||
14424 | + continue; | ||
14425 | + case CCS_NUMBER_UNION: | ||
14426 | + ccs_print_number_union_nospace | ||
14427 | + (head, numbers_p++); | ||
14428 | + break; | ||
14429 | + default: | ||
14430 | + ccs_set_string(head, | ||
14431 | + ccs_condition_keyword[left]); | ||
14432 | + break; | ||
14433 | + } | ||
14434 | + ccs_set_string(head, match ? "=" : "!="); | ||
14435 | + switch (right) { | ||
14436 | + case CCS_NAME_UNION: | ||
14437 | + ccs_print_name_union_quoted | ||
14438 | + (head, names_p++); | ||
14439 | + break; | ||
14440 | + case CCS_NUMBER_UNION: | ||
14441 | + ccs_print_number_union_nospace | ||
14442 | + (head, numbers_p++); | ||
14443 | + break; | ||
14444 | + default: | ||
14445 | + ccs_set_string(head, | ||
14446 | + ccs_condition_keyword[right]); | ||
14447 | + break; | ||
14448 | + } | ||
14449 | + } | ||
14450 | + } | ||
14451 | + head->r.cond_step++; | ||
14452 | + /* fall through */ | ||
14453 | + case 2: | ||
14454 | + if (!ccs_flush(head)) | ||
14455 | + break; | ||
14456 | + head->r.cond_step++; | ||
14457 | + /* fall through */ | ||
14458 | + case 3: | ||
14459 | + if (cond->grant_log != CCS_GRANTLOG_AUTO) | ||
14460 | + ccs_io_printf(head, " grant_log=%s", | ||
14461 | + ccs_yesno(cond->grant_log == | ||
14462 | + CCS_GRANTLOG_YES)); | ||
14463 | + if (cond->transit && !cond->exec_transit) { | ||
14464 | + const char *name = cond->transit->name; | ||
14465 | + ccs_set_string(head, " auto_domain_transition=\""); | ||
14466 | + ccs_set_string(head, name); | ||
14467 | + ccs_set_string(head, "\""); | ||
14468 | + } | ||
14469 | + ccs_set_lf(head); | ||
14470 | + return true; | ||
14471 | + } | ||
14472 | + return false; | ||
14473 | +} | ||
14474 | + | ||
14475 | +/** | ||
14476 | + * ccs_set_group - Print "acl_group " header keyword and category name. | ||
14477 | + * | ||
14478 | + * @head: Pointer to "struct ccs_io_buffer". | ||
14479 | + * @category: Category name. | ||
14480 | + * | ||
14481 | + * Returns nothing. | ||
14482 | + */ | ||
14483 | +static void ccs_set_group(struct ccs_io_buffer *head, const char *category) | ||
14484 | +{ | ||
14485 | + if (head->type == CCS_EXCEPTION_POLICY) { | ||
14486 | + ccs_print_namespace(head); | ||
14487 | + ccs_io_printf(head, "acl_group %u ", head->r.acl_group_index); | ||
14488 | + } | ||
14489 | + ccs_set_string(head, category); | ||
14490 | +} | ||
14491 | + | ||
14492 | +/** | ||
14493 | + * ccs_print_entry - Print an ACL entry. | ||
14494 | + * | ||
14495 | + * @head: Pointer to "struct ccs_io_buffer". | ||
14496 | + * @acl: Pointer to an ACL entry. | ||
14497 | + * | ||
14498 | + * Returns true on success, false otherwise. | ||
14499 | + */ | ||
14500 | +static bool ccs_print_entry(struct ccs_io_buffer *head, | ||
14501 | + const struct ccs_acl_info *acl) | ||
14502 | +{ | ||
14503 | + const u8 acl_type = acl->type; | ||
14504 | + const bool may_trigger_transition = acl->cond && acl->cond->transit; | ||
14505 | + bool first = true; | ||
14506 | + u8 bit; | ||
14507 | + if (head->r.print_cond_part) | ||
14508 | + goto print_cond_part; | ||
14509 | + if (acl->is_deleted) | ||
14510 | + return true; | ||
14511 | + if (!ccs_flush(head)) | ||
14512 | + return false; | ||
14513 | + else if (acl_type == CCS_TYPE_PATH_ACL) { | ||
14514 | + struct ccs_path_acl *ptr | ||
14515 | + = container_of(acl, typeof(*ptr), head); | ||
14516 | + for (bit = 0; bit < CCS_MAX_PATH_OPERATION; bit++) { | ||
14517 | + if (!(acl->perm & (1 << bit))) | ||
14518 | + continue; | ||
14519 | + if (head->r.print_transition_related_only && | ||
14520 | + bit != CCS_TYPE_EXECUTE && !may_trigger_transition) | ||
14521 | + continue; | ||
14522 | + if (first) { | ||
14523 | + ccs_set_group(head, "file "); | ||
14524 | + first = false; | ||
14525 | + } else { | ||
14526 | + ccs_set_slash(head); | ||
14527 | + } | ||
14528 | + ccs_set_string(head, ccs_path_keyword[bit]); | ||
14529 | + } | ||
14530 | + if (first) | ||
14531 | + return true; | ||
14532 | + ccs_print_name_union(head, &ptr->name); | ||
14533 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
14534 | + } else if (acl_type == CCS_TYPE_AUTO_EXECUTE_HANDLER || | ||
14535 | + acl_type == CCS_TYPE_DENIED_EXECUTE_HANDLER) { | ||
14536 | + struct ccs_handler_acl *ptr | ||
14537 | + = container_of(acl, typeof(*ptr), head); | ||
14538 | + ccs_set_group(head, "task "); | ||
14539 | + ccs_set_string(head, acl_type == CCS_TYPE_AUTO_EXECUTE_HANDLER | ||
14540 | + ? "auto_execute_handler " : | ||
14541 | + "denied_execute_handler "); | ||
14542 | + ccs_set_string(head, ptr->handler->name); | ||
14543 | +#endif | ||
14544 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
14545 | + } else if (acl_type == CCS_TYPE_AUTO_TASK_ACL || | ||
14546 | + acl_type == CCS_TYPE_MANUAL_TASK_ACL) { | ||
14547 | + struct ccs_task_acl *ptr = | ||
14548 | + container_of(acl, typeof(*ptr), head); | ||
14549 | + ccs_set_group(head, "task "); | ||
14550 | + ccs_set_string(head, acl_type == CCS_TYPE_AUTO_TASK_ACL ? | ||
14551 | + "auto_domain_transition " : | ||
14552 | + "manual_domain_transition "); | ||
14553 | + ccs_set_string(head, ptr->domainname->name); | ||
14554 | +#endif | ||
14555 | + } else if (head->r.print_transition_related_only && | ||
14556 | + !may_trigger_transition) { | ||
14557 | + return true; | ||
14558 | + } else if (acl_type == CCS_TYPE_MKDEV_ACL) { | ||
14559 | + struct ccs_mkdev_acl *ptr = | ||
14560 | + container_of(acl, typeof(*ptr), head); | ||
14561 | + for (bit = 0; bit < CCS_MAX_MKDEV_OPERATION; bit++) { | ||
14562 | + if (!(acl->perm & (1 << bit))) | ||
14563 | + continue; | ||
14564 | + if (first) { | ||
14565 | + ccs_set_group(head, "file "); | ||
14566 | + first = false; | ||
14567 | + } else { | ||
14568 | + ccs_set_slash(head); | ||
14569 | + } | ||
14570 | + ccs_set_string(head, ccs_mac_keywords | ||
14571 | + [ccs_pnnn2mac[bit]]); | ||
14572 | + } | ||
14573 | + if (first) | ||
14574 | + return true; | ||
14575 | + ccs_print_name_union(head, &ptr->name); | ||
14576 | + ccs_print_number_union(head, &ptr->mode); | ||
14577 | + ccs_print_number_union(head, &ptr->major); | ||
14578 | + ccs_print_number_union(head, &ptr->minor); | ||
14579 | + } else if (acl_type == CCS_TYPE_PATH2_ACL) { | ||
14580 | + struct ccs_path2_acl *ptr = | ||
14581 | + container_of(acl, typeof(*ptr), head); | ||
14582 | + for (bit = 0; bit < CCS_MAX_PATH2_OPERATION; bit++) { | ||
14583 | + if (!(acl->perm & (1 << bit))) | ||
14584 | + continue; | ||
14585 | + if (first) { | ||
14586 | + ccs_set_group(head, "file "); | ||
14587 | + first = false; | ||
14588 | + } else { | ||
14589 | + ccs_set_slash(head); | ||
14590 | + } | ||
14591 | + ccs_set_string(head, ccs_mac_keywords | ||
14592 | + [ccs_pp2mac[bit]]); | ||
14593 | + } | ||
14594 | + if (first) | ||
14595 | + return true; | ||
14596 | + ccs_print_name_union(head, &ptr->name1); | ||
14597 | + ccs_print_name_union(head, &ptr->name2); | ||
14598 | + } else if (acl_type == CCS_TYPE_PATH_NUMBER_ACL) { | ||
14599 | + struct ccs_path_number_acl *ptr = | ||
14600 | + container_of(acl, typeof(*ptr), head); | ||
14601 | + for (bit = 0; bit < CCS_MAX_PATH_NUMBER_OPERATION; bit++) { | ||
14602 | + if (!(acl->perm & (1 << bit))) | ||
14603 | + continue; | ||
14604 | + if (first) { | ||
14605 | + ccs_set_group(head, "file "); | ||
14606 | + first = false; | ||
14607 | + } else { | ||
14608 | + ccs_set_slash(head); | ||
14609 | + } | ||
14610 | + ccs_set_string(head, ccs_mac_keywords | ||
14611 | + [ccs_pn2mac[bit]]); | ||
14612 | + } | ||
14613 | + if (first) | ||
14614 | + return true; | ||
14615 | + ccs_print_name_union(head, &ptr->name); | ||
14616 | + ccs_print_number_union(head, &ptr->number); | ||
14617 | +#ifdef CONFIG_CCSECURITY_MISC | ||
14618 | + } else if (acl_type == CCS_TYPE_ENV_ACL) { | ||
14619 | + struct ccs_env_acl *ptr = | ||
14620 | + container_of(acl, typeof(*ptr), head); | ||
14621 | + ccs_set_group(head, "misc env "); | ||
14622 | + ccs_set_string(head, ptr->env->name); | ||
14623 | +#endif | ||
14624 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
14625 | + } else if (acl_type == CCS_TYPE_CAPABILITY_ACL) { | ||
14626 | + struct ccs_capability_acl *ptr = | ||
14627 | + container_of(acl, typeof(*ptr), head); | ||
14628 | + ccs_set_group(head, "capability "); | ||
14629 | + ccs_set_string(head, ccs_mac_keywords | ||
14630 | + [ccs_c2mac[ptr->operation]]); | ||
14631 | +#endif | ||
14632 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
14633 | + } else if (acl_type == CCS_TYPE_INET_ACL) { | ||
14634 | + struct ccs_inet_acl *ptr = | ||
14635 | + container_of(acl, typeof(*ptr), head); | ||
14636 | + for (bit = 0; bit < CCS_MAX_NETWORK_OPERATION; bit++) { | ||
14637 | + if (!(acl->perm & (1 << bit))) | ||
14638 | + continue; | ||
14639 | + if (first) { | ||
14640 | + ccs_set_group(head, "network inet "); | ||
14641 | + ccs_set_string(head, ccs_proto_keyword | ||
14642 | + [ptr->protocol]); | ||
14643 | + ccs_set_space(head); | ||
14644 | + first = false; | ||
14645 | + } else { | ||
14646 | + ccs_set_slash(head); | ||
14647 | + } | ||
14648 | + ccs_set_string(head, ccs_socket_keyword[bit]); | ||
14649 | + } | ||
14650 | + if (first) | ||
14651 | + return true; | ||
14652 | + ccs_set_space(head); | ||
14653 | + if (!ccs_print_group(head, ptr->address.group)) { | ||
14654 | + char buf[128]; | ||
14655 | + ccs_print_ip(buf, sizeof(buf), &ptr->address); | ||
14656 | + ccs_io_printf(head, "%s", buf); | ||
14657 | + } | ||
14658 | + ccs_print_number_union(head, &ptr->port); | ||
14659 | + } else if (acl_type == CCS_TYPE_UNIX_ACL) { | ||
14660 | + struct ccs_unix_acl *ptr = | ||
14661 | + container_of(acl, typeof(*ptr), head); | ||
14662 | + for (bit = 0; bit < CCS_MAX_NETWORK_OPERATION; bit++) { | ||
14663 | + if (!(acl->perm & (1 << bit))) | ||
14664 | + continue; | ||
14665 | + if (first) { | ||
14666 | + ccs_set_group(head, "network unix "); | ||
14667 | + ccs_set_string(head, ccs_proto_keyword | ||
14668 | + [ptr->protocol]); | ||
14669 | + ccs_set_space(head); | ||
14670 | + first = false; | ||
14671 | + } else { | ||
14672 | + ccs_set_slash(head); | ||
14673 | + } | ||
14674 | + ccs_set_string(head, ccs_socket_keyword[bit]); | ||
14675 | + } | ||
14676 | + if (first) | ||
14677 | + return true; | ||
14678 | + ccs_print_name_union(head, &ptr->name); | ||
14679 | +#endif | ||
14680 | +#ifdef CONFIG_CCSECURITY_IPC | ||
14681 | + } else if (acl_type == CCS_TYPE_SIGNAL_ACL) { | ||
14682 | + struct ccs_signal_acl *ptr = | ||
14683 | + container_of(acl, typeof(*ptr), head); | ||
14684 | + ccs_set_group(head, "ipc signal "); | ||
14685 | + ccs_print_number_union_nospace(head, &ptr->sig); | ||
14686 | + ccs_set_space(head); | ||
14687 | + ccs_set_string(head, ptr->domainname->name); | ||
14688 | +#endif | ||
14689 | + } else if (acl_type == CCS_TYPE_MOUNT_ACL) { | ||
14690 | + struct ccs_mount_acl *ptr = | ||
14691 | + container_of(acl, typeof(*ptr), head); | ||
14692 | + ccs_set_group(head, "file mount"); | ||
14693 | + ccs_print_name_union(head, &ptr->dev_name); | ||
14694 | + ccs_print_name_union(head, &ptr->dir_name); | ||
14695 | + ccs_print_name_union(head, &ptr->fs_type); | ||
14696 | + ccs_print_number_union(head, &ptr->flags); | ||
14697 | + } | ||
14698 | + if (acl->cond) { | ||
14699 | + head->r.print_cond_part = true; | ||
14700 | + head->r.cond_step = 0; | ||
14701 | + if (!ccs_flush(head)) | ||
14702 | + return false; | ||
14703 | +print_cond_part: | ||
14704 | + if (!ccs_print_condition(head, acl->cond)) | ||
14705 | + return false; | ||
14706 | + head->r.print_cond_part = false; | ||
14707 | + } else { | ||
14708 | + ccs_set_lf(head); | ||
14709 | + } | ||
14710 | + return true; | ||
14711 | +} | ||
14712 | + | ||
14713 | +/** | ||
14714 | + * ccs_read_acl - Read "struct ccs_acl_info" list. | ||
14715 | + * | ||
14716 | + * @head: Pointer to "struct ccs_io_buffer". | ||
14717 | + * @list: Pointer to "struct list_head". | ||
14718 | + * | ||
14719 | + * Returns true on success, false otherwise. | ||
14720 | + * | ||
14721 | + * Caller holds ccs_read_lock(). | ||
14722 | + */ | ||
14723 | +static bool ccs_read_acl(struct ccs_io_buffer *head, struct list_head *list) | ||
14724 | +{ | ||
14725 | + list_for_each_cookie(head->r.acl, list) { | ||
14726 | + struct ccs_acl_info *ptr = | ||
14727 | + list_entry(head->r.acl, typeof(*ptr), list); | ||
14728 | + if (!ccs_print_entry(head, ptr)) | ||
14729 | + return false; | ||
14730 | + } | ||
14731 | + head->r.acl = NULL; | ||
14732 | + return true; | ||
14733 | +} | ||
14734 | + | ||
14735 | +/** | ||
14736 | + * ccs_read_domain - Read domain policy. | ||
14737 | + * | ||
14738 | + * @head: Pointer to "struct ccs_io_buffer". | ||
14739 | + * | ||
14740 | + * Returns nothing. | ||
14741 | + * | ||
14742 | + * Caller holds ccs_read_lock(). | ||
14743 | + */ | ||
14744 | +static void ccs_read_domain(struct ccs_io_buffer *head) | ||
14745 | +{ | ||
14746 | + if (head->r.eof) | ||
14747 | + return; | ||
14748 | + list_for_each_cookie(head->r.domain, &ccs_domain_list) { | ||
14749 | + struct ccs_domain_info *domain = | ||
14750 | + list_entry(head->r.domain, typeof(*domain), list); | ||
14751 | + switch (head->r.step) { | ||
14752 | + u8 i; | ||
14753 | + case 0: | ||
14754 | + if (domain->is_deleted && | ||
14755 | + !head->r.print_this_domain_only) | ||
14756 | + continue; | ||
14757 | + /* Print domainname and flags. */ | ||
14758 | + ccs_set_string(head, domain->domainname->name); | ||
14759 | + ccs_set_lf(head); | ||
14760 | + ccs_io_printf(head, "use_profile %u\n", | ||
14761 | + domain->profile); | ||
14762 | + for (i = 0; i < CCS_MAX_DOMAIN_INFO_FLAGS; i++) | ||
14763 | + if (domain->flags[i]) | ||
14764 | + ccs_set_string(head, ccs_dif[i]); | ||
14765 | + head->r.index = 0; | ||
14766 | + head->r.step++; | ||
14767 | + /* fall through */ | ||
14768 | + case 1: | ||
14769 | + while (head->r.index < CCS_MAX_ACL_GROUPS) { | ||
14770 | + i = head->r.index++; | ||
14771 | + if (!test_bit(i, domain->group)) | ||
14772 | + continue; | ||
14773 | + ccs_io_printf(head, "use_group %u\n", i); | ||
14774 | + if (!ccs_flush(head)) | ||
14775 | + return; | ||
14776 | + } | ||
14777 | + head->r.index = 0; | ||
14778 | + head->r.step++; | ||
14779 | + ccs_set_lf(head); | ||
14780 | + /* fall through */ | ||
14781 | + case 2: | ||
14782 | + if (!ccs_read_acl(head, &domain->acl_info_list)) | ||
14783 | + return; | ||
14784 | + head->r.step++; | ||
14785 | + if (!ccs_set_lf(head)) | ||
14786 | + return; | ||
14787 | + /* fall through */ | ||
14788 | + case 3: | ||
14789 | + head->r.step = 0; | ||
14790 | + if (head->r.print_this_domain_only) | ||
14791 | + goto done; | ||
14792 | + } | ||
14793 | + } | ||
14794 | +done: | ||
14795 | + head->r.eof = true; | ||
14796 | +} | ||
14797 | + | ||
14798 | +/** | ||
14799 | + * ccs_write_pid - Specify PID to obtain domainname. | ||
14800 | + * | ||
14801 | + * @head: Pointer to "struct ccs_io_buffer". | ||
14802 | + * | ||
14803 | + * Returns 0. | ||
14804 | + */ | ||
14805 | +static int ccs_write_pid(struct ccs_io_buffer *head) | ||
14806 | +{ | ||
14807 | + head->r.eof = false; | ||
14808 | + return 0; | ||
14809 | +} | ||
14810 | + | ||
14811 | +/** | ||
14812 | + * ccs_read_pid - Read information of a process. | ||
14813 | + * | ||
14814 | + * @head: Pointer to "struct ccs_io_buffer". | ||
14815 | + * | ||
14816 | + * Returns the domainname which the specified PID is in or | ||
14817 | + * process information of the specified PID on success, | ||
14818 | + * empty string otherwise. | ||
14819 | + * | ||
14820 | + * Caller holds ccs_read_lock(). | ||
14821 | + */ | ||
14822 | +static void ccs_read_pid(struct ccs_io_buffer *head) | ||
14823 | +{ | ||
14824 | + char *buf = head->write_buf; | ||
14825 | + bool task_info = false; | ||
14826 | + bool global_pid = false; | ||
14827 | + unsigned int pid; | ||
14828 | + struct task_struct *p; | ||
14829 | + struct ccs_domain_info *domain = NULL; | ||
14830 | + u32 ccs_flags = 0; | ||
14831 | + /* Accessing write_buf is safe because head->io_sem is held. */ | ||
14832 | + if (!buf) { | ||
14833 | + head->r.eof = true; | ||
14834 | + return; /* Do nothing if open(O_RDONLY). */ | ||
14835 | + } | ||
14836 | + if (head->r.w_pos || head->r.eof) | ||
14837 | + return; | ||
14838 | + head->r.eof = true; | ||
14839 | + if (ccs_str_starts(&buf, "info ")) | ||
14840 | + task_info = true; | ||
14841 | + if (ccs_str_starts(&buf, "global-pid ")) | ||
14842 | + global_pid = true; | ||
14843 | + pid = (unsigned int) simple_strtoul(buf, NULL, 10); | ||
14844 | + ccs_tasklist_lock(); | ||
14845 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | ||
14846 | + if (global_pid) | ||
14847 | + p = ccsecurity_exports.find_task_by_pid_ns(pid, &init_pid_ns); | ||
14848 | + else | ||
14849 | + p = ccsecurity_exports.find_task_by_vpid(pid); | ||
14850 | +#else | ||
14851 | + p = find_task_by_pid(pid); | ||
14852 | +#endif | ||
14853 | + if (p) { | ||
14854 | + domain = ccs_task_domain(p); | ||
14855 | + ccs_flags = ccs_task_flags(p); | ||
14856 | + } | ||
14857 | + ccs_tasklist_unlock(); | ||
14858 | + if (!domain) | ||
14859 | + return; | ||
14860 | + if (!task_info) { | ||
14861 | + ccs_io_printf(head, "%u %u ", pid, domain->profile); | ||
14862 | + ccs_set_string(head, domain->domainname->name); | ||
14863 | + } else { | ||
14864 | + ccs_io_printf(head, "%u manager=%s execute_handler=%s ", pid, | ||
14865 | + ccs_yesno(ccs_flags & | ||
14866 | + CCS_TASK_IS_MANAGER), | ||
14867 | + ccs_yesno(ccs_flags & | ||
14868 | + CCS_TASK_IS_EXECUTE_HANDLER)); | ||
14869 | + } | ||
14870 | +} | ||
14871 | + | ||
14872 | +/** | ||
14873 | + * ccs_write_group - Write "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group" list. | ||
14874 | + * | ||
14875 | + * @param: Pointer to "struct ccs_acl_param". | ||
14876 | + * @type: Type of this group. | ||
14877 | + * | ||
14878 | + * Returns 0 on success, negative value otherwise. | ||
14879 | + */ | ||
14880 | +static int ccs_write_group(struct ccs_acl_param *param, const u8 type) | ||
14881 | +{ | ||
14882 | + struct ccs_group *group = ccs_get_group(param, type); | ||
14883 | + int error = -EINVAL; | ||
14884 | + if (!group) | ||
14885 | + return -ENOMEM; | ||
14886 | + param->list = &group->member_list; | ||
14887 | + if (type == CCS_PATH_GROUP) { | ||
14888 | + struct ccs_path_group *e = ¶m->e.path_group; | ||
14889 | + e->member_name = ccs_get_name(ccs_read_token(param)); | ||
14890 | + if (!e->member_name) { | ||
14891 | + error = -ENOMEM; | ||
14892 | + goto out; | ||
14893 | + } | ||
14894 | + error = ccs_update_policy(sizeof(*e), param); | ||
14895 | + ccs_put_name(e->member_name); | ||
14896 | + } else if (type == CCS_NUMBER_GROUP) { | ||
14897 | + struct ccs_number_group *e = ¶m->e.number_group; | ||
14898 | + if (param->data[0] == '@' || | ||
14899 | + !ccs_parse_number_union(param, &e->number)) | ||
14900 | + goto out; | ||
14901 | + error = ccs_update_policy(sizeof(*e), param); | ||
14902 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
14903 | + } else { | ||
14904 | + struct ccs_address_group *e = ¶m->e.address_group; | ||
14905 | + if (param->data[0] == '@' || | ||
14906 | + !ccs_parse_ipaddr_union(param, &e->address)) | ||
14907 | + goto out; | ||
14908 | + error = ccs_update_policy(sizeof(*e), param); | ||
14909 | +#endif | ||
14910 | + } | ||
14911 | +out: | ||
14912 | + ccs_put_group(group); | ||
14913 | + return error; | ||
14914 | +} | ||
14915 | + | ||
14916 | +#ifdef CONFIG_CCSECURITY_PORTRESERVE | ||
14917 | +/** | ||
14918 | + * ccs_lport_reserved - Check whether local port is reserved or not. | ||
14919 | + * | ||
14920 | + * @port: Port number. | ||
14921 | + * | ||
14922 | + * Returns true if local port is reserved, false otherwise. | ||
14923 | + */ | ||
14924 | +static bool __ccs_lport_reserved(const u16 port) | ||
14925 | +{ | ||
14926 | + return ccs_reserved_port_map[port >> 3] & (1 << (port & 7)) | ||
14927 | + ? true : false; | ||
14928 | +} | ||
14929 | + | ||
14930 | +/** | ||
14931 | + * ccs_write_reserved_port - Update "struct ccs_reserved" list. | ||
14932 | + * | ||
14933 | + * @param: Pointer to "struct ccs_acl_param". | ||
14934 | + * | ||
14935 | + * Returns 0 on success, negative value otherwise. | ||
14936 | + * | ||
14937 | + * Caller holds ccs_read_lock(). | ||
14938 | + */ | ||
14939 | +static int ccs_write_reserved_port(struct ccs_acl_param *param) | ||
14940 | +{ | ||
14941 | + struct ccs_reserved *e = ¶m->e.reserved; | ||
14942 | + struct ccs_policy_namespace *ns = param->ns; | ||
14943 | + int error; | ||
14944 | + u8 *tmp; | ||
14945 | + if (param->data[0] == '@' || | ||
14946 | + !ccs_parse_number_union(param, &e->port) || | ||
14947 | + e->port.values[1] > 65535 || param->data[0]) | ||
14948 | + return -EINVAL; | ||
14949 | + param->list = &ns->policy_list[CCS_ID_RESERVEDPORT]; | ||
14950 | + error = ccs_update_policy(sizeof(*e), param); | ||
14951 | + if (error) | ||
14952 | + return error; | ||
14953 | + tmp = kzalloc(sizeof(ccs_reserved_port_map), CCS_GFP_FLAGS); | ||
14954 | + if (!tmp) | ||
14955 | + return -ENOMEM; | ||
14956 | + list_for_each_entry_srcu(ns, &ccs_namespace_list, namespace_list, | ||
14957 | + &ccs_ss) { | ||
14958 | + struct ccs_reserved *ptr; | ||
14959 | + struct list_head *list = &ns->policy_list[CCS_ID_RESERVEDPORT]; | ||
14960 | + list_for_each_entry_srcu(ptr, list, head.list, &ccs_ss) { | ||
14961 | + unsigned int port; | ||
14962 | + if (ptr->head.is_deleted) | ||
14963 | + continue; | ||
14964 | + for (port = ptr->port.values[0]; | ||
14965 | + port <= ptr->port.values[1]; port++) | ||
14966 | + tmp[port >> 3] |= 1 << (port & 7); | ||
14967 | + } | ||
14968 | + } | ||
14969 | + memmove(ccs_reserved_port_map, tmp, sizeof(ccs_reserved_port_map)); | ||
14970 | + kfree(tmp); | ||
14971 | + /* | ||
14972 | + * Since this feature is no-op by default, we don't need to register | ||
14973 | + * this callback hook unless the first entry is added. | ||
14974 | + */ | ||
14975 | + ccsecurity_ops.lport_reserved = __ccs_lport_reserved; | ||
14976 | + return 0; | ||
14977 | +} | ||
14978 | +#endif | ||
14979 | + | ||
14980 | +/** | ||
14981 | + * ccs_write_aggregator - Write "struct ccs_aggregator" list. | ||
14982 | + * | ||
14983 | + * @param: Pointer to "struct ccs_acl_param". | ||
14984 | + * | ||
14985 | + * Returns 0 on success, negative value otherwise. | ||
14986 | + */ | ||
14987 | +static int ccs_write_aggregator(struct ccs_acl_param *param) | ||
14988 | +{ | ||
14989 | + struct ccs_aggregator *e = ¶m->e.aggregator; | ||
14990 | + int error = param->is_delete ? -ENOENT : -ENOMEM; | ||
14991 | + const char *original_name = ccs_read_token(param); | ||
14992 | + const char *aggregated_name = ccs_read_token(param); | ||
14993 | + if (!ccs_correct_word(original_name) || | ||
14994 | + !ccs_correct_path(aggregated_name)) | ||
14995 | + return -EINVAL; | ||
14996 | + e->original_name = ccs_get_name(original_name); | ||
14997 | + e->aggregated_name = ccs_get_name(aggregated_name); | ||
14998 | + if (!e->original_name || !e->aggregated_name || | ||
14999 | + e->aggregated_name->is_patterned) /* No patterns allowed. */ | ||
15000 | + goto out; | ||
15001 | + param->list = ¶m->ns->policy_list[CCS_ID_AGGREGATOR]; | ||
15002 | + error = ccs_update_policy(sizeof(*e), param); | ||
15003 | +out: | ||
15004 | + ccs_put_name(e->original_name); | ||
15005 | + ccs_put_name(e->aggregated_name); | ||
15006 | + return error; | ||
15007 | +} | ||
15008 | + | ||
15009 | +/** | ||
15010 | + * ccs_write_transition_control - Write "struct ccs_transition_control" list. | ||
15011 | + * | ||
15012 | + * @param: Pointer to "struct ccs_acl_param". | ||
15013 | + * @type: Type of this entry. | ||
15014 | + * | ||
15015 | + * Returns 0 on success, negative value otherwise. | ||
15016 | + */ | ||
15017 | +static int ccs_write_transition_control(struct ccs_acl_param *param, | ||
15018 | + const u8 type) | ||
15019 | +{ | ||
15020 | + struct ccs_transition_control *e = ¶m->e.transition_control; | ||
15021 | + int error = param->is_delete ? -ENOENT : -ENOMEM; | ||
15022 | + char *program = param->data; | ||
15023 | + char *domainname = strstr(program, " from "); | ||
15024 | + e->type = type; | ||
15025 | + if (domainname) { | ||
15026 | + *domainname = '\0'; | ||
15027 | + domainname += 6; | ||
15028 | + } else if (type == CCS_TRANSITION_CONTROL_NO_KEEP || | ||
15029 | + type == CCS_TRANSITION_CONTROL_KEEP) { | ||
15030 | + domainname = program; | ||
15031 | + program = NULL; | ||
15032 | + } | ||
15033 | + if (program && strcmp(program, "any")) { | ||
15034 | + if (!ccs_correct_path(program)) | ||
15035 | + return -EINVAL; | ||
15036 | + e->program = ccs_get_name(program); | ||
15037 | + if (!e->program) | ||
15038 | + goto out; | ||
15039 | + } | ||
15040 | + if (domainname && strcmp(domainname, "any")) { | ||
15041 | + if (!ccs_correct_domain(domainname)) { | ||
15042 | + if (!ccs_correct_path(domainname)) | ||
15043 | + goto out; | ||
15044 | + e->is_last_name = true; | ||
15045 | + } | ||
15046 | + e->domainname = ccs_get_name(domainname); | ||
15047 | + if (!e->domainname) | ||
15048 | + goto out; | ||
15049 | + } | ||
15050 | + param->list = ¶m->ns->policy_list[CCS_ID_TRANSITION_CONTROL]; | ||
15051 | + error = ccs_update_policy(sizeof(*e), param); | ||
15052 | +out: | ||
15053 | + ccs_put_name(e->domainname); | ||
15054 | + ccs_put_name(e->program); | ||
15055 | + return error; | ||
15056 | +} | ||
15057 | + | ||
15058 | +/** | ||
15059 | + * ccs_write_exception - Write exception policy. | ||
15060 | + * | ||
15061 | + * @head: Pointer to "struct ccs_io_buffer". | ||
15062 | + * | ||
15063 | + * Returns 0 on success, negative value otherwise. | ||
15064 | + */ | ||
15065 | +static int ccs_write_exception(struct ccs_io_buffer *head) | ||
15066 | +{ | ||
15067 | + const bool is_delete = head->w.is_delete; | ||
15068 | + struct ccs_acl_param param = { | ||
15069 | + .ns = head->w.ns, | ||
15070 | + .is_delete = is_delete, | ||
15071 | + .data = head->write_buf, | ||
15072 | + }; | ||
15073 | + u8 i; | ||
15074 | + /* Forced zero clear for using memcmp() at ccs_update_policy(). */ | ||
15075 | + memset(¶m.e, 0, sizeof(param.e)); | ||
15076 | + if (ccs_str_starts(¶m.data, "aggregator ")) | ||
15077 | + return ccs_write_aggregator(¶m); | ||
15078 | +#ifdef CONFIG_CCSECURITY_PORTRESERVE | ||
15079 | + if (ccs_str_starts(¶m.data, "deny_autobind ")) | ||
15080 | + return ccs_write_reserved_port(¶m); | ||
15081 | +#endif | ||
15082 | + for (i = 0; i < CCS_MAX_TRANSITION_TYPE; i++) | ||
15083 | + if (ccs_str_starts(¶m.data, ccs_transition_type[i])) | ||
15084 | + return ccs_write_transition_control(¶m, i); | ||
15085 | + for (i = 0; i < CCS_MAX_GROUP; i++) | ||
15086 | + if (ccs_str_starts(¶m.data, ccs_group_name[i])) | ||
15087 | + return ccs_write_group(¶m, i); | ||
15088 | + if (ccs_str_starts(¶m.data, "acl_group ")) { | ||
15089 | + unsigned int group; | ||
15090 | + char *data; | ||
15091 | + group = simple_strtoul(param.data, &data, 10); | ||
15092 | + if (group < CCS_MAX_ACL_GROUPS && *data++ == ' ') | ||
15093 | + return ccs_write_acl(head->w.ns, | ||
15094 | + &head->w.ns->acl_group[group], | ||
15095 | + data, is_delete); | ||
15096 | + } | ||
15097 | + return -EINVAL; | ||
15098 | +} | ||
15099 | + | ||
15100 | +/** | ||
15101 | + * ccs_read_group - Read "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group" list. | ||
15102 | + * | ||
15103 | + * @head: Pointer to "struct ccs_io_buffer". | ||
15104 | + * @idx: Index number. | ||
15105 | + * | ||
15106 | + * Returns true on success, false otherwise. | ||
15107 | + * | ||
15108 | + * Caller holds ccs_read_lock(). | ||
15109 | + */ | ||
15110 | +static bool ccs_read_group(struct ccs_io_buffer *head, const int idx) | ||
15111 | +{ | ||
15112 | + struct ccs_policy_namespace *ns = container_of(head->r.ns, typeof(*ns), | ||
15113 | + namespace_list); | ||
15114 | + struct list_head *list = &ns->group_list[idx]; | ||
15115 | + list_for_each_cookie(head->r.group, list) { | ||
15116 | + struct ccs_group *group = | ||
15117 | + list_entry(head->r.group, typeof(*group), head.list); | ||
15118 | + list_for_each_cookie(head->r.acl, &group->member_list) { | ||
15119 | + struct ccs_acl_head *ptr = | ||
15120 | + list_entry(head->r.acl, typeof(*ptr), list); | ||
15121 | + if (ptr->is_deleted) | ||
15122 | + continue; | ||
15123 | + if (!ccs_flush(head)) | ||
15124 | + return false; | ||
15125 | + ccs_print_namespace(head); | ||
15126 | + ccs_set_string(head, ccs_group_name[idx]); | ||
15127 | + ccs_set_string(head, group->group_name->name); | ||
15128 | + if (idx == CCS_PATH_GROUP) { | ||
15129 | + ccs_set_space(head); | ||
15130 | + ccs_set_string(head, container_of | ||
15131 | + (ptr, struct ccs_path_group, | ||
15132 | + head)->member_name->name); | ||
15133 | + } else if (idx == CCS_NUMBER_GROUP) { | ||
15134 | + ccs_print_number_union(head, &container_of | ||
15135 | + (ptr, struct ccs_number_group, | ||
15136 | + head)->number); | ||
15137 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
15138 | + } else if (idx == CCS_ADDRESS_GROUP) { | ||
15139 | + char buffer[128]; | ||
15140 | + struct ccs_address_group *member = | ||
15141 | + container_of(ptr, typeof(*member), | ||
15142 | + head); | ||
15143 | + ccs_print_ip(buffer, sizeof(buffer), | ||
15144 | + &member->address); | ||
15145 | + ccs_io_printf(head, " %s", buffer); | ||
15146 | +#endif | ||
15147 | + } | ||
15148 | + ccs_set_lf(head); | ||
15149 | + } | ||
15150 | + head->r.acl = NULL; | ||
15151 | + } | ||
15152 | + head->r.group = NULL; | ||
15153 | + return true; | ||
15154 | +} | ||
15155 | + | ||
15156 | +/** | ||
15157 | + * ccs_read_policy - Read "struct ccs_..._entry" list. | ||
15158 | + * | ||
15159 | + * @head: Pointer to "struct ccs_io_buffer". | ||
15160 | + * @idx: Index number. | ||
15161 | + * | ||
15162 | + * Returns true on success, false otherwise. | ||
15163 | + * | ||
15164 | + * Caller holds ccs_read_lock(). | ||
15165 | + */ | ||
15166 | +static bool ccs_read_policy(struct ccs_io_buffer *head, const int idx) | ||
15167 | +{ | ||
15168 | + struct ccs_policy_namespace *ns = container_of(head->r.ns, typeof(*ns), | ||
15169 | + namespace_list); | ||
15170 | + struct list_head *list = &ns->policy_list[idx]; | ||
15171 | + list_for_each_cookie(head->r.acl, list) { | ||
15172 | + struct ccs_acl_head *acl = | ||
15173 | + container_of(head->r.acl, typeof(*acl), list); | ||
15174 | + if (acl->is_deleted) | ||
15175 | + continue; | ||
15176 | + if (head->r.print_transition_related_only && | ||
15177 | + idx != CCS_ID_TRANSITION_CONTROL) | ||
15178 | + continue; | ||
15179 | + if (!ccs_flush(head)) | ||
15180 | + return false; | ||
15181 | + switch (idx) { | ||
15182 | + case CCS_ID_TRANSITION_CONTROL: | ||
15183 | + { | ||
15184 | + struct ccs_transition_control *ptr = | ||
15185 | + container_of(acl, typeof(*ptr), head); | ||
15186 | + ccs_print_namespace(head); | ||
15187 | + ccs_set_string(head, | ||
15188 | + ccs_transition_type[ptr->type]); | ||
15189 | + ccs_set_string(head, ptr->program ? | ||
15190 | + ptr->program->name : "any"); | ||
15191 | + ccs_set_string(head, " from "); | ||
15192 | + ccs_set_string(head, ptr->domainname ? | ||
15193 | + ptr->domainname->name : "any"); | ||
15194 | + } | ||
15195 | + break; | ||
15196 | + case CCS_ID_AGGREGATOR: | ||
15197 | + { | ||
15198 | + struct ccs_aggregator *ptr = | ||
15199 | + container_of(acl, typeof(*ptr), head); | ||
15200 | + ccs_print_namespace(head); | ||
15201 | + ccs_set_string(head, "aggregator "); | ||
15202 | + ccs_set_string(head, ptr->original_name->name); | ||
15203 | + ccs_set_space(head); | ||
15204 | + ccs_set_string(head, | ||
15205 | + ptr->aggregated_name->name); | ||
15206 | + } | ||
15207 | + break; | ||
15208 | +#ifdef CONFIG_CCSECURITY_PORTRESERVE | ||
15209 | + case CCS_ID_RESERVEDPORT: | ||
15210 | + { | ||
15211 | + struct ccs_reserved *ptr = | ||
15212 | + container_of(acl, typeof(*ptr), head); | ||
15213 | + ccs_print_namespace(head); | ||
15214 | + ccs_set_string(head, "deny_autobind "); | ||
15215 | + ccs_print_number_union_nospace(head, | ||
15216 | + &ptr->port); | ||
15217 | + } | ||
15218 | + break; | ||
15219 | +#endif | ||
15220 | + default: | ||
15221 | + continue; | ||
15222 | + } | ||
15223 | + ccs_set_lf(head); | ||
15224 | + } | ||
15225 | + head->r.acl = NULL; | ||
15226 | + return true; | ||
15227 | +} | ||
15228 | + | ||
15229 | +/** | ||
15230 | + * ccs_read_exception - Read exception policy. | ||
15231 | + * | ||
15232 | + * @head: Pointer to "struct ccs_io_buffer". | ||
15233 | + * | ||
15234 | + * Returns nothing. | ||
15235 | + * | ||
15236 | + * Caller holds ccs_read_lock(). | ||
15237 | + */ | ||
15238 | +static void ccs_read_exception(struct ccs_io_buffer *head) | ||
15239 | +{ | ||
15240 | + struct ccs_policy_namespace *ns = container_of(head->r.ns, typeof(*ns), | ||
15241 | + namespace_list); | ||
15242 | + if (head->r.eof) | ||
15243 | + return; | ||
15244 | + while (head->r.step < CCS_MAX_POLICY && | ||
15245 | + ccs_read_policy(head, head->r.step)) | ||
15246 | + head->r.step++; | ||
15247 | + if (head->r.step < CCS_MAX_POLICY) | ||
15248 | + return; | ||
15249 | + while (head->r.step < CCS_MAX_POLICY + CCS_MAX_GROUP && | ||
15250 | + ccs_read_group(head, head->r.step - CCS_MAX_POLICY)) | ||
15251 | + head->r.step++; | ||
15252 | + if (head->r.step < CCS_MAX_POLICY + CCS_MAX_GROUP) | ||
15253 | + return; | ||
15254 | + while (head->r.step < CCS_MAX_POLICY + CCS_MAX_GROUP | ||
15255 | + + CCS_MAX_ACL_GROUPS) { | ||
15256 | + head->r.acl_group_index = | ||
15257 | + head->r.step - CCS_MAX_POLICY - CCS_MAX_GROUP; | ||
15258 | + if (!ccs_read_acl(head, &ns->acl_group | ||
15259 | + [head->r.acl_group_index])) | ||
15260 | + return; | ||
15261 | + head->r.step++; | ||
15262 | + } | ||
15263 | + head->r.eof = true; | ||
15264 | +} | ||
15265 | + | ||
15266 | +/** | ||
15267 | + * ccs_truncate - Truncate a line. | ||
15268 | + * | ||
15269 | + * @str: String to truncate. | ||
15270 | + * | ||
15271 | + * Returns length of truncated @str. | ||
15272 | + */ | ||
15273 | +static int ccs_truncate(char *str) | ||
15274 | +{ | ||
15275 | + char *start = str; | ||
15276 | + while (*(unsigned char *) str > (unsigned char) ' ') | ||
15277 | + str++; | ||
15278 | + *str = '\0'; | ||
15279 | + return strlen(start) + 1; | ||
15280 | +} | ||
15281 | + | ||
15282 | +/** | ||
15283 | + * ccs_add_entry - Add an ACL to current thread's domain. Used by learning mode. | ||
15284 | + * | ||
15285 | + * @header: Lines containing ACL. | ||
15286 | + * | ||
15287 | + * Returns nothing. | ||
15288 | + */ | ||
15289 | +static void ccs_add_entry(char *header) | ||
15290 | +{ | ||
15291 | + char *buffer; | ||
15292 | + char *realpath = NULL; | ||
15293 | + char *argv0 = NULL; | ||
15294 | + char *symlink = NULL; | ||
15295 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
15296 | + char *handler; | ||
15297 | +#endif | ||
15298 | + char *cp = strchr(header, '\n'); | ||
15299 | + int len; | ||
15300 | + if (!cp) | ||
15301 | + return; | ||
15302 | + cp = strchr(cp + 1, '\n'); | ||
15303 | + if (!cp) | ||
15304 | + return; | ||
15305 | + *cp++ = '\0'; | ||
15306 | + len = strlen(cp) + 1; | ||
15307 | + /* strstr() will return NULL if ordering is wrong. */ | ||
15308 | + if (*cp == 'f') { | ||
15309 | + argv0 = strstr(header, " argv[]={ \""); | ||
15310 | + if (argv0) { | ||
15311 | + argv0 += 10; | ||
15312 | + len += ccs_truncate(argv0) + 14; | ||
15313 | + } | ||
15314 | + realpath = strstr(header, " exec={ realpath=\""); | ||
15315 | + if (realpath) { | ||
15316 | + realpath += 8; | ||
15317 | + len += ccs_truncate(realpath) + 6; | ||
15318 | + } | ||
15319 | + symlink = strstr(header, " symlink.target=\""); | ||
15320 | + if (symlink) | ||
15321 | + len += ccs_truncate(symlink + 1) + 1; | ||
15322 | + } | ||
15323 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
15324 | + handler = strstr(header, "type=execute_handler"); | ||
15325 | + if (handler) | ||
15326 | + len += ccs_truncate(handler) + 6; | ||
15327 | +#endif | ||
15328 | + buffer = kmalloc(len, CCS_GFP_FLAGS); | ||
15329 | + if (!buffer) | ||
15330 | + return; | ||
15331 | + snprintf(buffer, len - 1, "%s", cp); | ||
15332 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
15333 | + if (handler) | ||
15334 | + ccs_addprintf(buffer, len, " task.%s", handler); | ||
15335 | +#endif | ||
15336 | + if (realpath) | ||
15337 | + ccs_addprintf(buffer, len, " exec.%s", realpath); | ||
15338 | + if (argv0) | ||
15339 | + ccs_addprintf(buffer, len, " exec.argv[0]=%s", argv0); | ||
15340 | + if (symlink) | ||
15341 | + ccs_addprintf(buffer, len, "%s", symlink); | ||
15342 | + ccs_normalize_line(buffer); | ||
15343 | + { | ||
15344 | + struct ccs_domain_info *domain = ccs_current_domain(); | ||
15345 | + if (!ccs_write_acl(domain->ns, &domain->acl_info_list, | ||
15346 | + buffer, false)) | ||
15347 | + ccs_update_stat(CCS_STAT_POLICY_UPDATES); | ||
15348 | + } | ||
15349 | + kfree(buffer); | ||
15350 | +} | ||
15351 | + | ||
15352 | +/** | ||
15353 | + * ccs_domain_quota_ok - Check for domain's quota. | ||
15354 | + * | ||
15355 | + * @r: Pointer to "struct ccs_request_info". | ||
15356 | + * | ||
15357 | + * Returns true if the domain is not exceeded quota, false otherwise. | ||
15358 | + * | ||
15359 | + * Caller holds ccs_read_lock(). | ||
15360 | + */ | ||
15361 | +static bool ccs_domain_quota_ok(struct ccs_request_info *r) | ||
15362 | +{ | ||
15363 | + unsigned int count = 0; | ||
15364 | + struct ccs_domain_info * const domain = ccs_current_domain(); | ||
15365 | + struct ccs_acl_info *ptr; | ||
15366 | + if (r->mode != CCS_CONFIG_LEARNING) | ||
15367 | + return false; | ||
15368 | + if (!domain) | ||
15369 | + return true; | ||
15370 | + list_for_each_entry_srcu(ptr, &domain->acl_info_list, list, &ccs_ss) { | ||
15371 | + u16 perm; | ||
15372 | + u8 i; | ||
15373 | + if (ptr->is_deleted) | ||
15374 | + continue; | ||
15375 | + switch (ptr->type) { | ||
15376 | + case CCS_TYPE_PATH_ACL: | ||
15377 | + case CCS_TYPE_PATH2_ACL: | ||
15378 | + case CCS_TYPE_PATH_NUMBER_ACL: | ||
15379 | + case CCS_TYPE_MKDEV_ACL: | ||
15380 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
15381 | + case CCS_TYPE_INET_ACL: | ||
15382 | + case CCS_TYPE_UNIX_ACL: | ||
15383 | +#endif | ||
15384 | + perm = ptr->perm; | ||
15385 | + break; | ||
15386 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
15387 | + case CCS_TYPE_AUTO_EXECUTE_HANDLER: | ||
15388 | + case CCS_TYPE_DENIED_EXECUTE_HANDLER: | ||
15389 | +#endif | ||
15390 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
15391 | + case CCS_TYPE_AUTO_TASK_ACL: | ||
15392 | + case CCS_TYPE_MANUAL_TASK_ACL: | ||
15393 | +#endif | ||
15394 | + perm = 0; | ||
15395 | + break; | ||
15396 | + default: | ||
15397 | + perm = 1; | ||
15398 | + } | ||
15399 | + for (i = 0; i < 16; i++) | ||
15400 | + if (perm & (1 << i)) | ||
15401 | + count++; | ||
15402 | + } | ||
15403 | + if (count < ccs_profile(r->profile)->pref[CCS_PREF_MAX_LEARNING_ENTRY]) | ||
15404 | + return true; | ||
15405 | + if (!domain->flags[CCS_DIF_QUOTA_WARNED]) { | ||
15406 | + domain->flags[CCS_DIF_QUOTA_WARNED] = true; | ||
15407 | + /* r->granted = false; */ | ||
15408 | + ccs_write_log(r, "%s", ccs_dif[CCS_DIF_QUOTA_WARNED]); | ||
15409 | + printk(KERN_WARNING "WARNING: " | ||
15410 | + "Domain '%s' has too many ACLs to hold. " | ||
15411 | + "Stopped learning mode.\n", domain->domainname->name); | ||
15412 | + } | ||
15413 | + return false; | ||
15414 | +} | ||
15415 | + | ||
15416 | +/** | ||
15417 | + * ccs_supervisor - Ask for the supervisor's decision. | ||
15418 | + * | ||
15419 | + * @r: Pointer to "struct ccs_request_info". | ||
15420 | + * @fmt: The printf()'s format string, followed by parameters. | ||
15421 | + * | ||
15422 | + * Returns 0 if the supervisor decided to permit the access request which | ||
15423 | + * violated the policy in enforcing mode, CCS_RETRY_REQUEST if the supervisor | ||
15424 | + * decided to retry the access request which violated the policy in enforcing | ||
15425 | + * mode, 0 if it is not in enforcing mode, -EPERM otherwise. | ||
15426 | + */ | ||
15427 | +static int ccs_supervisor(struct ccs_request_info *r, const char *fmt, ...) | ||
15428 | +{ | ||
15429 | + va_list args; | ||
15430 | + int error; | ||
15431 | + int len; | ||
15432 | + static unsigned int ccs_serial; | ||
15433 | + struct ccs_query entry = { }; | ||
15434 | + bool quota_exceeded = false; | ||
15435 | + va_start(args, fmt); | ||
15436 | + len = vsnprintf((char *) &len, 1, fmt, args) + 1; | ||
15437 | + va_end(args); | ||
15438 | + /* Write /proc/ccs/audit. */ | ||
15439 | + va_start(args, fmt); | ||
15440 | + ccs_write_log2(r, len, fmt, args); | ||
15441 | + va_end(args); | ||
15442 | + /* Nothing more to do if granted. */ | ||
15443 | + if (r->granted) | ||
15444 | + return 0; | ||
15445 | + if (r->mode) | ||
15446 | + ccs_update_stat(r->mode); | ||
15447 | + switch (r->mode) { | ||
15448 | + int i; | ||
15449 | + struct ccs_profile *p; | ||
15450 | + case CCS_CONFIG_ENFORCING: | ||
15451 | + error = -EPERM; | ||
15452 | + if (atomic_read(&ccs_query_observers)) | ||
15453 | + break; | ||
15454 | + if (r->dont_sleep_on_enforce_error) | ||
15455 | + goto out; | ||
15456 | + p = ccs_profile(r->profile); | ||
15457 | + /* Check enforcing_penalty parameter. */ | ||
15458 | + for (i = 0; i < p->pref[CCS_PREF_ENFORCING_PENALTY]; i++) { | ||
15459 | + set_current_state(TASK_INTERRUPTIBLE); | ||
15460 | + schedule_timeout(HZ / 10); | ||
15461 | + } | ||
15462 | + goto out; | ||
15463 | + case CCS_CONFIG_LEARNING: | ||
15464 | + error = 0; | ||
15465 | + /* Check max_learning_entry parameter. */ | ||
15466 | + if (ccs_domain_quota_ok(r)) | ||
15467 | + break; | ||
15468 | + /* fall through */ | ||
15469 | + default: | ||
15470 | + return 0; | ||
15471 | + } | ||
15472 | + /* Get message. */ | ||
15473 | + va_start(args, fmt); | ||
15474 | + entry.query = ccs_init_log(r, len, fmt, args); | ||
15475 | + va_end(args); | ||
15476 | + if (!entry.query) | ||
15477 | + goto out; | ||
15478 | + entry.query_len = strlen(entry.query) + 1; | ||
15479 | + if (!error) { | ||
15480 | + ccs_add_entry(entry.query); | ||
15481 | + goto out; | ||
15482 | + } | ||
15483 | + len = ccs_round2(entry.query_len); | ||
15484 | + entry.domain = ccs_current_domain(); | ||
15485 | + spin_lock(&ccs_query_list_lock); | ||
15486 | + if (ccs_memory_quota[CCS_MEMORY_QUERY] && | ||
15487 | + ccs_memory_used[CCS_MEMORY_QUERY] + len | ||
15488 | + >= ccs_memory_quota[CCS_MEMORY_QUERY]) { | ||
15489 | + quota_exceeded = true; | ||
15490 | + } else { | ||
15491 | + entry.serial = ccs_serial++; | ||
15492 | + entry.retry = r->retry; | ||
15493 | + ccs_memory_used[CCS_MEMORY_QUERY] += len; | ||
15494 | + list_add_tail(&entry.list, &ccs_query_list); | ||
15495 | + } | ||
15496 | + spin_unlock(&ccs_query_list_lock); | ||
15497 | + if (quota_exceeded) | ||
15498 | + goto out; | ||
15499 | + /* Give 10 seconds for supervisor's opinion. */ | ||
15500 | + while (entry.timer < 10) { | ||
15501 | + wake_up_all(&ccs_query_wait); | ||
15502 | + if (wait_event_interruptible_timeout | ||
15503 | + (ccs_answer_wait, entry.answer || | ||
15504 | + !atomic_read(&ccs_query_observers), HZ)) | ||
15505 | + break; | ||
15506 | + else | ||
15507 | + entry.timer++; | ||
15508 | + } | ||
15509 | + spin_lock(&ccs_query_list_lock); | ||
15510 | + list_del(&entry.list); | ||
15511 | + ccs_memory_used[CCS_MEMORY_QUERY] -= len; | ||
15512 | + spin_unlock(&ccs_query_list_lock); | ||
15513 | + switch (entry.answer) { | ||
15514 | + case 3: /* Asked to retry by administrator. */ | ||
15515 | + error = CCS_RETRY_REQUEST; | ||
15516 | + r->retry++; | ||
15517 | + break; | ||
15518 | + case 1: | ||
15519 | + /* Granted by administrator. */ | ||
15520 | + error = 0; | ||
15521 | + break; | ||
15522 | + default: | ||
15523 | + /* Timed out or rejected by administrator. */ | ||
15524 | + break; | ||
15525 | + } | ||
15526 | +out: | ||
15527 | + kfree(entry.query); | ||
15528 | + return error; | ||
15529 | +} | ||
15530 | + | ||
15531 | +/** | ||
15532 | + * ccs_audit_log - Audit permission check log. | ||
15533 | + * | ||
15534 | + * @r: Pointer to "struct ccs_request_info". | ||
15535 | + * | ||
15536 | + * Returns return value of ccs_supervisor(). | ||
15537 | + */ | ||
15538 | +int ccs_audit_log(struct ccs_request_info *r) | ||
15539 | +{ | ||
15540 | + switch (r->param_type) { | ||
15541 | + u8 type; | ||
15542 | + char buf[48]; | ||
15543 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
15544 | + const u32 *address; | ||
15545 | +#endif | ||
15546 | + case CCS_TYPE_PATH_ACL: | ||
15547 | + return ccs_supervisor(r, "file %s %s\n", ccs_path_keyword | ||
15548 | + [r->param.path.operation], | ||
15549 | + r->param.path.filename->name); | ||
15550 | + case CCS_TYPE_PATH2_ACL: | ||
15551 | + return ccs_supervisor(r, "file %s %s %s\n", ccs_mac_keywords | ||
15552 | + [ccs_pp2mac[r->param.path2.operation]], | ||
15553 | + r->param.path2.filename1->name, | ||
15554 | + r->param.path2.filename2->name); | ||
15555 | + case CCS_TYPE_PATH_NUMBER_ACL: | ||
15556 | + type = r->param.path_number.operation; | ||
15557 | + switch (type) { | ||
15558 | + case CCS_TYPE_CREATE: | ||
15559 | + case CCS_TYPE_MKDIR: | ||
15560 | + case CCS_TYPE_MKFIFO: | ||
15561 | + case CCS_TYPE_MKSOCK: | ||
15562 | + case CCS_TYPE_CHMOD: | ||
15563 | + snprintf(buf, sizeof(buf), "0%lo", | ||
15564 | + r->param.path_number.number); | ||
15565 | + break; | ||
15566 | + case CCS_TYPE_IOCTL: | ||
15567 | + snprintf(buf, sizeof(buf), "0x%lX", | ||
15568 | + r->param.path_number.number); | ||
15569 | + break; | ||
15570 | + default: | ||
15571 | + snprintf(buf, sizeof(buf), "%lu", | ||
15572 | + r->param.path_number.number); | ||
15573 | + break; | ||
15574 | + } | ||
15575 | + return ccs_supervisor(r, "file %s %s %s\n", ccs_mac_keywords | ||
15576 | + [ccs_pn2mac[type]], | ||
15577 | + r->param.path_number.filename->name, | ||
15578 | + buf); | ||
15579 | + case CCS_TYPE_MKDEV_ACL: | ||
15580 | + return ccs_supervisor(r, "file %s %s 0%o %u %u\n", | ||
15581 | + ccs_mac_keywords | ||
15582 | + [ccs_pnnn2mac[r->param.mkdev.operation]], | ||
15583 | + r->param.mkdev.filename->name, | ||
15584 | + r->param.mkdev.mode, | ||
15585 | + r->param.mkdev.major, | ||
15586 | + r->param.mkdev.minor); | ||
15587 | + case CCS_TYPE_MOUNT_ACL: | ||
15588 | + return ccs_supervisor(r, "file mount %s %s %s 0x%lX\n", | ||
15589 | + r->param.mount.dev->name, | ||
15590 | + r->param.mount.dir->name, | ||
15591 | + r->param.mount.type->name, | ||
15592 | + r->param.mount.flags); | ||
15593 | +#ifdef CONFIG_CCSECURITY_MISC | ||
15594 | + case CCS_TYPE_ENV_ACL: | ||
15595 | + return ccs_supervisor(r, "misc env %s\n", | ||
15596 | + r->param.environ.name->name); | ||
15597 | +#endif | ||
15598 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | ||
15599 | + case CCS_TYPE_CAPABILITY_ACL: | ||
15600 | + return ccs_supervisor(r, "capability %s\n", ccs_mac_keywords | ||
15601 | + [ccs_c2mac[r->param.capability. | ||
15602 | + operation]]); | ||
15603 | +#endif | ||
15604 | +#ifdef CONFIG_CCSECURITY_NETWORK | ||
15605 | + case CCS_TYPE_INET_ACL: | ||
15606 | + address = r->param.inet_network.address; | ||
15607 | + if (r->param.inet_network.is_ipv6) | ||
15608 | + ccs_print_ipv6(buf, sizeof(buf), | ||
15609 | + (const struct in6_addr *) address); | ||
15610 | + else | ||
15611 | + ccs_print_ipv4(buf, sizeof(buf), address); | ||
15612 | + return ccs_supervisor(r, "network inet %s %s %s %u\n", | ||
15613 | + ccs_proto_keyword[r->param.inet_network. | ||
15614 | + protocol], | ||
15615 | + ccs_socket_keyword[r->param.inet_network. | ||
15616 | + operation], | ||
15617 | + buf, r->param.inet_network.port); | ||
15618 | + case CCS_TYPE_UNIX_ACL: | ||
15619 | + return ccs_supervisor(r, "network unix %s %s %s\n", | ||
15620 | + ccs_proto_keyword[r->param. | ||
15621 | + unix_network.protocol], | ||
15622 | + ccs_socket_keyword[r->param.unix_network. | ||
15623 | + operation], | ||
15624 | + r->param.unix_network.address->name); | ||
15625 | +#endif | ||
15626 | +#ifdef CONFIG_CCSECURITY_IPC | ||
15627 | + case CCS_TYPE_SIGNAL_ACL: | ||
15628 | + return ccs_supervisor(r, "ipc signal %d %s\n", | ||
15629 | + r->param.signal.sig, | ||
15630 | + r->param.signal.dest_pattern); | ||
15631 | +#endif | ||
15632 | + } | ||
15633 | + return 0; | ||
15634 | +} | ||
15635 | + | ||
15636 | +/** | ||
15637 | + * ccs_find_domain_by_qid - Get domain by query id. | ||
15638 | + * | ||
15639 | + * @serial: Query ID assigned by ccs_supervisor(). | ||
15640 | + * | ||
15641 | + * Returns pointer to "struct ccs_domain_info" if found, NULL otherwise. | ||
15642 | + */ | ||
15643 | +static struct ccs_domain_info *ccs_find_domain_by_qid(unsigned int serial) | ||
15644 | +{ | ||
15645 | + struct ccs_query *ptr; | ||
15646 | + struct ccs_domain_info *domain = NULL; | ||
15647 | + spin_lock(&ccs_query_list_lock); | ||
15648 | + list_for_each_entry(ptr, &ccs_query_list, list) { | ||
15649 | + if (ptr->serial != serial) | ||
15650 | + continue; | ||
15651 | + domain = ptr->domain; | ||
15652 | + break; | ||
15653 | + } | ||
15654 | + spin_unlock(&ccs_query_list_lock); | ||
15655 | + return domain; | ||
15656 | +} | ||
15657 | + | ||
15658 | +/** | ||
15659 | + * ccs_read_query - Read access requests which violated policy in enforcing mode. | ||
15660 | + * | ||
15661 | + * @head: Pointer to "struct ccs_io_buffer". | ||
15662 | + * | ||
15663 | + * Returns nothing. | ||
15664 | + */ | ||
15665 | +static void ccs_read_query(struct ccs_io_buffer *head) | ||
15666 | +{ | ||
15667 | + struct list_head *tmp; | ||
15668 | + unsigned int pos = 0; | ||
15669 | + size_t len = 0; | ||
15670 | + char *buf; | ||
15671 | + if (head->r.w_pos) | ||
15672 | + return; | ||
15673 | + kfree(head->read_buf); | ||
15674 | + head->read_buf = NULL; | ||
15675 | + spin_lock(&ccs_query_list_lock); | ||
15676 | + list_for_each(tmp, &ccs_query_list) { | ||
15677 | + struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | ||
15678 | + if (pos++ != head->r.query_index) | ||
15679 | + continue; | ||
15680 | + len = ptr->query_len; | ||
15681 | + break; | ||
15682 | + } | ||
15683 | + spin_unlock(&ccs_query_list_lock); | ||
15684 | + if (!len) { | ||
15685 | + head->r.query_index = 0; | ||
15686 | + return; | ||
15687 | + } | ||
15688 | + buf = kzalloc(len + 32, CCS_GFP_FLAGS); | ||
15689 | + if (!buf) | ||
15690 | + return; | ||
15691 | + pos = 0; | ||
15692 | + spin_lock(&ccs_query_list_lock); | ||
15693 | + list_for_each(tmp, &ccs_query_list) { | ||
15694 | + struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | ||
15695 | + if (pos++ != head->r.query_index) | ||
15696 | + continue; | ||
15697 | + /* | ||
15698 | + * Some query can be skipped because ccs_query_list | ||
15699 | + * can change, but I don't care. | ||
15700 | + */ | ||
15701 | + if (len == ptr->query_len) | ||
15702 | + snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial, | ||
15703 | + ptr->retry, ptr->query); | ||
15704 | + break; | ||
15705 | + } | ||
15706 | + spin_unlock(&ccs_query_list_lock); | ||
15707 | + if (buf[0]) { | ||
15708 | + head->read_buf = buf; | ||
15709 | + head->r.w[head->r.w_pos++] = buf; | ||
15710 | + head->r.query_index++; | ||
15711 | + } else { | ||
15712 | + kfree(buf); | ||
15713 | + } | ||
15714 | +} | ||
15715 | + | ||
15716 | +/** | ||
15717 | + * ccs_write_answer - Write the supervisor's decision. | ||
15718 | + * | ||
15719 | + * @head: Pointer to "struct ccs_io_buffer". | ||
15720 | + * | ||
15721 | + * Returns 0 on success, -EINVAL otherwise. | ||
15722 | + */ | ||
15723 | +static int ccs_write_answer(struct ccs_io_buffer *head) | ||
15724 | +{ | ||
15725 | + char *data = head->write_buf; | ||
15726 | + struct list_head *tmp; | ||
15727 | + unsigned int serial; | ||
15728 | + unsigned int answer; | ||
15729 | + spin_lock(&ccs_query_list_lock); | ||
15730 | + list_for_each(tmp, &ccs_query_list) { | ||
15731 | + struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | ||
15732 | + ptr->timer = 0; | ||
15733 | + } | ||
15734 | + spin_unlock(&ccs_query_list_lock); | ||
15735 | + if (sscanf(data, "A%u=%u", &serial, &answer) != 2) | ||
15736 | + return -EINVAL; | ||
15737 | + spin_lock(&ccs_query_list_lock); | ||
15738 | + list_for_each(tmp, &ccs_query_list) { | ||
15739 | + struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | ||
15740 | + if (ptr->serial != serial) | ||
15741 | + continue; | ||
15742 | + ptr->answer = (u8) answer; | ||
15743 | + /* Remove from ccs_query_list. */ | ||
15744 | + if (ptr->answer) { | ||
15745 | + list_del(&ptr->list); | ||
15746 | + INIT_LIST_HEAD(&ptr->list); | ||
15747 | + } | ||
15748 | + break; | ||
15749 | + } | ||
15750 | + spin_unlock(&ccs_query_list_lock); | ||
15751 | + wake_up_all(&ccs_answer_wait); | ||
15752 | + return 0; | ||
15753 | +} | ||
15754 | + | ||
15755 | +/** | ||
15756 | + * ccs_read_version - Get version. | ||
15757 | + * | ||
15758 | + * @head: Pointer to "struct ccs_io_buffer". | ||
15759 | + * | ||
15760 | + * Returns nothing. | ||
15761 | + */ | ||
15762 | +static void ccs_read_version(struct ccs_io_buffer *head) | ||
15763 | +{ | ||
15764 | + if (head->r.eof) | ||
15765 | + return; | ||
15766 | + ccs_set_string(head, "1.8.4"); | ||
15767 | + head->r.eof = true; | ||
15768 | +} | ||
15769 | + | ||
15770 | +/** | ||
15771 | + * ccs_update_stat - Update statistic counters. | ||
15772 | + * | ||
15773 | + * @index: Index for policy type. | ||
15774 | + * | ||
15775 | + * Returns nothing. | ||
15776 | + */ | ||
15777 | +static void ccs_update_stat(const u8 index) | ||
15778 | +{ | ||
15779 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) | ||
15780 | + struct timeval tv; | ||
15781 | + do_gettimeofday(&tv); | ||
15782 | + /* | ||
15783 | + * I don't use atomic operations because race condition is not fatal. | ||
15784 | + */ | ||
15785 | + ccs_stat_updated[index]++; | ||
15786 | + ccs_stat_modified[index] = tv.tv_sec; | ||
15787 | +#else | ||
15788 | + /* | ||
15789 | + * I don't use atomic operations because race condition is not fatal. | ||
15790 | + */ | ||
15791 | + ccs_stat_updated[index]++; | ||
15792 | + ccs_stat_modified[index] = get_seconds(); | ||
15793 | +#endif | ||
15794 | +} | ||
15795 | + | ||
15796 | +/** | ||
15797 | + * ccs_read_stat - Read statistic data. | ||
15798 | + * | ||
15799 | + * @head: Pointer to "struct ccs_io_buffer". | ||
15800 | + * | ||
15801 | + * Returns nothing. | ||
15802 | + */ | ||
15803 | +static void ccs_read_stat(struct ccs_io_buffer *head) | ||
15804 | +{ | ||
15805 | + u8 i; | ||
15806 | + unsigned int total = 0; | ||
15807 | + if (head->r.eof) | ||
15808 | + return; | ||
15809 | + for (i = 0; i < CCS_MAX_POLICY_STAT; i++) { | ||
15810 | + ccs_io_printf(head, "Policy %-30s %10u", ccs_policy_headers[i], | ||
15811 | + ccs_stat_updated[i]); | ||
15812 | + if (ccs_stat_modified[i]) { | ||
15813 | + struct ccs_time stamp; | ||
15814 | + ccs_convert_time(ccs_stat_modified[i], &stamp); | ||
15815 | + ccs_io_printf(head, " (Last: %04u/%02u/%02u " | ||
15816 | + "%02u:%02u:%02u)", | ||
15817 | + stamp.year, stamp.month, stamp.day, | ||
15818 | + stamp.hour, stamp.min, stamp.sec); | ||
15819 | + } | ||
15820 | + ccs_set_lf(head); | ||
15821 | + } | ||
15822 | + for (i = 0; i < CCS_MAX_MEMORY_STAT; i++) { | ||
15823 | + unsigned int used = ccs_memory_used[i]; | ||
15824 | + total += used; | ||
15825 | + ccs_io_printf(head, "Memory used by %-22s %10u", | ||
15826 | + ccs_memory_headers[i], used); | ||
15827 | + used = ccs_memory_quota[i]; | ||
15828 | + if (used) | ||
15829 | + ccs_io_printf(head, " (Quota: %10u)", used); | ||
15830 | + ccs_set_lf(head); | ||
15831 | + } | ||
15832 | + ccs_io_printf(head, "Total memory used: %10u\n", | ||
15833 | + total); | ||
15834 | + head->r.eof = true; | ||
15835 | +} | ||
15836 | + | ||
15837 | +/** | ||
15838 | + * ccs_write_stat - Set memory quota. | ||
15839 | + * | ||
15840 | + * @head: Pointer to "struct ccs_io_buffer". | ||
15841 | + * | ||
15842 | + * Returns 0. | ||
15843 | + */ | ||
15844 | +static int ccs_write_stat(struct ccs_io_buffer *head) | ||
15845 | +{ | ||
15846 | + char *data = head->write_buf; | ||
15847 | + u8 i; | ||
15848 | + if (ccs_str_starts(&data, "Memory used by ")) | ||
15849 | + for (i = 0; i < CCS_MAX_MEMORY_STAT; i++) | ||
15850 | + if (ccs_str_starts(&data, ccs_memory_headers[i])) { | ||
15851 | + if (*data == ' ') | ||
15852 | + data++; | ||
15853 | + ccs_memory_quota[i] = | ||
15854 | + simple_strtoul(data, NULL, 10); | ||
15855 | + } | ||
15856 | + return 0; | ||
15857 | +} | ||
15858 | + | ||
15859 | +/** | ||
15860 | + * ccs_print_bprm - Print "struct linux_binprm" for auditing. | ||
15861 | + * | ||
15862 | + * @bprm: Pointer to "struct linux_binprm". | ||
15863 | + * @dump: Pointer to "struct ccs_page_dump". | ||
15864 | + * | ||
15865 | + * Returns the contents of @bprm on success, NULL otherwise. | ||
15866 | + * | ||
15867 | + * This function uses kzalloc(), so caller must kfree() if this function | ||
15868 | + * didn't return NULL. | ||
15869 | + */ | ||
15870 | +static char *ccs_print_bprm(struct linux_binprm *bprm, | ||
15871 | + struct ccs_page_dump *dump) | ||
15872 | +{ | ||
15873 | + static const int ccs_buffer_len = 4096 * 2; | ||
15874 | + char *buffer = kzalloc(ccs_buffer_len, CCS_GFP_FLAGS); | ||
15875 | + char *cp; | ||
15876 | + char *last_start; | ||
15877 | + int len; | ||
15878 | + unsigned long pos = bprm->p; | ||
15879 | + int offset = pos % PAGE_SIZE; | ||
15880 | + int argv_count = bprm->argc; | ||
15881 | + int envp_count = bprm->envc; | ||
15882 | + bool truncated = false; | ||
15883 | + if (!buffer) | ||
15884 | + return NULL; | ||
15885 | + len = snprintf(buffer, ccs_buffer_len - 1, "argv[]={ "); | ||
15886 | + cp = buffer + len; | ||
15887 | + if (!argv_count) { | ||
15888 | + memmove(cp, "} envp[]={ ", 11); | ||
15889 | + cp += 11; | ||
15890 | + } | ||
15891 | + last_start = cp; | ||
15892 | + while (argv_count || envp_count) { | ||
15893 | + if (!ccs_dump_page(bprm, pos, dump)) | ||
15894 | + goto out; | ||
15895 | + pos += PAGE_SIZE - offset; | ||
15896 | + /* Read. */ | ||
15897 | + while (offset < PAGE_SIZE) { | ||
15898 | + const char *kaddr = dump->data; | ||
15899 | + const unsigned char c = kaddr[offset++]; | ||
15900 | + if (cp == last_start) | ||
15901 | + *cp++ = '"'; | ||
15902 | + if (cp >= buffer + ccs_buffer_len - 32) { | ||
15903 | + /* Reserve some room for "..." string. */ | ||
15904 | + truncated = true; | ||
15905 | + } else if (c == '\\') { | ||
15906 | + *cp++ = '\\'; | ||
15907 | + *cp++ = '\\'; | ||
15908 | + } else if (c > ' ' && c < 127) { | ||
15909 | + *cp++ = c; | ||
15910 | + } else if (!c) { | ||
15911 | + *cp++ = '"'; | ||
15912 | + *cp++ = ' '; | ||
15913 | + last_start = cp; | ||
15914 | + } else { | ||
15915 | + *cp++ = '\\'; | ||
15916 | + *cp++ = (c >> 6) + '0'; | ||
15917 | + *cp++ = ((c >> 3) & 7) + '0'; | ||
15918 | + *cp++ = (c & 7) + '0'; | ||
15919 | + } | ||
15920 | + if (c) | ||
15921 | + continue; | ||
15922 | + if (argv_count) { | ||
15923 | + if (--argv_count == 0) { | ||
15924 | + if (truncated) { | ||
15925 | + cp = last_start; | ||
15926 | + memmove(cp, "... ", 4); | ||
15927 | + cp += 4; | ||
15928 | + } | ||
15929 | + memmove(cp, "} envp[]={ ", 11); | ||
15930 | + cp += 11; | ||
15931 | + last_start = cp; | ||
15932 | + truncated = false; | ||
15933 | + } | ||
15934 | + } else if (envp_count) { | ||
15935 | + if (--envp_count == 0) { | ||
15936 | + if (truncated) { | ||
15937 | + cp = last_start; | ||
15938 | + memmove(cp, "... ", 4); | ||
15939 | + cp += 4; | ||
15940 | + } | ||
15941 | + } | ||
15942 | + } | ||
15943 | + if (!argv_count && !envp_count) | ||
15944 | + break; | ||
15945 | + } | ||
15946 | + offset = 0; | ||
15947 | + } | ||
15948 | + *cp++ = '}'; | ||
15949 | + *cp = '\0'; | ||
15950 | + return buffer; | ||
15951 | +out: | ||
15952 | + snprintf(buffer, ccs_buffer_len - 1, "argv[]={ ... } envp[]= { ... }"); | ||
15953 | + return buffer; | ||
15954 | +} | ||
15955 | + | ||
15956 | +/** | ||
15957 | + * ccs_filetype - Get string representation of file type. | ||
15958 | + * | ||
15959 | + * @mode: Mode value for stat(). | ||
15960 | + * | ||
15961 | + * Returns file type string. | ||
15962 | + */ | ||
15963 | +static inline const char *ccs_filetype(const umode_t mode) | ||
15964 | +{ | ||
15965 | + switch (mode & S_IFMT) { | ||
15966 | + case S_IFREG: | ||
15967 | + case 0: | ||
15968 | + return ccs_condition_keyword[CCS_TYPE_IS_FILE]; | ||
15969 | + case S_IFDIR: | ||
15970 | + return ccs_condition_keyword[CCS_TYPE_IS_DIRECTORY]; | ||
15971 | + case S_IFLNK: | ||
15972 | + return ccs_condition_keyword[CCS_TYPE_IS_SYMLINK]; | ||
15973 | + case S_IFIFO: | ||
15974 | + return ccs_condition_keyword[CCS_TYPE_IS_FIFO]; | ||
15975 | + case S_IFSOCK: | ||
15976 | + return ccs_condition_keyword[CCS_TYPE_IS_SOCKET]; | ||
15977 | + case S_IFBLK: | ||
15978 | + return ccs_condition_keyword[CCS_TYPE_IS_BLOCK_DEV]; | ||
15979 | + case S_IFCHR: | ||
15980 | + return ccs_condition_keyword[CCS_TYPE_IS_CHAR_DEV]; | ||
15981 | + } | ||
15982 | + return "unknown"; /* This should not happen. */ | ||
15983 | +} | ||
15984 | + | ||
15985 | +/** | ||
15986 | + * ccs_print_header - Get header line of audit log. | ||
15987 | + * | ||
15988 | + * @r: Pointer to "struct ccs_request_info". | ||
15989 | + * | ||
15990 | + * Returns string representation. | ||
15991 | + * | ||
15992 | + * This function uses kmalloc(), so caller must kfree() if this function | ||
15993 | + * didn't return NULL. | ||
15994 | + */ | ||
15995 | +static char *ccs_print_header(struct ccs_request_info *r) | ||
15996 | +{ | ||
15997 | + struct ccs_time stamp; | ||
15998 | + struct ccs_obj_info *obj = r->obj; | ||
15999 | + const u32 ccs_flags = ccs_current_flags(); | ||
16000 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) | ||
16001 | + const pid_t gpid = ccs_sys_getpid(); | ||
16002 | +#else | ||
16003 | + const pid_t gpid = task_pid_nr(current); | ||
16004 | +#endif | ||
16005 | + static const int ccs_buffer_len = 4096; | ||
16006 | + char *buffer = kmalloc(ccs_buffer_len, CCS_GFP_FLAGS); | ||
16007 | + int pos; | ||
16008 | + u8 i; | ||
16009 | + if (!buffer) | ||
16010 | + return NULL; | ||
16011 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) | ||
16012 | + { | ||
16013 | + struct timeval tv; | ||
16014 | + do_gettimeofday(&tv); | ||
16015 | + ccs_convert_time(tv.tv_sec, &stamp); | ||
16016 | + } | ||
16017 | +#else | ||
16018 | + ccs_convert_time(get_seconds(), &stamp); | ||
16019 | +#endif | ||
16020 | + pos = snprintf(buffer, ccs_buffer_len - 1, | ||
16021 | + "#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s " | ||
16022 | + "granted=%s (global-pid=%u) task={ pid=%u ppid=%u " | ||
16023 | + "uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u " | ||
16024 | + "fsuid=%u fsgid=%u type%s=execute_handler }", | ||
16025 | + stamp.year, stamp.month, stamp.day, stamp.hour, | ||
16026 | + stamp.min, stamp.sec, r->profile, ccs_mode[r->mode], | ||
16027 | + ccs_yesno(r->granted), gpid, ccs_sys_getpid(), | ||
16028 | + ccs_sys_getppid(), | ||
16029 | + from_kuid(&init_user_ns, current_uid()), | ||
16030 | + from_kgid(&init_user_ns, current_gid()), | ||
16031 | + from_kuid(&init_user_ns, current_euid()), | ||
16032 | + from_kgid(&init_user_ns, current_egid()), | ||
16033 | + from_kuid(&init_user_ns, current_suid()), | ||
16034 | + from_kgid(&init_user_ns, current_sgid()), | ||
16035 | + from_kuid(&init_user_ns, current_fsuid()), | ||
16036 | + from_kgid(&init_user_ns, current_fsgid()), | ||
16037 | + ccs_flags & CCS_TASK_IS_EXECUTE_HANDLER ? "" : "!"); | ||
16038 | + if (!obj) | ||
16039 | + goto no_obj_info; | ||
16040 | + if (!obj->validate_done) { | ||
16041 | + ccs_get_attributes(obj); | ||
16042 | + obj->validate_done = true; | ||
16043 | + } | ||
16044 | + for (i = 0; i < CCS_MAX_PATH_STAT; i++) { | ||
16045 | + struct ccs_mini_stat *stat; | ||
16046 | + unsigned int dev; | ||
16047 | + umode_t mode; | ||
16048 | + if (!obj->stat_valid[i]) | ||
16049 | + continue; | ||
16050 | + stat = &obj->stat[i]; | ||
16051 | + dev = stat->dev; | ||
16052 | + mode = stat->mode; | ||
16053 | + if (i & 1) { | ||
16054 | + pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, | ||
16055 | + " path%u.parent={ uid=%u gid=%u " | ||
16056 | + "ino=%lu perm=0%o }", (i >> 1) + 1, | ||
16057 | + from_kuid(&init_user_ns, stat->uid), | ||
16058 | + from_kgid(&init_user_ns, stat->gid), | ||
16059 | + (unsigned long) stat->ino, | ||
16060 | + stat->mode & S_IALLUGO); | ||
16061 | + continue; | ||
16062 | + } | ||
16063 | + pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, | ||
16064 | + " path%u={ uid=%u gid=%u ino=%lu major=%u" | ||
16065 | + " minor=%u perm=0%o type=%s", (i >> 1) + 1, | ||
16066 | + from_kuid(&init_user_ns, stat->uid), | ||
16067 | + from_kgid(&init_user_ns, stat->gid), | ||
16068 | + (unsigned long) stat->ino, MAJOR(dev), | ||
16069 | + MINOR(dev), mode & S_IALLUGO, | ||
16070 | + ccs_filetype(mode)); | ||
16071 | + if (S_ISCHR(mode) || S_ISBLK(mode)) { | ||
16072 | + dev = stat->rdev; | ||
16073 | + pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, | ||
16074 | + " dev_major=%u dev_minor=%u", | ||
16075 | + MAJOR(dev), MINOR(dev)); | ||
16076 | + } | ||
16077 | + pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, " }"); | ||
16078 | + } | ||
16079 | +no_obj_info: | ||
16080 | + if (pos < ccs_buffer_len - 1) | ||
16081 | + return buffer; | ||
16082 | + kfree(buffer); | ||
16083 | + return NULL; | ||
16084 | +} | ||
16085 | + | ||
16086 | +/** | ||
16087 | + * ccs_init_log - Allocate buffer for audit logs. | ||
16088 | + * | ||
16089 | + * @r: Pointer to "struct ccs_request_info". | ||
16090 | + * @len: Buffer size needed for @fmt and @args. | ||
16091 | + * @fmt: The printf()'s format string. | ||
16092 | + * @args: va_list structure for @fmt. | ||
16093 | + * | ||
16094 | + * Returns pointer to allocated memory. | ||
16095 | + * | ||
16096 | + * This function uses kzalloc(), so caller must kfree() if this function | ||
16097 | + * didn't return NULL. | ||
16098 | + */ | ||
16099 | +static char *ccs_init_log(struct ccs_request_info *r, int len, const char *fmt, | ||
16100 | + va_list args) | ||
16101 | +{ | ||
16102 | + char *buf = NULL; | ||
16103 | + char *bprm_info = NULL; | ||
16104 | + char *realpath = NULL; | ||
16105 | + const char *symlink = NULL; | ||
16106 | + const char *header = NULL; | ||
16107 | + int pos; | ||
16108 | + const char *domainname = ccs_current_domain()->domainname->name; | ||
16109 | + header = ccs_print_header(r); | ||
16110 | + if (!header) | ||
16111 | + return NULL; | ||
16112 | + /* +10 is for '\n' etc. and '\0'. */ | ||
16113 | + len += strlen(domainname) + strlen(header) + 10; | ||
16114 | + if (r->ee) { | ||
16115 | + struct file *file = r->ee->bprm->file; | ||
16116 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) | ||
16117 | + struct path path = { file->f_vfsmnt, file->f_dentry }; | ||
16118 | + realpath = ccs_realpath(&path); | ||
16119 | +#else | ||
16120 | + realpath = ccs_realpath(&file->f_path); | ||
16121 | +#endif | ||
16122 | + bprm_info = ccs_print_bprm(r->ee->bprm, &r->ee->dump); | ||
16123 | + if (!realpath || !bprm_info) | ||
16124 | + goto out; | ||
16125 | + /* +80 is for " exec={ realpath=\"%s\" argc=%d envc=%d %s }" */ | ||
16126 | + len += strlen(realpath) + 80 + strlen(bprm_info); | ||
16127 | + } else if (r->obj && r->obj->symlink_target) { | ||
16128 | + symlink = r->obj->symlink_target->name; | ||
16129 | + /* +18 is for " symlink.target=\"%s\"" */ | ||
16130 | + len += 18 + strlen(symlink); | ||
16131 | + } | ||
16132 | + len = ccs_round2(len); | ||
16133 | + buf = kzalloc(len, CCS_GFP_FLAGS); | ||
16134 | + if (!buf) | ||
16135 | + goto out; | ||
16136 | + len--; | ||
16137 | + pos = snprintf(buf, len, "%s", header); | ||
16138 | + if (realpath) { | ||
16139 | + struct linux_binprm *bprm = r->ee->bprm; | ||
16140 | + pos += snprintf(buf + pos, len - pos, | ||
16141 | + " exec={ realpath=\"%s\" argc=%d envc=%d %s }", | ||
16142 | + realpath, bprm->argc, bprm->envc, bprm_info); | ||
16143 | + } else if (symlink) | ||
16144 | + pos += snprintf(buf + pos, len - pos, " symlink.target=\"%s\"", | ||
16145 | + symlink); | ||
16146 | + pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname); | ||
16147 | + vsnprintf(buf + pos, len - pos, fmt, args); | ||
16148 | +out: | ||
16149 | + kfree(realpath); | ||
16150 | + kfree(bprm_info); | ||
16151 | + kfree(header); | ||
16152 | + return buf; | ||
16153 | +} | ||
16154 | + | ||
16155 | +/** | ||
16156 | + * ccs_transition_failed - Print waning message and send signal when domain transition failed. | ||
16157 | + * | ||
16158 | + * @domainname: Name of domain to transit. | ||
16159 | + * | ||
16160 | + * Returns nothing. | ||
16161 | + * | ||
16162 | + * Note that if current->pid == 1, sending SIGKILL won't work. | ||
16163 | + */ | ||
16164 | +void ccs_transition_failed(const char *domainname) | ||
16165 | +{ | ||
16166 | + printk(KERN_WARNING | ||
16167 | + "ERROR: Unable to transit to '%s' domain.\n", domainname); | ||
16168 | + force_sig(SIGKILL, current); | ||
16169 | +} | ||
16170 | + | ||
16171 | +/** | ||
16172 | + * ccs_update_task_domain - Update task's domain. | ||
16173 | + * | ||
16174 | + * @r: Pointer to "struct ccs_request_info". | ||
16175 | + * | ||
16176 | + * Returns nothing. | ||
16177 | + * | ||
16178 | + * The task will retry as hard as possible. But if domain transition failed, | ||
16179 | + * the task will be killed by SIGKILL. | ||
16180 | + */ | ||
16181 | +static void ccs_update_task_domain(struct ccs_request_info *r) | ||
16182 | +{ | ||
16183 | + char *buf; | ||
16184 | + const char *cp; | ||
16185 | + const struct ccs_acl_info *acl = r->matched_acl; | ||
16186 | + r->matched_acl = NULL; | ||
16187 | + if (!acl || !acl->cond || !acl->cond->transit || | ||
16188 | + acl->cond->exec_transit) | ||
16189 | + return; | ||
16190 | + while (1) { | ||
16191 | + buf = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS); | ||
16192 | + if (buf) | ||
16193 | + break; | ||
16194 | + ssleep(1); | ||
16195 | + if (fatal_signal_pending(current)) | ||
16196 | + return; | ||
16197 | + } | ||
16198 | + cp = acl->cond->transit->name; | ||
16199 | + if (*cp == '/') | ||
16200 | + snprintf(buf, CCS_EXEC_TMPSIZE - 1, "%s %s", | ||
16201 | + ccs_current_domain()->domainname->name, cp); | ||
16202 | + else | ||
16203 | + strncpy(buf, cp, CCS_EXEC_TMPSIZE - 1); | ||
16204 | + if (!ccs_assign_domain(buf, true)) | ||
16205 | + ccs_transition_failed(buf); | ||
16206 | + kfree(buf); | ||
16207 | +} | ||
16208 | + | ||
16209 | +/** | ||
16210 | + * ccs_get_audit - Get audit mode. | ||
16211 | + * | ||
16212 | + * @r: Pointer to "struct ccs_request_info". | ||
16213 | + * | ||
16214 | + * Returns true if this request should be audited, false otherwise. | ||
16215 | + */ | ||
16216 | +static bool ccs_get_audit(const struct ccs_request_info *r) | ||
16217 | +{ | ||
16218 | + const struct ccs_acl_info *matched_acl = r->matched_acl; | ||
16219 | + const u8 profile = r->profile; | ||
16220 | + const u8 index = r->type; | ||
16221 | + const bool is_granted = r->granted; | ||
16222 | + u8 mode; | ||
16223 | + struct ccs_profile *p; | ||
16224 | + if (!ccs_policy_loaded) | ||
16225 | + return false; | ||
16226 | + p = ccs_profile(profile); | ||
16227 | + if (ccs_log_count >= p->pref[CCS_PREF_MAX_AUDIT_LOG]) | ||
16228 | + return false; | ||
16229 | + if (is_granted && matched_acl && matched_acl->cond && | ||
16230 | + matched_acl->cond->grant_log != CCS_GRANTLOG_AUTO) | ||
16231 | + return matched_acl->cond->grant_log == CCS_GRANTLOG_YES; | ||
16232 | + mode = p->config[index]; | ||
16233 | + if (mode == CCS_CONFIG_USE_DEFAULT) | ||
16234 | + mode = p->config | ||
16235 | + [ccs_index2category[index] + CCS_MAX_MAC_INDEX]; | ||
16236 | + if (mode == CCS_CONFIG_USE_DEFAULT) | ||
16237 | + mode = p->default_config; | ||
16238 | + if (is_granted) | ||
16239 | + return mode & CCS_CONFIG_WANT_GRANT_LOG; | ||
16240 | + return mode & CCS_CONFIG_WANT_REJECT_LOG; | ||
16241 | +} | ||
16242 | + | ||
16243 | +/** | ||
16244 | + * ccs_write_log2 - Write an audit log. | ||
16245 | + * | ||
16246 | + * @r: Pointer to "struct ccs_request_info". | ||
16247 | + * @len: Buffer size needed for @fmt and @args. | ||
16248 | + * @fmt: The printf()'s format string. | ||
16249 | + * @args: va_list structure for @fmt. | ||
16250 | + * | ||
16251 | + * Returns nothing. | ||
16252 | + */ | ||
16253 | +static void ccs_write_log2(struct ccs_request_info *r, int len, | ||
16254 | + const char *fmt, va_list args) | ||
16255 | +{ | ||
16256 | + char *buf; | ||
16257 | + struct ccs_log *entry; | ||
16258 | + bool quota_exceeded = false; | ||
16259 | + if (!ccs_get_audit(r)) | ||
16260 | + goto out; | ||
16261 | + buf = ccs_init_log(r, len, fmt, args); | ||
16262 | + if (!buf) | ||
16263 | + goto out; | ||
16264 | + entry = kzalloc(sizeof(*entry), CCS_GFP_FLAGS); | ||
16265 | + if (!entry) { | ||
16266 | + kfree(buf); | ||
16267 | + goto out; | ||
16268 | + } | ||
16269 | + entry->log = buf; | ||
16270 | + len = ccs_round2(strlen(buf) + 1); | ||
16271 | + /* | ||
16272 | + * The entry->size is used for memory quota checks. | ||
16273 | + * Don't go beyond strlen(entry->log). | ||
16274 | + */ | ||
16275 | + entry->size = len + ccs_round2(sizeof(*entry)); | ||
16276 | + spin_lock(&ccs_log_lock); | ||
16277 | + if (ccs_memory_quota[CCS_MEMORY_AUDIT] && | ||
16278 | + ccs_memory_used[CCS_MEMORY_AUDIT] + entry->size >= | ||
16279 | + ccs_memory_quota[CCS_MEMORY_AUDIT]) { | ||
16280 | + quota_exceeded = true; | ||
16281 | + } else { | ||
16282 | + ccs_memory_used[CCS_MEMORY_AUDIT] += entry->size; | ||
16283 | + list_add_tail(&entry->list, &ccs_log); | ||
16284 | + ccs_log_count++; | ||
16285 | + } | ||
16286 | + spin_unlock(&ccs_log_lock); | ||
16287 | + if (quota_exceeded) { | ||
16288 | + kfree(buf); | ||
16289 | + kfree(entry); | ||
16290 | + goto out; | ||
16291 | + } | ||
16292 | + wake_up(&ccs_log_wait); | ||
16293 | +out: | ||
16294 | + ccs_update_task_domain(r); | ||
16295 | +} | ||
16296 | + | ||
16297 | +/** | ||
16298 | + * ccs_write_log - Write an audit log. | ||
16299 | + * | ||
16300 | + * @r: Pointer to "struct ccs_request_info". | ||
16301 | + * @fmt: The printf()'s format string, followed by parameters. | ||
16302 | + * | ||
16303 | + * Returns nothing. | ||
16304 | + */ | ||
16305 | +void ccs_write_log(struct ccs_request_info *r, const char *fmt, ...) | ||
16306 | +{ | ||
16307 | + va_list args; | ||
16308 | + int len; | ||
16309 | + va_start(args, fmt); | ||
16310 | + len = vsnprintf((char *) &len, 1, fmt, args) + 1; | ||
16311 | + va_end(args); | ||
16312 | + va_start(args, fmt); | ||
16313 | + ccs_write_log2(r, len, fmt, args); | ||
16314 | + va_end(args); | ||
16315 | +} | ||
16316 | + | ||
16317 | +/** | ||
16318 | + * ccs_read_log - Read an audit log. | ||
16319 | + * | ||
16320 | + * @head: Pointer to "struct ccs_io_buffer". | ||
16321 | + * | ||
16322 | + * Returns nothing. | ||
16323 | + */ | ||
16324 | +static void ccs_read_log(struct ccs_io_buffer *head) | ||
16325 | +{ | ||
16326 | + struct ccs_log *ptr = NULL; | ||
16327 | + if (head->r.w_pos) | ||
16328 | + return; | ||
16329 | + kfree(head->read_buf); | ||
16330 | + head->read_buf = NULL; | ||
16331 | + spin_lock(&ccs_log_lock); | ||
16332 | + if (!list_empty(&ccs_log)) { | ||
16333 | + ptr = list_entry(ccs_log.next, typeof(*ptr), list); | ||
16334 | + list_del(&ptr->list); | ||
16335 | + ccs_log_count--; | ||
16336 | + ccs_memory_used[CCS_MEMORY_AUDIT] -= ptr->size; | ||
16337 | + } | ||
16338 | + spin_unlock(&ccs_log_lock); | ||
16339 | + if (ptr) { | ||
16340 | + head->read_buf = ptr->log; | ||
16341 | + head->r.w[head->r.w_pos++] = head->read_buf; | ||
16342 | + kfree(ptr); | ||
16343 | + } | ||
16344 | +} | ||
16345 | + | ||
16346 | +/** | ||
16347 | + * ccs_set_namespace_cursor - Set namespace to read. | ||
16348 | + * | ||
16349 | + * @head: Pointer to "struct ccs_io_buffer". | ||
16350 | + * | ||
16351 | + * Returns nothing. | ||
16352 | + */ | ||
16353 | +static void ccs_set_namespace_cursor(struct ccs_io_buffer *head) | ||
16354 | +{ | ||
16355 | + struct list_head *ns; | ||
16356 | + if (head->type != CCS_EXCEPTION_POLICY && head->type != CCS_PROFILE) | ||
16357 | + return; | ||
16358 | + /* | ||
16359 | + * If this is the first read, or reading previous namespace finished | ||
16360 | + * and has more namespaces to read, update the namespace cursor. | ||
16361 | + */ | ||
16362 | + ns = head->r.ns; | ||
16363 | + if (!ns || (head->r.eof && ns->next != &ccs_namespace_list)) { | ||
16364 | + /* Clearing is OK because ccs_flush() returned true. */ | ||
16365 | + memset(&head->r, 0, sizeof(head->r)); | ||
16366 | + head->r.ns = ns ? ns->next : ccs_namespace_list.next; | ||
16367 | + } | ||
16368 | +} | ||
16369 | + | ||
16370 | +/** | ||
16371 | + * ccs_has_more_namespace - Check for unread namespaces. | ||
16372 | + * | ||
16373 | + * @head: Pointer to "struct ccs_io_buffer". | ||
16374 | + * | ||
16375 | + * Returns true if we have more entries to print, false otherwise. | ||
16376 | + */ | ||
16377 | +static bool ccs_has_more_namespace(struct ccs_io_buffer *head) | ||
16378 | +{ | ||
16379 | + return (head->type == CCS_EXCEPTION_POLICY || | ||
16380 | + head->type == CCS_PROFILE) && head->r.eof && | ||
16381 | + head->r.ns->next != &ccs_namespace_list; | ||
16382 | +} | ||
16383 | + | ||
16384 | +/** | ||
16385 | + * ccs_find_namespace - Find specified namespace. | ||
16386 | + * | ||
16387 | + * @name: Name of namespace to find. | ||
16388 | + * @len: Length of @name. | ||
16389 | + * | ||
16390 | + * Returns pointer to "struct ccs_policy_namespace" if found, NULL otherwise. | ||
16391 | + * | ||
16392 | + * Caller holds ccs_read_lock(). | ||
16393 | + */ | ||
16394 | +static struct ccs_policy_namespace *ccs_find_namespace(const char *name, | ||
16395 | + const unsigned int len) | ||
16396 | +{ | ||
16397 | + struct ccs_policy_namespace *ns; | ||
16398 | + list_for_each_entry_srcu(ns, &ccs_namespace_list, namespace_list, | ||
16399 | + &ccs_ss) { | ||
16400 | + if (strncmp(name, ns->name, len) || | ||
16401 | + (name[len] && name[len] != ' ')) | ||
16402 | + continue; | ||
16403 | + return ns; | ||
16404 | + } | ||
16405 | + return NULL; | ||
16406 | +} | ||
16407 | + | ||
16408 | +/** | ||
16409 | + * ccs_assign_namespace - Create a new namespace. | ||
16410 | + * | ||
16411 | + * @domainname: Name of namespace to create. | ||
16412 | + * | ||
16413 | + * Returns pointer to "struct ccs_policy_namespace" on success, NULL otherwise. | ||
16414 | + * | ||
16415 | + * Caller holds ccs_read_lock(). | ||
16416 | + */ | ||
16417 | +static struct ccs_policy_namespace *ccs_assign_namespace | ||
16418 | +(const char *domainname) | ||
16419 | +{ | ||
16420 | + struct ccs_policy_namespace *ptr; | ||
16421 | + struct ccs_policy_namespace *entry; | ||
16422 | + const char *cp = domainname; | ||
16423 | + unsigned int len = 0; | ||
16424 | + while (*cp && *cp++ != ' ') | ||
16425 | + len++; | ||
16426 | + ptr = ccs_find_namespace(domainname, len); | ||
16427 | + if (ptr) | ||
16428 | + return ptr; | ||
16429 | + if (len >= CCS_EXEC_TMPSIZE - 10 || !ccs_domain_def(domainname)) | ||
16430 | + return NULL; | ||
16431 | + entry = kzalloc(sizeof(*entry) + len + 1, CCS_GFP_FLAGS); | ||
16432 | + if (!entry) | ||
16433 | + return NULL; | ||
16434 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | ||
16435 | + goto out; | ||
16436 | + ptr = ccs_find_namespace(domainname, len); | ||
16437 | + if (!ptr && ccs_memory_ok(entry, sizeof(*entry) + len + 1)) { | ||
16438 | + char *name = (char *) (entry + 1); | ||
16439 | + ptr = entry; | ||
16440 | + memmove(name, domainname, len); | ||
16441 | + name[len] = '\0'; | ||
16442 | + entry->name = name; | ||
16443 | + ccs_init_policy_namespace(entry); | ||
16444 | + entry = NULL; | ||
16445 | + } | ||
16446 | + mutex_unlock(&ccs_policy_lock); | ||
16447 | +out: | ||
16448 | + kfree(entry); | ||
16449 | + return ptr; | ||
16450 | +} | ||
16451 | + | ||
16452 | +/** | ||
16453 | + * ccs_namespace_jump - Check for namespace jump. | ||
16454 | + * | ||
16455 | + * @domainname: Name of domain. | ||
16456 | + * | ||
16457 | + * Returns true if namespace differs, false otherwise. | ||
16458 | + */ | ||
16459 | +static bool ccs_namespace_jump(const char *domainname) | ||
16460 | +{ | ||
16461 | + const char *namespace = ccs_current_namespace()->name; | ||
16462 | + const int len = strlen(namespace); | ||
16463 | + return strncmp(domainname, namespace, len) || | ||
16464 | + (domainname[len] && domainname[len] != ' '); | ||
16465 | +} | ||
16466 | + | ||
16467 | +/** | ||
16468 | + * ccs_assign_domain - Create a domain or a namespace. | ||
16469 | + * | ||
16470 | + * @domainname: The name of domain. | ||
16471 | + * @transit: True if transit to domain found or created. | ||
16472 | + * | ||
16473 | + * Returns pointer to "struct ccs_domain_info" on success, NULL otherwise. | ||
16474 | + * | ||
16475 | + * Caller holds ccs_read_lock(). | ||
16476 | + */ | ||
16477 | +struct ccs_domain_info *ccs_assign_domain(const char *domainname, | ||
16478 | + const bool transit) | ||
16479 | +{ | ||
16480 | + struct ccs_security *security = ccs_current_security(); | ||
16481 | + struct ccs_domain_info e = { }; | ||
16482 | + struct ccs_domain_info *entry = ccs_find_domain(domainname); | ||
16483 | + bool created = false; | ||
16484 | + if (entry) { | ||
16485 | + if (transit) { | ||
16486 | + /* | ||
16487 | + * Since namespace is created at runtime, profiles may | ||
16488 | + * not be created by the moment the process transits to | ||
16489 | + * that domain. Do not perform domain transition if | ||
16490 | + * profile for that domain is not yet created. | ||
16491 | + */ | ||
16492 | + if (ccs_policy_loaded && | ||
16493 | + !entry->ns->profile_ptr[entry->profile]) | ||
16494 | + return NULL; | ||
16495 | + security->ccs_domain_info = entry; | ||
16496 | + } | ||
16497 | + return entry; | ||
16498 | + } | ||
16499 | + /* Requested domain does not exist. */ | ||
16500 | + /* Don't create requested domain if domainname is invalid. */ | ||
16501 | + if (strlen(domainname) >= CCS_EXEC_TMPSIZE - 10 || | ||
16502 | + !ccs_correct_domain(domainname)) | ||
16503 | + return NULL; | ||
16504 | + /* | ||
16505 | + * Since definition of profiles and acl_groups may differ across | ||
16506 | + * namespaces, do not inherit "use_profile" and "use_group" settings | ||
16507 | + * by automatically creating requested domain upon domain transition. | ||
16508 | + */ | ||
16509 | + if (transit && ccs_namespace_jump(domainname)) | ||
16510 | + return NULL; | ||
16511 | + e.ns = ccs_assign_namespace(domainname); | ||
16512 | + if (!e.ns) | ||
16513 | + return NULL; | ||
16514 | + /* | ||
16515 | + * "use_profile" and "use_group" settings for automatically created | ||
16516 | + * domains are inherited from current domain. These are 0 for manually | ||
16517 | + * created domains. | ||
16518 | + */ | ||
16519 | + if (transit) { | ||
16520 | + const struct ccs_domain_info *domain = | ||
16521 | + security->ccs_domain_info; | ||
16522 | + e.profile = domain->profile; | ||
16523 | + memcpy(e.group, domain->group, sizeof(e.group)); | ||
16524 | + } | ||
16525 | + e.domainname = ccs_get_name(domainname); | ||
16526 | + if (!e.domainname) | ||
16527 | + return NULL; | ||
16528 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | ||
16529 | + goto out; | ||
16530 | + entry = ccs_find_domain(domainname); | ||
16531 | + if (!entry) { | ||
16532 | + entry = ccs_commit_ok(&e, sizeof(e)); | ||
16533 | + if (entry) { | ||
16534 | + INIT_LIST_HEAD(&entry->acl_info_list); | ||
16535 | + list_add_tail_rcu(&entry->list, &ccs_domain_list); | ||
16536 | + created = true; | ||
16537 | + } | ||
16538 | + } | ||
16539 | + mutex_unlock(&ccs_policy_lock); | ||
16540 | +out: | ||
16541 | + ccs_put_name(e.domainname); | ||
16542 | + if (entry && transit) { | ||
16543 | + security->ccs_domain_info = entry; | ||
16544 | + if (created) { | ||
16545 | + struct ccs_request_info r; | ||
16546 | + int i; | ||
16547 | + ccs_init_request_info(&r, CCS_MAC_FILE_EXECUTE); | ||
16548 | + r.granted = false; | ||
16549 | + ccs_write_log(&r, "use_profile %u\n", entry->profile); | ||
16550 | + for (i = 0; i < CCS_MAX_ACL_GROUPS; i++) | ||
16551 | + if (test_bit(i, entry->group)) | ||
16552 | + ccs_write_log(&r, "use_group %u\n", i); | ||
16553 | + ccs_update_stat(CCS_STAT_POLICY_UPDATES); | ||
16554 | + } | ||
16555 | + } | ||
16556 | + return entry; | ||
16557 | +} | ||
16558 | + | ||
16559 | +/** | ||
16560 | + * ccs_parse_policy - Parse a policy line. | ||
16561 | + * | ||
16562 | + * @head: Poiter to "struct ccs_io_buffer". | ||
16563 | + * @line: Line to parse. | ||
16564 | + * | ||
16565 | + * Returns 0 on success, negative value otherwise. | ||
16566 | + * | ||
16567 | + * Caller holds ccs_read_lock(). | ||
16568 | + */ | ||
16569 | +static int ccs_parse_policy(struct ccs_io_buffer *head, char *line) | ||
16570 | +{ | ||
16571 | + /* Delete request? */ | ||
16572 | + head->w.is_delete = !strncmp(line, "delete ", 7); | ||
16573 | + if (head->w.is_delete) | ||
16574 | + memmove(line, line + 7, strlen(line + 7) + 1); | ||
16575 | + /* Selecting namespace to update. */ | ||
16576 | + if (head->type == CCS_EXCEPTION_POLICY || head->type == CCS_PROFILE) { | ||
16577 | + if (*line == '<') { | ||
16578 | + char *cp = strchr(line, ' '); | ||
16579 | + if (cp) { | ||
16580 | + *cp++ = '\0'; | ||
16581 | + head->w.ns = ccs_assign_namespace(line); | ||
16582 | + memmove(line, cp, strlen(cp) + 1); | ||
16583 | + } else | ||
16584 | + head->w.ns = NULL; | ||
16585 | + } else | ||
16586 | + head->w.ns = &ccs_kernel_namespace; | ||
16587 | + /* Don't allow updating if namespace is invalid. */ | ||
16588 | + if (!head->w.ns) | ||
16589 | + return -ENOENT; | ||
16590 | + } | ||
16591 | + /* Do the update. */ | ||
16592 | + switch (head->type) { | ||
16593 | + case CCS_DOMAIN_POLICY: | ||
16594 | + return ccs_write_domain(head); | ||
16595 | + case CCS_EXCEPTION_POLICY: | ||
16596 | + return ccs_write_exception(head); | ||
16597 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
16598 | + case CCS_EXECUTE_HANDLER: | ||
16599 | +#endif | ||
16600 | + case CCS_PROCESS_STATUS: | ||
16601 | + return ccs_write_pid(head); | ||
16602 | + case CCS_STAT: | ||
16603 | + return ccs_write_stat(head); | ||
16604 | + case CCS_PROFILE: | ||
16605 | + return ccs_write_profile(head); | ||
16606 | + case CCS_QUERY: | ||
16607 | + return ccs_write_answer(head); | ||
16608 | + case CCS_MANAGER: | ||
16609 | + return ccs_write_manager(head); | ||
16610 | + default: | ||
16611 | + return -ENOSYS; | ||
16612 | + } | ||
16613 | +} | ||
16614 | + | ||
16615 | +/** | ||
16616 | + * ccs_policy_io_init - Register hooks for policy I/O. | ||
16617 | + * | ||
16618 | + * Returns nothing. | ||
16619 | + */ | ||
16620 | +static void __init ccs_policy_io_init(void) | ||
16621 | +{ | ||
16622 | + ccsecurity_ops.check_profile = ccs_check_profile; | ||
16623 | +} | ||
16624 | + | ||
16625 | +/** | ||
16626 | + * ccs_load_builtin_policy - Load built-in policy. | ||
16627 | + * | ||
16628 | + * Returns nothing. | ||
16629 | + */ | ||
16630 | +static void __init ccs_load_builtin_policy(void) | ||
16631 | +{ | ||
16632 | + /* | ||
16633 | + * This include file is manually created and contains built-in policy | ||
16634 | + * named "ccs_builtin_profile", "ccs_builtin_exception_policy", | ||
16635 | + * "ccs_builtin_domain_policy", "ccs_builtin_manager", | ||
16636 | + * "ccs_builtin_stat" in the form of "static char [] __initdata". | ||
16637 | + */ | ||
16638 | +#include "builtin-policy.h" | ||
16639 | + u8 i; | ||
16640 | + const int idx = ccs_read_lock(); | ||
16641 | + for (i = 0; i < 5; i++) { | ||
16642 | + struct ccs_io_buffer head = { }; | ||
16643 | + char *start = ""; | ||
16644 | + switch (i) { | ||
16645 | + case 0: | ||
16646 | + start = ccs_builtin_profile; | ||
16647 | + head.type = CCS_PROFILE; | ||
16648 | + break; | ||
16649 | + case 1: | ||
16650 | + start = ccs_builtin_exception_policy; | ||
16651 | + head.type = CCS_EXCEPTION_POLICY; | ||
16652 | + break; | ||
16653 | + case 2: | ||
16654 | + start = ccs_builtin_domain_policy; | ||
16655 | + head.type = CCS_DOMAIN_POLICY; | ||
16656 | + break; | ||
16657 | + case 3: | ||
16658 | + start = ccs_builtin_manager; | ||
16659 | + head.type = CCS_MANAGER; | ||
16660 | + break; | ||
16661 | + case 4: | ||
16662 | + start = ccs_builtin_stat; | ||
16663 | + head.type = CCS_STAT; | ||
16664 | + break; | ||
16665 | + } | ||
16666 | + while (1) { | ||
16667 | + char *end = strchr(start, '\n'); | ||
16668 | + if (!end) | ||
16669 | + break; | ||
16670 | + *end = '\0'; | ||
16671 | + ccs_normalize_line(start); | ||
16672 | + head.write_buf = start; | ||
16673 | + ccs_parse_policy(&head, start); | ||
16674 | + start = end + 1; | ||
16675 | + } | ||
16676 | + } | ||
16677 | + ccs_read_unlock(idx); | ||
16678 | +#ifdef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | ||
16679 | + ccs_check_profile(); | ||
16680 | +#endif | ||
16681 | +} | ||
16682 | + | ||
16683 | +/** | ||
16684 | + * ccs_read_self - read() for /proc/ccs/self_domain interface. | ||
16685 | + * | ||
16686 | + * @file: Pointer to "struct file". | ||
16687 | + * @buf: Domainname which current thread belongs to. | ||
16688 | + * @count: Size of @buf. | ||
16689 | + * @ppos: Bytes read by now. | ||
16690 | + * | ||
16691 | + * Returns read size on success, negative value otherwise. | ||
16692 | + */ | ||
16693 | +static ssize_t ccs_read_self(struct file *file, char __user *buf, size_t count, | ||
16694 | + loff_t *ppos) | ||
16695 | +{ | ||
16696 | + const char *domain = ccs_current_domain()->domainname->name; | ||
16697 | + loff_t len = strlen(domain); | ||
16698 | + loff_t pos = *ppos; | ||
16699 | + if (pos >= len || !count) | ||
16700 | + return 0; | ||
16701 | + len -= pos; | ||
16702 | + if (count < len) | ||
16703 | + len = count; | ||
16704 | + if (copy_to_user(buf, domain + pos, len)) | ||
16705 | + return -EFAULT; | ||
16706 | + *ppos += len; | ||
16707 | + return len; | ||
16708 | +} | ||
16709 | + | ||
16710 | +/** | ||
16711 | + * ccs_open - open() for /proc/ccs/ interface. | ||
16712 | + * | ||
16713 | + * @inode: Pointer to "struct inode". | ||
16714 | + * @file: Pointer to "struct file". | ||
16715 | + * | ||
16716 | + * Returns 0 on success, negative value otherwise. | ||
16717 | + */ | ||
16718 | +static int ccs_open(struct inode *inode, struct file *file) | ||
16719 | +{ | ||
16720 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) | ||
16721 | + const u8 type = (unsigned long) PDE_DATA(inode); | ||
16722 | +#else | ||
16723 | + const u8 type = (unsigned long) PDE(inode)->data; | ||
16724 | +#endif | ||
16725 | + struct ccs_io_buffer *head = kzalloc(sizeof(*head), CCS_GFP_FLAGS); | ||
16726 | + if (!head) | ||
16727 | + return -ENOMEM; | ||
16728 | + mutex_init(&head->io_sem); | ||
16729 | + head->type = type; | ||
16730 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
16731 | + if (type == CCS_EXECUTE_HANDLER) { | ||
16732 | + /* Allow execute_handler to read process's status. */ | ||
16733 | + if (!(ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER)) { | ||
16734 | + kfree(head); | ||
16735 | + return -EPERM; | ||
16736 | + } | ||
16737 | + } | ||
16738 | +#endif | ||
16739 | + if ((file->f_mode & FMODE_READ) && type != CCS_AUDIT && | ||
16740 | + type != CCS_QUERY) { | ||
16741 | + /* Don't allocate read_buf for poll() access. */ | ||
16742 | + head->readbuf_size = 4096; | ||
16743 | + head->read_buf = kzalloc(head->readbuf_size, CCS_GFP_FLAGS); | ||
16744 | + if (!head->read_buf) { | ||
16745 | + kfree(head); | ||
16746 | + return -ENOMEM; | ||
16747 | + } | ||
16748 | + } | ||
16749 | + if (file->f_mode & FMODE_WRITE) { | ||
16750 | + head->writebuf_size = 4096; | ||
16751 | + head->write_buf = kzalloc(head->writebuf_size, CCS_GFP_FLAGS); | ||
16752 | + if (!head->write_buf) { | ||
16753 | + kfree(head->read_buf); | ||
16754 | + kfree(head); | ||
16755 | + return -ENOMEM; | ||
16756 | + } | ||
16757 | + } | ||
16758 | + /* | ||
16759 | + * If the file is /proc/ccs/query, increment the observer counter. | ||
16760 | + * The obserber counter is used by ccs_supervisor() to see if | ||
16761 | + * there is some process monitoring /proc/ccs/query. | ||
16762 | + */ | ||
16763 | + if (type == CCS_QUERY) | ||
16764 | + atomic_inc(&ccs_query_observers); | ||
16765 | + file->private_data = head; | ||
16766 | + ccs_notify_gc(head, true); | ||
16767 | + return 0; | ||
16768 | +} | ||
16769 | + | ||
16770 | +/** | ||
16771 | + * ccs_release - close() for /proc/ccs/ interface. | ||
16772 | + * | ||
16773 | + * @inode: Pointer to "struct inode". | ||
16774 | + * @file: Pointer to "struct file". | ||
16775 | + * | ||
16776 | + * Returns 0. | ||
16777 | + */ | ||
16778 | +static int ccs_release(struct inode *inode, struct file *file) | ||
16779 | +{ | ||
16780 | + struct ccs_io_buffer *head = file->private_data; | ||
16781 | + /* | ||
16782 | + * If the file is /proc/ccs/query, decrement the observer counter. | ||
16783 | + */ | ||
16784 | + if (head->type == CCS_QUERY && | ||
16785 | + atomic_dec_and_test(&ccs_query_observers)) | ||
16786 | + wake_up_all(&ccs_answer_wait); | ||
16787 | + ccs_notify_gc(head, false); | ||
16788 | + return 0; | ||
16789 | +} | ||
16790 | + | ||
16791 | +/** | ||
16792 | + * ccs_poll - poll() for /proc/ccs/ interface. | ||
16793 | + * | ||
16794 | + * @file: Pointer to "struct file". | ||
16795 | + * @wait: Pointer to "poll_table". Maybe NULL. | ||
16796 | + * | ||
16797 | + * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, | ||
16798 | + * POLLOUT | POLLWRNORM otherwise. | ||
16799 | + */ | ||
16800 | +static unsigned int ccs_poll(struct file *file, poll_table *wait) | ||
16801 | +{ | ||
16802 | + struct ccs_io_buffer *head = file->private_data; | ||
16803 | + if (head->type == CCS_AUDIT) { | ||
16804 | + if (!ccs_memory_used[CCS_MEMORY_AUDIT]) { | ||
16805 | + poll_wait(file, &ccs_log_wait, wait); | ||
16806 | + if (!ccs_memory_used[CCS_MEMORY_AUDIT]) | ||
16807 | + return POLLOUT | POLLWRNORM; | ||
16808 | + } | ||
16809 | + } else if (head->type == CCS_QUERY) { | ||
16810 | + if (list_empty(&ccs_query_list)) { | ||
16811 | + poll_wait(file, &ccs_query_wait, wait); | ||
16812 | + if (list_empty(&ccs_query_list)) | ||
16813 | + return POLLOUT | POLLWRNORM; | ||
16814 | + } | ||
16815 | + } | ||
16816 | + return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; | ||
16817 | +} | ||
16818 | + | ||
16819 | +/** | ||
16820 | + * ccs_read - read() for /proc/ccs/ interface. | ||
16821 | + * | ||
16822 | + * @file: Pointer to "struct file". | ||
16823 | + * @buf: Pointer to buffer. | ||
16824 | + * @count: Size of @buf. | ||
16825 | + * @ppos: Unused. | ||
16826 | + * | ||
16827 | + * Returns bytes read on success, negative value otherwise. | ||
16828 | + */ | ||
16829 | +static ssize_t ccs_read(struct file *file, char __user *buf, size_t count, | ||
16830 | + loff_t *ppos) | ||
16831 | +{ | ||
16832 | + struct ccs_io_buffer *head = file->private_data; | ||
16833 | + int len; | ||
16834 | + int idx; | ||
16835 | + if (mutex_lock_interruptible(&head->io_sem)) | ||
16836 | + return -EINTR; | ||
16837 | + head->read_user_buf = buf; | ||
16838 | + head->read_user_buf_avail = count; | ||
16839 | + idx = ccs_read_lock(); | ||
16840 | + if (ccs_flush(head)) | ||
16841 | + /* Call the policy handler. */ | ||
16842 | + do { | ||
16843 | + ccs_set_namespace_cursor(head); | ||
16844 | + switch (head->type) { | ||
16845 | + case CCS_DOMAIN_POLICY: | ||
16846 | + ccs_read_domain(head); | ||
16847 | + break; | ||
16848 | + case CCS_EXCEPTION_POLICY: | ||
16849 | + ccs_read_exception(head); | ||
16850 | + break; | ||
16851 | + case CCS_AUDIT: | ||
16852 | + ccs_read_log(head); | ||
16853 | + break; | ||
16854 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
16855 | + case CCS_EXECUTE_HANDLER: | ||
16856 | +#endif | ||
16857 | + case CCS_PROCESS_STATUS: | ||
16858 | + ccs_read_pid(head); | ||
16859 | + break; | ||
16860 | + case CCS_VERSION: | ||
16861 | + ccs_read_version(head); | ||
16862 | + break; | ||
16863 | + case CCS_STAT: | ||
16864 | + ccs_read_stat(head); | ||
16865 | + break; | ||
16866 | + case CCS_PROFILE: | ||
16867 | + ccs_read_profile(head); | ||
16868 | + break; | ||
16869 | + case CCS_QUERY: | ||
16870 | + ccs_read_query(head); | ||
16871 | + break; | ||
16872 | + case CCS_MANAGER: | ||
16873 | + ccs_read_manager(head); | ||
16874 | + break; | ||
16875 | + } | ||
16876 | + } while (ccs_flush(head) && ccs_has_more_namespace(head)); | ||
16877 | + ccs_read_unlock(idx); | ||
16878 | + len = head->read_user_buf - buf; | ||
16879 | + mutex_unlock(&head->io_sem); | ||
16880 | + return len; | ||
16881 | +} | ||
16882 | + | ||
16883 | +#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION | ||
16884 | + | ||
16885 | +/** | ||
16886 | + * ccs_write_self - write() for /proc/ccs/self_domain interface. | ||
16887 | + * | ||
16888 | + * @file: Pointer to "struct file". | ||
16889 | + * @buf: Domainname to transit to. | ||
16890 | + * @count: Size of @buf. | ||
16891 | + * @ppos: Unused. | ||
16892 | + * | ||
16893 | + * Returns @count on success, negative value otherwise. | ||
16894 | + * | ||
16895 | + * If domain transition was permitted but the domain transition failed, this | ||
16896 | + * function returns error rather than terminating current thread with SIGKILL. | ||
16897 | + */ | ||
16898 | +static ssize_t ccs_write_self(struct file *file, const char __user *buf, | ||
16899 | + size_t count, loff_t *ppos) | ||
16900 | +{ | ||
16901 | + char *data; | ||
16902 | + int error; | ||
16903 | + if (!count || count >= CCS_EXEC_TMPSIZE - 10) | ||
16904 | + return -ENOMEM; | ||
16905 | + data = kzalloc(count + 1, CCS_GFP_FLAGS); | ||
16906 | + if (!data) | ||
16907 | + return -ENOMEM; | ||
16908 | + if (copy_from_user(data, buf, count)) { | ||
16909 | + error = -EFAULT; | ||
16910 | + goto out; | ||
16911 | + } | ||
16912 | + ccs_normalize_line(data); | ||
16913 | + if (ccs_correct_domain(data)) { | ||
16914 | + const int idx = ccs_read_lock(); | ||
16915 | + struct ccs_path_info name; | ||
16916 | + struct ccs_request_info r; | ||
16917 | + name.name = data; | ||
16918 | + ccs_fill_path_info(&name); | ||
16919 | + /* Check "task manual_domain_transition" permission. */ | ||
16920 | + ccs_init_request_info(&r, CCS_MAC_FILE_EXECUTE); | ||
16921 | + r.param_type = CCS_TYPE_MANUAL_TASK_ACL; | ||
16922 | + r.param.task.domainname = &name; | ||
16923 | + ccs_check_acl(&r); | ||
16924 | + if (!r.granted) | ||
16925 | + error = -EPERM; | ||
16926 | + else | ||
16927 | + error = ccs_assign_domain(data, true) ? 0 : -ENOENT; | ||
16928 | + ccs_read_unlock(idx); | ||
16929 | + } else | ||
16930 | + error = -EINVAL; | ||
16931 | +out: | ||
16932 | + kfree(data); | ||
16933 | + return error ? error : count; | ||
16934 | +} | ||
16935 | + | ||
16936 | +#endif | ||
16937 | + | ||
16938 | +/** | ||
16939 | + * ccs_write - write() for /proc/ccs/ interface. | ||
16940 | + * | ||
16941 | + * @file: Pointer to "struct file". | ||
16942 | + * @buf: Pointer to buffer. | ||
16943 | + * @count: Size of @buf. | ||
16944 | + * @ppos: Unused. | ||
16945 | + * | ||
16946 | + * Returns @count on success, negative value otherwise. | ||
16947 | + */ | ||
16948 | +static ssize_t ccs_write(struct file *file, const char __user *buf, | ||
16949 | + size_t count, loff_t *ppos) | ||
16950 | +{ | ||
16951 | + struct ccs_io_buffer *head = file->private_data; | ||
16952 | + int error = count; | ||
16953 | + char *cp0 = head->write_buf; | ||
16954 | + int idx; | ||
16955 | + if (mutex_lock_interruptible(&head->io_sem)) | ||
16956 | + return -EINTR; | ||
16957 | + head->read_user_buf_avail = 0; | ||
16958 | + idx = ccs_read_lock(); | ||
16959 | + /* Read a line and dispatch it to the policy handler. */ | ||
16960 | + while (count) { | ||
16961 | + char c; | ||
16962 | + if (head->w.avail >= head->writebuf_size - 1) { | ||
16963 | + const int len = head->writebuf_size * 2; | ||
16964 | + char *cp = kzalloc(len, CCS_GFP_FLAGS); | ||
16965 | + if (!cp) { | ||
16966 | + error = -ENOMEM; | ||
16967 | + break; | ||
16968 | + } | ||
16969 | + memmove(cp, cp0, head->w.avail); | ||
16970 | + kfree(cp0); | ||
16971 | + head->write_buf = cp; | ||
16972 | + cp0 = cp; | ||
16973 | + head->writebuf_size = len; | ||
16974 | + } | ||
16975 | + if (get_user(c, buf)) { | ||
16976 | + error = -EFAULT; | ||
16977 | + break; | ||
16978 | + } | ||
16979 | + buf++; | ||
16980 | + count--; | ||
16981 | + cp0[head->w.avail++] = c; | ||
16982 | + if (c != '\n') | ||
16983 | + continue; | ||
16984 | + cp0[head->w.avail - 1] = '\0'; | ||
16985 | + head->w.avail = 0; | ||
16986 | + ccs_normalize_line(cp0); | ||
16987 | + if (!strcmp(cp0, "reset")) { | ||
16988 | + head->w.ns = &ccs_kernel_namespace; | ||
16989 | + head->w.domain = NULL; | ||
16990 | + memset(&head->r, 0, sizeof(head->r)); | ||
16991 | + continue; | ||
16992 | + } | ||
16993 | + /* Don't allow updating policies by non manager programs. */ | ||
16994 | + switch (head->type) { | ||
16995 | + case CCS_PROCESS_STATUS: | ||
16996 | + /* This does not write anything. */ | ||
16997 | + break; | ||
16998 | + case CCS_DOMAIN_POLICY: | ||
16999 | + if (ccs_select_domain(head, cp0)) | ||
17000 | + continue; | ||
17001 | + /* fall through */ | ||
17002 | + case CCS_EXCEPTION_POLICY: | ||
17003 | + if (!strcmp(cp0, "select transition_only")) { | ||
17004 | + head->r.print_transition_related_only = true; | ||
17005 | + continue; | ||
17006 | + } | ||
17007 | + /* fall through */ | ||
17008 | + default: | ||
17009 | + if (!ccs_manager()) { | ||
17010 | + error = -EPERM; | ||
17011 | + goto out; | ||
17012 | + } | ||
17013 | + } | ||
17014 | + switch (ccs_parse_policy(head, cp0)) { | ||
17015 | + case -EPERM: | ||
17016 | + error = -EPERM; | ||
17017 | + goto out; | ||
17018 | + case 0: | ||
17019 | + /* Update statistics. */ | ||
17020 | + switch (head->type) { | ||
17021 | + case CCS_DOMAIN_POLICY: | ||
17022 | + case CCS_EXCEPTION_POLICY: | ||
17023 | + case CCS_STAT: | ||
17024 | + case CCS_PROFILE: | ||
17025 | + case CCS_MANAGER: | ||
17026 | + ccs_update_stat(CCS_STAT_POLICY_UPDATES); | ||
17027 | + break; | ||
17028 | + default: | ||
17029 | + break; | ||
17030 | + } | ||
17031 | + break; | ||
17032 | + } | ||
17033 | + } | ||
17034 | +out: | ||
17035 | + ccs_read_unlock(idx); | ||
17036 | + mutex_unlock(&head->io_sem); | ||
17037 | + return error; | ||
17038 | +} | ||
17039 | + | ||
17040 | +/** | ||
17041 | + * ccs_create_entry - Create interface files under /proc/ccs/ directory. | ||
17042 | + * | ||
17043 | + * @name: The name of the interface file. | ||
17044 | + * @mode: The permission of the interface file. | ||
17045 | + * @parent: The parent directory. | ||
17046 | + * @key: Type of interface. | ||
17047 | + * | ||
17048 | + * Returns nothing. | ||
17049 | + */ | ||
17050 | +static void __init ccs_create_entry(const char *name, const umode_t mode, | ||
17051 | + struct proc_dir_entry *parent, | ||
17052 | + const u8 key) | ||
17053 | +{ | ||
17054 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) | ||
17055 | + proc_create_data(name, mode, parent, &ccs_operations, | ||
17056 | + ((u8 *) NULL) + key); | ||
17057 | +#else | ||
17058 | + struct proc_dir_entry *entry = create_proc_entry(name, mode, parent); | ||
17059 | + if (entry) { | ||
17060 | + entry->proc_fops = &ccs_operations; | ||
17061 | + entry->data = ((u8 *) NULL) + key; | ||
17062 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
17063 | + if (entry->proc_iops) | ||
17064 | + ccs_file_inode_operations = *entry->proc_iops; | ||
17065 | + if (!ccs_file_inode_operations.setattr) | ||
17066 | + ccs_file_inode_operations.setattr = proc_notify_change; | ||
17067 | + entry->proc_iops = &ccs_file_inode_operations; | ||
17068 | +#endif | ||
17069 | + } | ||
17070 | +#endif | ||
17071 | +} | ||
17072 | + | ||
17073 | +/** | ||
17074 | + * ccs_proc_init - Initialize /proc/ccs/ interface. | ||
17075 | + * | ||
17076 | + * Returns nothing. | ||
17077 | + */ | ||
17078 | +static void __init ccs_proc_init(void) | ||
17079 | +{ | ||
17080 | + struct proc_dir_entry *ccs_dir = proc_mkdir("ccs", NULL); | ||
17081 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
17082 | + if (ccs_dir->proc_iops) | ||
17083 | + ccs_dir_inode_operations = *ccs_dir->proc_iops; | ||
17084 | + if (!ccs_dir_inode_operations.setattr) | ||
17085 | + ccs_dir_inode_operations.setattr = proc_notify_change; | ||
17086 | + ccs_dir->proc_iops = &ccs_dir_inode_operations; | ||
17087 | +#endif | ||
17088 | + ccs_create_entry("query", 0600, ccs_dir, CCS_QUERY); | ||
17089 | + ccs_create_entry("domain_policy", 0600, ccs_dir, CCS_DOMAIN_POLICY); | ||
17090 | + ccs_create_entry("exception_policy", 0600, ccs_dir, | ||
17091 | + CCS_EXCEPTION_POLICY); | ||
17092 | + ccs_create_entry("audit", 0400, ccs_dir, CCS_AUDIT); | ||
17093 | + ccs_create_entry(".process_status", 0600, ccs_dir, | ||
17094 | + CCS_PROCESS_STATUS); | ||
17095 | + ccs_create_entry("stat", 0644, ccs_dir, CCS_STAT); | ||
17096 | + ccs_create_entry("profile", 0600, ccs_dir, CCS_PROFILE); | ||
17097 | + ccs_create_entry("manager", 0600, ccs_dir, CCS_MANAGER); | ||
17098 | + ccs_create_entry("version", 0400, ccs_dir, CCS_VERSION); | ||
17099 | +#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER | ||
17100 | + ccs_create_entry(".execute_handler", 0666, ccs_dir, | ||
17101 | + CCS_EXECUTE_HANDLER); | ||
17102 | +#endif | ||
17103 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) | ||
17104 | + proc_create("self_domain", 0666, ccs_dir, &ccs_self_operations); | ||
17105 | +#else | ||
17106 | + { | ||
17107 | + struct proc_dir_entry *e = create_proc_entry("self_domain", | ||
17108 | + 0666, ccs_dir); | ||
17109 | + if (e) | ||
17110 | + e->proc_fops = &ccs_self_operations; | ||
17111 | + } | ||
17112 | +#endif | ||
17113 | +} | ||
17114 | + | ||
17115 | +/** | ||
17116 | + * ccs_init_module - Initialize this module. | ||
17117 | + * | ||
17118 | + * Returns 0 on success, negative value otherwise. | ||
17119 | + */ | ||
17120 | +static int __init ccs_init_module(void) | ||
17121 | +{ | ||
17122 | + if (ccsecurity_ops.disabled) | ||
17123 | + return -EINVAL; | ||
17124 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) | ||
17125 | + MOD_INC_USE_COUNT; | ||
17126 | +#endif | ||
17127 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) | ||
17128 | + if (init_srcu_struct(&ccs_ss)) | ||
17129 | + panic("Out of memory."); | ||
17130 | +#endif | ||
17131 | + ccs_kernel_namespace.name = "<kernel>"; | ||
17132 | + ccs_init_policy_namespace(&ccs_kernel_namespace); | ||
17133 | + ccs_kernel_domain.ns = &ccs_kernel_namespace; | ||
17134 | + INIT_LIST_HEAD(&ccs_kernel_domain.acl_info_list); | ||
17135 | + ccs_mm_init(); | ||
17136 | + ccs_policy_io_init(); | ||
17137 | + ccs_permission_init(); | ||
17138 | + ccs_proc_init(); | ||
17139 | + ccs_load_builtin_policy(); | ||
17140 | + return 0; | ||
17141 | +} | ||
17142 | + | ||
17143 | +MODULE_LICENSE("GPL"); | ||
17144 | +module_init(ccs_init_module); | ||
17145 | diff --git a/security/ccsecurity/realpath.c b/security/ccsecurity/realpath.c | ||
17146 | new file mode 100644 | ||
17147 | index 0000000..df821ed | ||
17148 | --- /dev/null | ||
17149 | +++ b/security/ccsecurity/realpath.c | ||
17150 | @@ -0,0 +1,767 @@ | ||
17151 | +/* | ||
17152 | + * security/ccsecurity/realpath.c | ||
17153 | + * | ||
17154 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | ||
17155 | + * | ||
17156 | + * Version: 1.8.4 2015/05/05 | ||
17157 | + */ | ||
17158 | + | ||
17159 | +#include "internal.h" | ||
17160 | + | ||
17161 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) | ||
17162 | +#include <linux/nsproxy.h> | ||
17163 | +#include <linux/mnt_namespace.h> | ||
17164 | +#endif | ||
17165 | + | ||
17166 | +/***** SECTION1: Constants definition *****/ | ||
17167 | + | ||
17168 | +#define SOCKFS_MAGIC 0x534F434B | ||
17169 | + | ||
17170 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
17171 | +#define s_fs_info u.generic_sbp | ||
17172 | +#endif | ||
17173 | + | ||
17174 | +/***** SECTION2: Structure definition *****/ | ||
17175 | + | ||
17176 | +/***** SECTION3: Prototype definition section *****/ | ||
17177 | + | ||
17178 | +char *ccs_encode(const char *str); | ||
17179 | +char *ccs_encode2(const char *str, int str_len); | ||
17180 | +char *ccs_realpath(const struct path *path); | ||
17181 | +const char *ccs_get_exe(void); | ||
17182 | +void ccs_fill_path_info(struct ccs_path_info *ptr); | ||
17183 | + | ||
17184 | +static char *ccs_get_absolute_path(const struct path *path, | ||
17185 | + char * const buffer, const int buflen); | ||
17186 | +static char *ccs_get_dentry_path(struct dentry *dentry, char * const buffer, | ||
17187 | + const int buflen); | ||
17188 | +static char *ccs_get_local_path(struct dentry *dentry, char * const buffer, | ||
17189 | + const int buflen); | ||
17190 | +static char *ccs_get_socket_name(const struct path *path, char * const buffer, | ||
17191 | + const int buflen); | ||
17192 | +static int ccs_const_part_length(const char *filename); | ||
17193 | + | ||
17194 | +/***** SECTION4: Standalone functions section *****/ | ||
17195 | + | ||
17196 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) | ||
17197 | + | ||
17198 | +/** | ||
17199 | + * SOCKET_I - Get "struct socket". | ||
17200 | + * | ||
17201 | + * @inode: Pointer to "struct inode". | ||
17202 | + * | ||
17203 | + * Returns pointer to "struct socket". | ||
17204 | + * | ||
17205 | + * This is for compatibility with older kernels. | ||
17206 | + */ | ||
17207 | +static inline struct socket *SOCKET_I(struct inode *inode) | ||
17208 | +{ | ||
17209 | + return inode->i_sock ? &inode->u.socket_i : NULL; | ||
17210 | +} | ||
17211 | + | ||
17212 | +#endif | ||
17213 | + | ||
17214 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) | ||
17215 | + | ||
17216 | +/** | ||
17217 | + * ccs_realpath_lock - Take locks for __d_path(). | ||
17218 | + * | ||
17219 | + * Returns nothing. | ||
17220 | + */ | ||
17221 | +static inline void ccs_realpath_lock(void) | ||
17222 | +{ | ||
17223 | + /* dcache_lock is locked by __d_path(). */ | ||
17224 | + /* vfsmount_lock is locked by __d_path(). */ | ||
17225 | +} | ||
17226 | + | ||
17227 | +/** | ||
17228 | + * ccs_realpath_unlock - Release locks for __d_path(). | ||
17229 | + * | ||
17230 | + * Returns nothing. | ||
17231 | + */ | ||
17232 | +static inline void ccs_realpath_unlock(void) | ||
17233 | +{ | ||
17234 | + /* vfsmount_lock is unlocked by __d_path(). */ | ||
17235 | + /* dcache_lock is unlocked by __d_path(). */ | ||
17236 | +} | ||
17237 | + | ||
17238 | +#elif LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 36) | ||
17239 | + | ||
17240 | +/** | ||
17241 | + * ccs_realpath_lock - Take locks for __d_path(). | ||
17242 | + * | ||
17243 | + * Returns nothing. | ||
17244 | + */ | ||
17245 | +static inline void ccs_realpath_lock(void) | ||
17246 | +{ | ||
17247 | + spin_lock(&dcache_lock); | ||
17248 | + /* vfsmount_lock is locked by __d_path(). */ | ||
17249 | +} | ||
17250 | + | ||
17251 | +/** | ||
17252 | + * ccs_realpath_unlock - Release locks for __d_path(). | ||
17253 | + * | ||
17254 | + * Returns nothing. | ||
17255 | + */ | ||
17256 | +static inline void ccs_realpath_unlock(void) | ||
17257 | +{ | ||
17258 | + /* vfsmount_lock is unlocked by __d_path(). */ | ||
17259 | + spin_unlock(&dcache_lock); | ||
17260 | +} | ||
17261 | + | ||
17262 | +#elif defined(D_PATH_DISCONNECT) && !defined(CONFIG_SUSE_KERNEL) | ||
17263 | + | ||
17264 | +/** | ||
17265 | + * ccs_realpath_lock - Take locks for __d_path(). | ||
17266 | + * | ||
17267 | + * Returns nothing. | ||
17268 | + * | ||
17269 | + * Original unambiguous-__d_path.diff in patches.apparmor.tar.bz2 inversed the | ||
17270 | + * order of holding dcache_lock and vfsmount_lock. That patch was applied on | ||
17271 | + * (at least) SUSE 11.1 and Ubuntu 8.10 and Ubuntu 9.04 kernels. | ||
17272 | + * | ||
17273 | + * However, that patch was updated to use original order and the updated patch | ||
17274 | + * is applied to (as far as I know) only SUSE kernels. | ||
17275 | + * | ||
17276 | + * Therefore, I need to use original order for SUSE 11.1 kernels and inversed | ||
17277 | + * order for other kernels. I detect it by checking D_PATH_DISCONNECT and | ||
17278 | + * CONFIG_SUSE_KERNEL. I don't know whether other distributions are using the | ||
17279 | + * updated patch or not. If you got deadlock, check fs/dcache.c for locking | ||
17280 | + * order, and add " && 0" to this "#elif " block if fs/dcache.c uses original | ||
17281 | + * order. | ||
17282 | + */ | ||
17283 | +static inline void ccs_realpath_lock(void) | ||
17284 | +{ | ||
17285 | + spin_lock(ccsecurity_exports.vfsmount_lock); | ||
17286 | + spin_lock(&dcache_lock); | ||
17287 | +} | ||
17288 | + | ||
17289 | +/** | ||
17290 | + * ccs_realpath_unlock - Release locks for __d_path(). | ||
17291 | + * | ||
17292 | + * Returns nothing. | ||
17293 | + */ | ||
17294 | +static inline void ccs_realpath_unlock(void) | ||
17295 | +{ | ||
17296 | + spin_unlock(&dcache_lock); | ||
17297 | + spin_unlock(ccsecurity_exports.vfsmount_lock); | ||
17298 | +} | ||
17299 | + | ||
17300 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) | ||
17301 | + | ||
17302 | +/** | ||
17303 | + * ccs_realpath_lock - Take locks for __d_path(). | ||
17304 | + * | ||
17305 | + * Returns nothing. | ||
17306 | + */ | ||
17307 | +static inline void ccs_realpath_lock(void) | ||
17308 | +{ | ||
17309 | + spin_lock(&dcache_lock); | ||
17310 | + spin_lock(ccsecurity_exports.vfsmount_lock); | ||
17311 | +} | ||
17312 | + | ||
17313 | +/** | ||
17314 | + * ccs_realpath_unlock - Release locks for __d_path(). | ||
17315 | + * | ||
17316 | + * Returns nothing. | ||
17317 | + */ | ||
17318 | +static inline void ccs_realpath_unlock(void) | ||
17319 | +{ | ||
17320 | + spin_unlock(ccsecurity_exports.vfsmount_lock); | ||
17321 | + spin_unlock(&dcache_lock); | ||
17322 | +} | ||
17323 | + | ||
17324 | +#else | ||
17325 | + | ||
17326 | +/** | ||
17327 | + * ccs_realpath_lock - Take locks for __d_path(). | ||
17328 | + * | ||
17329 | + * Returns nothing. | ||
17330 | + */ | ||
17331 | +static inline void ccs_realpath_lock(void) | ||
17332 | +{ | ||
17333 | + spin_lock(&dcache_lock); | ||
17334 | +} | ||
17335 | + | ||
17336 | +/** | ||
17337 | + * ccs_realpath_unlock - Release locks for __d_path(). | ||
17338 | + * | ||
17339 | + * Returns nothing. | ||
17340 | + */ | ||
17341 | +static inline void ccs_realpath_unlock(void) | ||
17342 | +{ | ||
17343 | + spin_unlock(&dcache_lock); | ||
17344 | +} | ||
17345 | + | ||
17346 | +#endif | ||
17347 | + | ||
17348 | +/***** SECTION5: Variables definition section *****/ | ||
17349 | + | ||
17350 | +/***** SECTION6: Dependent functions section *****/ | ||
17351 | + | ||
17352 | +/** | ||
17353 | + * ccs_get_absolute_path - Get the path of a dentry but ignores chroot'ed root. | ||
17354 | + * | ||
17355 | + * @path: Pointer to "struct path". | ||
17356 | + * @buffer: Pointer to buffer to return value in. | ||
17357 | + * @buflen: Sizeof @buffer. | ||
17358 | + * | ||
17359 | + * Returns the buffer on success, an error code otherwise. | ||
17360 | + * | ||
17361 | + * Caller holds the dcache_lock and vfsmount_lock. | ||
17362 | + * Based on __d_path() in fs/dcache.c | ||
17363 | + * | ||
17364 | + * If dentry is a directory, trailing '/' is appended. | ||
17365 | + */ | ||
17366 | +static char *ccs_get_absolute_path(const struct path *path, | ||
17367 | + char * const buffer, const int buflen) | ||
17368 | +{ | ||
17369 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) | ||
17370 | + char *pos = ERR_PTR(-ENOMEM); | ||
17371 | + if (buflen >= 256) { | ||
17372 | + pos = ccsecurity_exports.d_absolute_path(path, buffer, | ||
17373 | + buflen - 1); | ||
17374 | + if (!IS_ERR(pos) && *pos == '/' && pos[1]) { | ||
17375 | + struct inode *inode = path->dentry->d_inode; | ||
17376 | + if (inode && S_ISDIR(inode->i_mode)) { | ||
17377 | + buffer[buflen - 2] = '/'; | ||
17378 | + buffer[buflen - 1] = '\0'; | ||
17379 | + } | ||
17380 | + } | ||
17381 | + } | ||
17382 | + return pos; | ||
17383 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | ||
17384 | + /* | ||
17385 | + * __d_path() will start returning NULL by backporting commit 02125a82 | ||
17386 | + * "fix apparmor dereferencing potentially freed dentry, sanitize | ||
17387 | + * __d_path() API". | ||
17388 | + * | ||
17389 | + * Unfortunately, __d_path() after applying that commit always returns | ||
17390 | + * NULL when root is empty. d_absolute_path() is provided for TOMOYO | ||
17391 | + * 2.x and AppArmor but TOMOYO 1.x does not use it, for TOMOYO 1.x | ||
17392 | + * might be built as a loadable kernel module and there is no warrantee | ||
17393 | + * that TOMOYO 1.x is recompiled after applying that commit. Also, | ||
17394 | + * I don't want to search /proc/kallsyms for d_absolute_path() because | ||
17395 | + * I want to keep TOMOYO 1.x architecture independent. Thus, supply | ||
17396 | + * non empty root like AppArmor's d_namespace_path() did. | ||
17397 | + */ | ||
17398 | + char *pos = ERR_PTR(-ENOMEM); | ||
17399 | + if (buflen >= 256) { | ||
17400 | + static bool ccs_no_empty; | ||
17401 | + if (!ccs_no_empty) { | ||
17402 | + struct path root = { }; | ||
17403 | + pos = ccsecurity_exports.__d_path(path, &root, buffer, | ||
17404 | + buflen - 1); | ||
17405 | + } else { | ||
17406 | + pos = NULL; | ||
17407 | + } | ||
17408 | + if (!pos) { | ||
17409 | + struct task_struct *task = current; | ||
17410 | + struct path root; | ||
17411 | + struct path tmp; | ||
17412 | + spin_lock(&task->fs->lock); | ||
17413 | + root.mnt = task->nsproxy->mnt_ns->root; | ||
17414 | + root.dentry = root.mnt->mnt_root; | ||
17415 | + path_get(&root); | ||
17416 | + spin_unlock(&task->fs->lock); | ||
17417 | + tmp = root; | ||
17418 | + pos = ccsecurity_exports.__d_path(path, &tmp, buffer, | ||
17419 | + buflen - 1); | ||
17420 | + path_put(&root); | ||
17421 | + if (!pos) | ||
17422 | + return ERR_PTR(-EINVAL); | ||
17423 | + /* Remember if __d_path() needs non empty root. */ | ||
17424 | + ccs_no_empty = true; | ||
17425 | + } | ||
17426 | + if (!IS_ERR(pos) && *pos == '/' && pos[1]) { | ||
17427 | + struct inode *inode = path->dentry->d_inode; | ||
17428 | + if (inode && S_ISDIR(inode->i_mode)) { | ||
17429 | + buffer[buflen - 2] = '/'; | ||
17430 | + buffer[buflen - 1] = '\0'; | ||
17431 | + } | ||
17432 | + } | ||
17433 | + } | ||
17434 | + return pos; | ||
17435 | +#else | ||
17436 | + char *pos = buffer + buflen - 1; | ||
17437 | + struct dentry *dentry = path->dentry; | ||
17438 | + struct vfsmount *vfsmnt = path->mnt; | ||
17439 | + const char *name; | ||
17440 | + int len; | ||
17441 | + | ||
17442 | + if (buflen < 256) | ||
17443 | + goto out; | ||
17444 | + | ||
17445 | + *pos = '\0'; | ||
17446 | + if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) | ||
17447 | + *--pos = '/'; | ||
17448 | + for (;;) { | ||
17449 | + struct dentry *parent; | ||
17450 | + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { | ||
17451 | + if (vfsmnt->mnt_parent == vfsmnt) | ||
17452 | + break; | ||
17453 | + dentry = vfsmnt->mnt_mountpoint; | ||
17454 | + vfsmnt = vfsmnt->mnt_parent; | ||
17455 | + continue; | ||
17456 | + } | ||
17457 | + parent = dentry->d_parent; | ||
17458 | + name = dentry->d_name.name; | ||
17459 | + len = dentry->d_name.len; | ||
17460 | + pos -= len; | ||
17461 | + if (pos <= buffer) | ||
17462 | + goto out; | ||
17463 | + memmove(pos, name, len); | ||
17464 | + *--pos = '/'; | ||
17465 | + dentry = parent; | ||
17466 | + } | ||
17467 | + if (*pos == '/') | ||
17468 | + pos++; | ||
17469 | + len = dentry->d_name.len; | ||
17470 | + pos -= len; | ||
17471 | + if (pos < buffer) | ||
17472 | + goto out; | ||
17473 | + memmove(pos, dentry->d_name.name, len); | ||
17474 | + return pos; | ||
17475 | +out: | ||
17476 | + return ERR_PTR(-ENOMEM); | ||
17477 | +#endif | ||
17478 | +} | ||
17479 | + | ||
17480 | +/** | ||
17481 | + * ccs_get_dentry_path - Get the path of a dentry. | ||
17482 | + * | ||
17483 | + * @dentry: Pointer to "struct dentry". | ||
17484 | + * @buffer: Pointer to buffer to return value in. | ||
17485 | + * @buflen: Sizeof @buffer. | ||
17486 | + * | ||
17487 | + * Returns the buffer on success, an error code otherwise. | ||
17488 | + * | ||
17489 | + * Based on dentry_path() in fs/dcache.c | ||
17490 | + * | ||
17491 | + * If dentry is a directory, trailing '/' is appended. | ||
17492 | + */ | ||
17493 | +static char *ccs_get_dentry_path(struct dentry *dentry, char * const buffer, | ||
17494 | + const int buflen) | ||
17495 | +{ | ||
17496 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) | ||
17497 | + char *pos = ERR_PTR(-ENOMEM); | ||
17498 | + if (buflen >= 256) { | ||
17499 | + pos = dentry_path_raw(dentry, buffer, buflen - 1); | ||
17500 | + if (!IS_ERR(pos) && *pos == '/' && pos[1] && | ||
17501 | + d_is_dir(dentry)) { | ||
17502 | + buffer[buflen - 2] = '/'; | ||
17503 | + buffer[buflen - 1] = '\0'; | ||
17504 | + } | ||
17505 | + } | ||
17506 | + return pos; | ||
17507 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) | ||
17508 | + char *pos = ERR_PTR(-ENOMEM); | ||
17509 | + if (buflen >= 256) { | ||
17510 | + /* rename_lock is locked/unlocked by dentry_path_raw(). */ | ||
17511 | + pos = dentry_path_raw(dentry, buffer, buflen - 1); | ||
17512 | + if (!IS_ERR(pos) && *pos == '/' && pos[1]) { | ||
17513 | + struct inode *inode = dentry->d_inode; | ||
17514 | + if (inode && S_ISDIR(inode->i_mode)) { | ||
17515 | + buffer[buflen - 2] = '/'; | ||
17516 | + buffer[buflen - 1] = '\0'; | ||
17517 | + } | ||
17518 | + } | ||
17519 | + } | ||
17520 | + return pos; | ||
17521 | +#else | ||
17522 | + char *pos = buffer + buflen - 1; | ||
17523 | + if (buflen < 256) | ||
17524 | + return ERR_PTR(-ENOMEM); | ||
17525 | + *pos = '\0'; | ||
17526 | + if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) | ||
17527 | + *--pos = '/'; | ||
17528 | + spin_lock(&dcache_lock); | ||
17529 | + while (!IS_ROOT(dentry)) { | ||
17530 | + struct dentry *parent = dentry->d_parent; | ||
17531 | + const char *name = dentry->d_name.name; | ||
17532 | + const int len = dentry->d_name.len; | ||
17533 | + pos -= len; | ||
17534 | + if (pos <= buffer) { | ||
17535 | + pos = ERR_PTR(-ENOMEM); | ||
17536 | + break; | ||
17537 | + } | ||
17538 | + memmove(pos, name, len); | ||
17539 | + *--pos = '/'; | ||
17540 | + dentry = parent; | ||
17541 | + } | ||
17542 | + spin_unlock(&dcache_lock); | ||
17543 | + return pos; | ||
17544 | +#endif | ||
17545 | +} | ||
17546 | + | ||
17547 | +/** | ||
17548 | + * ccs_get_local_path - Get the path of a dentry. | ||
17549 | + * | ||
17550 | + * @dentry: Pointer to "struct dentry". | ||
17551 | + * @buffer: Pointer to buffer to return value in. | ||
17552 | + * @buflen: Sizeof @buffer. | ||
17553 | + * | ||
17554 | + * Returns the buffer on success, an error code otherwise. | ||
17555 | + */ | ||
17556 | +static char *ccs_get_local_path(struct dentry *dentry, char * const buffer, | ||
17557 | + const int buflen) | ||
17558 | +{ | ||
17559 | + struct super_block *sb = dentry->d_sb; | ||
17560 | + char *pos = ccs_get_dentry_path(dentry, buffer, buflen); | ||
17561 | + if (IS_ERR(pos)) | ||
17562 | + return pos; | ||
17563 | + /* Convert from $PID to self if $PID is current thread. */ | ||
17564 | + if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') { | ||
17565 | + char *ep; | ||
17566 | + const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10); | ||
17567 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) | ||
17568 | + if (*ep == '/' && pid && pid == | ||
17569 | + task_tgid_nr_ns(current, sb->s_fs_info)) { | ||
17570 | + pos = ep - 5; | ||
17571 | + if (pos < buffer) | ||
17572 | + goto out; | ||
17573 | + memmove(pos, "/self", 5); | ||
17574 | + } | ||
17575 | +#else | ||
17576 | + if (*ep == '/' && pid == ccs_sys_getpid()) { | ||
17577 | + pos = ep - 5; | ||
17578 | + if (pos < buffer) | ||
17579 | + goto out; | ||
17580 | + memmove(pos, "/self", 5); | ||
17581 | + } | ||
17582 | +#endif | ||
17583 | + goto prepend_filesystem_name; | ||
17584 | + } | ||
17585 | + /* Use filesystem name for unnamed devices. */ | ||
17586 | + if (!MAJOR(sb->s_dev)) | ||
17587 | + goto prepend_filesystem_name; | ||
17588 | + { | ||
17589 | + struct inode *inode = sb->s_root->d_inode; | ||
17590 | + /* | ||
17591 | + * Use filesystem name if filesystems does not support rename() | ||
17592 | + * operation. | ||
17593 | + */ | ||
17594 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) | ||
17595 | + if (inode->i_op && !inode->i_op->rename) | ||
17596 | + goto prepend_filesystem_name; | ||
17597 | +#else | ||
17598 | + if (!inode->i_op->rename && !inode->i_op->rename2) | ||
17599 | + goto prepend_filesystem_name; | ||
17600 | +#endif | ||
17601 | + } | ||
17602 | + /* Prepend device name. */ | ||
17603 | + { | ||
17604 | + char name[64]; | ||
17605 | + int name_len; | ||
17606 | + const dev_t dev = sb->s_dev; | ||
17607 | + name[sizeof(name) - 1] = '\0'; | ||
17608 | + snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev), | ||
17609 | + MINOR(dev)); | ||
17610 | + name_len = strlen(name); | ||
17611 | + pos -= name_len; | ||
17612 | + if (pos < buffer) | ||
17613 | + goto out; | ||
17614 | + memmove(pos, name, name_len); | ||
17615 | + return pos; | ||
17616 | + } | ||
17617 | + /* Prepend filesystem name. */ | ||
17618 | +prepend_filesystem_name: | ||
17619 | + { | ||
17620 | + const char *name = sb->s_type->name; | ||
17621 | + const int name_len = strlen(name); | ||
17622 | + pos -= name_len + 1; | ||
17623 | + if (pos < buffer) | ||
17624 | + goto out; | ||
17625 | + memmove(pos, name, name_len); | ||
17626 | + pos[name_len] = ':'; | ||
17627 | + } | ||
17628 | + return pos; | ||
17629 | +out: | ||
17630 | + return ERR_PTR(-ENOMEM); | ||
17631 | +} | ||
17632 | + | ||
17633 | +/** | ||
17634 | + * ccs_get_socket_name - Get the name of a socket. | ||
17635 | + * | ||
17636 | + * @path: Pointer to "struct path". | ||
17637 | + * @buffer: Pointer to buffer to return value in. | ||
17638 | + * @buflen: Sizeof @buffer. | ||
17639 | + * | ||
17640 | + * Returns the buffer. | ||
17641 | + */ | ||
17642 | +static char *ccs_get_socket_name(const struct path *path, char * const buffer, | ||
17643 | + const int buflen) | ||
17644 | +{ | ||
17645 | + struct inode *inode = path->dentry->d_inode; | ||
17646 | + struct socket *sock = inode ? SOCKET_I(inode) : NULL; | ||
17647 | + struct sock *sk = sock ? sock->sk : NULL; | ||
17648 | + if (sk) { | ||
17649 | + snprintf(buffer, buflen, "socket:[family=%u:type=%u:" | ||
17650 | + "protocol=%u]", sk->sk_family, sk->sk_type, | ||
17651 | + sk->sk_protocol); | ||
17652 | + } else { | ||
17653 | + snprintf(buffer, buflen, "socket:[unknown]"); | ||
17654 | + } | ||
17655 | + return buffer; | ||
17656 | +} | ||
17657 | + | ||
17658 | +#define SOCKFS_MAGIC 0x534F434B | ||
17659 | + | ||
17660 | +/** | ||
17661 | + * ccs_realpath - Returns realpath(3) of the given pathname but ignores chroot'ed root. | ||
17662 | + * | ||
17663 | + * @path: Pointer to "struct path". | ||
17664 | + * | ||
17665 | + * Returns the realpath of the given @path on success, NULL otherwise. | ||
17666 | + * | ||
17667 | + * This function uses kzalloc(), so caller must kfree() if this function | ||
17668 | + * didn't return NULL. | ||
17669 | + */ | ||
17670 | +char *ccs_realpath(const struct path *path) | ||
17671 | +{ | ||
17672 | + char *buf = NULL; | ||
17673 | + char *name = NULL; | ||
17674 | + unsigned int buf_len = PAGE_SIZE / 2; | ||
17675 | + struct dentry *dentry = path->dentry; | ||
17676 | + struct super_block *sb; | ||
17677 | + if (!dentry) | ||
17678 | + return NULL; | ||
17679 | + sb = dentry->d_sb; | ||
17680 | + while (1) { | ||
17681 | + char *pos; | ||
17682 | + struct inode *inode; | ||
17683 | + buf_len <<= 1; | ||
17684 | + kfree(buf); | ||
17685 | + buf = kmalloc(buf_len, CCS_GFP_FLAGS); | ||
17686 | + if (!buf) | ||
17687 | + break; | ||
17688 | + /* To make sure that pos is '\0' terminated. */ | ||
17689 | + buf[buf_len - 1] = '\0'; | ||
17690 | + /* Get better name for socket. */ | ||
17691 | + if (sb->s_magic == SOCKFS_MAGIC) { | ||
17692 | + pos = ccs_get_socket_name(path, buf, buf_len - 1); | ||
17693 | + goto encode; | ||
17694 | + } | ||
17695 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) | ||
17696 | + /* For "pipe:[\$]". */ | ||
17697 | + if (dentry->d_op && dentry->d_op->d_dname) { | ||
17698 | + pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); | ||
17699 | + goto encode; | ||
17700 | + } | ||
17701 | +#endif | ||
17702 | + inode = sb->s_root->d_inode; | ||
17703 | + /* | ||
17704 | + * Use local name for "filesystems without rename() operation" | ||
17705 | + * or "path without vfsmount" or "absolute name is unavailable" | ||
17706 | + * cases. | ||
17707 | + */ | ||
17708 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) | ||
17709 | + if (!path->mnt || (inode->i_op && !inode->i_op->rename)) | ||
17710 | + pos = ERR_PTR(-EINVAL); | ||
17711 | + else { | ||
17712 | + /* Get absolute name for the rest. */ | ||
17713 | + ccs_realpath_lock(); | ||
17714 | + pos = ccs_get_absolute_path(path, buf, buf_len - 1); | ||
17715 | + ccs_realpath_unlock(); | ||
17716 | + } | ||
17717 | + if (pos == ERR_PTR(-EINVAL)) | ||
17718 | + pos = ccs_get_local_path(path->dentry, buf, | ||
17719 | + buf_len - 1); | ||
17720 | +#else | ||
17721 | + if (!path->mnt || | ||
17722 | + (!inode->i_op->rename && !inode->i_op->rename2)) | ||
17723 | + pos = ccs_get_local_path(path->dentry, buf, | ||
17724 | + buf_len - 1); | ||
17725 | + else | ||
17726 | + pos = ccs_get_absolute_path(path, buf, buf_len - 1); | ||
17727 | +#endif | ||
17728 | +encode: | ||
17729 | + if (IS_ERR(pos)) | ||
17730 | + continue; | ||
17731 | + name = ccs_encode(pos); | ||
17732 | + break; | ||
17733 | + } | ||
17734 | + kfree(buf); | ||
17735 | + if (!name) | ||
17736 | + ccs_warn_oom(__func__); | ||
17737 | + return name; | ||
17738 | +} | ||
17739 | + | ||
17740 | +/** | ||
17741 | + * ccs_encode2 - Encode binary string to ascii string. | ||
17742 | + * | ||
17743 | + * @str: String in binary format. | ||
17744 | + * @str_len: Size of @str in byte. | ||
17745 | + * | ||
17746 | + * Returns pointer to @str in ascii format on success, NULL otherwise. | ||
17747 | + * | ||
17748 | + * This function uses kzalloc(), so caller must kfree() if this function | ||
17749 | + * didn't return NULL. | ||
17750 | + */ | ||
17751 | +char *ccs_encode2(const char *str, int str_len) | ||
17752 | +{ | ||
17753 | + int i; | ||
17754 | + int len = 0; | ||
17755 | + const char *p = str; | ||
17756 | + char *cp; | ||
17757 | + char *cp0; | ||
17758 | + if (!p) | ||
17759 | + return NULL; | ||
17760 | + for (i = 0; i < str_len; i++) { | ||
17761 | + const unsigned char c = p[i]; | ||
17762 | + if (c == '\\') | ||
17763 | + len += 2; | ||
17764 | + else if (c > ' ' && c < 127) | ||
17765 | + len++; | ||
17766 | + else | ||
17767 | + len += 4; | ||
17768 | + } | ||
17769 | + len++; | ||
17770 | + /* Reserve space for appending "/". */ | ||
17771 | + cp = kzalloc(len + 10, CCS_GFP_FLAGS); | ||
17772 | + if (!cp) | ||
17773 | + return NULL; | ||
17774 | + cp0 = cp; | ||
17775 | + p = str; | ||
17776 | + for (i = 0; i < str_len; i++) { | ||
17777 | + const unsigned char c = p[i]; | ||
17778 | + if (c == '\\') { | ||
17779 | + *cp++ = '\\'; | ||
17780 | + *cp++ = '\\'; | ||
17781 | + } else if (c > ' ' && c < 127) { | ||
17782 | + *cp++ = c; | ||
17783 | + } else { | ||
17784 | + *cp++ = '\\'; | ||
17785 | + *cp++ = (c >> 6) + '0'; | ||
17786 | + *cp++ = ((c >> 3) & 7) + '0'; | ||
17787 | + *cp++ = (c & 7) + '0'; | ||
17788 | + } | ||
17789 | + } | ||
17790 | + return cp0; | ||
17791 | +} | ||
17792 | + | ||
17793 | +/** | ||
17794 | + * ccs_encode - Encode binary string to ascii string. | ||
17795 | + * | ||
17796 | + * @str: String in binary format. | ||
17797 | + * | ||
17798 | + * Returns pointer to @str in ascii format on success, NULL otherwise. | ||
17799 | + * | ||
17800 | + * This function uses kzalloc(), so caller must kfree() if this function | ||
17801 | + * didn't return NULL. | ||
17802 | + */ | ||
17803 | +char *ccs_encode(const char *str) | ||
17804 | +{ | ||
17805 | + return str ? ccs_encode2(str, strlen(str)) : NULL; | ||
17806 | +} | ||
17807 | + | ||
17808 | +/** | ||
17809 | + * ccs_const_part_length - Evaluate the initial length without a pattern in a token. | ||
17810 | + * | ||
17811 | + * @filename: The string to evaluate. | ||
17812 | + * | ||
17813 | + * Returns the initial length without a pattern in @filename. | ||
17814 | + */ | ||
17815 | +static int ccs_const_part_length(const char *filename) | ||
17816 | +{ | ||
17817 | + char c; | ||
17818 | + int len = 0; | ||
17819 | + if (!filename) | ||
17820 | + return 0; | ||
17821 | + while (1) { | ||
17822 | + c = *filename++; | ||
17823 | + if (!c) | ||
17824 | + break; | ||
17825 | + if (c != '\\') { | ||
17826 | + len++; | ||
17827 | + continue; | ||
17828 | + } | ||
17829 | + c = *filename++; | ||
17830 | + switch (c) { | ||
17831 | + case '\\': /* "\\" */ | ||
17832 | + len += 2; | ||
17833 | + continue; | ||
17834 | + case '0': /* "\ooo" */ | ||
17835 | + case '1': | ||
17836 | + case '2': | ||
17837 | + case '3': | ||
17838 | + c = *filename++; | ||
17839 | + if (c < '0' || c > '7') | ||
17840 | + break; | ||
17841 | + c = *filename++; | ||
17842 | + if (c < '0' || c > '7') | ||
17843 | + break; | ||
17844 | + len += 4; | ||
17845 | + continue; | ||
17846 | + } | ||
17847 | + break; | ||
17848 | + } | ||
17849 | + return len; | ||
17850 | +} | ||
17851 | + | ||
17852 | +/** | ||
17853 | + * ccs_fill_path_info - Fill in "struct ccs_path_info" members. | ||
17854 | + * | ||
17855 | + * @ptr: Pointer to "struct ccs_path_info" to fill in. | ||
17856 | + * | ||
17857 | + * The caller sets "struct ccs_path_info"->name. | ||
17858 | + */ | ||
17859 | +void ccs_fill_path_info(struct ccs_path_info *ptr) | ||
17860 | +{ | ||
17861 | + const char *name = ptr->name; | ||
17862 | + const int len = strlen(name); | ||
17863 | + ptr->total_len = len; | ||
17864 | + ptr->const_len = ccs_const_part_length(name); | ||
17865 | + ptr->is_dir = len && (name[len - 1] == '/'); | ||
17866 | + ptr->is_patterned = (ptr->const_len < len); | ||
17867 | + ptr->hash = full_name_hash(name, len); | ||
17868 | +} | ||
17869 | + | ||
17870 | +/** | ||
17871 | + * ccs_get_exe - Get ccs_realpath() of current process. | ||
17872 | + * | ||
17873 | + * Returns the ccs_realpath() of current process on success, NULL otherwise. | ||
17874 | + * | ||
17875 | + * This function uses kzalloc(), so the caller must kfree() | ||
17876 | + * if this function didn't return NULL. | ||
17877 | + */ | ||
17878 | +const char *ccs_get_exe(void) | ||
17879 | +{ | ||
17880 | + struct mm_struct *mm = current->mm; | ||
17881 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) | ||
17882 | + struct vm_area_struct *vma; | ||
17883 | +#endif | ||
17884 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) | ||
17885 | + struct path path; | ||
17886 | +#endif | ||
17887 | + struct file *exe_file = NULL; | ||
17888 | + const char *cp; | ||
17889 | + if (!mm) | ||
17890 | + return NULL; | ||
17891 | + down_read(&mm->mmap_sem); | ||
17892 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) | ||
17893 | + /* Not using get_mm_exe_file() as it is not exported. */ | ||
17894 | + exe_file = mm->exe_file; | ||
17895 | +#else | ||
17896 | + for (vma = mm->mmap; vma; vma = vma->vm_next) { | ||
17897 | + if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { | ||
17898 | + exe_file = vma->vm_file; | ||
17899 | + break; | ||
17900 | + } | ||
17901 | + } | ||
17902 | +#endif | ||
17903 | + if (exe_file) | ||
17904 | + get_file(exe_file); | ||
17905 | + up_read(&mm->mmap_sem); | ||
17906 | + if (!exe_file) | ||
17907 | + return NULL; | ||
17908 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) | ||
17909 | + cp = ccs_realpath(&exe_file->f_path); | ||
17910 | +#else | ||
17911 | + path.mnt = exe_file->f_vfsmnt; | ||
17912 | + path.dentry = exe_file->f_dentry; | ||
17913 | + cp = ccs_realpath(&path); | ||
17914 | +#endif | ||
17915 | + fput(exe_file); | ||
17916 | + return cp; | ||
17917 | +} | ||
17918 | -- | ||
17919 | 1.9.1 | ||
17920 | |||
diff --git a/recipes-kernel/linux/linux-yocto-4.1/tomoyo.cfg b/recipes-kernel/linux/linux-yocto-4.1/tomoyo.cfg new file mode 100644 index 0000000..8f78270 --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-4.1/tomoyo.cfg | |||
@@ -0,0 +1,16 @@ | |||
1 | CONFIG_CCSECURITY=y | ||
2 | CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY=y | ||
3 | CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY=2048 | ||
4 | CONFIG_CCSECURITY_MAX_AUDIT_LOG=1024 | ||
5 | CONFIG_CCSECURITY_POLICY_LOADER="/sbin/ccs-init" | ||
6 | CONFIG_CCSECURITY_ACTIVATION_TRIGGER="/sbin/init" | ||
7 | CONFIG_CCSECURITY_FILE_READDIR=y | ||
8 | CONFIG_CCSECURITY_FILE_GETATTR=y | ||
9 | CONFIG_CCSECURITY_NETWORK=y | ||
10 | CONFIG_CCSECURITY_NETWORK_RECVMSG=y | ||
11 | CONFIG_CCSECURITY_CAPABILITY=y | ||
12 | CONFIG_CCSECURITY_IPC=y | ||
13 | CONFIG_CCSECURITY_MISC=y | ||
14 | CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER=y | ||
15 | CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION=y | ||
16 | CONFIG_CCSECURITY_PORTRESERVE=y | ||
diff --git a/recipes-kernel/linux/linux-yocto-4.1/tomoyo.scc b/recipes-kernel/linux/linux-yocto-4.1/tomoyo.scc new file mode 100644 index 0000000..588864c --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-4.1/tomoyo.scc | |||
@@ -0,0 +1,4 @@ | |||
1 | define KFEATURE_DESCRIPTION "Tomoyo Kernel Support" | ||
2 | define KFEATURE_COMPATIBILITY arch | ||
3 | |||
4 | kconf non-hardware tomoyo.cfg | ||
diff --git a/recipes-kernel/linux/linux-yocto_4.1.bbappend b/recipes-kernel/linux/linux-yocto_4.1.bbappend new file mode 100644 index 0000000..1043d5c --- /dev/null +++ b/recipes-kernel/linux/linux-yocto_4.1.bbappend | |||
@@ -0,0 +1,9 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}-4.1:" | ||
2 | |||
3 | # Tomoyo kernel support | ||
4 | SRC_URI += "\ | ||
5 | ${@base_contains('DISTRO_FEATURES', 'tomoyo', ' file://ccs-tools-yocto.4.1.patch', '', d)} \ | ||
6 | ${@base_contains('DISTRO_FEATURES', 'tomoyo', ' file://ccs-tools-yocto_security.patch', '', d)} \ | ||
7 | ${@base_contains('DISTRO_FEATURES', 'tomoyo', ' file://tomoyo.cfg', '', d)} \ | ||
8 | ${@base_contains('DISTRO_FEATURES', 'tomoyo', ' file://tomoyo.scc', '', d)} \ | ||
9 | " | ||