(メッセージはありません)
@@ -0,0 +1,782 @@ | ||
1 | +/* | |
2 | + * include/linux/ccsecurity.h | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#ifndef _LINUX_CCSECURITY_H | |
10 | +#define _LINUX_CCSECURITY_H | |
11 | + | |
12 | +#include <linux/version.h> | |
13 | + | |
14 | +#ifndef __user | |
15 | +#define __user | |
16 | +#endif | |
17 | + | |
18 | +struct nameidata; | |
19 | +struct path; | |
20 | +struct dentry; | |
21 | +struct vfsmount; | |
22 | +struct linux_binprm; | |
23 | +struct pt_regs; | |
24 | +struct file; | |
25 | +struct ctl_table; | |
26 | +struct socket; | |
27 | +struct sockaddr; | |
28 | +struct sock; | |
29 | +struct sk_buff; | |
30 | +struct msghdr; | |
31 | +struct pid_namespace; | |
32 | +int search_binary_handler(struct linux_binprm *bprm, struct pt_regs *regs); | |
33 | + | |
34 | +#ifdef CONFIG_CCSECURITY | |
35 | + | |
36 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
37 | +/* Obtain prototype of __d_path() or d_absolute_path(). */ | |
38 | +#include <linux/dcache.h> | |
39 | +#endif | |
40 | +/* Obtain prototype of find_task_by_vpid() and find_task_by_pid_ns(). */ | |
41 | +#include <linux/sched.h> | |
42 | + | |
43 | +/* For exporting variables and functions. */ | |
44 | +struct ccsecurity_exports { | |
45 | + void (*load_policy) (const char *filename); | |
46 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) | |
47 | + typeof(d_absolute_path) (*d_absolute_path); | |
48 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
49 | + typeof(__d_path) (*__d_path); | |
50 | +#else | |
51 | + spinlock_t *vfsmount_lock; | |
52 | +#endif | |
53 | + typeof(find_task_by_vpid) (*find_task_by_vpid); | |
54 | + typeof(find_task_by_pid_ns) (*find_task_by_pid_ns); | |
55 | +}; | |
56 | + | |
57 | +/* For doing access control. */ | |
58 | +struct ccsecurity_operations { | |
59 | + void (*check_profile) (void); | |
60 | + int (*chroot_permission) (struct path *path); | |
61 | + int (*pivot_root_permission) (struct path *old_path, | |
62 | + struct path *new_path); | |
63 | + int (*mount_permission) (char *dev_name, struct path *path, | |
64 | + const char *type, unsigned long flags, | |
65 | + void *data_page); | |
66 | + int (*umount_permission) (struct vfsmount *mnt, int flags); | |
67 | + _Bool (*lport_reserved) (const u16 port); /* Not implemented. */ | |
68 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
69 | + void (*save_open_mode) (int mode); | |
70 | + void (*clear_open_mode) (void); | |
71 | + int (*open_permission) (struct dentry *dentry, struct vfsmount *mnt, | |
72 | + const int flag); | |
73 | +#else | |
74 | + int (*open_permission) (struct file *file); | |
75 | +#endif | |
76 | + int (*ptrace_permission) (long request, long pid); | |
77 | + int (*ioctl_permission) (struct file *filp, unsigned int cmd, | |
78 | + unsigned long arg); | |
79 | + int (*parse_table) (int __user *name, int nlen, void __user *oldval, | |
80 | + void __user *newval, struct ctl_table *table); | |
81 | + _Bool (*capable) (const u8 operation); | |
82 | + int (*mknod_permission) (struct dentry *dentry, struct vfsmount *mnt, | |
83 | + unsigned int mode, unsigned int dev); | |
84 | + int (*mkdir_permission) (struct dentry *dentry, struct vfsmount *mnt, | |
85 | + unsigned int mode); | |
86 | + int (*rmdir_permission) (struct dentry *dentry, struct vfsmount *mnt); | |
87 | + int (*unlink_permission) (struct dentry *dentry, struct vfsmount *mnt); | |
88 | + int (*symlink_permission) (struct dentry *dentry, struct vfsmount *mnt, | |
89 | + const char *from); | |
90 | + int (*truncate_permission) (struct dentry *dentry, | |
91 | + struct vfsmount *mnt); | |
92 | + int (*rename_permission) (struct dentry *old_dentry, | |
93 | + struct dentry *new_dentry, | |
94 | + struct vfsmount *mnt); | |
95 | + int (*link_permission) (struct dentry *old_dentry, | |
96 | + struct dentry *new_dentry, | |
97 | + struct vfsmount *mnt); | |
98 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | |
99 | + int (*open_exec_permission) (struct dentry *dentry, | |
100 | + struct vfsmount *mnt); | |
101 | + int (*uselib_permission) (struct dentry *dentry, struct vfsmount *mnt); | |
102 | +#endif | |
103 | + int (*fcntl_permission) (struct file *file, unsigned int cmd, | |
104 | + unsigned long arg); | |
105 | + int (*kill_permission) (pid_t pid, int sig); | |
106 | + int (*tgkill_permission) (pid_t tgid, pid_t pid, int sig); | |
107 | + int (*tkill_permission) (pid_t pid, int sig); | |
108 | + int (*socket_create_permission) (int family, int type, int protocol); | |
109 | + int (*socket_listen_permission) (struct socket *sock); | |
110 | + int (*socket_connect_permission) (struct socket *sock, | |
111 | + struct sockaddr *addr, int addr_len); | |
112 | + int (*socket_bind_permission) (struct socket *sock, | |
113 | + struct sockaddr *addr, int addr_len); | |
114 | + int (*socket_post_accept_permission) (struct socket *sock, | |
115 | + struct socket *newsock); | |
116 | + int (*socket_sendmsg_permission) (struct socket *sock, | |
117 | + struct msghdr *msg, int size); | |
118 | + int (*socket_post_recvmsg_permission) (struct sock *sk, | |
119 | + struct sk_buff *skb, int flags); | |
120 | + int (*chown_permission) (struct dentry *dentry, struct vfsmount *mnt, | |
121 | + uid_t user, gid_t group); | |
122 | + int (*chmod_permission) (struct dentry *dentry, struct vfsmount *mnt, | |
123 | + mode_t mode); | |
124 | + int (*getattr_permission) (struct vfsmount *mnt, | |
125 | + struct dentry *dentry); | |
126 | + int (*sigqueue_permission) (pid_t pid, int sig); | |
127 | + int (*tgsigqueue_permission) (pid_t tgid, pid_t pid, int sig); | |
128 | + int (*search_binary_handler) (struct linux_binprm *bprm, | |
129 | + struct pt_regs *regs); | |
130 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | |
131 | + int (*alloc_task_security) (const struct task_struct *task); | |
132 | + void (*free_task_security) (const struct task_struct *task); | |
133 | +#endif | |
134 | + _Bool disabled; | |
135 | +}; | |
136 | + | |
137 | +extern struct ccsecurity_operations ccsecurity_ops; | |
138 | + | |
139 | +static inline int ccs_chroot_permission(struct path *path) | |
140 | +{ | |
141 | + int (*func) (struct path *) = ccsecurity_ops.chroot_permission; | |
142 | + return func ? func(path) : 0; | |
143 | +} | |
144 | + | |
145 | +static inline int ccs_pivot_root_permission(struct path *old_path, | |
146 | + struct path *new_path) | |
147 | +{ | |
148 | + int (*func) (struct path *, struct path *) | |
149 | + = ccsecurity_ops.pivot_root_permission; | |
150 | + return func ? func(old_path, new_path) : 0; | |
151 | +} | |
152 | + | |
153 | +static inline int ccs_mount_permission(char *dev_name, struct path *path, | |
154 | + char *type, unsigned long flags, | |
155 | + void *data_page) | |
156 | +{ | |
157 | + int (*func) (char *, struct path *, const char *, unsigned long, | |
158 | + void *) = ccsecurity_ops.mount_permission; | |
159 | + return func ? func(dev_name, path, (const char *) type, flags, | |
160 | + data_page) : 0; | |
161 | +} | |
162 | + | |
163 | +static inline int ccs_umount_permission(struct vfsmount *mnt, int flags) | |
164 | +{ | |
165 | + int (*func) (struct vfsmount *, int) | |
166 | + = ccsecurity_ops.umount_permission; | |
167 | + return func ? func(mnt, flags) : 0; | |
168 | +} | |
169 | + | |
170 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
171 | + | |
172 | +static inline void ccs_save_open_mode(int mode) | |
173 | +{ | |
174 | + void (*func) (int) = ccsecurity_ops.save_open_mode; | |
175 | + if (func) | |
176 | + func(mode); | |
177 | +} | |
178 | + | |
179 | +static inline void ccs_clear_open_mode(void) | |
180 | +{ | |
181 | + void (*func) (void) = ccsecurity_ops.clear_open_mode; | |
182 | + if (func) | |
183 | + func(); | |
184 | +} | |
185 | + | |
186 | +static inline int ccs_open_permission(struct dentry *dentry, | |
187 | + struct vfsmount *mnt, const int flag) | |
188 | +{ | |
189 | + int (*func) (struct dentry *, struct vfsmount *, const int) | |
190 | + = ccsecurity_ops.open_permission; | |
191 | + return func ? func(dentry, mnt, flag) : 0; | |
192 | +} | |
193 | + | |
194 | +#else | |
195 | + | |
196 | +static inline int ccs_open_permission(struct file *filp) | |
197 | +{ | |
198 | + int (*func) (struct file *) = ccsecurity_ops.open_permission; | |
199 | + return func ? func(filp) : 0; | |
200 | +} | |
201 | + | |
202 | +#endif | |
203 | + | |
204 | +static inline int ccs_fcntl_permission(struct file *file, unsigned int cmd, | |
205 | + unsigned long arg) | |
206 | +{ | |
207 | + int (*func) (struct file *, unsigned int, unsigned long) | |
208 | + = ccsecurity_ops.fcntl_permission; | |
209 | + return func ? func(file, cmd, arg) : 0; | |
210 | +} | |
211 | + | |
212 | +static inline int ccs_ioctl_permission(struct file *filp, unsigned int cmd, | |
213 | + unsigned long arg) | |
214 | +{ | |
215 | + int (*func) (struct file *, unsigned int, unsigned long) | |
216 | + = ccsecurity_ops.ioctl_permission; | |
217 | + return func ? func(filp, cmd, arg) : 0; | |
218 | +} | |
219 | + | |
220 | +static inline int ccs_parse_table(int __user *name, int nlen, | |
221 | + void __user *oldval, void __user *newval, | |
222 | + struct ctl_table *table) | |
223 | +{ | |
224 | + int (*func) (int __user *, int, void __user *, void __user *, | |
225 | + struct ctl_table *) = ccsecurity_ops.parse_table; | |
226 | + return func ? func(name, nlen, oldval, newval, table) : 0; | |
227 | +} | |
228 | + | |
229 | +static inline int ccs_mknod_permission(struct dentry *dentry, | |
230 | + struct vfsmount *mnt, unsigned int mode, | |
231 | + unsigned int dev) | |
232 | +{ | |
233 | + int (*func) (struct dentry *, struct vfsmount *, unsigned int, | |
234 | + unsigned int) = ccsecurity_ops.mknod_permission; | |
235 | + return func ? func(dentry, mnt, mode, dev) : 0; | |
236 | +} | |
237 | + | |
238 | +static inline int ccs_mkdir_permission(struct dentry *dentry, | |
239 | + struct vfsmount *mnt, unsigned int mode) | |
240 | +{ | |
241 | + int (*func) (struct dentry *, struct vfsmount *, unsigned int) | |
242 | + = ccsecurity_ops.mkdir_permission; | |
243 | + return func ? func(dentry, mnt, mode) : 0; | |
244 | +} | |
245 | + | |
246 | +static inline int ccs_rmdir_permission(struct dentry *dentry, | |
247 | + struct vfsmount *mnt) | |
248 | +{ | |
249 | + int (*func) (struct dentry *, struct vfsmount *) | |
250 | + = ccsecurity_ops.rmdir_permission; | |
251 | + return func ? func(dentry, mnt) : 0; | |
252 | +} | |
253 | + | |
254 | +static inline int ccs_unlink_permission(struct dentry *dentry, | |
255 | + struct vfsmount *mnt) | |
256 | +{ | |
257 | + int (*func) (struct dentry *, struct vfsmount *) | |
258 | + = ccsecurity_ops.unlink_permission; | |
259 | + return func ? func(dentry, mnt) : 0; | |
260 | +} | |
261 | + | |
262 | +static inline int ccs_symlink_permission(struct dentry *dentry, | |
263 | + struct vfsmount *mnt, | |
264 | + const char *from) | |
265 | +{ | |
266 | + int (*func) (struct dentry *, struct vfsmount *, const char *) | |
267 | + = ccsecurity_ops.symlink_permission; | |
268 | + return func ? func(dentry, mnt, from) : 0; | |
269 | +} | |
270 | + | |
271 | +static inline int ccs_truncate_permission(struct dentry *dentry, | |
272 | + struct vfsmount *mnt) | |
273 | +{ | |
274 | + int (*func) (struct dentry *, struct vfsmount *) | |
275 | + = ccsecurity_ops.truncate_permission; | |
276 | + return func ? func(dentry, mnt) : 0; | |
277 | +} | |
278 | + | |
279 | +static inline int ccs_rename_permission(struct dentry *old_dentry, | |
280 | + struct dentry *new_dentry, | |
281 | + struct vfsmount *mnt) | |
282 | +{ | |
283 | + int (*func) (struct dentry *, struct dentry *, struct vfsmount *) | |
284 | + = ccsecurity_ops.rename_permission; | |
285 | + return func ? func(old_dentry, new_dentry, mnt) : 0; | |
286 | +} | |
287 | + | |
288 | +static inline int ccs_link_permission(struct dentry *old_dentry, | |
289 | + struct dentry *new_dentry, | |
290 | + struct vfsmount *mnt) | |
291 | +{ | |
292 | + int (*func) (struct dentry *, struct dentry *, struct vfsmount *) | |
293 | + = ccsecurity_ops.link_permission; | |
294 | + return func ? func(old_dentry, new_dentry, mnt) : 0; | |
295 | +} | |
296 | + | |
297 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | |
298 | + | |
299 | +static inline int ccs_open_exec_permission(struct dentry *dentry, | |
300 | + struct vfsmount *mnt) | |
301 | +{ | |
302 | + int (*func) (struct dentry *, struct vfsmount *) | |
303 | + = ccsecurity_ops.open_exec_permission; | |
304 | + return func ? func(dentry, mnt) : 0; | |
305 | +} | |
306 | + | |
307 | +static inline int ccs_uselib_permission(struct dentry *dentry, | |
308 | + struct vfsmount *mnt) | |
309 | +{ | |
310 | + int (*func) (struct dentry *, struct vfsmount *) | |
311 | + = ccsecurity_ops.uselib_permission; | |
312 | + return func ? func(dentry, mnt) : 0; | |
313 | +} | |
314 | + | |
315 | +#endif | |
316 | + | |
317 | +static inline int ccs_chown_permission(struct dentry *dentry, | |
318 | + struct vfsmount *mnt, uid_t user, | |
319 | + gid_t group) | |
320 | +{ | |
321 | + int (*func) (struct dentry *, struct vfsmount *, uid_t, gid_t) | |
322 | + = ccsecurity_ops.chown_permission; | |
323 | + return func ? func(dentry, mnt, user, group) : 0; | |
324 | +} | |
325 | + | |
326 | +static inline int ccs_chmod_permission(struct dentry *dentry, | |
327 | + struct vfsmount *mnt, mode_t mode) | |
328 | +{ | |
329 | + int (*func) (struct dentry *, struct vfsmount *, mode_t) | |
330 | + = ccsecurity_ops.chmod_permission; | |
331 | + return func ? func(dentry, mnt, mode) : 0; | |
332 | +} | |
333 | + | |
334 | +static inline int ccs_search_binary_handler(struct linux_binprm *bprm, | |
335 | + struct pt_regs *regs) | |
336 | +{ | |
337 | + return ccsecurity_ops.search_binary_handler(bprm, regs); | |
338 | +} | |
339 | + | |
340 | +#else | |
341 | + | |
342 | +static inline int ccs_chroot_permission(struct path *path) | |
343 | +{ | |
344 | + return 0; | |
345 | +} | |
346 | + | |
347 | +static inline int ccs_pivot_root_permission(struct path *old_path, | |
348 | + struct path *new_path) | |
349 | +{ | |
350 | + return 0; | |
351 | +} | |
352 | + | |
353 | +static inline int ccs_mount_permission(char *dev_name, struct path *path, | |
354 | + char *type, unsigned long flags, | |
355 | + void *data_page) | |
356 | +{ | |
357 | + return 0; | |
358 | +} | |
359 | + | |
360 | +static inline int ccs_umount_permission(struct vfsmount *mnt, int flags) | |
361 | +{ | |
362 | + return 0; | |
363 | +} | |
364 | + | |
365 | +static inline void ccs_save_open_mode(int mode) | |
366 | +{ | |
367 | +} | |
368 | + | |
369 | +static inline void ccs_clear_open_mode(void) | |
370 | +{ | |
371 | +} | |
372 | + | |
373 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
374 | + | |
375 | +static inline int ccs_open_permission(struct dentry *dentry, | |
376 | + struct vfsmount *mnt, const int flag) | |
377 | +{ | |
378 | + return 0; | |
379 | +} | |
380 | + | |
381 | +#else | |
382 | + | |
383 | +static inline int ccs_open_permission(struct file *filp) | |
384 | +{ | |
385 | + return 0; | |
386 | +} | |
387 | + | |
388 | +#endif | |
389 | + | |
390 | +static inline int ccs_ioctl_permission(struct file *filp, unsigned int cmd, | |
391 | + unsigned long arg) | |
392 | +{ | |
393 | + return 0; | |
394 | +} | |
395 | + | |
396 | +static inline int ccs_parse_table(int __user *name, int nlen, | |
397 | + void __user *oldval, void __user *newval, | |
398 | + struct ctl_table *table) | |
399 | +{ | |
400 | + return 0; | |
401 | +} | |
402 | + | |
403 | +static inline int ccs_mknod_permission(struct dentry *dentry, | |
404 | + struct vfsmount *mnt, unsigned int mode, | |
405 | + unsigned int dev) | |
406 | +{ | |
407 | + return 0; | |
408 | +} | |
409 | + | |
410 | +static inline int ccs_mkdir_permission(struct dentry *dentry, | |
411 | + struct vfsmount *mnt, unsigned int mode) | |
412 | +{ | |
413 | + return 0; | |
414 | +} | |
415 | + | |
416 | +static inline int ccs_rmdir_permission(struct dentry *dentry, | |
417 | + struct vfsmount *mnt) | |
418 | +{ | |
419 | + return 0; | |
420 | +} | |
421 | + | |
422 | +static inline int ccs_unlink_permission(struct dentry *dentry, | |
423 | + struct vfsmount *mnt) | |
424 | +{ | |
425 | + return 0; | |
426 | +} | |
427 | + | |
428 | +static inline int ccs_symlink_permission(struct dentry *dentry, | |
429 | + struct vfsmount *mnt, | |
430 | + const char *from) | |
431 | +{ | |
432 | + return 0; | |
433 | +} | |
434 | + | |
435 | +static inline int ccs_truncate_permission(struct dentry *dentry, | |
436 | + struct vfsmount *mnt) | |
437 | +{ | |
438 | + return 0; | |
439 | +} | |
440 | + | |
441 | +static inline int ccs_rename_permission(struct dentry *old_dentry, | |
442 | + struct dentry *new_dentry, | |
443 | + struct vfsmount *mnt) | |
444 | +{ | |
445 | + return 0; | |
446 | +} | |
447 | + | |
448 | +static inline int ccs_link_permission(struct dentry *old_dentry, | |
449 | + struct dentry *new_dentry, | |
450 | + struct vfsmount *mnt) | |
451 | +{ | |
452 | + return 0; | |
453 | +} | |
454 | + | |
455 | +static inline int ccs_open_exec_permission(struct dentry *dentry, | |
456 | + struct vfsmount *mnt) | |
457 | +{ | |
458 | + return 0; | |
459 | +} | |
460 | + | |
461 | +static inline int ccs_uselib_permission(struct dentry *dentry, | |
462 | + struct vfsmount *mnt) | |
463 | +{ | |
464 | + return 0; | |
465 | +} | |
466 | + | |
467 | +static inline int ccs_fcntl_permission(struct file *file, unsigned int cmd, | |
468 | + unsigned long arg) | |
469 | +{ | |
470 | + return 0; | |
471 | +} | |
472 | + | |
473 | +static inline int ccs_chown_permission(struct dentry *dentry, | |
474 | + struct vfsmount *mnt, uid_t user, | |
475 | + gid_t group) | |
476 | +{ | |
477 | + return 0; | |
478 | +} | |
479 | + | |
480 | +static inline int ccs_chmod_permission(struct dentry *dentry, | |
481 | + struct vfsmount *mnt, mode_t mode) | |
482 | +{ | |
483 | + return 0; | |
484 | +} | |
485 | + | |
486 | +static inline int ccs_search_binary_handler(struct linux_binprm *bprm, | |
487 | + struct pt_regs *regs) | |
488 | +{ | |
489 | + return search_binary_handler(bprm, regs); | |
490 | +} | |
491 | + | |
492 | +#endif | |
493 | + | |
494 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | |
495 | + | |
496 | +static inline int ccs_alloc_task_security(const struct task_struct *task) | |
497 | +{ | |
498 | + int (*func) (const struct task_struct *) | |
499 | + = ccsecurity_ops.alloc_task_security; | |
500 | + return func ? func(task) : 0; | |
501 | +} | |
502 | + | |
503 | +static inline void ccs_free_task_security(const struct task_struct *task) | |
504 | +{ | |
505 | + void (*func) (const struct task_struct *) | |
506 | + = ccsecurity_ops.free_task_security; | |
507 | + if (func) | |
508 | + func(task); | |
509 | +} | |
510 | + | |
511 | +#else | |
512 | + | |
513 | +static inline int ccs_alloc_task_security(const struct task_struct *task) | |
514 | +{ | |
515 | + return 0; | |
516 | +} | |
517 | + | |
518 | +static inline void ccs_free_task_security(const struct task_struct *task) | |
519 | +{ | |
520 | +} | |
521 | + | |
522 | +#endif | |
523 | + | |
524 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
525 | + | |
526 | +static inline int ccs_getattr_permission(struct vfsmount *mnt, | |
527 | + struct dentry *dentry) | |
528 | +{ | |
529 | + int (*func) (struct vfsmount *, struct dentry *) | |
530 | + = ccsecurity_ops.getattr_permission; | |
531 | + return func ? func(mnt, dentry) : 0; | |
532 | +} | |
533 | + | |
534 | +#else | |
535 | + | |
536 | +static inline int ccs_getattr_permission(struct vfsmount *mnt, | |
537 | + struct dentry *dentry) | |
538 | +{ | |
539 | + return 0; | |
540 | +} | |
541 | + | |
542 | +#endif | |
543 | + | |
544 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
545 | + | |
546 | +static inline int ccs_socket_listen_permission(struct socket *sock) | |
547 | +{ | |
548 | + int (*func) (struct socket *) | |
549 | + = ccsecurity_ops.socket_listen_permission; | |
550 | + return func ? func(sock) : 0; | |
551 | +} | |
552 | + | |
553 | +static inline int ccs_socket_connect_permission(struct socket *sock, | |
554 | + struct sockaddr *addr, | |
555 | + int addr_len) | |
556 | +{ | |
557 | + int (*func) (struct socket *, struct sockaddr *, int) | |
558 | + = ccsecurity_ops.socket_connect_permission; | |
559 | + return func ? func(sock, addr, addr_len) : 0; | |
560 | +} | |
561 | + | |
562 | +static inline int ccs_socket_bind_permission(struct socket *sock, | |
563 | + struct sockaddr *addr, | |
564 | + int addr_len) | |
565 | +{ | |
566 | + int (*func) (struct socket *, struct sockaddr *, int) | |
567 | + = ccsecurity_ops.socket_bind_permission; | |
568 | + return func ? func(sock, addr, addr_len) : 0; | |
569 | +} | |
570 | + | |
571 | +static inline int ccs_socket_post_accept_permission(struct socket *sock, | |
572 | + struct socket *newsock) | |
573 | +{ | |
574 | + int (*func) (struct socket *, struct socket *) | |
575 | + = ccsecurity_ops.socket_post_accept_permission; | |
576 | + return func ? func(sock, newsock) : 0; | |
577 | +} | |
578 | + | |
579 | +static inline int ccs_socket_sendmsg_permission(struct socket *sock, | |
580 | + struct msghdr *msg, | |
581 | + int size) | |
582 | +{ | |
583 | + int (*func) (struct socket *, struct msghdr *, int) | |
584 | + = ccsecurity_ops.socket_sendmsg_permission; | |
585 | + return func ? func(sock, msg, size) : 0; | |
586 | +} | |
587 | + | |
588 | +#else | |
589 | + | |
590 | +static inline int ccs_socket_listen_permission(struct socket *sock) | |
591 | +{ | |
592 | + return 0; | |
593 | +} | |
594 | + | |
595 | +static inline int ccs_socket_connect_permission(struct socket *sock, | |
596 | + struct sockaddr *addr, | |
597 | + int addr_len) | |
598 | +{ | |
599 | + return 0; | |
600 | +} | |
601 | + | |
602 | +static inline int ccs_socket_bind_permission(struct socket *sock, | |
603 | + struct sockaddr *addr, | |
604 | + int addr_len) | |
605 | +{ | |
606 | + return 0; | |
607 | +} | |
608 | + | |
609 | +static inline int ccs_socket_post_accept_permission(struct socket *sock, | |
610 | + struct socket *newsock) | |
611 | +{ | |
612 | + return 0; | |
613 | +} | |
614 | + | |
615 | +static inline int ccs_socket_sendmsg_permission(struct socket *sock, | |
616 | + struct msghdr *msg, | |
617 | + int size) | |
618 | +{ | |
619 | + return 0; | |
620 | +} | |
621 | + | |
622 | +#endif | |
623 | + | |
624 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
625 | + | |
626 | +static inline int ccs_socket_post_recvmsg_permission(struct sock *sk, | |
627 | + struct sk_buff *skb, | |
628 | + int flags) | |
629 | +{ | |
630 | + int (*func) (struct sock *, struct sk_buff *, int) | |
631 | + = ccsecurity_ops.socket_post_recvmsg_permission; | |
632 | + return func ? func(sk, skb, flags) : 0; | |
633 | +} | |
634 | + | |
635 | +#else | |
636 | + | |
637 | +static inline int ccs_socket_post_recvmsg_permission(struct sock *sk, | |
638 | + struct sk_buff *skb, | |
639 | + int flags) | |
640 | +{ | |
641 | + return 0; | |
642 | +} | |
643 | + | |
644 | +#endif | |
645 | + | |
646 | +static inline _Bool ccs_lport_reserved(const u16 port) | |
647 | +{ | |
648 | + return 0; | |
649 | +} | |
650 | + | |
651 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
652 | + | |
653 | +static inline _Bool ccs_capable(const u8 operation) | |
654 | +{ | |
655 | + _Bool (*func) (const u8) = ccsecurity_ops.capable; | |
656 | + return func ? func(operation) : 1; | |
657 | +} | |
658 | + | |
659 | +static inline int ccs_socket_create_permission(int family, int type, | |
660 | + int protocol) | |
661 | +{ | |
662 | + int (*func) (int, int, int) = ccsecurity_ops.socket_create_permission; | |
663 | + return func ? func(family, type, protocol) : 0; | |
664 | +} | |
665 | + | |
666 | +#else | |
667 | + | |
668 | +static inline _Bool ccs_capable(const u8 operation) | |
669 | +{ | |
670 | + return 1; | |
671 | +} | |
672 | + | |
673 | +static inline int ccs_socket_create_permission(int family, int type, | |
674 | + int protocol) | |
675 | +{ | |
676 | + return 0; | |
677 | +} | |
678 | + | |
679 | +#endif | |
680 | + | |
681 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
682 | + | |
683 | +static inline int ccs_ptrace_permission(long request, long pid) | |
684 | +{ | |
685 | + int (*func) (long, long) = ccsecurity_ops.ptrace_permission; | |
686 | + return func ? func(request, pid) : 0; | |
687 | +} | |
688 | + | |
689 | +#else | |
690 | + | |
691 | +static inline int ccs_ptrace_permission(long request, long pid) | |
692 | +{ | |
693 | + return 0; | |
694 | +} | |
695 | + | |
696 | +#endif | |
697 | + | |
698 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
699 | + | |
700 | +static inline int ccs_kill_permission(pid_t pid, int sig) | |
701 | +{ | |
702 | + int (*func) (pid_t, int) = ccsecurity_ops.kill_permission; | |
703 | + return func ? func(pid, sig) : 0; | |
704 | +} | |
705 | + | |
706 | +static inline int ccs_tgkill_permission(pid_t tgid, pid_t pid, int sig) | |
707 | +{ | |
708 | + int (*func) (pid_t, pid_t, int) = ccsecurity_ops.tgkill_permission; | |
709 | + return func ? func(tgid, pid, sig) : 0; | |
710 | +} | |
711 | + | |
712 | +static inline int ccs_tkill_permission(pid_t pid, int sig) | |
713 | +{ | |
714 | + int (*func) (pid_t, int) = ccsecurity_ops.tkill_permission; | |
715 | + return func ? func(pid, sig) : 0; | |
716 | +} | |
717 | + | |
718 | +static inline int ccs_sigqueue_permission(pid_t pid, int sig) | |
719 | +{ | |
720 | + int (*func) (pid_t, int) = ccsecurity_ops.sigqueue_permission; | |
721 | + return func ? func(pid, sig) : 0; | |
722 | +} | |
723 | + | |
724 | +static inline int ccs_tgsigqueue_permission(pid_t tgid, pid_t pid, int sig) | |
725 | +{ | |
726 | + int (*func) (pid_t, pid_t, int) = ccsecurity_ops.tgsigqueue_permission; | |
727 | + return func ? func(tgid, pid, sig) : 0; | |
728 | +} | |
729 | + | |
730 | +#else | |
731 | + | |
732 | +static inline int ccs_kill_permission(pid_t pid, int sig) | |
733 | +{ | |
734 | + return 0; | |
735 | +} | |
736 | + | |
737 | +static inline int ccs_tgkill_permission(pid_t tgid, pid_t pid, int sig) | |
738 | +{ | |
739 | + return 0; | |
740 | +} | |
741 | + | |
742 | +static inline int ccs_tkill_permission(pid_t pid, int sig) | |
743 | +{ | |
744 | + return 0; | |
745 | +} | |
746 | + | |
747 | +static inline int ccs_sigqueue_permission(pid_t pid, int sig) | |
748 | +{ | |
749 | + return 0; | |
750 | +} | |
751 | + | |
752 | +static inline int ccs_tgsigqueue_permission(pid_t tgid, pid_t pid, int sig) | |
753 | +{ | |
754 | + return 0; | |
755 | +} | |
756 | + | |
757 | +#endif | |
758 | + | |
759 | +/* Index numbers for Capability Controls. */ | |
760 | +enum ccs_capability_acl_index { | |
761 | + /* socket(PF_ROUTE, *, *) */ | |
762 | + CCS_USE_ROUTE_SOCKET, | |
763 | + /* socket(PF_PACKET, *, *) */ | |
764 | + CCS_USE_PACKET_SOCKET, | |
765 | + /* sys_reboot() */ | |
766 | + CCS_SYS_REBOOT, | |
767 | + /* sys_vhangup() */ | |
768 | + CCS_SYS_VHANGUP, | |
769 | + /* do_settimeofday(), sys_adjtimex() */ | |
770 | + CCS_SYS_SETTIME, | |
771 | + /* sys_nice(), sys_setpriority() */ | |
772 | + CCS_SYS_NICE, | |
773 | + /* sys_sethostname(), sys_setdomainname() */ | |
774 | + CCS_SYS_SETHOSTNAME, | |
775 | + /* sys_create_module(), sys_init_module(), sys_delete_module() */ | |
776 | + CCS_USE_KERNEL_MODULE, | |
777 | + /* sys_kexec_load() */ | |
778 | + CCS_SYS_KEXEC_LOAD, | |
779 | + CCS_MAX_CAPABILITY_INDEX | |
780 | +}; | |
781 | + | |
782 | +#endif |
@@ -0,0 +1,229 @@ | ||
1 | +/* | |
2 | + * security/caitsith/load_policy.c | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#include <linux/version.h> | |
10 | +#include <linux/module.h> | |
11 | +#include <linux/init.h> | |
12 | +#include <linux/binfmts.h> | |
13 | +#include <linux/sched.h> | |
14 | +#include <linux/fs.h> | |
15 | +#include <linux/namei.h> | |
16 | +#ifndef LOOKUP_POSITIVE | |
17 | +#define LOOKUP_POSITIVE 0 | |
18 | +#endif | |
19 | + | |
20 | +/* | |
21 | + * TOMOYO specific part start. | |
22 | + */ | |
23 | + | |
24 | +#include <linux/caitsith.h> | |
25 | + | |
26 | +/** | |
27 | + * ccs_setup - Set enable/disable upon boot. | |
28 | + * | |
29 | + * @str: "off" to disable, "on" to enable. | |
30 | + * | |
31 | + * Returns 0. | |
32 | + */ | |
33 | +static int __init ccs_setup(char *str) | |
34 | +{ | |
35 | + if (!strcmp(str, "off")) | |
36 | + ccsecurity_ops.disabled = 1; | |
37 | + else if (!strcmp(str, "on")) | |
38 | + ccsecurity_ops.disabled = 0; | |
39 | + return 0; | |
40 | +} | |
41 | + | |
42 | +__setup("caitsith=", ccs_setup); | |
43 | + | |
44 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
45 | + | |
46 | +/* Path to the policy loader. (default = CONFIG_CCSECURITY_POLICY_LOADER) */ | |
47 | +static const char *ccs_loader; | |
48 | + | |
49 | +/** | |
50 | + * ccs_loader_setup - Set policy loader. | |
51 | + * | |
52 | + * @str: Program to use as a policy loader (e.g. /sbin/caitsith-init ). | |
53 | + * | |
54 | + * Returns 0. | |
55 | + */ | |
56 | +static int __init ccs_loader_setup(char *str) | |
57 | +{ | |
58 | + ccs_loader = str; | |
59 | + return 0; | |
60 | +} | |
61 | + | |
62 | +__setup("CCS_loader=", ccs_loader_setup); | |
63 | + | |
64 | +/** | |
65 | + * ccs_policy_loader_exists - Check whether /sbin/caitsith-init exists. | |
66 | + * | |
67 | + * Returns true if /sbin/caitsith-init exists, false otherwise. | |
68 | + */ | |
69 | +static _Bool ccs_policy_loader_exists(void) | |
70 | +{ | |
71 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) | |
72 | + struct path path; | |
73 | + if (!ccs_loader) | |
74 | + ccs_loader = CONFIG_CCSECURITY_POLICY_LOADER; | |
75 | + if (kern_path(ccs_loader, LOOKUP_FOLLOW | LOOKUP_POSITIVE, | |
76 | + &path) == 0) { | |
77 | + path_put(&path); | |
78 | + return 1; | |
79 | + } | |
80 | +#else | |
81 | + struct nameidata nd; | |
82 | + if (!ccs_loader) | |
83 | + ccs_loader = CONFIG_CCSECURITY_POLICY_LOADER; | |
84 | + if (path_lookup(ccs_loader, LOOKUP_FOLLOW | LOOKUP_POSITIVE, | |
85 | + &nd) == 0) { | |
86 | + path_put(&nd.path); | |
87 | + return 1; | |
88 | + } | |
89 | +#endif | |
90 | + printk(KERN_INFO "Not activating Mandatory Access Control " | |
91 | + "as %s does not exist.\n", ccs_loader); | |
92 | + return 0; | |
93 | +} | |
94 | + | |
95 | +/* Path to the trigger. (default = CONFIG_CCSECURITY_ACTIVATION_TRIGGER) */ | |
96 | +static const char *ccs_trigger; | |
97 | + | |
98 | +/** | |
99 | + * ccs_trigger_setup - Set trigger for activation. | |
100 | + * | |
101 | + * @str: Program to use as an activation trigger (e.g. /sbin/init ). | |
102 | + * | |
103 | + * Returns 0. | |
104 | + */ | |
105 | +static int __init ccs_trigger_setup(char *str) | |
106 | +{ | |
107 | + ccs_trigger = str; | |
108 | + return 0; | |
109 | +} | |
110 | + | |
111 | +__setup("CCS_trigger=", ccs_trigger_setup); | |
112 | + | |
113 | +/** | |
114 | + * ccs_load_policy - Run external policy loader to load policy. | |
115 | + * | |
116 | + * @filename: The program about to start. | |
117 | + * | |
118 | + * Returns nothing. | |
119 | + * | |
120 | + * This function checks whether @filename is /sbin/init, and if so | |
121 | + * invoke /sbin/caitsith-init and wait for the termination of /sbin/caitsith-init | |
122 | + * and then continues invocation of /sbin/init. | |
123 | + * /sbin/caitsith-init reads policy files in /etc/ccs/ directory and | |
124 | + * writes to /proc/ccs/ interfaces. | |
125 | + */ | |
126 | +static void ccs_load_policy(const char *filename) | |
127 | +{ | |
128 | + static _Bool done; | |
129 | + if (ccsecurity_ops.disabled || done) | |
130 | + return; | |
131 | + if (!ccs_trigger) | |
132 | + ccs_trigger = CONFIG_CCSECURITY_ACTIVATION_TRIGGER; | |
133 | + if (strcmp(filename, ccs_trigger)) | |
134 | + return; | |
135 | + if (!ccs_policy_loader_exists()) | |
136 | + return; | |
137 | + done = 1; | |
138 | + { | |
139 | + char *argv[2]; | |
140 | + char *envp[3]; | |
141 | + printk(KERN_INFO "Calling %s to load policy. Please wait.\n", | |
142 | + ccs_loader); | |
143 | + argv[0] = (char *) ccs_loader; | |
144 | + argv[1] = NULL; | |
145 | + envp[0] = "HOME=/"; | |
146 | + envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | |
147 | + envp[2] = NULL; | |
148 | + call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); | |
149 | + } | |
150 | + if (ccsecurity_ops.check_profile) | |
151 | + ccsecurity_ops.check_profile(); | |
152 | + else | |
153 | + panic("Failed to load policy."); | |
154 | +} | |
155 | + | |
156 | +#endif | |
157 | + | |
158 | +/** | |
159 | + * __ccs_search_binary_handler - Load policy before calling search_binary_handler(). | |
160 | + * | |
161 | + * @bprm: Pointer to "struct linux_binprm". | |
162 | + * @regs: Pointer to "struct pt_regs". | |
163 | + * | |
164 | + * Returns 0 on success, negative value otherwise. | |
165 | + */ | |
166 | +static int __ccs_search_binary_handler(struct linux_binprm *bprm, | |
167 | + struct pt_regs *regs) | |
168 | +{ | |
169 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
170 | + ccs_load_policy(bprm->filename); | |
171 | +#endif | |
172 | + /* | |
173 | + * ccs_load_policy() executes /sbin/caitsith-init if bprm->filename is | |
174 | + * /sbin/init. /sbin/caitsith-init executes | |
175 | + * /etc/ccs/caitsith-load-module to load loadable kernel module. | |
176 | + * The loadable kernel module modifies "struct ccsecurity_ops". Thus, | |
177 | + * we need to transfer control to __ccs_search_binary_handler() in | |
178 | + * security/caitsith/permission.c if "struct ccsecurity_ops" was | |
179 | + * modified. | |
180 | + */ | |
181 | + if (ccsecurity_ops.search_binary_handler | |
182 | + != __ccs_search_binary_handler) | |
183 | + return ccsecurity_ops.search_binary_handler(bprm, regs); | |
184 | + return search_binary_handler(bprm, regs); | |
185 | +} | |
186 | + | |
187 | +/* | |
188 | + * Some exports for loadable kernel module part. | |
189 | + * | |
190 | + * Although scripts/checkpatch.pl complains about use of "extern" in C file, | |
191 | + * we don't put these into security/caitsith/internal.h because we want to | |
192 | + * split built-in part and loadable kernel module part. | |
193 | + */ | |
194 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) | |
195 | +extern spinlock_t vfsmount_lock; | |
196 | +#endif | |
197 | + | |
198 | +/* For exporting variables and functions. */ | |
199 | +const struct ccsecurity_exports ccsecurity_exports = { | |
200 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
201 | + .load_policy = ccs_load_policy, | |
202 | +#endif | |
203 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) | |
204 | + .d_absolute_path = d_absolute_path, | |
205 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
206 | + .__d_path = __d_path, | |
207 | +#else | |
208 | + .vfsmount_lock = &vfsmount_lock, | |
209 | +#endif | |
210 | + .find_task_by_vpid = find_task_by_vpid, | |
211 | + .find_task_by_pid_ns = find_task_by_pid_ns, | |
212 | +}; | |
213 | +#ifdef CONFIG_CCSECURITY_LKM | |
214 | +/* Only ccsecurity module need to access this struct. */ | |
215 | +EXPORT_SYMBOL_GPL(ccsecurity_exports); | |
216 | +#endif | |
217 | + | |
218 | +/* Members are updated by loadable kernel module. */ | |
219 | +struct ccsecurity_operations ccsecurity_ops = { | |
220 | + .search_binary_handler = __ccs_search_binary_handler, | |
221 | +#ifdef CONFIG_CCSECURITY_DISABLE_BY_DEFAULT | |
222 | + .disabled = 1, | |
223 | +#endif | |
224 | +}; | |
225 | +/* | |
226 | + * Non-GPL modules might need to access this struct via inlined functions | |
227 | + * embedded into include/linux/security.h and include/net/ip.h | |
228 | + */ | |
229 | +EXPORT_SYMBOL(ccsecurity_ops); |
@@ -0,0 +1,644 @@ | ||
1 | +/* | |
2 | + * security/caitsith/realpath.c | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#include "internal.h" | |
10 | + | |
11 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) | |
12 | +#include <linux/nsproxy.h> | |
13 | +#include <linux/mnt_namespace.h> | |
14 | +#endif | |
15 | + | |
16 | +/***** SECTION1: Constants definition *****/ | |
17 | + | |
18 | +#define SOCKFS_MAGIC 0x534F434B | |
19 | + | |
20 | +/***** SECTION2: Structure definition *****/ | |
21 | + | |
22 | +/***** SECTION3: Prototype definition section *****/ | |
23 | + | |
24 | +static char *ccs_get_absolute_path(struct path *path, char * const buffer, | |
25 | + const int buflen); | |
26 | +static char *ccs_get_dentry_path(struct dentry *dentry, char * const buffer, | |
27 | + const int buflen); | |
28 | +static char *ccs_get_local_path(struct dentry *dentry, char * const buffer, | |
29 | + const int buflen); | |
30 | +static char *ccs_get_socket_name(struct path *path, char * const buffer, | |
31 | + const int buflen); | |
32 | +static int ccs_const_part_length(const char *filename); | |
33 | + | |
34 | +/***** SECTION4: Standalone functions section *****/ | |
35 | + | |
36 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) | |
37 | + | |
38 | +/** | |
39 | + * ccs_realpath_lock - Take locks for __d_path(). | |
40 | + * | |
41 | + * Returns nothing. | |
42 | + */ | |
43 | +static inline void ccs_realpath_lock(void) | |
44 | +{ | |
45 | + /* dcache_lock is locked by __d_path(). */ | |
46 | + /* vfsmount_lock is locked by __d_path(). */ | |
47 | +} | |
48 | + | |
49 | +/** | |
50 | + * ccs_realpath_unlock - Release locks for __d_path(). | |
51 | + * | |
52 | + * Returns nothing. | |
53 | + */ | |
54 | +static inline void ccs_realpath_unlock(void) | |
55 | +{ | |
56 | + /* vfsmount_lock is unlocked by __d_path(). */ | |
57 | + /* dcache_lock is unlocked by __d_path(). */ | |
58 | +} | |
59 | + | |
60 | +#elif LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 36) | |
61 | + | |
62 | +/** | |
63 | + * ccs_realpath_lock - Take locks for __d_path(). | |
64 | + * | |
65 | + * Returns nothing. | |
66 | + */ | |
67 | +static inline void ccs_realpath_lock(void) | |
68 | +{ | |
69 | + spin_lock(&dcache_lock); | |
70 | + /* vfsmount_lock is locked by __d_path(). */ | |
71 | +} | |
72 | + | |
73 | +/** | |
74 | + * ccs_realpath_unlock - Release locks for __d_path(). | |
75 | + * | |
76 | + * Returns nothing. | |
77 | + */ | |
78 | +static inline void ccs_realpath_unlock(void) | |
79 | +{ | |
80 | + /* vfsmount_lock is unlocked by __d_path(). */ | |
81 | + spin_unlock(&dcache_lock); | |
82 | +} | |
83 | + | |
84 | +#elif defined(D_PATH_DISCONNECT) && !defined(CONFIG_SUSE_KERNEL) | |
85 | + | |
86 | +/** | |
87 | + * ccs_realpath_lock - Take locks for __d_path(). | |
88 | + * | |
89 | + * Returns nothing. | |
90 | + * | |
91 | + * Original unambiguous-__d_path.diff in patches.apparmor.tar.bz2 inversed the | |
92 | + * order of holding dcache_lock and vfsmount_lock. That patch was applied on | |
93 | + * (at least) SUSE 11.1 and Ubuntu 8.10 and Ubuntu 9.04 kernels. | |
94 | + * | |
95 | + * However, that patch was updated to use original order and the updated patch | |
96 | + * is applied to (as far as I know) only SUSE kernels. | |
97 | + * | |
98 | + * Therefore, I need to use original order for SUSE 11.1 kernels and inversed | |
99 | + * order for other kernels. I detect it by checking D_PATH_DISCONNECT and | |
100 | + * CONFIG_SUSE_KERNEL. I don't know whether other distributions are using the | |
101 | + * updated patch or not. If you got deadlock, check fs/dcache.c for locking | |
102 | + * order, and add " && 0" to this "#elif " block if fs/dcache.c uses original | |
103 | + * order. | |
104 | + */ | |
105 | +static inline void ccs_realpath_lock(void) | |
106 | +{ | |
107 | + spin_lock(ccsecurity_exports.vfsmount_lock); | |
108 | + spin_lock(&dcache_lock); | |
109 | +} | |
110 | + | |
111 | +/** | |
112 | + * ccs_realpath_unlock - Release locks for __d_path(). | |
113 | + * | |
114 | + * Returns nothing. | |
115 | + */ | |
116 | +static inline void ccs_realpath_unlock(void) | |
117 | +{ | |
118 | + spin_unlock(&dcache_lock); | |
119 | + spin_unlock(ccsecurity_exports.vfsmount_lock); | |
120 | +} | |
121 | + | |
122 | +#else | |
123 | + | |
124 | +/** | |
125 | + * ccs_realpath_lock - Take locks for __d_path(). | |
126 | + * | |
127 | + * Returns nothing. | |
128 | + */ | |
129 | +static inline void ccs_realpath_lock(void) | |
130 | +{ | |
131 | + spin_lock(&dcache_lock); | |
132 | + spin_lock(ccsecurity_exports.vfsmount_lock); | |
133 | +} | |
134 | + | |
135 | +/** | |
136 | + * ccs_realpath_unlock - Release locks for __d_path(). | |
137 | + * | |
138 | + * Returns nothing. | |
139 | + */ | |
140 | +static inline void ccs_realpath_unlock(void) | |
141 | +{ | |
142 | + spin_unlock(ccsecurity_exports.vfsmount_lock); | |
143 | + spin_unlock(&dcache_lock); | |
144 | +} | |
145 | + | |
146 | +#endif | |
147 | + | |
148 | +/***** SECTION5: Variables definition section *****/ | |
149 | + | |
150 | +/***** SECTION6: Dependent functions section *****/ | |
151 | + | |
152 | +/** | |
153 | + * ccs_get_absolute_path - Get the path of a dentry but ignores chroot'ed root. | |
154 | + * | |
155 | + * @path: Pointer to "struct path". | |
156 | + * @buffer: Pointer to buffer to return value in. | |
157 | + * @buflen: Sizeof @buffer. | |
158 | + * | |
159 | + * Returns the buffer on success, an error code otherwise. | |
160 | + * | |
161 | + * Caller holds the dcache_lock and vfsmount_lock. | |
162 | + * Based on __d_path() in fs/dcache.c | |
163 | + */ | |
164 | +static char *ccs_get_absolute_path(struct path *path, char * const buffer, | |
165 | + const int buflen) | |
166 | +{ | |
167 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) | |
168 | + if (buflen < 256) | |
169 | + return ERR_PTR(-ENOMEM); | |
170 | + return ccsecurity_exports.d_absolute_path(path, buffer, buflen - 1); | |
171 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
172 | + /* | |
173 | + * __d_path() will start returning NULL by backporting commit 02125a82 | |
174 | + * "fix apparmor dereferencing potentially freed dentry, sanitize | |
175 | + * __d_path() API". | |
176 | + * | |
177 | + * Unfortunately, __d_path() after applying that commit always returns | |
178 | + * NULL when root is empty. d_absolute_path() is provided for TOMOYO | |
179 | + * 2.x and AppArmor but TOMOYO 1.x does not use it, for TOMOYO 1.x | |
180 | + * might be built as a loadable kernel module and there is no warrantee | |
181 | + * that TOMOYO 1.x is recompiled after applying that commit. Also, | |
182 | + * I don't want to search /proc/kallsyms for d_absolute_path() because | |
183 | + * I want to keep TOMOYO 1.x architecture independent. Thus, supply | |
184 | + * non empty root like AppArmor's d_namespace_path() did. | |
185 | + */ | |
186 | + static bool ccs_no_empty; | |
187 | + char *pos; | |
188 | + if (buflen < 256) | |
189 | + return ERR_PTR(-ENOMEM); | |
190 | + if (!ccs_no_empty) { | |
191 | + struct path root = { }; | |
192 | + pos = ccsecurity_exports.__d_path(path, &root, buffer, | |
193 | + buflen - 1); | |
194 | + } else { | |
195 | + pos = NULL; | |
196 | + } | |
197 | + if (!pos) { | |
198 | + struct task_struct *task = current; | |
199 | + struct path root; | |
200 | + struct path tmp; | |
201 | + spin_lock(&task->fs->lock); | |
202 | + root.mnt = task->nsproxy->mnt_ns->root; | |
203 | + root.dentry = root.mnt->mnt_root; | |
204 | + path_get(&root); | |
205 | + spin_unlock(&task->fs->lock); | |
206 | + tmp = root; | |
207 | + pos = ccsecurity_exports.__d_path(path, &tmp, buffer, | |
208 | + buflen - 1); | |
209 | + path_put(&root); | |
210 | + if (pos) | |
211 | + return pos; | |
212 | + /* Remember if __d_path() needs non empty root. */ | |
213 | + ccs_no_empty = true; | |
214 | + pos = ERR_PTR(-EINVAL); | |
215 | + } | |
216 | + return pos; | |
217 | +#else | |
218 | + char *pos = buffer + buflen - 1; | |
219 | + struct dentry *dentry = path->dentry; | |
220 | + struct vfsmount *vfsmnt = path->mnt; | |
221 | + const char *name; | |
222 | + int len; | |
223 | + | |
224 | + if (buflen < 256) | |
225 | + goto out; | |
226 | + | |
227 | + *pos = '\0'; | |
228 | + for (;;) { | |
229 | + struct dentry *parent; | |
230 | + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { | |
231 | + if (vfsmnt->mnt_parent == vfsmnt) | |
232 | + break; | |
233 | + dentry = vfsmnt->mnt_mountpoint; | |
234 | + vfsmnt = vfsmnt->mnt_parent; | |
235 | + continue; | |
236 | + } | |
237 | + parent = dentry->d_parent; | |
238 | + name = dentry->d_name.name; | |
239 | + len = dentry->d_name.len; | |
240 | + pos -= len; | |
241 | + if (pos <= buffer) | |
242 | + goto out; | |
243 | + memmove(pos, name, len); | |
244 | + *--pos = '/'; | |
245 | + dentry = parent; | |
246 | + } | |
247 | + if (*pos == '/') | |
248 | + pos++; | |
249 | + len = dentry->d_name.len; | |
250 | + pos -= len; | |
251 | + if (pos < buffer) | |
252 | + goto out; | |
253 | + memmove(pos, dentry->d_name.name, len); | |
254 | + return pos; | |
255 | +out: | |
256 | + return ERR_PTR(-ENOMEM); | |
257 | +#endif | |
258 | +} | |
259 | + | |
260 | +/** | |
261 | + * ccs_get_dentry_path - Get the path of a dentry. | |
262 | + * | |
263 | + * @dentry: Pointer to "struct dentry". | |
264 | + * @buffer: Pointer to buffer to return value in. | |
265 | + * @buflen: Sizeof @buffer. | |
266 | + * | |
267 | + * Returns the buffer on success, an error code otherwise. | |
268 | + * | |
269 | + * Based on dentry_path() in fs/dcache.c | |
270 | + */ | |
271 | +static char *ccs_get_dentry_path(struct dentry *dentry, char * const buffer, | |
272 | + const int buflen) | |
273 | +{ | |
274 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) | |
275 | + if (buflen < 256) | |
276 | + return ERR_PTR(-ENOMEM); | |
277 | + /* rename_lock is locked/unlocked by dentry_path_raw(). */ | |
278 | + return dentry_path_raw(dentry, buffer, buflen - 1); | |
279 | +#else | |
280 | + char *pos = buffer + buflen - 1; | |
281 | + if (buflen < 256) | |
282 | + return ERR_PTR(-ENOMEM); | |
283 | + *pos = '\0'; | |
284 | + spin_lock(&dcache_lock); | |
285 | + while (!IS_ROOT(dentry)) { | |
286 | + struct dentry *parent = dentry->d_parent; | |
287 | + const char *name = dentry->d_name.name; | |
288 | + const int len = dentry->d_name.len; | |
289 | + pos -= len; | |
290 | + if (pos <= buffer) { | |
291 | + pos = ERR_PTR(-ENOMEM); | |
292 | + break; | |
293 | + } | |
294 | + memmove(pos, name, len); | |
295 | + *--pos = '/'; | |
296 | + dentry = parent; | |
297 | + } | |
298 | + spin_unlock(&dcache_lock); | |
299 | + if (pos == buffer + buflen - 1) | |
300 | + *--pos = '/'; | |
301 | + return pos; | |
302 | +#endif | |
303 | +} | |
304 | + | |
305 | +/** | |
306 | + * ccs_get_local_path - Get the path of a dentry. | |
307 | + * | |
308 | + * @dentry: Pointer to "struct dentry". | |
309 | + * @buffer: Pointer to buffer to return value in. | |
310 | + * @buflen: Sizeof @buffer. | |
311 | + * | |
312 | + * Returns the buffer on success, an error code otherwise. | |
313 | + */ | |
314 | +static char *ccs_get_local_path(struct dentry *dentry, char * const buffer, | |
315 | + const int buflen) | |
316 | +{ | |
317 | + struct super_block *sb = dentry->d_sb; | |
318 | + char *pos = ccs_get_dentry_path(dentry, buffer, buflen); | |
319 | + if (IS_ERR(pos)) | |
320 | + return pos; | |
321 | + /* Convert from $PID to self if $PID is current thread. */ | |
322 | + if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') { | |
323 | + char *ep; | |
324 | + const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10); | |
325 | + if (*ep == '/' && pid && pid == | |
326 | + task_tgid_nr_ns(current, sb->s_fs_info)) { | |
327 | + pos = ep - 5; | |
328 | + if (pos < buffer) | |
329 | + goto out; | |
330 | + memmove(pos, "/self", 5); | |
331 | + } | |
332 | + goto prepend_filesystem_name; | |
333 | + } | |
334 | + /* Use filesystem name for unnamed devices. */ | |
335 | + if (!MAJOR(sb->s_dev)) | |
336 | + goto prepend_filesystem_name; | |
337 | + { | |
338 | + struct inode *inode = sb->s_root->d_inode; | |
339 | + /* | |
340 | + * Use filesystem name if filesystems does not support rename() | |
341 | + * operation. | |
342 | + */ | |
343 | + if (inode->i_op && !inode->i_op->rename) | |
344 | + goto prepend_filesystem_name; | |
345 | + } | |
346 | + /* Prepend device name. */ | |
347 | + { | |
348 | + char name[64]; | |
349 | + int name_len; | |
350 | + const dev_t dev = sb->s_dev; | |
351 | + name[sizeof(name) - 1] = '\0'; | |
352 | + snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev), | |
353 | + MINOR(dev)); | |
354 | + name_len = strlen(name); | |
355 | + pos -= name_len; | |
356 | + if (pos < buffer) | |
357 | + goto out; | |
358 | + memmove(pos, name, name_len); | |
359 | + return pos; | |
360 | + } | |
361 | + /* Prepend filesystem name. */ | |
362 | +prepend_filesystem_name: | |
363 | + { | |
364 | + const char *name = sb->s_type->name; | |
365 | + const int name_len = strlen(name); | |
366 | + pos -= name_len + 1; | |
367 | + if (pos < buffer) | |
368 | + goto out; | |
369 | + memmove(pos, name, name_len); | |
370 | + pos[name_len] = ':'; | |
371 | + } | |
372 | + return pos; | |
373 | +out: | |
374 | + return ERR_PTR(-ENOMEM); | |
375 | +} | |
376 | + | |
377 | +/** | |
378 | + * ccs_get_socket_name - Get the name of a socket. | |
379 | + * | |
380 | + * @path: Pointer to "struct path". | |
381 | + * @buffer: Pointer to buffer to return value in. | |
382 | + * @buflen: Sizeof @buffer. | |
383 | + * | |
384 | + * Returns the buffer. | |
385 | + */ | |
386 | +static char *ccs_get_socket_name(struct path *path, char * const buffer, | |
387 | + const int buflen) | |
388 | +{ | |
389 | + struct inode *inode = path->dentry->d_inode; | |
390 | + struct socket *sock = inode ? SOCKET_I(inode) : NULL; | |
391 | + struct sock *sk = sock ? sock->sk : NULL; | |
392 | + if (sk) { | |
393 | + snprintf(buffer, buflen, "socket:[family=%u:type=%u:" | |
394 | + "protocol=%u]", sk->sk_family, sk->sk_type, | |
395 | + sk->sk_protocol); | |
396 | + } else { | |
397 | + snprintf(buffer, buflen, "socket:[unknown]"); | |
398 | + } | |
399 | + return buffer; | |
400 | +} | |
401 | + | |
402 | +/** | |
403 | + * ccs_realpath - Returns realpath(3) of the given pathname but ignores chroot'ed root. | |
404 | + * | |
405 | + * @path: Pointer to "struct path". | |
406 | + * | |
407 | + * Returns the realpath of the given @path on success, NULL otherwise. | |
408 | + * | |
409 | + * This function uses kzalloc(), so caller must kfree() if this function | |
410 | + * didn't return NULL. | |
411 | + */ | |
412 | +char *ccs_realpath(struct path *path) | |
413 | +{ | |
414 | + char *buf = NULL; | |
415 | + char *name = NULL; | |
416 | + unsigned int buf_len = PAGE_SIZE / 2; | |
417 | + struct dentry *dentry = path->dentry; | |
418 | + struct super_block *sb; | |
419 | + if (!dentry) | |
420 | + return NULL; | |
421 | + sb = dentry->d_sb; | |
422 | + while (1) { | |
423 | + char *pos; | |
424 | + struct inode *inode; | |
425 | + buf_len <<= 1; | |
426 | + kfree(buf); | |
427 | + buf = kmalloc(buf_len, GFP_NOFS); | |
428 | + if (!buf) | |
429 | + break; | |
430 | + /* To make sure that pos is '\0' terminated. */ | |
431 | + buf[buf_len - 1] = '\0'; | |
432 | + /* Get better name for socket. */ | |
433 | + if (sb->s_magic == SOCKFS_MAGIC) { | |
434 | + pos = ccs_get_socket_name(path, buf, buf_len - 1); | |
435 | + goto encode; | |
436 | + } | |
437 | + /* For "pipe:[\$]". */ | |
438 | + if (dentry->d_op && dentry->d_op->d_dname) { | |
439 | + pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); | |
440 | + goto encode; | |
441 | + } | |
442 | + inode = sb->s_root->d_inode; | |
443 | + /* | |
444 | + * Use local name for "filesystems without rename() operation" | |
445 | + * or "path without vfsmount" or "absolute name is unavailable" | |
446 | + * cases. | |
447 | + */ | |
448 | + if (!path->mnt || (inode->i_op && !inode->i_op->rename)) | |
449 | + pos = ERR_PTR(-EINVAL); | |
450 | + else { | |
451 | + /* Get absolute name for the rest. */ | |
452 | + ccs_realpath_lock(); | |
453 | + pos = ccs_get_absolute_path(path, buf, buf_len - 1); | |
454 | + ccs_realpath_unlock(); | |
455 | + } | |
456 | + if (pos == ERR_PTR(-EINVAL)) | |
457 | + pos = ccs_get_local_path(path->dentry, buf, | |
458 | + buf_len - 1); | |
459 | +encode: | |
460 | + if (IS_ERR(pos)) | |
461 | + continue; | |
462 | + name = ccs_encode(pos); | |
463 | + break; | |
464 | + } | |
465 | + kfree(buf); | |
466 | + if (!name) | |
467 | + ccs_warn_oom(__func__); | |
468 | + return name; | |
469 | +} | |
470 | + | |
471 | +/** | |
472 | + * ccs_encode2 - Encode binary string to ascii string. | |
473 | + * | |
474 | + * @str: String in binary format. Maybe NULL. | |
475 | + * @str_len: Size of @str in byte. | |
476 | + * | |
477 | + * Returns pointer to @str in ascii format on success, NULL otherwise. | |
478 | + * | |
479 | + * This function uses kzalloc(), so caller must kfree() if this function | |
480 | + * didn't return NULL. | |
481 | + */ | |
482 | +char *ccs_encode2(const char *str, int str_len) | |
483 | +{ | |
484 | + int i; | |
485 | + int len; | |
486 | + const char *p = str; | |
487 | + char *cp; | |
488 | + char *cp0; | |
489 | + if (!p) | |
490 | + return NULL; | |
491 | + len = str_len; | |
492 | + for (i = 0; i < str_len; i++) { | |
493 | + const unsigned char c = p[i]; | |
494 | + if (!(c > ' ' && c < 127 && c != '\\')) | |
495 | + len += 3; | |
496 | + } | |
497 | + len++; | |
498 | + cp = kzalloc(len, GFP_NOFS); | |
499 | + if (!cp) | |
500 | + return NULL; | |
501 | + cp0 = cp; | |
502 | + p = str; | |
503 | + for (i = 0; i < str_len; i++) { | |
504 | + const unsigned char c = p[i]; | |
505 | + if (c > ' ' && c < 127 && c != '\\') { | |
506 | + *cp++ = c; | |
507 | + } else { | |
508 | + *cp++ = '\\'; | |
509 | + *cp++ = (c >> 6) + '0'; | |
510 | + *cp++ = ((c >> 3) & 7) + '0'; | |
511 | + *cp++ = (c & 7) + '0'; | |
512 | + } | |
513 | + } | |
514 | + return cp0; | |
515 | +} | |
516 | + | |
517 | +/** | |
518 | + * ccs_encode - Encode binary string to ascii string. | |
519 | + * | |
520 | + * @str: String in binary format. Maybe NULL. | |
521 | + * | |
522 | + * Returns pointer to @str in ascii format on success, NULL otherwise. | |
523 | + * | |
524 | + * This function uses kzalloc(), so caller must kfree() if this function | |
525 | + * didn't return NULL. | |
526 | + */ | |
527 | +char *ccs_encode(const char *str) | |
528 | +{ | |
529 | + return str ? ccs_encode2(str, strlen(str)) : NULL; | |
530 | +} | |
531 | + | |
532 | +/** | |
533 | + * ccs_const_part_length - Evaluate the initial length without a pattern in a token. | |
534 | + * | |
535 | + * @filename: The string to evaluate. Maybe NULL. | |
536 | + * | |
537 | + * Returns the initial length without a pattern in @filename. | |
538 | + */ | |
539 | +static int ccs_const_part_length(const char *filename) | |
540 | +{ | |
541 | + char c; | |
542 | + int len = 0; | |
543 | + if (!filename) | |
544 | + return 0; | |
545 | + while (1) { | |
546 | + c = *filename++; | |
547 | + if (!c) | |
548 | + break; | |
549 | + if (c != '\\') { | |
550 | + len++; | |
551 | + continue; | |
552 | + } | |
553 | + c = *filename++; | |
554 | + switch (c) { | |
555 | + case '0': /* "\ooo" */ | |
556 | + case '1': | |
557 | + case '2': | |
558 | + case '3': | |
559 | + c = *filename++; | |
560 | + if (c < '0' || c > '7') | |
561 | + break; | |
562 | + c = *filename++; | |
563 | + if (c < '0' || c > '7') | |
564 | + break; | |
565 | + len += 4; | |
566 | + continue; | |
567 | + } | |
568 | + break; | |
569 | + } | |
570 | + return len; | |
571 | +} | |
572 | + | |
573 | +/** | |
574 | + * ccs_fill_path_info - Fill in "struct ccs_path_info" members. | |
575 | + * | |
576 | + * @ptr: Pointer to "struct ccs_path_info" to fill in. | |
577 | + * | |
578 | + * Returns nothing. | |
579 | + * | |
580 | + * The caller sets "struct ccs_path_info"->name. | |
581 | + */ | |
582 | +void ccs_fill_path_info(struct ccs_path_info *ptr) | |
583 | +{ | |
584 | + const char *name = ptr->name; | |
585 | + const int len = strlen(name); | |
586 | + ptr->total_len = len; | |
587 | + ptr->const_len = ccs_const_part_length(name); | |
588 | + ptr->hash = full_name_hash(name, len); | |
589 | +} | |
590 | + | |
591 | +/** | |
592 | + * ccs_get_exe - Get ccs_realpath() of current process. | |
593 | + * | |
594 | + * Returns the ccs_realpath() of current process on success, NULL otherwise. | |
595 | + * | |
596 | + * This function uses kzalloc(), so the caller must kfree() | |
597 | + * if this function didn't return NULL. | |
598 | + */ | |
599 | +char *ccs_get_exe(void) | |
600 | +{ | |
601 | + struct mm_struct *mm; | |
602 | + struct vm_area_struct *vma; | |
603 | + bool done = false; | |
604 | + char *cp = NULL; | |
605 | + if (current->flags & PF_KTHREAD) | |
606 | + return kstrdup("<kernel>", GFP_NOFS); | |
607 | + mm = current->mm; | |
608 | + if (!mm) | |
609 | + goto task_has_no_mm; | |
610 | + down_read(&mm->mmap_sem); | |
611 | + for (vma = mm->mmap; vma; vma = vma->vm_next) { | |
612 | + if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { | |
613 | + cp = ccs_realpath(&vma->vm_file->f_path); | |
614 | + done = true; | |
615 | + break; | |
616 | + } | |
617 | + } | |
618 | + up_read(&mm->mmap_sem); | |
619 | + if (done) | |
620 | + return cp; | |
621 | +task_has_no_mm: | |
622 | + /* I'don't know. */ | |
623 | + return kstrdup("<unknown>", GFP_NOFS); | |
624 | +} | |
625 | + | |
626 | +/** | |
627 | + * ccs_get_exename - Get ccs_realpath() of current process. | |
628 | + * | |
629 | + * @buf: Pointer to "struct ccs_path_info". | |
630 | + * | |
631 | + * Returns true on success, false otherwise. | |
632 | + * | |
633 | + * This function uses kzalloc(), so the caller must kfree() | |
634 | + * if this function returned true. | |
635 | + */ | |
636 | +bool ccs_get_exename(struct ccs_path_info *buf) | |
637 | +{ | |
638 | + buf->name = ccs_get_exe(); | |
639 | + if (buf->name) { | |
640 | + ccs_fill_path_info(buf); | |
641 | + return true; | |
642 | + } | |
643 | + return false; | |
644 | +} |
@@ -0,0 +1,1162 @@ | ||
1 | +/* | |
2 | + * security/caitsith/internal.h | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#ifndef _SECURITY_CCSECURITY_INTERNAL_H | |
10 | +#define _SECURITY_CCSECURITY_INTERNAL_H | |
11 | + | |
12 | +#include <linux/version.h> | |
13 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) | |
14 | +#error This module supports only 2.6.27 and later kernels. | |
15 | +#endif | |
16 | +#include <linux/types.h> | |
17 | +#include <linux/kernel.h> | |
18 | +#include <linux/string.h> | |
19 | +#include <linux/mm.h> | |
20 | +#include <linux/utime.h> | |
21 | +#include <linux/file.h> | |
22 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 38) | |
23 | +#include <linux/smp_lock.h> | |
24 | +#endif | |
25 | +#include <linux/module.h> | |
26 | +#include <linux/init.h> | |
27 | +#include <linux/slab.h> | |
28 | +#include <linux/highmem.h> | |
29 | +#include <linux/poll.h> | |
30 | +#include <linux/binfmts.h> | |
31 | +#include <linux/delay.h> | |
32 | +#include <linux/sched.h> | |
33 | +#include <linux/dcache.h> | |
34 | +#include <linux/mount.h> | |
35 | +#include <linux/net.h> | |
36 | +#include <linux/inet.h> | |
37 | +#include <linux/in.h> | |
38 | +#include <linux/in6.h> | |
39 | +#include <linux/un.h> | |
40 | +#include <linux/ptrace.h> | |
41 | +#include <linux/namei.h> | |
42 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) | |
43 | +#include <linux/fs_struct.h> | |
44 | +#endif | |
45 | +#include <linux/proc_fs.h> | |
46 | +#include <linux/hash.h> | |
47 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL) | |
48 | +#include <linux/sysctl.h> | |
49 | +#endif | |
50 | +#include <linux/kthread.h> | |
51 | +#include <stdarg.h> | |
52 | +#include <asm/uaccess.h> | |
53 | +#include <net/sock.h> | |
54 | +#include <net/af_unix.h> | |
55 | +#include <net/ip.h> | |
56 | +#include <net/ipv6.h> | |
57 | +#include <net/udp.h> | |
58 | + | |
59 | +#ifndef __printf | |
60 | +#define __printf(a,b) __attribute__((format(printf,a,b))) | |
61 | +#endif | |
62 | +#ifndef __packed | |
63 | +#define __packed __attribute__((__packed__)) | |
64 | +#endif | |
65 | +#ifndef bool | |
66 | +#define bool _Bool | |
67 | +#endif | |
68 | +#ifndef false | |
69 | +#define false 0 | |
70 | +#endif | |
71 | +#ifndef true | |
72 | +#define true 1 | |
73 | +#endif | |
74 | + | |
75 | +#ifndef __user | |
76 | +#define __user | |
77 | +#endif | |
78 | + | |
79 | +#ifndef current_uid | |
80 | +#define current_uid() (current->uid) | |
81 | +#endif | |
82 | +#ifndef current_gid | |
83 | +#define current_gid() (current->gid) | |
84 | +#endif | |
85 | +#ifndef current_euid | |
86 | +#define current_euid() (current->euid) | |
87 | +#endif | |
88 | +#ifndef current_egid | |
89 | +#define current_egid() (current->egid) | |
90 | +#endif | |
91 | +#ifndef current_suid | |
92 | +#define current_suid() (current->suid) | |
93 | +#endif | |
94 | +#ifndef current_sgid | |
95 | +#define current_sgid() (current->sgid) | |
96 | +#endif | |
97 | +#ifndef current_fsuid | |
98 | +#define current_fsuid() (current->fsuid) | |
99 | +#endif | |
100 | +#ifndef current_fsgid | |
101 | +#define current_fsgid() (current->fsgid) | |
102 | +#endif | |
103 | + | |
104 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) | |
105 | + | |
106 | +/** | |
107 | + * __list_del_entry - Deletes entry from list without re-initialization. | |
108 | + * | |
109 | + * @entry: Pointer to "struct list_head". | |
110 | + * | |
111 | + * Returns nothing. | |
112 | + * | |
113 | + * This is for compatibility with older kernels. | |
114 | + */ | |
115 | +static inline void __list_del_entry(struct list_head *entry) | |
116 | +{ | |
117 | + __list_del(entry->prev, entry->next); | |
118 | +} | |
119 | + | |
120 | +#endif | |
121 | + | |
122 | +#ifndef list_for_each_entry_safe | |
123 | + | |
124 | +/** | |
125 | + * list_for_each_entry_safe - Iterate over list of given type safe against removal of list entry. | |
126 | + * | |
127 | + * @pos: The "type *" to use as a loop cursor. | |
128 | + * @n: Another "type *" to use as temporary storage. | |
129 | + * @head: Pointer to "struct list_head". | |
130 | + * @member: The name of the list_struct within the struct. | |
131 | + * | |
132 | + * This is for compatibility with older kernels. | |
133 | + */ | |
134 | +#define list_for_each_entry_safe(pos, n, head, member) \ | |
135 | + for (pos = list_entry((head)->next, typeof(*pos), member), \ | |
136 | + n = list_entry(pos->member.next, typeof(*pos), member); \ | |
137 | + &pos->member != (head); \ | |
138 | + pos = n, n = list_entry(n->member.next, typeof(*n), member)) | |
139 | + | |
140 | +#endif | |
141 | + | |
142 | +#ifndef srcu_dereference | |
143 | + | |
144 | +/** | |
145 | + * srcu_dereference - Fetch SRCU-protected pointer with checking. | |
146 | + * | |
147 | + * @p: The pointer to read, prior to dereferencing. | |
148 | + * @ss: Pointer to "struct srcu_struct". | |
149 | + * | |
150 | + * Returns @p. | |
151 | + * | |
152 | + * This is for compatibility with older kernels. | |
153 | + */ | |
154 | +#define srcu_dereference(p, ss) rcu_dereference(p) | |
155 | + | |
156 | +#endif | |
157 | + | |
158 | +#ifndef list_for_each_entry_srcu | |
159 | + | |
160 | +/** | |
161 | + * list_for_each_entry_srcu - Iterate over rcu list of given type. | |
162 | + * | |
163 | + * @pos: The type * to use as a loop cursor. | |
164 | + * @head: The head for your list. | |
165 | + * @member: The name of the list_struct within the struct. | |
166 | + * @ss: Pointer to "struct srcu_struct". | |
167 | + * | |
168 | + * As of 2.6.36, this macro is not provided because only TOMOYO wants it. | |
169 | + */ | |
170 | +#define list_for_each_entry_srcu(pos, head, member, ss) \ | |
171 | + for (pos = list_entry(srcu_dereference((head)->next, ss), \ | |
172 | + typeof(*pos), member); \ | |
173 | + prefetch(pos->member.next), &pos->member != (head); \ | |
174 | + pos = list_entry(srcu_dereference(pos->member.next, ss), \ | |
175 | + typeof(*pos), member)) | |
176 | + | |
177 | +#endif | |
178 | + | |
179 | +/* | |
180 | + * TOMOYO specific part start. | |
181 | + */ | |
182 | + | |
183 | +#include <linux/ccsecurity.h> | |
184 | + | |
185 | +/* Enumeration definition for internal use. */ | |
186 | + | |
187 | +/* Index numbers for "struct ccs_condition". */ | |
188 | +enum ccs_conditions_index { | |
189 | + /* 0 */ | |
190 | + CCS_SELF_UID, /* current_uid() */ | |
191 | + CCS_SELF_EUID, /* current_euid() */ | |
192 | + CCS_SELF_SUID, /* current_suid() */ | |
193 | + CCS_SELF_FSUID, /* current_fsuid() */ | |
194 | + CCS_SELF_GID, /* current_gid() */ | |
195 | + CCS_SELF_EGID, /* current_egid() */ | |
196 | + CCS_SELF_SGID, /* current_sgid() */ | |
197 | + CCS_SELF_FSGID, /* current_fsgid() */ | |
198 | + CCS_SELF_PID, /* sys_getpid() */ | |
199 | + CCS_SELF_PPID, /* sys_getppid() */ | |
200 | + /* 10 */ | |
201 | + CCS_TASK_TYPE, /* ((u8) task->ccs_flags) & | |
202 | + CCS_TASK_IS_EXECUTE_HANDLER */ | |
203 | + CCS_SELF_DOMAIN, | |
204 | + CCS_SELF_EXE, | |
205 | + CCS_EXEC_ARGC, /* "struct linux_binprm *"->argc */ | |
206 | + CCS_EXEC_ENVC, /* "struct linux_binprm *"->envc */ | |
207 | + CCS_OBJ_IS_SOCKET, /* S_IFSOCK */ | |
208 | + CCS_OBJ_IS_SYMLINK, /* S_IFLNK */ | |
209 | + CCS_OBJ_IS_FILE, /* S_IFREG */ | |
210 | + CCS_OBJ_IS_BLOCK_DEV, /* S_IFBLK */ | |
211 | + CCS_OBJ_IS_DIRECTORY, /* S_IFDIR */ | |
212 | + /* 20 */ | |
213 | + CCS_OBJ_IS_CHAR_DEV, /* S_IFCHR */ | |
214 | + CCS_OBJ_IS_FIFO, /* S_IFIFO */ | |
215 | + CCS_MODE_SETUID, /* S_ISUID */ | |
216 | + CCS_MODE_SETGID, /* S_ISGID */ | |
217 | + CCS_MODE_STICKY, /* S_ISVTX */ | |
218 | + CCS_MODE_OWNER_READ, /* S_IRUSR */ | |
219 | + CCS_MODE_OWNER_WRITE, /* S_IWUSR */ | |
220 | + CCS_MODE_OWNER_EXECUTE, /* S_IXUSR */ | |
221 | + CCS_MODE_GROUP_READ, /* S_IRGRP */ | |
222 | + CCS_MODE_GROUP_WRITE, /* S_IWGRP */ | |
223 | + /* 30 */ | |
224 | + CCS_MODE_GROUP_EXECUTE, /* S_IXGRP */ | |
225 | + CCS_MODE_OTHERS_READ, /* S_IROTH */ | |
226 | + CCS_MODE_OTHERS_WRITE, /* S_IWOTH */ | |
227 | + CCS_MODE_OTHERS_EXECUTE, /* S_IXOTH */ | |
228 | + CCS_TASK_EXECUTE_HANDLER, /* CCS_TASK_IS_EXECUTE_HANDLER */ | |
229 | + CCS_HANDLER_PATH, | |
230 | + CCS_TRANSIT_DOMAIN, | |
231 | + CCS_MAX_CONDITION_KEYWORD, | |
232 | + CCS_COND_SARG0, | |
233 | + CCS_COND_SARG1, | |
234 | + /* 40 */ | |
235 | + CCS_COND_SARG2, | |
236 | + CCS_COND_SARG3, | |
237 | + CCS_COND_NARG0, | |
238 | + CCS_COND_NARG1, | |
239 | + CCS_COND_NARG2, | |
240 | + CCS_COND_IPARG, | |
241 | + CCS_COND_DOMAIN, | |
242 | + CCS_IMM_GROUP, | |
243 | + CCS_IMM_NAME_ENTRY, | |
244 | + CCS_IMM_NUMBER_ENTRY1, | |
245 | + /* 50 */ | |
246 | + CCS_IMM_NUMBER_ENTRY2, | |
247 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
248 | + CCS_IMM_IPV4ADDR_ENTRY1, | |
249 | + CCS_IMM_IPV4ADDR_ENTRY2, | |
250 | + CCS_IMM_IPV6ADDR_ENTRY1, | |
251 | + CCS_IMM_IPV6ADDR_ENTRY2, | |
252 | +#endif | |
253 | + CCS_ARGV_ENTRY, | |
254 | + CCS_ENVP_ENTRY, | |
255 | + CCS_PATH_ATTRIBUTE_START = 192, | |
256 | + CCS_PATH_ATTRIBUTE_END = 255 | |
257 | +} __packed; | |
258 | + | |
259 | +enum ccs_path_attribute_index { | |
260 | + CCS_PATH_ATTRIBUTE_UID, | |
261 | + CCS_PATH_ATTRIBUTE_GID, | |
262 | + CCS_PATH_ATTRIBUTE_INO, | |
263 | + CCS_PATH_ATTRIBUTE_TYPE, | |
264 | + CCS_PATH_ATTRIBUTE_MAJOR, | |
265 | + CCS_PATH_ATTRIBUTE_MINOR, | |
266 | + CCS_PATH_ATTRIBUTE_PERM, | |
267 | + CCS_PATH_ATTRIBUTE_DEV_MAJOR, | |
268 | + CCS_PATH_ATTRIBUTE_DEV_MINOR, | |
269 | + CCS_PATH_ATTRIBUTE_FSMAGIC, | |
270 | + CCS_MAX_PATH_ATTRIBUTE | |
271 | +} __packed; | |
272 | + | |
273 | +/* Index numbers for group entries. */ | |
274 | +enum ccs_group_id { | |
275 | + CCS_STRING_GROUP, | |
276 | + CCS_NUMBER_GROUP, | |
277 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
278 | + CCS_IP_GROUP, | |
279 | +#endif | |
280 | + CCS_MAX_GROUP | |
281 | +} __packed; | |
282 | + | |
283 | +/* Index numbers for functionality. */ | |
284 | +enum ccs_mac_index { | |
285 | + CCS_MAC_EXECUTE, | |
286 | + CCS_MAC_READ, | |
287 | + CCS_MAC_WRITE, | |
288 | + CCS_MAC_APPEND, | |
289 | + CCS_MAC_CREATE, | |
290 | + CCS_MAC_UNLINK, | |
291 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
292 | + CCS_MAC_GETATTR, | |
293 | +#endif | |
294 | + CCS_MAC_MKDIR, | |
295 | + CCS_MAC_RMDIR, | |
296 | + CCS_MAC_MKFIFO, | |
297 | + CCS_MAC_MKSOCK, | |
298 | + CCS_MAC_TRUNCATE, | |
299 | + CCS_MAC_SYMLINK, | |
300 | + CCS_MAC_MKBLOCK, | |
301 | + CCS_MAC_MKCHAR, | |
302 | + CCS_MAC_LINK, | |
303 | + CCS_MAC_RENAME, | |
304 | + CCS_MAC_CHMOD, | |
305 | + CCS_MAC_CHOWN, | |
306 | + CCS_MAC_CHGRP, | |
307 | + CCS_MAC_IOCTL, | |
308 | + CCS_MAC_CHROOT, | |
309 | + CCS_MAC_MOUNT, | |
310 | + CCS_MAC_UMOUNT, | |
311 | + CCS_MAC_PIVOT_ROOT, | |
312 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
313 | + CCS_MAC_INET_STREAM_BIND, | |
314 | + CCS_MAC_INET_STREAM_LISTEN, | |
315 | + CCS_MAC_INET_STREAM_CONNECT, | |
316 | + CCS_MAC_INET_STREAM_ACCEPT, | |
317 | + CCS_MAC_INET_DGRAM_BIND, | |
318 | + CCS_MAC_INET_DGRAM_SEND, | |
319 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
320 | + CCS_MAC_INET_DGRAM_RECV, | |
321 | +#endif | |
322 | + CCS_MAC_INET_RAW_BIND, | |
323 | + CCS_MAC_INET_RAW_SEND, | |
324 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
325 | + CCS_MAC_INET_RAW_RECV, | |
326 | +#endif | |
327 | + CCS_MAC_UNIX_STREAM_BIND, | |
328 | + CCS_MAC_UNIX_STREAM_LISTEN, | |
329 | + CCS_MAC_UNIX_STREAM_CONNECT, | |
330 | + CCS_MAC_UNIX_STREAM_ACCEPT, | |
331 | + CCS_MAC_UNIX_DGRAM_BIND, | |
332 | + CCS_MAC_UNIX_DGRAM_SEND, | |
333 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
334 | + CCS_MAC_UNIX_DGRAM_RECV, | |
335 | +#endif | |
336 | + CCS_MAC_UNIX_SEQPACKET_BIND, | |
337 | + CCS_MAC_UNIX_SEQPACKET_LISTEN, | |
338 | + CCS_MAC_UNIX_SEQPACKET_CONNECT, | |
339 | + CCS_MAC_UNIX_SEQPACKET_ACCEPT, | |
340 | +#endif | |
341 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
342 | + CCS_MAC_ENVIRON, | |
343 | +#endif | |
344 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
345 | + CCS_MAC_PTRACE, | |
346 | +#endif | |
347 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
348 | + CCS_MAC_SIGNAL, | |
349 | +#endif | |
350 | + CCS_MAC_MODIFY_POLICY, | |
351 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
352 | + CCS_MAC_USE_NETLINK_SOCKET, | |
353 | + CCS_MAC_USE_PACKET_SOCKET, | |
354 | + CCS_MAC_USE_REBOOT, | |
355 | + CCS_MAC_USE_VHANGUP, | |
356 | + CCS_MAC_SET_TIME, | |
357 | + CCS_MAC_SET_PRIORITY, | |
358 | + CCS_MAC_SET_HOSTNAME, | |
359 | + CCS_MAC_USE_KERNEL_MODULE, | |
360 | + CCS_MAC_USE_NEW_KERNEL, | |
361 | +#endif | |
362 | +#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
363 | + CCS_MAC_AUTO_DOMAIN_TRANSITION, | |
364 | +#endif | |
365 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
366 | + CCS_MAC_MANUAL_DOMAIN_TRANSITION, | |
367 | +#endif | |
368 | + CCS_MAX_MAC_INDEX | |
369 | +} __packed; | |
370 | + | |
371 | +/* Index numbers for statistic information. */ | |
372 | +enum ccs_memory_stat_type { | |
373 | + CCS_MEMORY_POLICY, | |
374 | + CCS_MEMORY_AUDIT, | |
375 | + CCS_MEMORY_QUERY, | |
376 | + CCS_MAX_MEMORY_STAT | |
377 | +} __packed; | |
378 | + | |
379 | +enum ccs_matching_result { | |
380 | + CCS_MATCHING_UNMATCHED, | |
381 | + CCS_MATCHING_ALLOWED, | |
382 | + CCS_MATCHING_DENIED, | |
383 | + CCS_MAX_MATCHING | |
384 | +} __packed; | |
385 | + | |
386 | +/* Index numbers for stat(). */ | |
387 | +enum ccs_path_stat_index { | |
388 | + /* Do not change this order. */ | |
389 | + CCS_PATH1, | |
390 | + CCS_PATH1_PARENT, | |
391 | + CCS_PATH2, | |
392 | + CCS_PATH2_PARENT, | |
393 | + CCS_MAX_PATH_STAT | |
394 | +} __packed; | |
395 | + | |
396 | +/* Index numbers for entry type. */ | |
397 | +enum ccs_policy_id { | |
398 | + CCS_ID_GROUP, | |
399 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
400 | + CCS_ID_IP_GROUP, | |
401 | +#endif | |
402 | + CCS_ID_STRING_GROUP, | |
403 | + CCS_ID_NUMBER_GROUP, | |
404 | + CCS_ID_CONDITION, | |
405 | + CCS_ID_NAME, | |
406 | + CCS_ID_ACL, | |
407 | + CCS_ID_DOMAIN, | |
408 | + CCS_MAX_POLICY | |
409 | +} __packed; | |
410 | + | |
411 | +/* Index numbers for statistic information. */ | |
412 | +enum ccs_policy_stat_type { | |
413 | + CCS_STAT_POLICY_UPDATES, | |
414 | + CCS_STAT_REQUEST_DENIED, | |
415 | + CCS_MAX_POLICY_STAT | |
416 | +} __packed; | |
417 | + | |
418 | +/* Index numbers for /proc/ccs/ interfaces. */ | |
419 | +enum ccs_proc_interface_index { | |
420 | + CCS_POLICY, | |
421 | + CCS_PROCESS_STATUS, | |
422 | + CCS_AUDIT, | |
423 | + CCS_VERSION, | |
424 | + CCS_QUERY, | |
425 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
426 | + CCS_EXECUTE_HANDLER, | |
427 | +#endif | |
428 | +} __packed; | |
429 | + | |
430 | +/* Index numbers for special mount operations. */ | |
431 | +enum ccs_special_mount { | |
432 | + CCS_MOUNT_BIND, /* mount --bind /source /dest */ | |
433 | + CCS_MOUNT_MOVE, /* mount --move /old /new */ | |
434 | + CCS_MOUNT_REMOUNT, /* mount -o remount /dir */ | |
435 | + CCS_MOUNT_MAKE_UNBINDABLE, /* mount --make-unbindable /dir */ | |
436 | + CCS_MOUNT_MAKE_PRIVATE, /* mount --make-private /dir */ | |
437 | + CCS_MOUNT_MAKE_SLAVE, /* mount --make-slave /dir */ | |
438 | + CCS_MOUNT_MAKE_SHARED, /* mount --make-shared /dir */ | |
439 | + CCS_MAX_SPECIAL_MOUNT | |
440 | +} __packed; | |
441 | + | |
442 | +/* Index numbers for type of numeric values. */ | |
443 | +enum ccs_value_type { | |
444 | + CCS_VALUE_TYPE_INVALID, | |
445 | + CCS_VALUE_TYPE_DECIMAL, | |
446 | + CCS_VALUE_TYPE_OCTAL, | |
447 | + CCS_VALUE_TYPE_HEXADECIMAL, | |
448 | +} __packed; | |
449 | + | |
450 | +/* Index numbers for type of IP addresses. */ | |
451 | +enum ccs_ipaddr_type { | |
452 | + CCS_ADDRESS_TYPE_INVALID, | |
453 | + CCS_ADDRESS_TYPE_IPV4, | |
454 | + CCS_ADDRESS_TYPE_IPV4_RANGE, | |
455 | + CCS_ADDRESS_TYPE_IPV6, | |
456 | + CCS_ADDRESS_TYPE_IPV6_RANGE, | |
457 | +} __packed; | |
458 | + | |
459 | +/* Constants definition for internal use. */ | |
460 | + | |
461 | +/* | |
462 | + * TOMOYO uses this hash only when appending a string into the string table. | |
463 | + * Frequency of appending strings is very low. So we don't need large (e.g. | |
464 | + * 64k) hash size. 256 will be sufficient. | |
465 | + */ | |
466 | +#define CCS_HASH_BITS 8 | |
467 | +#define CCS_MAX_HASH (1u << CCS_HASH_BITS) | |
468 | + | |
469 | +/* | |
470 | + * TOMOYO checks only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET. | |
471 | + * Therefore, we don't need SOCK_MAX. | |
472 | + */ | |
473 | +#define CCS_SOCK_MAX 6 | |
474 | + | |
475 | +/* Size of temporary buffer for execve() operation. */ | |
476 | +#define CCS_EXEC_TMPSIZE 4096 | |
477 | + | |
478 | +/* Patterns for auditing logs quota. */ | |
479 | +#define CCS_MAX_LOG_QUOTA 256 | |
480 | + | |
481 | +/* Garbage collector is trying to kfree() this element. */ | |
482 | +#define CCS_GC_IN_PROGRESS -1 | |
483 | + | |
484 | +/* Current thread is doing open(3) ? */ | |
485 | +#define CCS_OPEN_FOR_IOCTL_ONLY 2 | |
486 | +/* Current thread is doing do_execve() ? */ | |
487 | +#define CCS_TASK_IS_IN_EXECVE 4 | |
488 | +/* Current thread is running as an execute handler program? */ | |
489 | +#define CCS_TASK_IS_EXECUTE_HANDLER 8 | |
490 | +/* Current thread is allowed to modify policy via /proc/ccs/ interface? */ | |
491 | +#define CCS_TASK_IS_MANAGER 16 | |
492 | + | |
493 | +/* | |
494 | + * Retry this request. Returned by ccs_supervisor() if policy violation has | |
495 | + * occurred in enforcing mode and the userspace daemon decided to retry. | |
496 | + * | |
497 | + * We must choose a positive value in order to distinguish "granted" (which is | |
498 | + * 0) and "rejected" (which is a negative value) and "retry". | |
499 | + */ | |
500 | +#define CCS_RETRY_REQUEST 1 | |
501 | + | |
502 | +/* Size of read buffer for /proc/ccs/ interface. */ | |
503 | +#define CCS_MAX_IO_READ_QUEUE 64 | |
504 | + | |
505 | +/* Structure definition for internal use. */ | |
506 | + | |
507 | +/* Common header for holding ACL entries. */ | |
508 | +struct ccs_acl_head { | |
509 | + struct list_head list; | |
510 | + s8 is_deleted; /* true or false or CCS_GC_IN_PROGRESS */ | |
511 | +} __packed; | |
512 | + | |
513 | +/* Common header for shared entries. */ | |
514 | +struct ccs_shared_acl_head { | |
515 | + struct list_head list; | |
516 | + atomic_t users; | |
517 | +} __packed; | |
518 | + | |
519 | +/* Common header for individual entries. */ | |
520 | +struct ccs_acl_info { | |
521 | + struct list_head list; | |
522 | + struct list_head acl_info_list; | |
523 | + struct ccs_condition *cond; /* Maybe NULL. */ | |
524 | + bool is_deleted; | |
525 | + bool is_deny; | |
526 | + u16 priority; | |
527 | + u8 audit; | |
528 | +}; | |
529 | + | |
530 | +/* Structure for "string_group"/"number_group"/"ip_group" directive. */ | |
531 | +struct ccs_group { | |
532 | + struct ccs_shared_acl_head head; | |
533 | + /* Name of group (without leading "@"). */ | |
534 | + const struct ccs_path_info *group_name; | |
535 | + /* | |
536 | + * List of "struct ccs_string_group" or "struct ccs_number_group" or | |
537 | + * "struct ccs_ip_group". | |
538 | + */ | |
539 | + struct list_head member_list; | |
540 | +}; | |
541 | + | |
542 | +/* Structure for "string_group" directive. */ | |
543 | +struct ccs_string_group { | |
544 | + struct ccs_acl_head head; | |
545 | + const struct ccs_path_info *member_name; | |
546 | +}; | |
547 | + | |
548 | +/* Structure for "number_group" directive. */ | |
549 | +struct ccs_number_group { | |
550 | + struct ccs_acl_head head; | |
551 | + u8 radix; | |
552 | + unsigned long value[2]; | |
553 | +}; | |
554 | + | |
555 | +/* Structure for "ip_group" directive. */ | |
556 | +struct ccs_ip_group { | |
557 | + struct ccs_acl_head head; | |
558 | + bool is_ipv6; | |
559 | + /* Structure for holding an IP address. */ | |
560 | + struct in6_addr ip[2]; /* Big endian. */ | |
561 | +}; | |
562 | + | |
563 | +/* Subset of "struct stat". Used by conditional ACL and audit logs. */ | |
564 | +struct ccs_mini_stat { | |
565 | + uid_t uid; | |
566 | + gid_t gid; | |
567 | + ino_t ino; | |
568 | + umode_t mode; | |
569 | + dev_t dev; | |
570 | + dev_t rdev; | |
571 | + unsigned long fsmagic; | |
572 | +}; | |
573 | + | |
574 | +/* Structure for dumping argv[] and envp[] of "struct linux_binprm". */ | |
575 | +struct ccs_page_dump { | |
576 | + struct page *page; /* Previously dumped page. */ | |
577 | + char *data; /* Contents of "page". Size is PAGE_SIZE. */ | |
578 | +}; | |
579 | + | |
580 | +/* Structure for entries which follows "struct ccs_condition". */ | |
581 | +union ccs_condition_element { | |
582 | + struct { | |
583 | + enum ccs_conditions_index left; | |
584 | + enum ccs_conditions_index right; | |
585 | + bool is_not; | |
586 | + u8 radix; | |
587 | + }; | |
588 | + struct ccs_group *group; | |
589 | + const struct ccs_path_info *path; | |
590 | + u32 ip; /* Repeat 4 times if IPv6 address. */ | |
591 | + unsigned long value; | |
592 | +}; | |
593 | + | |
594 | +/* Structure for optional arguments. */ | |
595 | +struct ccs_condition { | |
596 | + struct ccs_shared_acl_head head; | |
597 | + u32 size; /* Memory size allocated for this entry. */ | |
598 | + /* union ccs_condition_element condition[]; */ | |
599 | +}; | |
600 | + | |
601 | +/* Structure for holding a token. */ | |
602 | +struct ccs_path_info { | |
603 | + const char *name; | |
604 | + u32 hash; /* = full_name_hash(name, strlen(name)) */ | |
605 | + u32 total_len; /* = strlen(name) */ | |
606 | + u32 const_len; /* = ccs_const_part_length(name) */ | |
607 | +}; | |
608 | + | |
609 | +/* Structure for request info. */ | |
610 | +struct ccs_request_info { | |
611 | + /* For holding parameters. */ | |
612 | + struct ccs_request_param { | |
613 | + const struct ccs_path_info *s[4]; | |
614 | + unsigned long i[3]; | |
615 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
616 | + const u8 *ip; /* Big endian. */ | |
617 | + bool is_ipv6; | |
618 | +#endif | |
619 | + } param; | |
620 | + /* For holding pathnames and attributes. */ | |
621 | + struct { | |
622 | + /* | |
623 | + * True if ccs_get_attributes() was already called, false | |
624 | + * otherwise. | |
625 | + */ | |
626 | + bool validate_done; | |
627 | + /* True if @stat[] is valid. */ | |
628 | + bool stat_valid[CCS_MAX_PATH_STAT]; | |
629 | + /* Pointer to file objects. */ | |
630 | + struct path path[2]; | |
631 | + /* | |
632 | + * Information on @path[0], @path[0]'s parent directory, | |
633 | + * @path[1] and @path[1]'s parent directory. | |
634 | + */ | |
635 | + struct ccs_mini_stat stat[CCS_MAX_PATH_STAT]; | |
636 | + /* | |
637 | + * Name of @path[0] and @path[1]. | |
638 | + * Cleared by ccs_crear_request_info(). | |
639 | + */ | |
640 | + struct ccs_path_info pathname[2]; | |
641 | + } obj; | |
642 | + struct { | |
643 | + struct linux_binprm *bprm; | |
644 | + struct ccs_domain_info *previous_domain; | |
645 | + /* For execute_handler. */ | |
646 | + char *handler; /* kstrdup(handler_path->name, GFP_NOFS) */ | |
647 | + /* For dumping argv[] and envp[]. */ | |
648 | + struct ccs_page_dump dump; | |
649 | + /* For temporary use. Size is CCS_EXEC_TMPSIZE bytes. */ | |
650 | + char *tmp; | |
651 | + }; | |
652 | + /* | |
653 | + * Name of current thread's executable. | |
654 | + * Cleared by ccs_crear_request_info(). | |
655 | + */ | |
656 | + struct ccs_path_info exename; | |
657 | + /* | |
658 | + * Matching "struct ccs_acl_info" is copied. Used for ccs-queryd. | |
659 | + * Valid until ccs_read_unlock(). | |
660 | + */ | |
661 | + struct ccs_acl_info *matched_acl; | |
662 | + /* | |
663 | + * Matching handler and domain transition are copied. | |
664 | + * Valid until ccs_read_unlock(). | |
665 | + */ | |
666 | + const struct ccs_path_info *handler_path; | |
667 | + const struct ccs_path_info *transition; | |
668 | + const struct ccs_path_info *handler_path_candidate; | |
669 | + const struct ccs_path_info *transition_candidate; | |
670 | + /* | |
671 | + * For holding operation index used for this request. | |
672 | + * One of values in "enum ccs_mac_index". | |
673 | + */ | |
674 | + enum ccs_mac_index type; | |
675 | + /* For holding matching result. */ | |
676 | + enum ccs_matching_result result; | |
677 | + /* | |
678 | + * For counting number of retries made for this request. | |
679 | + * This counter is incremented whenever ccs_supervisor() returned | |
680 | + * CCS_RETRY_REQUEST. | |
681 | + */ | |
682 | + u8 retry; | |
683 | + /* For holding max audit log count for this matching entry. */ | |
684 | + u8 audit; | |
685 | + /* | |
686 | + * Set to true if condition could not be checked due to out of memory. | |
687 | + * This flag is used for returning out of memory flag back to | |
688 | + * ccs_check_acl_list(). Thus, this flag will not be set if out of | |
689 | + * memory occurred before ccs_check_acl_list() is called. | |
690 | + */ | |
691 | + bool failed_by_oom; | |
692 | +}; | |
693 | + | |
694 | +/* Structure for domain information. */ | |
695 | +struct ccs_domain_info { | |
696 | + struct list_head list; | |
697 | + /* Name of this domain. Never NULL. */ | |
698 | + const struct ccs_path_info *domainname; | |
699 | +}; | |
700 | + | |
701 | +/* Structure for holding string data. */ | |
702 | +struct ccs_name { | |
703 | + struct ccs_shared_acl_head head; | |
704 | + int size; /* Memory size allocated for this entry. */ | |
705 | + struct ccs_path_info entry; | |
706 | +}; | |
707 | + | |
708 | +/* Structure for reading/writing policy via /proc/ccs/ interfaces. */ | |
709 | +struct ccs_io_buffer { | |
710 | + /* Exclusive lock for this structure. */ | |
711 | + struct mutex io_sem; | |
712 | + char __user *read_user_buf; | |
713 | + size_t read_user_buf_avail; | |
714 | + struct { | |
715 | + struct list_head *group; | |
716 | + struct list_head *acl; | |
717 | + struct list_head *subacl; | |
718 | + const union ccs_condition_element *cond; | |
719 | + size_t avail; | |
720 | + unsigned int step; | |
721 | + unsigned int query_index; | |
722 | + u16 index; | |
723 | + u8 cond_step; | |
724 | + u8 w_pos; | |
725 | + enum ccs_mac_index acl_index; | |
726 | + bool eof; | |
727 | + bool print_this_acl_only; | |
728 | + bool version_done; | |
729 | + bool stat_done; | |
730 | + bool quota_done; | |
731 | + bool group_done; | |
732 | + const char *w[CCS_MAX_IO_READ_QUEUE]; | |
733 | + } r; | |
734 | + struct { | |
735 | + char *data; | |
736 | + struct ccs_acl_info *acl; | |
737 | + size_t avail; | |
738 | + enum ccs_mac_index acl_index; | |
739 | + bool is_delete; | |
740 | + bool is_deny; | |
741 | + u16 priority; | |
742 | + } w; | |
743 | + /* Buffer for reading. */ | |
744 | + char *read_buf; | |
745 | + /* Size of read buffer. */ | |
746 | + size_t readbuf_size; | |
747 | + /* Buffer for writing. */ | |
748 | + char *write_buf; | |
749 | + /* Size of write buffer. */ | |
750 | + size_t writebuf_size; | |
751 | + /* Type of interface. */ | |
752 | + enum ccs_proc_interface_index type; | |
753 | + /* Users counter protected by ccs_io_buffer_list_lock. */ | |
754 | + u8 users; | |
755 | + /* List for telling GC not to kfree() elements. */ | |
756 | + struct list_head list; | |
757 | +}; | |
758 | + | |
759 | +/* Structure for representing YYYY/MM/DD hh/mm/ss. */ | |
760 | +struct ccs_time { | |
761 | + u16 year; | |
762 | + u8 month; | |
763 | + u8 day; | |
764 | + u8 hour; | |
765 | + u8 min; | |
766 | + u8 sec; | |
767 | +}; | |
768 | + | |
769 | +/* Prototype definition for "struct ccsecurity_operations". */ | |
770 | + | |
771 | +void __init ccs_permission_init(void); | |
772 | +void __init ccs_mm_init(void); | |
773 | + | |
774 | +/* Prototype definition for internal use. */ | |
775 | + | |
776 | +bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos, | |
777 | + struct ccs_page_dump *dump); | |
778 | +bool ccs_get_exename(struct ccs_path_info *buf); | |
779 | +bool ccs_manager(void); | |
780 | +bool ccs_transit_domain(const char *domainname); | |
781 | +char *ccs_encode(const char *str); | |
782 | +char *ccs_encode2(const char *str, int str_len); | |
783 | +char *ccs_realpath(struct path *path); | |
784 | +char *ccs_get_exe(void); | |
785 | +int ccs_audit_log(struct ccs_request_info *r); | |
786 | +int ccs_check_acl(struct ccs_request_info *r, const bool clear); | |
787 | +void ccs_del_condition(struct list_head *element); | |
788 | +void ccs_fill_path_info(struct ccs_path_info *ptr); | |
789 | +void ccs_get_attributes(struct ccs_request_info *r); | |
790 | +void ccs_notify_gc(struct ccs_io_buffer *head, const bool is_register); | |
791 | +void ccs_populate_patharg(struct ccs_request_info *r, const bool first); | |
792 | +void ccs_transition_failed(const char *domainname); | |
793 | +void ccs_warn_oom(const char *function); | |
794 | + | |
795 | +/* Variable definition for internal use. */ | |
796 | + | |
797 | +extern bool ccs_policy_loaded; | |
798 | +extern struct ccs_domain_info ccs_kernel_domain; | |
799 | +extern struct ccs_path_info ccs_null_name; | |
800 | +extern struct list_head ccs_acl_list[CCS_MAX_MAC_INDEX]; | |
801 | +extern struct list_head ccs_condition_list; | |
802 | +extern struct list_head ccs_domain_list; | |
803 | +extern struct list_head ccs_group_list[CCS_MAX_GROUP]; | |
804 | +extern struct list_head ccs_name_list[CCS_MAX_HASH]; | |
805 | +extern struct mutex ccs_policy_lock; | |
806 | +extern struct srcu_struct ccs_ss; | |
807 | +extern unsigned int ccs_memory_used[CCS_MAX_MEMORY_STAT]; | |
808 | + | |
809 | +/* Inlined functions for internal use. */ | |
810 | + | |
811 | +/** | |
812 | + * ccs_pathcmp - strcmp() for "struct ccs_path_info" structure. | |
813 | + * | |
814 | + * @a: Pointer to "struct ccs_path_info". | |
815 | + * @b: Pointer to "struct ccs_path_info". | |
816 | + * | |
817 | + * Returns true if @a != @b, false otherwise. | |
818 | + */ | |
819 | +static inline bool ccs_pathcmp(const struct ccs_path_info *a, | |
820 | + const struct ccs_path_info *b) | |
821 | +{ | |
822 | + return a->hash != b->hash || strcmp(a->name, b->name); | |
823 | +} | |
824 | + | |
825 | +/** | |
826 | + * ccs_read_lock - Take lock for protecting policy. | |
827 | + * | |
828 | + * Returns index number for ccs_read_unlock(). | |
829 | + */ | |
830 | +static inline int ccs_read_lock(void) | |
831 | +{ | |
832 | + return srcu_read_lock(&ccs_ss); | |
833 | +} | |
834 | + | |
835 | +/** | |
836 | + * ccs_read_unlock - Release lock for protecting policy. | |
837 | + * | |
838 | + * @idx: Index number returned by ccs_read_lock(). | |
839 | + * | |
840 | + * Returns nothing. | |
841 | + */ | |
842 | +static inline void ccs_read_unlock(const int idx) | |
843 | +{ | |
844 | + srcu_read_unlock(&ccs_ss, idx); | |
845 | +} | |
846 | + | |
847 | +/** | |
848 | + * ccs_tasklist_lock - Take lock for reading list of "struct task_struct". | |
849 | + * | |
850 | + * Returns nothing. | |
851 | + */ | |
852 | +static inline void ccs_tasklist_lock(void) | |
853 | +{ | |
854 | + rcu_read_lock(); | |
855 | +} | |
856 | + | |
857 | +/** | |
858 | + * ccs_tasklist_unlock - Release lock for reading list of "struct task_struct". | |
859 | + * | |
860 | + * Returns nothing. | |
861 | + */ | |
862 | +static inline void ccs_tasklist_unlock(void) | |
863 | +{ | |
864 | + rcu_read_unlock(); | |
865 | +} | |
866 | + | |
867 | +/** | |
868 | + * ccs_sys_getppid - Copy of getppid(). | |
869 | + * | |
870 | + * Returns parent process's PID. | |
871 | + * | |
872 | + * Alpha does not have getppid() defined. To be able to build this module on | |
873 | + * Alpha, I have to copy getppid() from kernel/timer.c. | |
874 | + */ | |
875 | +static inline pid_t ccs_sys_getppid(void) | |
876 | +{ | |
877 | + pid_t pid; | |
878 | + rcu_read_lock(); | |
879 | + pid = task_tgid_vnr(rcu_dereference(current->real_parent)); | |
880 | + rcu_read_unlock(); | |
881 | + return pid; | |
882 | +} | |
883 | + | |
884 | +/** | |
885 | + * ccs_sys_getpid - Copy of getpid(). | |
886 | + * | |
887 | + * Returns current thread's PID. | |
888 | + * | |
889 | + * Alpha does not have getpid() defined. To be able to build this module on | |
890 | + * Alpha, I have to copy getpid() from kernel/timer.c. | |
891 | + */ | |
892 | +static inline pid_t ccs_sys_getpid(void) | |
893 | +{ | |
894 | + return task_tgid_vnr(current); | |
895 | +} | |
896 | + | |
897 | +#if defined(CONFIG_SLOB) | |
898 | + | |
899 | +/** | |
900 | + * ccs_round2 - Round up to power of 2 for calculating memory usage. | |
901 | + * | |
902 | + * @size: Size to be rounded up. | |
903 | + * | |
904 | + * Returns @size. | |
905 | + * | |
906 | + * Since SLOB does not round up, this function simply returns @size. | |
907 | + */ | |
908 | +static inline int ccs_round2(size_t size) | |
909 | +{ | |
910 | + return size; | |
911 | +} | |
912 | + | |
913 | +#else | |
914 | + | |
915 | +/** | |
916 | + * ccs_round2 - Round up to power of 2 for calculating memory usage. | |
917 | + * | |
918 | + * @size: Size to be rounded up. | |
919 | + * | |
920 | + * Returns rounded size. | |
921 | + * | |
922 | + * Strictly speaking, SLAB may be able to allocate (e.g.) 96 bytes instead of | |
923 | + * (e.g.) 128 bytes. | |
924 | + */ | |
925 | +static inline int ccs_round2(size_t size) | |
926 | +{ | |
927 | +#if PAGE_SIZE == 4096 | |
928 | + size_t bsize = 32; | |
929 | +#else | |
930 | + size_t bsize = 64; | |
931 | +#endif | |
932 | + if (!size) | |
933 | + return 0; | |
934 | + while (size > bsize) | |
935 | + bsize <<= 1; | |
936 | + return bsize; | |
937 | +} | |
938 | + | |
939 | +#endif | |
940 | + | |
941 | +/** | |
942 | + * ccs_put_condition - Drop reference on "struct ccs_condition". | |
943 | + * | |
944 | + * @cond: Pointer to "struct ccs_condition". Maybe NULL. | |
945 | + * | |
946 | + * Returns nothing. | |
947 | + */ | |
948 | +static inline void ccs_put_condition(struct ccs_condition *cond) | |
949 | +{ | |
950 | + if (cond) | |
951 | + atomic_dec(&cond->head.users); | |
952 | +} | |
953 | + | |
954 | +/** | |
955 | + * ccs_put_group - Drop reference on "struct ccs_group". | |
956 | + * | |
957 | + * @group: Pointer to "struct ccs_group". Maybe NULL. | |
958 | + * | |
959 | + * Returns nothing. | |
960 | + */ | |
961 | +static inline void ccs_put_group(struct ccs_group *group) | |
962 | +{ | |
963 | + if (group) | |
964 | + atomic_dec(&group->head.users); | |
965 | +} | |
966 | + | |
967 | +/** | |
968 | + * ccs_put_name - Drop reference on "struct ccs_name". | |
969 | + * | |
970 | + * @name: Pointer to "struct ccs_path_info". Maybe NULL. | |
971 | + * | |
972 | + * Returns nothing. | |
973 | + */ | |
974 | +static inline void ccs_put_name(const struct ccs_path_info *name) | |
975 | +{ | |
976 | + if (name) | |
977 | + atomic_dec(&container_of(name, struct ccs_name, entry)-> | |
978 | + head.users); | |
979 | +} | |
980 | + | |
981 | +/* For importing variables and functions. */ | |
982 | +extern const struct ccsecurity_exports ccsecurity_exports; | |
983 | + | |
984 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | |
985 | + | |
986 | +/* | |
987 | + * Structure for holding "struct ccs_domain_info *" and "u32 ccs_flags" for | |
988 | + * each "struct task_struct". | |
989 | + * | |
990 | + * "struct ccs_domain_info *" and "u32 ccs_flags" for each "struct task_struct" | |
991 | + * are maintained outside that "struct task_struct". Therefore, ccs_security | |
992 | + * != task_struct . This keeps KABI for distributor's prebuilt kernels but | |
993 | + * entails slow access. | |
994 | + * | |
995 | + * Memory for this structure is allocated when current thread tries to access | |
996 | + * it. Therefore, if memory allocation failed, current thread will be killed by | |
997 | + * SIGKILL. Note that if current->pid == 1, sending SIGKILL won't work. | |
998 | + */ | |
999 | +struct ccs_security { | |
1000 | + struct list_head list; | |
1001 | + const struct task_struct *task; | |
1002 | + struct ccs_domain_info *ccs_domain_info; | |
1003 | + u32 ccs_flags; | |
1004 | + struct rcu_head rcu; | |
1005 | +}; | |
1006 | + | |
1007 | +#define CCS_TASK_SECURITY_HASH_BITS 12 | |
1008 | +#define CCS_MAX_TASK_SECURITY_HASH (1u << CCS_TASK_SECURITY_HASH_BITS) | |
1009 | +extern struct list_head ccs_task_security_list[CCS_MAX_TASK_SECURITY_HASH]; | |
1010 | + | |
1011 | +struct ccs_security *ccs_find_task_security(const struct task_struct *task); | |
1012 | + | |
1013 | +/** | |
1014 | + * ccs_current_security - Get "struct ccs_security" for current thread. | |
1015 | + * | |
1016 | + * Returns pointer to "struct ccs_security" for current thread. | |
1017 | + */ | |
1018 | +static inline struct ccs_security *ccs_current_security(void) | |
1019 | +{ | |
1020 | + return ccs_find_task_security(current); | |
1021 | +} | |
1022 | + | |
1023 | +/** | |
1024 | + * ccs_task_domain - Get "struct ccs_domain_info" for specified thread. | |
1025 | + * | |
1026 | + * @task: Pointer to "struct task_struct". | |
1027 | + * | |
1028 | + * Returns pointer to "struct ccs_security" for specified thread. | |
1029 | + */ | |
1030 | +static inline struct ccs_domain_info *ccs_task_domain(struct task_struct *task) | |
1031 | +{ | |
1032 | + struct ccs_domain_info *domain; | |
1033 | + rcu_read_lock(); | |
1034 | + domain = ccs_find_task_security(task)->ccs_domain_info; | |
1035 | + rcu_read_unlock(); | |
1036 | + return domain; | |
1037 | +} | |
1038 | + | |
1039 | +/** | |
1040 | + * ccs_current_domain - Get "struct ccs_domain_info" for current thread. | |
1041 | + * | |
1042 | + * Returns pointer to "struct ccs_domain_info" for current thread. | |
1043 | + */ | |
1044 | +static inline struct ccs_domain_info *ccs_current_domain(void) | |
1045 | +{ | |
1046 | + return ccs_find_task_security(current)->ccs_domain_info; | |
1047 | +} | |
1048 | + | |
1049 | +/** | |
1050 | + * ccs_task_flags - Get flags for specified thread. | |
1051 | + * | |
1052 | + * @task: Pointer to "struct task_struct". | |
1053 | + * | |
1054 | + * Returns flags for specified thread. | |
1055 | + */ | |
1056 | +static inline u32 ccs_task_flags(struct task_struct *task) | |
1057 | +{ | |
1058 | + u32 ccs_flags; | |
1059 | + rcu_read_lock(); | |
1060 | + ccs_flags = ccs_find_task_security(task)->ccs_flags; | |
1061 | + rcu_read_unlock(); | |
1062 | + return ccs_flags; | |
1063 | +} | |
1064 | + | |
1065 | +/** | |
1066 | + * ccs_current_flags - Get flags for current thread. | |
1067 | + * | |
1068 | + * Returns flags for current thread. | |
1069 | + */ | |
1070 | +static inline u32 ccs_current_flags(void) | |
1071 | +{ | |
1072 | + return ccs_find_task_security(current)->ccs_flags; | |
1073 | +} | |
1074 | + | |
1075 | +#else | |
1076 | + | |
1077 | +/* | |
1078 | + * "struct ccs_domain_info *" and "u32 ccs_flags" for each "struct task_struct" | |
1079 | + * are maintained inside that "struct task_struct". Therefore, ccs_security == | |
1080 | + * task_struct . This allows fast access but breaks KABI checks for | |
1081 | + * distributor's prebuilt kernels due to changes in "struct task_struct". | |
1082 | + */ | |
1083 | +#define ccs_security task_struct | |
1084 | + | |
1085 | +/** | |
1086 | + * ccs_find_task_security - Find "struct ccs_security" for given task. | |
1087 | + * | |
1088 | + * @task: Pointer to "struct task_struct". | |
1089 | + * | |
1090 | + * Returns pointer to "struct ccs_security". | |
1091 | + */ | |
1092 | +static inline struct ccs_security *ccs_find_task_security(struct task_struct * | |
1093 | + task) | |
1094 | +{ | |
1095 | + return task; | |
1096 | +} | |
1097 | + | |
1098 | +/** | |
1099 | + * ccs_current_security - Get "struct ccs_security" for current thread. | |
1100 | + * | |
1101 | + * Returns pointer to "struct ccs_security" for current thread. | |
1102 | + */ | |
1103 | +static inline struct ccs_security *ccs_current_security(void) | |
1104 | +{ | |
1105 | + return ccs_find_task_security(current); | |
1106 | +} | |
1107 | + | |
1108 | +/** | |
1109 | + * ccs_task_domain - Get "struct ccs_domain_info" for specified thread. | |
1110 | + * | |
1111 | + * @task: Pointer to "struct task_struct". | |
1112 | + * | |
1113 | + * Returns pointer to "struct ccs_security" for specified thread. | |
1114 | + */ | |
1115 | +static inline struct ccs_domain_info *ccs_task_domain(struct task_struct *task) | |
1116 | +{ | |
1117 | + struct ccs_domain_info *domain = task->ccs_domain_info; | |
1118 | + return domain ? domain : &ccs_kernel_domain; | |
1119 | +} | |
1120 | + | |
1121 | +/** | |
1122 | + * ccs_current_domain - Get "struct ccs_domain_info" for current thread. | |
1123 | + * | |
1124 | + * Returns pointer to "struct ccs_domain_info" for current thread. | |
1125 | + * | |
1126 | + * If current thread does not belong to a domain (which is true for initial | |
1127 | + * init_task in order to hide ccs_kernel_domain from this module), | |
1128 | + * current thread enters into ccs_kernel_domain. | |
1129 | + */ | |
1130 | +static inline struct ccs_domain_info *ccs_current_domain(void) | |
1131 | +{ | |
1132 | + struct task_struct *task = current; | |
1133 | + if (!task->ccs_domain_info) | |
1134 | + task->ccs_domain_info = &ccs_kernel_domain; | |
1135 | + return task->ccs_domain_info; | |
1136 | +} | |
1137 | + | |
1138 | +/** | |
1139 | + * ccs_task_flags - Get flags for specified thread. | |
1140 | + * | |
1141 | + * @task: Pointer to "struct task_struct". | |
1142 | + * | |
1143 | + * Returns flags for specified thread. | |
1144 | + */ | |
1145 | +static inline u32 ccs_task_flags(struct task_struct *task) | |
1146 | +{ | |
1147 | + return ccs_find_task_security(task)->ccs_flags; | |
1148 | +} | |
1149 | + | |
1150 | +/** | |
1151 | + * ccs_current_flags - Get flags for current thread. | |
1152 | + * | |
1153 | + * Returns flags for current thread. | |
1154 | + */ | |
1155 | +static inline u32 ccs_current_flags(void) | |
1156 | +{ | |
1157 | + return ccs_find_task_security(current)->ccs_flags; | |
1158 | +} | |
1159 | + | |
1160 | +#endif | |
1161 | + | |
1162 | +#endif |
@@ -0,0 +1,4401 @@ | ||
1 | +/* | |
2 | + * security/caitsith/policy_io.c | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#include "internal.h" | |
10 | + | |
11 | +/***** SECTION1: Constants definition *****/ | |
12 | + | |
13 | +/* Define this to enable debug mode. */ | |
14 | +/* #define DEBUG_CONDITION */ | |
15 | + | |
16 | +#ifdef DEBUG_CONDITION | |
17 | +#define dprintk printk | |
18 | +#else | |
19 | +#define dprintk(...) do { } while (0) | |
20 | +#endif | |
21 | + | |
22 | +/* String table for operation. */ | |
23 | +static const char * const ccs_mac_keywords[CCS_MAX_MAC_INDEX] = { | |
24 | + [CCS_MAC_EXECUTE] = "execute", | |
25 | + [CCS_MAC_READ] = "read", | |
26 | + [CCS_MAC_WRITE] = "write", | |
27 | + [CCS_MAC_APPEND] = "append", | |
28 | + [CCS_MAC_CREATE] = "create", | |
29 | + [CCS_MAC_UNLINK] = "unlink", | |
30 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
31 | + [CCS_MAC_GETATTR] = "getattr", | |
32 | +#endif | |
33 | + [CCS_MAC_MKDIR] = "mkdir", | |
34 | + [CCS_MAC_RMDIR] = "rmdir", | |
35 | + [CCS_MAC_MKFIFO] = "mkfifo", | |
36 | + [CCS_MAC_MKSOCK] = "mksock", | |
37 | + [CCS_MAC_TRUNCATE] = "truncate", | |
38 | + [CCS_MAC_SYMLINK] = "symlink", | |
39 | + [CCS_MAC_MKBLOCK] = "mkblock", | |
40 | + [CCS_MAC_MKCHAR] = "mkchar", | |
41 | + [CCS_MAC_LINK] = "link", | |
42 | + [CCS_MAC_RENAME] = "rename", | |
43 | + [CCS_MAC_CHMOD] = "chmod", | |
44 | + [CCS_MAC_CHOWN] = "chown", | |
45 | + [CCS_MAC_CHGRP] = "chgrp", | |
46 | + [CCS_MAC_IOCTL] = "ioctl", | |
47 | + [CCS_MAC_CHROOT] = "chroot", | |
48 | + [CCS_MAC_MOUNT] = "mount", | |
49 | + [CCS_MAC_UMOUNT] = "unmount", | |
50 | + [CCS_MAC_PIVOT_ROOT] = "pivot_root", | |
51 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
52 | + [CCS_MAC_INET_STREAM_BIND] = "inet_stream_bind", | |
53 | + [CCS_MAC_INET_STREAM_LISTEN] = "inet_stream_listen", | |
54 | + [CCS_MAC_INET_STREAM_CONNECT] = "inet_stream_connect", | |
55 | + [CCS_MAC_INET_STREAM_ACCEPT] = "inet_stream_accept", | |
56 | + [CCS_MAC_INET_DGRAM_BIND] = "inet_dgram_bind", | |
57 | + [CCS_MAC_INET_DGRAM_SEND] = "inet_dgram_send", | |
58 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
59 | + [CCS_MAC_INET_DGRAM_RECV] = "inet_dgram_recv", | |
60 | +#endif | |
61 | + [CCS_MAC_INET_RAW_BIND] = "inet_raw_bind", | |
62 | + [CCS_MAC_INET_RAW_SEND] = "inet_raw_send", | |
63 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
64 | + [CCS_MAC_INET_RAW_RECV] = "inet_raw_recv", | |
65 | +#endif | |
66 | + [CCS_MAC_UNIX_STREAM_BIND] = "unix_stream_bind", | |
67 | + [CCS_MAC_UNIX_STREAM_LISTEN] = "unix_stream_listen", | |
68 | + [CCS_MAC_UNIX_STREAM_CONNECT] = "unix_stream_connect", | |
69 | + [CCS_MAC_UNIX_STREAM_ACCEPT] = "unix_stream_accept", | |
70 | + [CCS_MAC_UNIX_DGRAM_BIND] = "unix_dgram_bind", | |
71 | + [CCS_MAC_UNIX_DGRAM_SEND] = "unix_dgram_send", | |
72 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
73 | + [CCS_MAC_UNIX_DGRAM_RECV] = "unix_dgram_recv", | |
74 | +#endif | |
75 | + [CCS_MAC_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind", | |
76 | + [CCS_MAC_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen", | |
77 | + [CCS_MAC_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect", | |
78 | + [CCS_MAC_UNIX_SEQPACKET_ACCEPT] = "unix_seqpacket_accept", | |
79 | +#endif | |
80 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
81 | + [CCS_MAC_ENVIRON] = "environ", | |
82 | +#endif | |
83 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
84 | + [CCS_MAC_PTRACE] = "ptrace", | |
85 | +#endif | |
86 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
87 | + [CCS_MAC_SIGNAL] = "signal", | |
88 | +#endif | |
89 | + [CCS_MAC_MODIFY_POLICY] = "modify_policy", | |
90 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
91 | + [CCS_MAC_USE_NETLINK_SOCKET] = "use_netlink_socket", | |
92 | + [CCS_MAC_USE_PACKET_SOCKET] = "use_packet_socket", | |
93 | + [CCS_MAC_USE_REBOOT] = "use_reboot", | |
94 | + [CCS_MAC_USE_VHANGUP] = "use_vhangup", | |
95 | + [CCS_MAC_SET_TIME] = "set_time", | |
96 | + [CCS_MAC_SET_PRIORITY] = "set_priority", | |
97 | + [CCS_MAC_SET_HOSTNAME] = "set_hostname", | |
98 | + [CCS_MAC_USE_KERNEL_MODULE] = "use_kernel_module", | |
99 | + [CCS_MAC_USE_NEW_KERNEL] = "use_new_kernel", | |
100 | +#endif | |
101 | +#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
102 | + [CCS_MAC_AUTO_DOMAIN_TRANSITION] = "auto_domain_transition", | |
103 | +#endif | |
104 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
105 | + [CCS_MAC_MANUAL_DOMAIN_TRANSITION] = "manual_domain_transition", | |
106 | +#endif | |
107 | +}; | |
108 | + | |
109 | +/* String table for conditions. */ | |
110 | +static const char *const ccs_condition_keyword[CCS_MAX_CONDITION_KEYWORD] = { | |
111 | + [CCS_SELF_UID] = "uid", | |
112 | + [CCS_SELF_EUID] = "euid", | |
113 | + [CCS_SELF_SUID] = "suid", | |
114 | + [CCS_SELF_FSUID] = "fsuid", | |
115 | + [CCS_SELF_GID] = "gid", | |
116 | + [CCS_SELF_EGID] = "egid", | |
117 | + [CCS_SELF_SGID] = "sgid", | |
118 | + [CCS_SELF_FSGID] = "fsgid", | |
119 | + [CCS_SELF_PID] = "pid", | |
120 | + [CCS_SELF_PPID] = "ppid", | |
121 | + [CCS_TASK_TYPE] = "type", | |
122 | + [CCS_SELF_DOMAIN] = "domain", | |
123 | + [CCS_SELF_EXE] = "exe", | |
124 | + [CCS_EXEC_ARGC] = "argc", | |
125 | + [CCS_EXEC_ENVC] = "envc", | |
126 | + [CCS_OBJ_IS_SOCKET] = "socket", | |
127 | + [CCS_OBJ_IS_SYMLINK] = "symlink", | |
128 | + [CCS_OBJ_IS_FILE] = "file", | |
129 | + [CCS_OBJ_IS_BLOCK_DEV] = "block", | |
130 | + [CCS_OBJ_IS_DIRECTORY] = "directory", | |
131 | + [CCS_OBJ_IS_CHAR_DEV] = "char", | |
132 | + [CCS_OBJ_IS_FIFO] = "fifo", | |
133 | + [CCS_MODE_SETUID] = "setuid", | |
134 | + [CCS_MODE_SETGID] = "setgid", | |
135 | + [CCS_MODE_STICKY] = "sticky", | |
136 | + [CCS_MODE_OWNER_READ] = "owner_read", | |
137 | + [CCS_MODE_OWNER_WRITE] = "owner_write", | |
138 | + [CCS_MODE_OWNER_EXECUTE] = "owner_execute", | |
139 | + [CCS_MODE_GROUP_READ] = "group_read", | |
140 | + [CCS_MODE_GROUP_WRITE] = "group_write", | |
141 | + [CCS_MODE_GROUP_EXECUTE] = "group_execute", | |
142 | + [CCS_MODE_OTHERS_READ] = "others_read", | |
143 | + [CCS_MODE_OTHERS_WRITE] = "others_write", | |
144 | + [CCS_MODE_OTHERS_EXECUTE] = "others_execute", | |
145 | + [CCS_TASK_EXECUTE_HANDLER] = "execute_handler", | |
146 | + [CCS_HANDLER_PATH] = "handler", | |
147 | + [CCS_TRANSIT_DOMAIN] = "transition", | |
148 | +}; | |
149 | + | |
150 | +/* String table for file attributes. */ | |
151 | +static const char *const ccs_path_attribute[CCS_MAX_PATH_ATTRIBUTE] = { | |
152 | + [CCS_PATH_ATTRIBUTE_UID] = "uid", | |
153 | + [CCS_PATH_ATTRIBUTE_GID] = "gid", | |
154 | + [CCS_PATH_ATTRIBUTE_INO] = "ino", | |
155 | + [CCS_PATH_ATTRIBUTE_MAJOR] = "major", | |
156 | + [CCS_PATH_ATTRIBUTE_MINOR] = "minor", | |
157 | + [CCS_PATH_ATTRIBUTE_PERM] = "perm", | |
158 | + [CCS_PATH_ATTRIBUTE_TYPE] = "type", | |
159 | + [CCS_PATH_ATTRIBUTE_DEV_MAJOR] = "dev_major", | |
160 | + [CCS_PATH_ATTRIBUTE_DEV_MINOR] = "dev_minor", | |
161 | + [CCS_PATH_ATTRIBUTE_FSMAGIC] = "fsmagic", | |
162 | +}; | |
163 | + | |
164 | +/* String table for grouping keywords. */ | |
165 | +static const char * const ccs_group_name[CCS_MAX_GROUP] = { | |
166 | + [CCS_STRING_GROUP] = "string_group", | |
167 | + [CCS_NUMBER_GROUP] = "number_group", | |
168 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
169 | + [CCS_IP_GROUP] = "ip_group", | |
170 | +#endif | |
171 | +}; | |
172 | + | |
173 | +/* String table for stat info. */ | |
174 | +static const char * const ccs_memory_headers[CCS_MAX_MEMORY_STAT] = { | |
175 | + [CCS_MEMORY_POLICY] = "policy", | |
176 | + [CCS_MEMORY_AUDIT] = "audit", | |
177 | + [CCS_MEMORY_QUERY] = "query", | |
178 | +}; | |
179 | + | |
180 | +/***** SECTION2: Structure definition *****/ | |
181 | + | |
182 | +struct iattr; | |
183 | + | |
184 | +/* Structure for query. */ | |
185 | +struct ccs_query { | |
186 | + struct list_head list; | |
187 | + struct ccs_acl_info *acl; | |
188 | + char *query; | |
189 | + size_t query_len; | |
190 | + unsigned int serial; | |
191 | + u8 timer; | |
192 | + u8 answer; | |
193 | + u8 retry; | |
194 | +}; | |
195 | + | |
196 | +/* Structure for audit log. */ | |
197 | +struct ccs_log { | |
198 | + struct list_head list; | |
199 | + char *log; | |
200 | + int size; | |
201 | + enum ccs_matching_result result; | |
202 | +}; | |
203 | + | |
204 | +/* Structure for holding single condition component. */ | |
205 | +struct ccs_cond_tmp { | |
206 | + u8 left; | |
207 | + u8 right; | |
208 | + bool is_not; | |
209 | + u8 radix; | |
210 | + struct ccs_group *group; | |
211 | + const struct ccs_path_info *path; | |
212 | + struct in6_addr ipv6[2]; | |
213 | + unsigned long value[2]; | |
214 | + unsigned long argv; | |
215 | + const struct ccs_path_info *envp; | |
216 | +}; | |
217 | + | |
218 | +/***** SECTION3: Prototype definition section *****/ | |
219 | + | |
220 | +static bool ccs_correct_domain(const unsigned char *domainname); | |
221 | +static bool ccs_correct_word(const char *string); | |
222 | +static bool ccs_flush(struct ccs_io_buffer *head); | |
223 | +static bool ccs_print_condition(struct ccs_io_buffer *head, | |
224 | + const struct ccs_condition *cond); | |
225 | +static bool ccs_memory_ok(const void *ptr, const unsigned int size); | |
226 | +static bool ccs_read_acl(struct ccs_io_buffer *head, | |
227 | + const struct ccs_acl_info *acl); | |
228 | +static bool ccs_read_group(struct ccs_io_buffer *head); | |
229 | +static bool ccs_select_acl(struct ccs_io_buffer *head, const char *data); | |
230 | +static bool ccs_set_lf(struct ccs_io_buffer *head); | |
231 | +static bool ccs_str_starts(char **src, const char *find); | |
232 | +static char *ccs_init_log(struct ccs_request_info *r); | |
233 | +static char *ccs_print_bprm(struct linux_binprm *bprm, | |
234 | + struct ccs_page_dump *dump); | |
235 | +static char *ccs_print_trailer(struct ccs_request_info *r); | |
236 | +static char *ccs_read_token(struct ccs_io_buffer *head); | |
237 | +static const char *ccs_yesno(const unsigned int value); | |
238 | +static const struct ccs_path_info *ccs_get_dqword(char *start); | |
239 | +static const struct ccs_path_info *ccs_get_name(const char *name); | |
240 | +static int __init ccs_init_module(void); | |
241 | +static int ccs_open(struct inode *inode, struct file *file); | |
242 | +static int ccs_parse_policy(struct ccs_io_buffer *head, char *line); | |
243 | +static int ccs_release(struct inode *inode, struct file *file); | |
244 | +static int ccs_supervisor(struct ccs_request_info *r); | |
245 | +static int ccs_update_group(struct ccs_io_buffer *head, | |
246 | + const enum ccs_group_id type); | |
247 | +static int ccs_write_answer(struct ccs_io_buffer *head); | |
248 | +static int ccs_write_audit_quota(char *data); | |
249 | +static int ccs_write_memory_quota(char *data); | |
250 | +static int ccs_write_pid(struct ccs_io_buffer *head); | |
251 | +static int ccs_write_policy(struct ccs_io_buffer *head); | |
252 | +static ssize_t ccs_read(struct file *file, char __user *buf, size_t count, | |
253 | + loff_t *ppos); | |
254 | +static ssize_t ccs_read_self(struct file *file, char __user *buf, size_t count, | |
255 | + loff_t *ppos); | |
256 | +static ssize_t ccs_write(struct file *file, const char __user *buf, | |
257 | + size_t count, loff_t *ppos); | |
258 | +static struct ccs_condition *ccs_get_condition(struct ccs_io_buffer *head); | |
259 | +static struct ccs_domain_info *ccs_find_domain(const char *domainname); | |
260 | +static struct ccs_acl_info *ccs_find_acl_by_qid(unsigned int serial); | |
261 | +static struct ccs_group *ccs_get_group(struct ccs_io_buffer *head, | |
262 | + const enum ccs_group_id idx); | |
263 | +static enum ccs_value_type ccs_parse_ulong(unsigned long *result, char **str); | |
264 | +static unsigned int ccs_poll(struct file *file, poll_table *wait); | |
265 | +static void __init ccs_create_entry(const char *name, const umode_t mode, | |
266 | + struct proc_dir_entry *parent, | |
267 | + const u8 key); | |
268 | +static void __init ccs_load_builtin_policy(void); | |
269 | +static void __init ccs_policy_io_init(void); | |
270 | +static void __init ccs_proc_init(void); | |
271 | +static void ccs_check_profile(void); | |
272 | +static void ccs_convert_time(time_t time, struct ccs_time *stamp); | |
273 | +static void ccs_io_printf(struct ccs_io_buffer *head, const char *fmt, ...) | |
274 | + __printf(2, 3); | |
275 | +static void ccs_normalize_line(unsigned char *buffer); | |
276 | +static void ccs_read_log(struct ccs_io_buffer *head); | |
277 | +static void ccs_read_pid(struct ccs_io_buffer *head); | |
278 | +static void ccs_read_policy(struct ccs_io_buffer *head); | |
279 | +static void ccs_read_query(struct ccs_io_buffer *head); | |
280 | +static void *ccs_commit_ok(void *data, const unsigned int size); | |
281 | +static bool ccs_read_quota(struct ccs_io_buffer *head); | |
282 | +static void ccs_read_stat(struct ccs_io_buffer *head); | |
283 | +static void ccs_read_version(struct ccs_io_buffer *head); | |
284 | +static void ccs_set_space(struct ccs_io_buffer *head); | |
285 | +static void ccs_set_string(struct ccs_io_buffer *head, const char *string); | |
286 | +static void ccs_update_stat(const u8 index); | |
287 | +static void ccs_write_log(struct ccs_request_info *r); | |
288 | + | |
289 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
290 | +static enum ccs_ipaddr_type ccs_parse_ipaddr(char *address, | |
291 | + struct in6_addr ipv6[2]); | |
292 | +static void ccs_print_ipv4(struct ccs_io_buffer *head, const u32 *ip); | |
293 | +static void ccs_print_ipv6(struct ccs_io_buffer *head, | |
294 | + const struct in6_addr *ip); | |
295 | +static void ccs_print_ip(struct ccs_io_buffer *head, | |
296 | + struct ccs_ip_group *member); | |
297 | +#endif | |
298 | + | |
299 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
300 | +static ssize_t ccs_write_self(struct file *file, const char __user *buf, | |
301 | + size_t count, loff_t *ppos); | |
302 | +#endif | |
303 | + | |
304 | +/***** SECTION4: Standalone functions section *****/ | |
305 | + | |
306 | +/** | |
307 | + * ccs_convert_time - Convert time_t to YYYY/MM/DD hh/mm/ss. | |
308 | + * | |
309 | + * @time: Seconds since 1970/01/01 00:00:00. | |
310 | + * @stamp: Pointer to "struct ccs_time". | |
311 | + * | |
312 | + * Returns nothing. | |
313 | + * | |
314 | + * This function does not handle Y2038 problem. | |
315 | + */ | |
316 | +static void ccs_convert_time(time_t time, struct ccs_time *stamp) | |
317 | +{ | |
318 | + static const u16 ccs_eom[2][12] = { | |
319 | + { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, | |
320 | + { 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } | |
321 | + }; | |
322 | + u16 y; | |
323 | + u8 m; | |
324 | + bool r; | |
325 | + stamp->sec = time % 60; | |
326 | + time /= 60; | |
327 | + stamp->min = time % 60; | |
328 | + time /= 60; | |
329 | + stamp->hour = time % 24; | |
330 | + time /= 24; | |
331 | + for (y = 1970; ; y++) { | |
332 | + const unsigned short days = (y & 3) ? 365 : 366; | |
333 | + if (time < days) | |
334 | + break; | |
335 | + time -= days; | |
336 | + } | |
337 | + r = (y & 3) == 0; | |
338 | + for (m = 0; m < 11 && time >= ccs_eom[r][m]; m++); | |
339 | + if (m) | |
340 | + time -= ccs_eom[r][m - 1]; | |
341 | + stamp->year = y; | |
342 | + stamp->month = ++m; | |
343 | + stamp->day = ++time; | |
344 | +} | |
345 | + | |
346 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
347 | + | |
348 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) | |
349 | + | |
350 | +/* | |
351 | + * Routines for printing IPv4 or IPv6 address. | |
352 | + * These are copied from include/linux/kernel.h include/net/ipv6.h | |
353 | + * include/net/addrconf.h lib/hexdump.c lib/vsprintf.c and simplified. | |
354 | + */ | |
355 | +static inline int ipv6_addr_is_isatap(const struct in6_addr *addr) | |
356 | +{ | |
357 | + return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE); | |
358 | +} | |
359 | + | |
360 | +static char *ip4_string(char *p, const u8 *addr) | |
361 | +{ | |
362 | + /* | |
363 | + * Since this function is called outside vsnprintf(), I can use | |
364 | + * sprintf() here. | |
365 | + */ | |
366 | + return p + | |
367 | + sprintf(p, "%u.%u.%u.%u", addr[0], addr[1], addr[2], addr[3]); | |
368 | +} | |
369 | + | |
370 | +static char *ip6_compressed_string(char *p, const char *addr) | |
371 | +{ | |
372 | + int i, j, range; | |
373 | + unsigned char zerolength[8]; | |
374 | + int longest = 1; | |
375 | + int colonpos = -1; | |
376 | + u16 word; | |
377 | + u8 hi, lo; | |
378 | + bool needcolon = false; | |
379 | + bool useIPv4; | |
380 | + struct in6_addr in6; | |
381 | + | |
382 | + memcpy(&in6, addr, sizeof(struct in6_addr)); | |
383 | + | |
384 | + useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6); | |
385 | + | |
386 | + memset(zerolength, 0, sizeof(zerolength)); | |
387 | + | |
388 | + if (useIPv4) | |
389 | + range = 6; | |
390 | + else | |
391 | + range = 8; | |
392 | + | |
393 | + /* find position of longest 0 run */ | |
394 | + for (i = 0; i < range; i++) { | |
395 | + for (j = i; j < range; j++) { | |
396 | + if (in6.s6_addr16[j] != 0) | |
397 | + break; | |
398 | + zerolength[i]++; | |
399 | + } | |
400 | + } | |
401 | + for (i = 0; i < range; i++) { | |
402 | + if (zerolength[i] > longest) { | |
403 | + longest = zerolength[i]; | |
404 | + colonpos = i; | |
405 | + } | |
406 | + } | |
407 | + if (longest == 1) /* don't compress a single 0 */ | |
408 | + colonpos = -1; | |
409 | + | |
410 | + /* emit address */ | |
411 | + for (i = 0; i < range; i++) { | |
412 | + if (i == colonpos) { | |
413 | + if (needcolon || i == 0) | |
414 | + *p++ = ':'; | |
415 | + *p++ = ':'; | |
416 | + needcolon = false; | |
417 | + i += longest - 1; | |
418 | + continue; | |
419 | + } | |
420 | + if (needcolon) { | |
421 | + *p++ = ':'; | |
422 | + needcolon = false; | |
423 | + } | |
424 | + /* hex u16 without leading 0s */ | |
425 | + word = ntohs(in6.s6_addr16[i]); | |
426 | + hi = word >> 8; | |
427 | + lo = word & 0xff; | |
428 | + if (hi) { | |
429 | + if (hi > 0x0f) | |
430 | + p = pack_hex_byte(p, hi); | |
431 | + else | |
432 | + *p++ = hex_asc_lo(hi); | |
433 | + p = pack_hex_byte(p, lo); | |
434 | + } else if (lo > 0x0f) | |
435 | + p = pack_hex_byte(p, lo); | |
436 | + else | |
437 | + *p++ = hex_asc_lo(lo); | |
438 | + needcolon = true; | |
439 | + } | |
440 | + | |
441 | + if (useIPv4) { | |
442 | + if (needcolon) | |
443 | + *p++ = ':'; | |
444 | + p = ip4_string(p, &in6.s6_addr[12]); | |
445 | + } | |
446 | + *p = '\0'; | |
447 | + | |
448 | + return p; | |
449 | +} | |
450 | +#endif | |
451 | + | |
452 | +/** | |
453 | + * ccs_print_ipv4 - Print an IPv4 address. | |
454 | + * | |
455 | + * @head: Pointer to "struct ccs_io_buffer". | |
456 | + * @ip: Pointer to "u32" in network byte order. | |
457 | + * | |
458 | + * Returns nothing. | |
459 | + */ | |
460 | +static void ccs_print_ipv4(struct ccs_io_buffer *head, const u32 *ip) | |
461 | +{ | |
462 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | |
463 | + ccs_io_printf(head, "%pI4", ip); | |
464 | +#else | |
465 | + char addr[sizeof("255.255.255.255")]; | |
466 | + ip4_string(addr, (const u8 *) ip); | |
467 | + ccs_io_printf(head, "%s", addr); | |
468 | +#endif | |
469 | +} | |
470 | + | |
471 | +/** | |
472 | + * ccs_print_ipv6 - Print an IPv6 address. | |
473 | + * | |
474 | + * @head: Pointer to "struct ccs_io_buffer". | |
475 | + * @ip: Pointer to "struct in6_addr". | |
476 | + * | |
477 | + * Returns nothing. | |
478 | + */ | |
479 | +static void ccs_print_ipv6(struct ccs_io_buffer *head, | |
480 | + const struct in6_addr *ip) | |
481 | +{ | |
482 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | |
483 | + ccs_io_printf(head, "%pI6c", ip); | |
484 | +#else | |
485 | + char addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:" | |
486 | + "255.255.255.255")]; | |
487 | + ip6_compressed_string(addr, (const u8 *) ip); | |
488 | + ccs_io_printf(head, "%s", addr); | |
489 | +#endif | |
490 | +} | |
491 | + | |
492 | +/** | |
493 | + * ccs_print_ip - Print an IP address. | |
494 | + * | |
495 | + * @head: Pointer to "struct ccs_io_buffer". | |
496 | + * @member: Pointer to "struct ccs_ip_group". | |
497 | + * | |
498 | + * Returns nothing. | |
499 | + */ | |
500 | +static void ccs_print_ip(struct ccs_io_buffer *head, | |
501 | + struct ccs_ip_group *member) | |
502 | +{ | |
503 | + u8 i; | |
504 | + for (i = 0; i < 2; i++) { | |
505 | + if (member->is_ipv6) | |
506 | + ccs_print_ipv6(head, &member->ip[i]); | |
507 | + else | |
508 | + ccs_print_ipv4(head, (const u32 *) &member->ip[i]); | |
509 | + if (i) | |
510 | + break; | |
511 | + if (!memcmp(&member->ip[0], &member->ip[1], 16)) | |
512 | + break; | |
513 | + ccs_set_string(head, "-"); | |
514 | + } | |
515 | +} | |
516 | + | |
517 | +#endif | |
518 | + | |
519 | +/** | |
520 | + * ccs_get_sarg - Get attribute name of CCS_SARG argument. | |
521 | + * | |
522 | + * @type: One of values in "enum ccs_mac_index". | |
523 | + * @index: Index to return. | |
524 | + * | |
525 | + * Returns attribute name. | |
526 | + */ | |
527 | +static const char *ccs_get_sarg(const enum ccs_mac_index type, const u8 index) | |
528 | +{ | |
529 | + switch (type) { | |
530 | + case CCS_MAC_LINK: | |
531 | + case CCS_MAC_RENAME: | |
532 | + if (index == 0) | |
533 | + return "old_path"; | |
534 | + if (index == 1) | |
535 | + return "new_path"; | |
536 | + break; | |
537 | + case CCS_MAC_MOUNT: | |
538 | + if (index == 0) | |
539 | + return "source"; | |
540 | + if (index == 1) | |
541 | + return "target"; | |
542 | + if (index == 2) | |
543 | + return "fstype"; | |
544 | + if (index == 3) | |
545 | + return "data"; | |
546 | + break; | |
547 | + case CCS_MAC_PIVOT_ROOT: | |
548 | + if (index == 0) | |
549 | + return "new_root"; | |
550 | + if (index == 1) | |
551 | + return "put_old"; | |
552 | + break; | |
553 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
554 | + case CCS_MAC_ENVIRON: | |
555 | + if (index == 2) | |
556 | + return "name"; | |
557 | + if (index == 3) | |
558 | + return "value"; | |
559 | + /* fall through */ | |
560 | +#endif | |
561 | + case CCS_MAC_EXECUTE: | |
562 | + if (index == 0) | |
563 | + return "path"; | |
564 | + if (index == 1) | |
565 | + return "exec"; | |
566 | + break; | |
567 | + case CCS_MAC_SYMLINK: | |
568 | + if (index == 0) | |
569 | + return "path"; | |
570 | + if (index == 1) | |
571 | + return "target"; | |
572 | + break; | |
573 | + case CCS_MAC_READ: | |
574 | + case CCS_MAC_WRITE: | |
575 | + case CCS_MAC_APPEND: | |
576 | + case CCS_MAC_UNLINK: | |
577 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
578 | + case CCS_MAC_GETATTR: | |
579 | +#endif | |
580 | + case CCS_MAC_RMDIR: | |
581 | + case CCS_MAC_TRUNCATE: | |
582 | + case CCS_MAC_CHROOT: | |
583 | + case CCS_MAC_CHMOD: | |
584 | + case CCS_MAC_CHOWN: | |
585 | + case CCS_MAC_CHGRP: | |
586 | + case CCS_MAC_IOCTL: | |
587 | + case CCS_MAC_MKDIR: | |
588 | + case CCS_MAC_CREATE: | |
589 | + case CCS_MAC_MKFIFO: | |
590 | + case CCS_MAC_MKSOCK: | |
591 | + case CCS_MAC_MKBLOCK: | |
592 | + case CCS_MAC_MKCHAR: | |
593 | + case CCS_MAC_UMOUNT: | |
594 | + if (index == 0) | |
595 | + return "path"; | |
596 | + break; | |
597 | + case CCS_MAC_MODIFY_POLICY: | |
598 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
599 | + case CCS_MAC_USE_NETLINK_SOCKET: | |
600 | + case CCS_MAC_USE_PACKET_SOCKET: | |
601 | + case CCS_MAC_USE_REBOOT: | |
602 | + case CCS_MAC_USE_VHANGUP: | |
603 | + case CCS_MAC_SET_TIME: | |
604 | + case CCS_MAC_SET_PRIORITY: | |
605 | + case CCS_MAC_SET_HOSTNAME: | |
606 | + case CCS_MAC_USE_KERNEL_MODULE: | |
607 | + case CCS_MAC_USE_NEW_KERNEL: | |
608 | +#endif | |
609 | + break; | |
610 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
611 | + case CCS_MAC_INET_STREAM_BIND: | |
612 | + case CCS_MAC_INET_STREAM_LISTEN: | |
613 | + case CCS_MAC_INET_STREAM_CONNECT: | |
614 | + case CCS_MAC_INET_STREAM_ACCEPT: | |
615 | + case CCS_MAC_INET_DGRAM_BIND: | |
616 | + case CCS_MAC_INET_DGRAM_SEND: | |
617 | + case CCS_MAC_INET_DGRAM_RECV: | |
618 | + case CCS_MAC_INET_RAW_BIND: | |
619 | + case CCS_MAC_INET_RAW_SEND: | |
620 | + case CCS_MAC_INET_RAW_RECV: | |
621 | + if (index == 0) | |
622 | + return "ip"; | |
623 | + break; | |
624 | + case CCS_MAC_UNIX_STREAM_BIND: | |
625 | + case CCS_MAC_UNIX_STREAM_LISTEN: | |
626 | + case CCS_MAC_UNIX_STREAM_CONNECT: | |
627 | + case CCS_MAC_UNIX_STREAM_ACCEPT: | |
628 | + case CCS_MAC_UNIX_DGRAM_BIND: | |
629 | + case CCS_MAC_UNIX_DGRAM_SEND: | |
630 | + case CCS_MAC_UNIX_DGRAM_RECV: | |
631 | + case CCS_MAC_UNIX_SEQPACKET_BIND: | |
632 | + case CCS_MAC_UNIX_SEQPACKET_LISTEN: | |
633 | + case CCS_MAC_UNIX_SEQPACKET_CONNECT: | |
634 | + case CCS_MAC_UNIX_SEQPACKET_ACCEPT: | |
635 | + if (index == 0) | |
636 | + return "addr"; | |
637 | + break; | |
638 | +#endif | |
639 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
640 | + case CCS_MAC_PTRACE: | |
641 | + if (index == 0) | |
642 | + return "domain"; | |
643 | + break; | |
644 | +#endif | |
645 | + default: | |
646 | + break; | |
647 | + } | |
648 | + return "unknown"; /* This should not happen. */ | |
649 | +} | |
650 | + | |
651 | +/** | |
652 | + * ccs_get_narg - Get attribute name of CCS_NARG argument. | |
653 | + * | |
654 | + * @type: One of values in "enum ccs_mac_index". | |
655 | + * @index: Index to return. | |
656 | + * | |
657 | + * Returns attribute name. | |
658 | + */ | |
659 | +static const char *ccs_get_narg(const enum ccs_mac_index type, const u8 index) | |
660 | +{ | |
661 | + switch (type) { | |
662 | + case CCS_MAC_MOUNT: | |
663 | + case CCS_MAC_UMOUNT: | |
664 | + if (index == 0) | |
665 | + return "flags"; | |
666 | + break; | |
667 | + case CCS_MAC_CHMOD: | |
668 | + if (index == 0) | |
669 | + return "perm"; | |
670 | + break; | |
671 | + case CCS_MAC_CHOWN: | |
672 | + if (index == 0) | |
673 | + return "uid"; | |
674 | + break; | |
675 | + case CCS_MAC_CHGRP: | |
676 | + if (index == 0) | |
677 | + return "gid"; | |
678 | + break; | |
679 | + case CCS_MAC_IOCTL: | |
680 | + if (index == 0) | |
681 | + return "cmd"; | |
682 | + break; | |
683 | + case CCS_MAC_MKDIR: | |
684 | + case CCS_MAC_CREATE: | |
685 | + case CCS_MAC_MKFIFO: | |
686 | + case CCS_MAC_MKSOCK: | |
687 | + if (index == 0) | |
688 | + return "perm"; | |
689 | + break; | |
690 | + case CCS_MAC_MKBLOCK: | |
691 | + case CCS_MAC_MKCHAR: | |
692 | + if (index == 0) | |
693 | + return "perm"; | |
694 | + if (index == 1) | |
695 | + return "dev_major"; | |
696 | + if (index == 2) | |
697 | + return "dev_minor"; | |
698 | + break; | |
699 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
700 | + case CCS_MAC_INET_STREAM_BIND: | |
701 | + case CCS_MAC_INET_STREAM_LISTEN: | |
702 | + case CCS_MAC_INET_STREAM_CONNECT: | |
703 | + case CCS_MAC_INET_STREAM_ACCEPT: | |
704 | + case CCS_MAC_INET_DGRAM_BIND: | |
705 | + case CCS_MAC_INET_DGRAM_SEND: | |
706 | + case CCS_MAC_INET_DGRAM_RECV: | |
707 | + if (index == 0) | |
708 | + return "port"; | |
709 | + break; | |
710 | + case CCS_MAC_INET_RAW_BIND: | |
711 | + case CCS_MAC_INET_RAW_SEND: | |
712 | + case CCS_MAC_INET_RAW_RECV: | |
713 | + if (index == 0) | |
714 | + return "proto"; | |
715 | + break; | |
716 | +#endif | |
717 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
718 | + case CCS_MAC_PTRACE: | |
719 | + if (index == 0) | |
720 | + return "cmd"; | |
721 | + break; | |
722 | +#endif | |
723 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
724 | + case CCS_MAC_SIGNAL: | |
725 | + if (index == 0) | |
726 | + return "cmd"; | |
727 | + break; | |
728 | +#endif | |
729 | + default: | |
730 | + break; | |
731 | + } | |
732 | + return "unknown"; /* This should not happen. */ | |
733 | +} | |
734 | + | |
735 | +/***** SECTION5: Variables definition section *****/ | |
736 | + | |
737 | +/* Lock for protecting policy. */ | |
738 | +DEFINE_MUTEX(ccs_policy_lock); | |
739 | + | |
740 | +/* Has /sbin/init started? */ | |
741 | +bool ccs_policy_loaded; | |
742 | + | |
743 | +/* List of "struct ccs_group". */ | |
744 | +struct list_head ccs_group_list[CCS_MAX_GROUP]; | |
745 | +/* Policy version. Currently only 20120401 is defined. */ | |
746 | +static unsigned int ccs_policy_version = 20120401; | |
747 | + | |
748 | +/* List of "struct ccs_condition". */ | |
749 | +LIST_HEAD(ccs_condition_list); | |
750 | + | |
751 | +/* Wait queue for kernel -> userspace notification. */ | |
752 | +static DECLARE_WAIT_QUEUE_HEAD(ccs_query_wait); | |
753 | +/* Wait queue for userspace -> kernel notification. */ | |
754 | +static DECLARE_WAIT_QUEUE_HEAD(ccs_answer_wait); | |
755 | + | |
756 | +/* The list for "struct ccs_query". */ | |
757 | +static LIST_HEAD(ccs_query_list); | |
758 | + | |
759 | +/* Lock for manipulating ccs_query_list. */ | |
760 | +static DEFINE_SPINLOCK(ccs_query_list_lock); | |
761 | + | |
762 | +/* Number of "struct file" referring /proc/ccs/query interface. */ | |
763 | +static atomic_t ccs_query_observers = ATOMIC_INIT(0); | |
764 | + | |
765 | +/* Wait queue for /proc/ccs/audit. */ | |
766 | +static DECLARE_WAIT_QUEUE_HEAD(ccs_log_wait); | |
767 | + | |
768 | +/* The list for "struct ccs_log". */ | |
769 | +static LIST_HEAD(ccs_log); | |
770 | + | |
771 | +/* Lock for "struct list_head ccs_log". */ | |
772 | +static DEFINE_SPINLOCK(ccs_log_lock); | |
773 | + | |
774 | +/* Length of "stuct list_head ccs_log". */ | |
775 | +static unsigned int ccs_log_count[CCS_MAX_MATCHING]; | |
776 | +/* Quota for audit logs. */ | |
777 | +static unsigned int ccs_log_quota[CCS_MAX_LOG_QUOTA][CCS_MAX_MATCHING]; | |
778 | + | |
779 | +/* Memoy currently used by policy/audit log/query. */ | |
780 | +unsigned int ccs_memory_used[CCS_MAX_MEMORY_STAT]; | |
781 | + | |
782 | +/* Memory quota for "policy"/"audit log"/"query". */ | |
783 | +static unsigned int ccs_memory_quota[CCS_MAX_MEMORY_STAT]; | |
784 | + | |
785 | +/* The list for "struct ccs_name". */ | |
786 | +struct list_head ccs_name_list[CCS_MAX_HASH]; | |
787 | + | |
788 | +/* Timestamp counter for last updated. */ | |
789 | +static unsigned int ccs_stat_updated[CCS_MAX_POLICY_STAT]; | |
790 | + | |
791 | +/* Counter for number of updates. */ | |
792 | +static unsigned int ccs_stat_modified[CCS_MAX_POLICY_STAT]; | |
793 | + | |
794 | +/* Operations for /proc/ccs/self_domain interface. */ | |
795 | +static const struct file_operations ccs_self_operations = { | |
796 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
797 | + .write = ccs_write_self, | |
798 | +#endif | |
799 | + .read = ccs_read_self, | |
800 | +}; | |
801 | + | |
802 | +/* Operations for /proc/ccs/ interface. */ | |
803 | +static const struct file_operations ccs_operations = { | |
804 | + .open = ccs_open, | |
805 | + .release = ccs_release, | |
806 | + .poll = ccs_poll, | |
807 | + .read = ccs_read, | |
808 | + .write = ccs_write, | |
809 | +}; | |
810 | + | |
811 | +/***** SECTION6: Dependent functions section *****/ | |
812 | + | |
813 | +/** | |
814 | + * list_for_each_cookie - iterate over a list with cookie. | |
815 | + * | |
816 | + * @pos: Pointer to "struct list_head". | |
817 | + * @head: Pointer to "struct list_head". | |
818 | + */ | |
819 | +#define list_for_each_cookie(pos, head) \ | |
820 | + for (pos = pos ? pos : srcu_dereference((head)->next, &ccs_ss); \ | |
821 | + pos != (head); pos = srcu_dereference(pos->next, &ccs_ss)) | |
822 | + | |
823 | +/** | |
824 | + * ccs_warn_oom - Print out of memory warning message. | |
825 | + * | |
826 | + * @function: Function's name. | |
827 | + * | |
828 | + * Returns nothing. | |
829 | + */ | |
830 | +void ccs_warn_oom(const char *function) | |
831 | +{ | |
832 | + /* Reduce error messages. */ | |
833 | + static pid_t ccs_last_pid; | |
834 | + const pid_t pid = current->pid; | |
835 | + if (ccs_last_pid != pid) { | |
836 | + printk(KERN_WARNING "ERROR: Out of memory at %s.\n", | |
837 | + function); | |
838 | + ccs_last_pid = pid; | |
839 | + } | |
840 | + if (!ccs_policy_loaded) | |
841 | + panic("MAC Initialization failed.\n"); | |
842 | +} | |
843 | + | |
844 | +/** | |
845 | + * ccs_memory_ok - Check memory quota. | |
846 | + * | |
847 | + * @ptr: Pointer to allocated memory. Maybe NULL. | |
848 | + * @size: Size in byte. Not used if @ptr is NULL. | |
849 | + * | |
850 | + * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. | |
851 | + * | |
852 | + * Caller holds ccs_policy_lock mutex. | |
853 | + */ | |
854 | +static bool ccs_memory_ok(const void *ptr, const unsigned int size) | |
855 | +{ | |
856 | + if (ptr) { | |
857 | + const size_t s = ccs_round2(size); | |
858 | + ccs_memory_used[CCS_MEMORY_POLICY] += s; | |
859 | + if (!ccs_memory_quota[CCS_MEMORY_POLICY] || | |
860 | + ccs_memory_used[CCS_MEMORY_POLICY] <= | |
861 | + ccs_memory_quota[CCS_MEMORY_POLICY]) | |
862 | + return true; | |
863 | + ccs_memory_used[CCS_MEMORY_POLICY] -= s; | |
864 | + } | |
865 | + ccs_warn_oom(__func__); | |
866 | + return false; | |
867 | +} | |
868 | + | |
869 | +/** | |
870 | + * ccs_get_name - Allocate memory for string data. | |
871 | + * | |
872 | + * @name: The string to store into the permernent memory. Maybe NULL. | |
873 | + * | |
874 | + * Returns pointer to "struct ccs_path_info" on success, NULL otherwise. | |
875 | + */ | |
876 | +static const struct ccs_path_info *ccs_get_name(const char *name) | |
877 | +{ | |
878 | + struct ccs_name *ptr; | |
879 | + unsigned int hash; | |
880 | + int len; | |
881 | + int allocated_len; | |
882 | + struct list_head *head; | |
883 | + | |
884 | + if (!name) | |
885 | + return NULL; | |
886 | + len = strlen(name) + 1; | |
887 | + hash = full_name_hash((const unsigned char *) name, len - 1); | |
888 | + head = &ccs_name_list[hash_long(hash, CCS_HASH_BITS)]; | |
889 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | |
890 | + return NULL; | |
891 | + list_for_each_entry(ptr, head, head.list) { | |
892 | + if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) || | |
893 | + atomic_read(&ptr->head.users) == CCS_GC_IN_PROGRESS) | |
894 | + continue; | |
895 | + atomic_inc(&ptr->head.users); | |
896 | + goto out; | |
897 | + } | |
898 | + allocated_len = sizeof(*ptr) + len; | |
899 | + ptr = kzalloc(allocated_len, GFP_NOFS); | |
900 | + if (ccs_memory_ok(ptr, allocated_len)) { | |
901 | + ptr->entry.name = ((char *) ptr) + sizeof(*ptr); | |
902 | + memmove((char *) ptr->entry.name, name, len); | |
903 | + atomic_set(&ptr->head.users, 1); | |
904 | + ccs_fill_path_info(&ptr->entry); | |
905 | + ptr->size = allocated_len; | |
906 | + list_add_tail(&ptr->head.list, head); | |
907 | + } else { | |
908 | + kfree(ptr); | |
909 | + ptr = NULL; | |
910 | + } | |
911 | +out: | |
912 | + mutex_unlock(&ccs_policy_lock); | |
913 | + return ptr ? &ptr->entry : NULL; | |
914 | +} | |
915 | + | |
916 | +/** | |
917 | + * ccs_read_token - Read a word from a line. | |
918 | + * | |
919 | + * @head: Pointer to "struct ccs_io_buffer". | |
920 | + * | |
921 | + * Returns a word on success, "" otherwise. | |
922 | + * | |
923 | + * To allow the caller to skip NULL check, this function returns "" rather than | |
924 | + * NULL if there is no more words to read. | |
925 | + */ | |
926 | +static char *ccs_read_token(struct ccs_io_buffer *head) | |
927 | +{ | |
928 | + char *pos = head->w.data; | |
929 | + char *del = strchr(pos, ' '); | |
930 | + if (del) | |
931 | + *del++ = '\0'; | |
932 | + else | |
933 | + del = pos + strlen(pos); | |
934 | + head->w.data = del; | |
935 | + return pos; | |
936 | +} | |
937 | + | |
938 | +/** | |
939 | + * ccs_correct_word - Check whether the given string follows the naming rules. | |
940 | + * | |
941 | + * @string: The string to check. | |
942 | + * | |
943 | + * Returns true if @string follows the naming rules, false otherwise. | |
944 | + */ | |
945 | +static bool ccs_correct_word(const char *string) | |
946 | +{ | |
947 | + const char *const start = string; | |
948 | + u8 in_repetition = 0; | |
949 | + if (!*string) | |
950 | + goto out; | |
951 | + while (*string) { | |
952 | + unsigned char c = *string++; | |
953 | + if (in_repetition && c == '/') | |
954 | + goto out; | |
955 | + if (c <= ' ' || c >= 127) | |
956 | + goto out; | |
957 | + if (c != '\\') | |
958 | + continue; | |
959 | + c = *string++; | |
960 | + if (c >= '0' && c <= '3') { | |
961 | + unsigned char d; | |
962 | + unsigned char e; | |
963 | + d = *string++; | |
964 | + if (d < '0' || d > '7') | |
965 | + goto out; | |
966 | + e = *string++; | |
967 | + if (e < '0' || e > '7') | |
968 | + goto out; | |
969 | + c = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0'); | |
970 | + if (c <= ' ' || c >= 127 || c == '\\') | |
971 | + continue; | |
972 | + goto out; | |
973 | + } | |
974 | + switch (c) { | |
975 | + case '$': /* "\$" */ | |
976 | + case '+': /* "\+" */ | |
977 | + case '?': /* "\?" */ | |
978 | + case '*': /* "\*" */ | |
979 | + case '@': /* "\@" */ | |
980 | + case 'x': /* "\x" */ | |
981 | + case 'X': /* "\X" */ | |
982 | + case 'a': /* "\a" */ | |
983 | + case 'A': /* "\A" */ | |
984 | + case '-': /* "\-" */ | |
985 | + continue; | |
986 | + case '{': /* "/\{" */ | |
987 | + if (string - 3 < start || *(string - 3) != '/') | |
988 | + goto out; | |
989 | + in_repetition = 1; | |
990 | + continue; | |
991 | + case '}': /* "\}/" */ | |
992 | + if (in_repetition != 1 || *string++ != '/') | |
993 | + goto out; | |
994 | + in_repetition = 0; | |
995 | + continue; | |
996 | + case '(': /* "/\(" */ | |
997 | + if (string - 3 < start || *(string - 3) != '/') | |
998 | + goto out; | |
999 | + in_repetition = 2; | |
1000 | + continue; | |
1001 | + case ')': /* "\)/" */ | |
1002 | + if (in_repetition != 2 || *string++ != '/') | |
1003 | + goto out; | |
1004 | + in_repetition = 0; | |
1005 | + continue; | |
1006 | + } | |
1007 | + goto out; | |
1008 | + } | |
1009 | + if (in_repetition) | |
1010 | + goto out; | |
1011 | + return true; | |
1012 | +out: | |
1013 | + return false; | |
1014 | +} | |
1015 | + | |
1016 | +/** | |
1017 | + * ccs_commit_ok - Allocate memory and check memory quota. | |
1018 | + * | |
1019 | + * @data: Data to copy from. | |
1020 | + * @size: Size in byte. | |
1021 | + * | |
1022 | + * Returns pointer to allocated memory on success, NULL otherwise. | |
1023 | + * @data is zero-cleared on success. | |
1024 | + * | |
1025 | + * Caller holds ccs_policy_lock mutex. | |
1026 | + */ | |
1027 | +static void *ccs_commit_ok(void *data, const unsigned int size) | |
1028 | +{ | |
1029 | + void *ptr = kmalloc(size, GFP_NOFS); | |
1030 | + if (ccs_memory_ok(ptr, size)) { | |
1031 | + memmove(ptr, data, size); | |
1032 | + memset(data, 0, size); | |
1033 | + return ptr; | |
1034 | + } | |
1035 | + kfree(ptr); | |
1036 | + return NULL; | |
1037 | +} | |
1038 | + | |
1039 | +/** | |
1040 | + * ccs_get_group - Allocate memory for "struct ccs_string_group"/"struct ccs_number_group"/"struct ccs_ip_group". | |
1041 | + * | |
1042 | + * @head: Pointer to "struct ccs_io_buffer". | |
1043 | + * @idx: Index number. | |
1044 | + * | |
1045 | + * Returns pointer to "struct ccs_group" on success, NULL otherwise. | |
1046 | + */ | |
1047 | +static struct ccs_group *ccs_get_group(struct ccs_io_buffer *head, | |
1048 | + const enum ccs_group_id idx) | |
1049 | +{ | |
1050 | + struct ccs_group e = { }; | |
1051 | + struct ccs_group *group = NULL; | |
1052 | + struct list_head *list; | |
1053 | + const char *group_name = ccs_read_token(head); | |
1054 | + bool found = false; | |
1055 | + if (!ccs_correct_word(group_name) || idx >= CCS_MAX_GROUP) | |
1056 | + return NULL; | |
1057 | + e.group_name = ccs_get_name(group_name); | |
1058 | + if (!e.group_name) | |
1059 | + return NULL; | |
1060 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | |
1061 | + goto out; | |
1062 | + list = &ccs_group_list[idx]; | |
1063 | + list_for_each_entry(group, list, head.list) { | |
1064 | + if (e.group_name != group->group_name || | |
1065 | + atomic_read(&group->head.users) == CCS_GC_IN_PROGRESS) | |
1066 | + continue; | |
1067 | + atomic_inc(&group->head.users); | |
1068 | + found = true; | |
1069 | + break; | |
1070 | + } | |
1071 | + if (!found) { | |
1072 | + struct ccs_group *entry = ccs_commit_ok(&e, sizeof(e)); | |
1073 | + if (entry) { | |
1074 | + INIT_LIST_HEAD(&entry->member_list); | |
1075 | + atomic_set(&entry->head.users, 1); | |
1076 | + list_add_tail_rcu(&entry->head.list, list); | |
1077 | + group = entry; | |
1078 | + found = true; | |
1079 | + } | |
1080 | + } | |
1081 | + mutex_unlock(&ccs_policy_lock); | |
1082 | +out: | |
1083 | + ccs_put_name(e.group_name); | |
1084 | + return found ? group : NULL; | |
1085 | +} | |
1086 | + | |
1087 | +/** | |
1088 | + * ccs_parse_ulong - Parse an "unsigned long" value. | |
1089 | + * | |
1090 | + * @result: Pointer to "unsigned long". | |
1091 | + * @str: Pointer to string to parse. | |
1092 | + * | |
1093 | + * Returns one of values in "enum ccs_value_type". | |
1094 | + * | |
1095 | + * The @src is updated to point the first character after the value | |
1096 | + * on success. | |
1097 | + */ | |
1098 | +static enum ccs_value_type ccs_parse_ulong(unsigned long *result, char **str) | |
1099 | +{ | |
1100 | + const char *cp = *str; | |
1101 | + char *ep; | |
1102 | + int base = 10; | |
1103 | + if (*cp == '0') { | |
1104 | + char c = *(cp + 1); | |
1105 | + if (c == 'x' || c == 'X') { | |
1106 | + base = 16; | |
1107 | + cp += 2; | |
1108 | + } else if (c >= '0' && c <= '7') { | |
1109 | + base = 8; | |
1110 | + cp++; | |
1111 | + } | |
1112 | + } | |
1113 | + *result = simple_strtoul(cp, &ep, base); | |
1114 | + if (cp == ep) | |
1115 | + return CCS_VALUE_TYPE_INVALID; | |
1116 | + *str = ep; | |
1117 | + switch (base) { | |
1118 | + case 16: | |
1119 | + return CCS_VALUE_TYPE_HEXADECIMAL; | |
1120 | + case 8: | |
1121 | + return CCS_VALUE_TYPE_OCTAL; | |
1122 | + default: | |
1123 | + return CCS_VALUE_TYPE_DECIMAL; | |
1124 | + } | |
1125 | +} | |
1126 | + | |
1127 | +/** | |
1128 | + * ccs_get_dqword - ccs_get_name() for a quoted string. | |
1129 | + * | |
1130 | + * @start: String to parse. | |
1131 | + * | |
1132 | + * Returns pointer to "struct ccs_path_info" on success, NULL otherwise. | |
1133 | + */ | |
1134 | +static const struct ccs_path_info *ccs_get_dqword(char *start) | |
1135 | +{ | |
1136 | + char *cp = start + strlen(start) - 1; | |
1137 | + if (cp == start || *start++ != '"' || *cp != '"') | |
1138 | + return NULL; | |
1139 | + *cp = '\0'; | |
1140 | + if (*start && !ccs_correct_word(start)) | |
1141 | + return NULL; | |
1142 | + return ccs_get_name(start); | |
1143 | +} | |
1144 | + | |
1145 | +/** | |
1146 | + * ccs_same_condition - Check for duplicated "struct ccs_condition" entry. | |
1147 | + * | |
1148 | + * @a: Pointer to "struct ccs_condition". | |
1149 | + * @b: Pointer to "struct ccs_condition". | |
1150 | + * | |
1151 | + * Returns true if @a == @b, false otherwise. | |
1152 | + */ | |
1153 | +static inline bool ccs_same_condition(const struct ccs_condition *a, | |
1154 | + const struct ccs_condition *b) | |
1155 | +{ | |
1156 | + return a->size == b->size && | |
1157 | + !memcmp(a + 1, b + 1, a->size - sizeof(*a)); | |
1158 | +} | |
1159 | + | |
1160 | +/** | |
1161 | + * ccs_commit_condition - Commit "struct ccs_condition". | |
1162 | + * | |
1163 | + * @entry: Pointer to "struct ccs_condition". | |
1164 | + * | |
1165 | + * Returns pointer to "struct ccs_condition" on success, NULL otherwise. | |
1166 | + * | |
1167 | + * This function merges duplicated entries. This function returns NULL if | |
1168 | + * @entry is not duplicated but memory quota for policy has exceeded. | |
1169 | + */ | |
1170 | +static struct ccs_condition *ccs_commit_condition(struct ccs_condition *entry) | |
1171 | +{ | |
1172 | + struct ccs_condition *ptr; | |
1173 | + bool found = false; | |
1174 | + if (mutex_lock_interruptible(&ccs_policy_lock)) { | |
1175 | + dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__); | |
1176 | + ptr = NULL; | |
1177 | + found = true; | |
1178 | + goto out; | |
1179 | + } | |
1180 | + list_for_each_entry(ptr, &ccs_condition_list, head.list) { | |
1181 | + if (!ccs_same_condition(ptr, entry) || | |
1182 | + atomic_read(&ptr->head.users) == CCS_GC_IN_PROGRESS) | |
1183 | + continue; | |
1184 | + /* Same entry found. Share this entry. */ | |
1185 | + atomic_inc(&ptr->head.users); | |
1186 | + found = true; | |
1187 | + break; | |
1188 | + } | |
1189 | + if (!found) { | |
1190 | + if (ccs_memory_ok(entry, entry->size)) { | |
1191 | + atomic_set(&entry->head.users, 1); | |
1192 | + list_add(&entry->head.list, &ccs_condition_list); | |
1193 | + } else { | |
1194 | + found = true; | |
1195 | + ptr = NULL; | |
1196 | + } | |
1197 | + } | |
1198 | + mutex_unlock(&ccs_policy_lock); | |
1199 | +out: | |
1200 | + if (found) { | |
1201 | + ccs_del_condition(&entry->head.list); | |
1202 | + kfree(entry); | |
1203 | + entry = ptr; | |
1204 | + } | |
1205 | + return entry; | |
1206 | +} | |
1207 | + | |
1208 | +/** | |
1209 | + * ccs_correct_domain - Check whether the given domainname follows the naming rules. | |
1210 | + * | |
1211 | + * @domainname: The domainname to check. | |
1212 | + * | |
1213 | + * Returns true if @domainname follows the naming rules, false otherwise. | |
1214 | + */ | |
1215 | +static bool ccs_correct_domain(const unsigned char *domainname) | |
1216 | +{ | |
1217 | + if (!ccs_correct_word(domainname)) | |
1218 | + return false; | |
1219 | + while (*domainname) { | |
1220 | + if (*domainname++ != '\\') | |
1221 | + continue; | |
1222 | + if (*domainname < '0' || *domainname++ > '3') | |
1223 | + return false; | |
1224 | + } | |
1225 | + return true; | |
1226 | +} | |
1227 | + | |
1228 | +/** | |
1229 | + * ccs_normalize_line - Format string. | |
1230 | + * | |
1231 | + * @buffer: The line to normalize. | |
1232 | + * | |
1233 | + * Returns nothing. | |
1234 | + * | |
1235 | + * Leading and trailing whitespaces are removed. | |
1236 | + * Multiple whitespaces are packed into single space. | |
1237 | + */ | |
1238 | +static void ccs_normalize_line(unsigned char *buffer) | |
1239 | +{ | |
1240 | + unsigned char *sp = buffer; | |
1241 | + unsigned char *dp = buffer; | |
1242 | + bool first = true; | |
1243 | + while (*sp && (*sp <= ' ' || *sp >= 127)) | |
1244 | + sp++; | |
1245 | + while (*sp) { | |
1246 | + if (!first) | |
1247 | + *dp++ = ' '; | |
1248 | + first = false; | |
1249 | + while (*sp > ' ' && *sp < 127) | |
1250 | + *dp++ = *sp++; | |
1251 | + while (*sp && (*sp <= ' ' || *sp >= 127)) | |
1252 | + sp++; | |
1253 | + } | |
1254 | + *dp = '\0'; | |
1255 | +} | |
1256 | + | |
1257 | +/** | |
1258 | + * ccs_parse_values - Parse an numeric argument. | |
1259 | + * | |
1260 | + * @value: Values to parse. | |
1261 | + * @v: Pointer to "unsigned long". | |
1262 | + * | |
1263 | + * Returns "enum ccs_value_type" if @value is a single value, bitwise-OR-ed | |
1264 | + * value if @value is value range. | |
1265 | + */ | |
1266 | +static u8 ccs_parse_values(char *value, unsigned long v[2]) | |
1267 | +{ | |
1268 | + enum ccs_value_type radix1 = ccs_parse_ulong(&v[0], &value); | |
1269 | + enum ccs_value_type radix2; | |
1270 | + if (radix1 == CCS_VALUE_TYPE_INVALID) | |
1271 | + return CCS_VALUE_TYPE_INVALID; | |
1272 | + if (!*value) { | |
1273 | + v[1] = v[0]; | |
1274 | + return radix1; | |
1275 | + } | |
1276 | + if (*value++ != '-') | |
1277 | + return CCS_VALUE_TYPE_INVALID; | |
1278 | + radix2 = ccs_parse_ulong(&v[1], &value); | |
1279 | + if (radix2 == CCS_VALUE_TYPE_INVALID || *value || v[0] > v[1]) | |
1280 | + return CCS_VALUE_TYPE_INVALID; | |
1281 | + return radix1 | (radix2 << 2); | |
1282 | +} | |
1283 | + | |
1284 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
1285 | + | |
1286 | +/** | |
1287 | + * ccs_parse_ipaddr - Parse an IP address. | |
1288 | + * | |
1289 | + * @address: Address to parse. | |
1290 | + * @ipv6: Pointer to "struct in6_addr". | |
1291 | + * | |
1292 | + * Returns one of values in "enum ccs_ipaddr_type". | |
1293 | + */ | |
1294 | +/* Index numbers for type of IP addresses. */ | |
1295 | +static enum ccs_ipaddr_type ccs_parse_ipaddr(char *address, | |
1296 | + struct in6_addr ipv6[2]) | |
1297 | +{ | |
1298 | + const char *end; | |
1299 | + if (!strchr(address, ':') && | |
1300 | + in4_pton(address, -1, ipv6[0].s6_addr, '-', &end) > 0) { | |
1301 | + if (!*end) { | |
1302 | + ipv6[0].s6_addr32[0] = ipv6[0].s6_addr32[0]; | |
1303 | + ipv6[1].s6_addr32[0] = ipv6[0].s6_addr32[0]; | |
1304 | + return CCS_ADDRESS_TYPE_IPV4; | |
1305 | + } | |
1306 | + if (*end++ != '-' || | |
1307 | + in4_pton(end, -1, ipv6[1].s6_addr, '\0', &end) <= 0 || | |
1308 | + *end || memcmp(&ipv6[0], &ipv6[1], 4) >= 0) | |
1309 | + return CCS_ADDRESS_TYPE_INVALID; | |
1310 | + return CCS_ADDRESS_TYPE_IPV4_RANGE; | |
1311 | + } | |
1312 | + if (in6_pton(address, -1, ipv6[0].s6_addr, '-', &end) > 0) { | |
1313 | + if (!*end) { | |
1314 | + ipv6[1] = ipv6[0]; | |
1315 | + return CCS_ADDRESS_TYPE_IPV6; | |
1316 | + } | |
1317 | + if (*end++ != '-' || | |
1318 | + in6_pton(end, -1, ipv6[1].s6_addr, '\0', &end) <= 0 || | |
1319 | + *end || memcmp(&ipv6[0], &ipv6[1], 16) >= 0) | |
1320 | + return CCS_ADDRESS_TYPE_INVALID; | |
1321 | + return CCS_ADDRESS_TYPE_IPV6_RANGE; | |
1322 | + } | |
1323 | + return CCS_ADDRESS_TYPE_INVALID; | |
1324 | +} | |
1325 | + | |
1326 | +#endif | |
1327 | + | |
1328 | +/** | |
1329 | + * ccs_parse_task_cond - Find index for variable's name. | |
1330 | + * | |
1331 | + * @word: Keyword to search. | |
1332 | + * | |
1333 | + * Returns one of "ccs_conditions_index" value. | |
1334 | + */ | |
1335 | +static enum ccs_conditions_index ccs_parse_task_cond(const char *word) | |
1336 | +{ | |
1337 | + if (!strncmp(word, "task.", 5)) { | |
1338 | + word += 5; | |
1339 | + if (!strcmp(word, "uid")) | |
1340 | + return CCS_SELF_UID; | |
1341 | + if (!strcmp(word, "euid")) | |
1342 | + return CCS_SELF_EUID; | |
1343 | + if (!strcmp(word, "suid")) | |
1344 | + return CCS_SELF_SUID; | |
1345 | + if (!strcmp(word, "fsuid")) | |
1346 | + return CCS_SELF_FSUID; | |
1347 | + if (!strcmp(word, "gid")) | |
1348 | + return CCS_SELF_GID; | |
1349 | + if (!strcmp(word, "egid")) | |
1350 | + return CCS_SELF_EGID; | |
1351 | + if (!strcmp(word, "sgid")) | |
1352 | + return CCS_SELF_SGID; | |
1353 | + if (!strcmp(word, "fsgid")) | |
1354 | + return CCS_SELF_FSGID; | |
1355 | + if (!strcmp(word, "pid")) | |
1356 | + return CCS_SELF_PID; | |
1357 | + if (!strcmp(word, "ppid")) | |
1358 | + return CCS_SELF_PPID; | |
1359 | + if (!strcmp(word, "type")) | |
1360 | + return CCS_TASK_TYPE; | |
1361 | + if (!strcmp(word, "domain")) | |
1362 | + return CCS_SELF_DOMAIN; | |
1363 | + if (!strcmp(word, "exe")) | |
1364 | + return CCS_SELF_EXE; | |
1365 | + } | |
1366 | + return CCS_MAX_CONDITION_KEYWORD; | |
1367 | +} | |
1368 | + | |
1369 | +/** | |
1370 | + * ccs_parse_syscall_arg - Find index for variable's name. | |
1371 | + * | |
1372 | + * @word: Keyword to search. | |
1373 | + * @type: One of values in "enum ccs_mac_index". | |
1374 | + * | |
1375 | + * Returns one of "ccs_conditions_index" value. | |
1376 | + */ | |
1377 | +static enum ccs_conditions_index ccs_parse_syscall_arg | |
1378 | +(const char *word, const enum ccs_mac_index type) | |
1379 | +{ | |
1380 | + switch (type) { | |
1381 | + case CCS_MAC_READ: | |
1382 | + case CCS_MAC_WRITE: | |
1383 | + case CCS_MAC_APPEND: | |
1384 | + case CCS_MAC_UNLINK: | |
1385 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
1386 | + case CCS_MAC_GETATTR: | |
1387 | +#endif | |
1388 | + case CCS_MAC_RMDIR: | |
1389 | + case CCS_MAC_TRUNCATE: | |
1390 | + case CCS_MAC_CHROOT: | |
1391 | + case CCS_MAC_CHOWN: | |
1392 | + case CCS_MAC_CHGRP: | |
1393 | + case CCS_MAC_IOCTL: | |
1394 | + case CCS_MAC_EXECUTE: | |
1395 | + case CCS_MAC_SYMLINK: | |
1396 | + if (!strcmp(word, "path")) | |
1397 | + return CCS_COND_SARG0; | |
1398 | + if (type == CCS_MAC_CHOWN && !strcmp(word, "uid")) | |
1399 | + return CCS_COND_NARG0; | |
1400 | + if (type == CCS_MAC_CHGRP && !strcmp(word, "gid")) | |
1401 | + return CCS_COND_NARG0; | |
1402 | + if (type == CCS_MAC_IOCTL && !strcmp(word, "cmd")) | |
1403 | + return CCS_COND_NARG0; | |
1404 | + if (type == CCS_MAC_EXECUTE && !strcmp(word, "exec")) | |
1405 | + return CCS_COND_SARG1; | |
1406 | + if (type == CCS_MAC_SYMLINK && !strcmp(word, "target")) | |
1407 | + return CCS_COND_SARG1; | |
1408 | + break; | |
1409 | + case CCS_MAC_CHMOD: | |
1410 | + case CCS_MAC_MKDIR: | |
1411 | + case CCS_MAC_CREATE: | |
1412 | + case CCS_MAC_MKFIFO: | |
1413 | + case CCS_MAC_MKSOCK: | |
1414 | + case CCS_MAC_MKBLOCK: | |
1415 | + case CCS_MAC_MKCHAR: | |
1416 | + if (!strcmp(word, "path")) | |
1417 | + return CCS_COND_SARG0; | |
1418 | + if (!strcmp(word, "perm")) | |
1419 | + return CCS_COND_NARG0; | |
1420 | + if (type == CCS_MAC_MKBLOCK || type == CCS_MAC_MKCHAR) { | |
1421 | + if (!strcmp(word, "dev_major")) | |
1422 | + return CCS_COND_NARG1; | |
1423 | + if (!strcmp(word, "dev_minor")) | |
1424 | + return CCS_COND_NARG2; | |
1425 | + } | |
1426 | + break; | |
1427 | + case CCS_MAC_LINK: | |
1428 | + case CCS_MAC_RENAME: | |
1429 | + if (!strcmp(word, "old_path")) | |
1430 | + return CCS_COND_SARG0; | |
1431 | + if (!strcmp(word, "new_path")) | |
1432 | + return CCS_COND_SARG1; | |
1433 | + break; | |
1434 | + case CCS_MAC_MOUNT: | |
1435 | + if (!strcmp(word, "source")) | |
1436 | + return CCS_COND_SARG0; | |
1437 | + if (!strcmp(word, "target")) | |
1438 | + return CCS_COND_SARG1; | |
1439 | + if (!strcmp(word, "fstype")) | |
1440 | + return CCS_COND_SARG2; | |
1441 | + if (!strcmp(word, "data")) | |
1442 | + return CCS_COND_SARG3; | |
1443 | + if (!strcmp(word, "flags")) | |
1444 | + return CCS_COND_NARG0; | |
1445 | + break; | |
1446 | + case CCS_MAC_UMOUNT: | |
1447 | + if (!strcmp(word, "path")) | |
1448 | + return CCS_COND_SARG0; | |
1449 | + if (!strcmp(word, "flags")) | |
1450 | + return CCS_COND_NARG0; | |
1451 | + break; | |
1452 | + case CCS_MAC_PIVOT_ROOT: | |
1453 | + if (!strcmp(word, "new_root")) | |
1454 | + return CCS_COND_SARG0; | |
1455 | + if (!strcmp(word, "put_old")) | |
1456 | + return CCS_COND_SARG1; | |
1457 | + break; | |
1458 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
1459 | + case CCS_MAC_INET_STREAM_BIND: | |
1460 | + case CCS_MAC_INET_STREAM_LISTEN: | |
1461 | + case CCS_MAC_INET_STREAM_CONNECT: | |
1462 | + case CCS_MAC_INET_STREAM_ACCEPT: | |
1463 | + case CCS_MAC_INET_DGRAM_BIND: | |
1464 | + case CCS_MAC_INET_DGRAM_SEND: | |
1465 | + case CCS_MAC_INET_DGRAM_RECV: | |
1466 | + if (!strcmp(word, "ip")) | |
1467 | + return CCS_COND_IPARG; | |
1468 | + if (!strcmp(word, "port")) | |
1469 | + return CCS_COND_NARG0; | |
1470 | + break; | |
1471 | + case CCS_MAC_INET_RAW_BIND: | |
1472 | + case CCS_MAC_INET_RAW_SEND: | |
1473 | + case CCS_MAC_INET_RAW_RECV: | |
1474 | + if (!strcmp(word, "ip")) | |
1475 | + return CCS_COND_IPARG; | |
1476 | + if (!strcmp(word, "proto")) | |
1477 | + return CCS_COND_NARG0; | |
1478 | + break; | |
1479 | + case CCS_MAC_UNIX_STREAM_BIND: | |
1480 | + case CCS_MAC_UNIX_STREAM_LISTEN: | |
1481 | + case CCS_MAC_UNIX_STREAM_CONNECT: | |
1482 | + case CCS_MAC_UNIX_STREAM_ACCEPT: | |
1483 | + case CCS_MAC_UNIX_DGRAM_BIND: | |
1484 | + case CCS_MAC_UNIX_DGRAM_SEND: | |
1485 | + case CCS_MAC_UNIX_DGRAM_RECV: | |
1486 | + case CCS_MAC_UNIX_SEQPACKET_BIND: | |
1487 | + case CCS_MAC_UNIX_SEQPACKET_LISTEN: | |
1488 | + case CCS_MAC_UNIX_SEQPACKET_CONNECT: | |
1489 | + case CCS_MAC_UNIX_SEQPACKET_ACCEPT: | |
1490 | + if (!strcmp(word, "addr")) | |
1491 | + return CCS_COND_SARG0; | |
1492 | + break; | |
1493 | +#endif | |
1494 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
1495 | + case CCS_MAC_ENVIRON: | |
1496 | + if (!strcmp(word, "path")) | |
1497 | + return CCS_COND_SARG0; | |
1498 | + if (!strcmp(word, "exec")) | |
1499 | + return CCS_COND_SARG1; | |
1500 | + if (!strcmp(word, "name")) | |
1501 | + return CCS_COND_SARG2; | |
1502 | + if (!strcmp(word, "value")) | |
1503 | + return CCS_COND_SARG3; | |
1504 | + break; | |
1505 | +#endif | |
1506 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
1507 | + case CCS_MAC_PTRACE: | |
1508 | + if (!strcmp(word, "domain")) | |
1509 | + return CCS_COND_DOMAIN; | |
1510 | + if (!strcmp(word, "cmd")) | |
1511 | + return CCS_COND_NARG0; | |
1512 | + break; | |
1513 | +#endif | |
1514 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
1515 | + case CCS_MAC_SIGNAL: | |
1516 | + if (!strcmp(word, "sig")) | |
1517 | + return CCS_COND_NARG0; | |
1518 | + break; | |
1519 | +#endif | |
1520 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
1521 | + case CCS_MAC_MANUAL_DOMAIN_TRANSITION: | |
1522 | + if (!strcmp(word, "domain")) | |
1523 | + return CCS_COND_DOMAIN; | |
1524 | + break; | |
1525 | +#endif | |
1526 | + default: | |
1527 | + break; | |
1528 | + } | |
1529 | + return CCS_MAX_CONDITION_KEYWORD; | |
1530 | +} | |
1531 | + | |
1532 | +/** | |
1533 | + * ccs_parse_path_attributes - Find index for variable's name. | |
1534 | + * | |
1535 | + * @word: Keyword to search. | |
1536 | + * @type: One of values in "enum ccs_mac_index". | |
1537 | + * | |
1538 | + * Returns one of "ccs_conditions_index" value. | |
1539 | + */ | |
1540 | +static enum ccs_conditions_index ccs_parse_path_attribute | |
1541 | +(char *word, const enum ccs_mac_index type) | |
1542 | +{ | |
1543 | + u8 i; | |
1544 | + enum ccs_conditions_index start; | |
1545 | + switch (type) { | |
1546 | + case CCS_MAC_READ: | |
1547 | + case CCS_MAC_WRITE: | |
1548 | + case CCS_MAC_APPEND: | |
1549 | + case CCS_MAC_UNLINK: | |
1550 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
1551 | + case CCS_MAC_GETATTR: | |
1552 | +#endif | |
1553 | + case CCS_MAC_RMDIR: | |
1554 | + case CCS_MAC_TRUNCATE: | |
1555 | + case CCS_MAC_CHROOT: | |
1556 | + case CCS_MAC_CHMOD: | |
1557 | + case CCS_MAC_CHOWN: | |
1558 | + case CCS_MAC_CHGRP: | |
1559 | + case CCS_MAC_IOCTL: | |
1560 | + case CCS_MAC_EXECUTE: | |
1561 | + case CCS_MAC_UMOUNT: | |
1562 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
1563 | + case CCS_MAC_ENVIRON: | |
1564 | +#endif | |
1565 | + if (ccs_str_starts(&word, "path")) | |
1566 | + goto path1; | |
1567 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
1568 | + if ((type == CCS_MAC_EXECUTE || type == CCS_MAC_ENVIRON) && | |
1569 | + ccs_str_starts(&word, "exec")) | |
1570 | + goto path2; | |
1571 | +#else | |
1572 | + if (type == CCS_MAC_EXECUTE && | |
1573 | + ccs_str_starts(&word, "exec")) | |
1574 | + goto path2; | |
1575 | +#endif | |
1576 | + break; | |
1577 | + case CCS_MAC_MKDIR: | |
1578 | + case CCS_MAC_CREATE: | |
1579 | + case CCS_MAC_MKFIFO: | |
1580 | + case CCS_MAC_MKSOCK: | |
1581 | + case CCS_MAC_MKBLOCK: | |
1582 | + case CCS_MAC_MKCHAR: | |
1583 | + case CCS_MAC_SYMLINK: | |
1584 | + if (ccs_str_starts(&word, "path")) | |
1585 | + goto path1_parent; | |
1586 | + break; | |
1587 | + case CCS_MAC_LINK: | |
1588 | + case CCS_MAC_RENAME: | |
1589 | + if (ccs_str_starts(&word, "old_path")) | |
1590 | + goto path1; | |
1591 | + if (ccs_str_starts(&word, "new_path")) | |
1592 | + goto path2_parent; | |
1593 | + break; | |
1594 | + case CCS_MAC_MOUNT: | |
1595 | + if (ccs_str_starts(&word, "source")) | |
1596 | + goto path1; | |
1597 | + if (ccs_str_starts(&word, "target")) | |
1598 | + goto path2; | |
1599 | + break; | |
1600 | + case CCS_MAC_PIVOT_ROOT: | |
1601 | + if (ccs_str_starts(&word, "new_root")) | |
1602 | + goto path1; | |
1603 | + if (ccs_str_starts(&word, "put_old")) | |
1604 | + goto path2; | |
1605 | + break; | |
1606 | + default: | |
1607 | + break; | |
1608 | + } | |
1609 | + goto out; | |
1610 | +path1_parent: | |
1611 | + if (strncmp(word, ".parent", 7)) | |
1612 | + goto out; | |
1613 | +path1: | |
1614 | + start = CCS_PATH_ATTRIBUTE_START; | |
1615 | + goto check; | |
1616 | +path2_parent: | |
1617 | + if (strncmp(word, ".parent", 7)) | |
1618 | + goto out; | |
1619 | +path2: | |
1620 | + start = CCS_PATH_ATTRIBUTE_START + 32; | |
1621 | +check: | |
1622 | + if (ccs_str_starts(&word, ".parent")) | |
1623 | + start += 16; | |
1624 | + if (*word++ == '.') | |
1625 | + for (i = 0; i < CCS_MAX_PATH_ATTRIBUTE; i++) | |
1626 | + if (!strcmp(word, ccs_path_attribute[i])) | |
1627 | + return start + i; | |
1628 | +out: | |
1629 | + return CCS_MAX_CONDITION_KEYWORD; | |
1630 | +} | |
1631 | + | |
1632 | +/** | |
1633 | + * ccs_find_pathtype - Find index for file's type. | |
1634 | + * | |
1635 | + * @word: Keyword to search. | |
1636 | + * | |
1637 | + * Returns one of "ccs_conditions_index" value. | |
1638 | + */ | |
1639 | +static enum ccs_conditions_index ccs_find_path_type(const char *word) | |
1640 | +{ | |
1641 | + if (!strcmp(word, "socket")) | |
1642 | + return CCS_OBJ_IS_SOCKET; | |
1643 | + if (!strcmp(word, "symlink")) | |
1644 | + return CCS_OBJ_IS_SYMLINK; | |
1645 | + if (!strcmp(word, "file")) | |
1646 | + return CCS_OBJ_IS_FILE; | |
1647 | + if (!strcmp(word, "block")) | |
1648 | + return CCS_OBJ_IS_BLOCK_DEV; | |
1649 | + if (!strcmp(word, "directory")) | |
1650 | + return CCS_OBJ_IS_DIRECTORY; | |
1651 | + if (!strcmp(word, "char")) | |
1652 | + return CCS_OBJ_IS_CHAR_DEV; | |
1653 | + if (!strcmp(word, "fifo")) | |
1654 | + return CCS_OBJ_IS_FIFO; | |
1655 | + return CCS_MAX_CONDITION_KEYWORD; | |
1656 | +} | |
1657 | + | |
1658 | +/** | |
1659 | + * ccs_find_path_perm - Find index for file's DAC attribute. | |
1660 | + * | |
1661 | + * @word: Keyword to search. | |
1662 | + * | |
1663 | + * Returns one of "ccs_conditions_index" value. | |
1664 | + */ | |
1665 | +static enum ccs_conditions_index ccs_find_path_perm(const char *word) | |
1666 | +{ | |
1667 | + if (!strcmp(word, "setuid")) | |
1668 | + return CCS_MODE_SETUID; | |
1669 | + if (!strcmp(word, "setgid")) | |
1670 | + return CCS_MODE_SETGID; | |
1671 | + if (!strcmp(word, "sticky")) | |
1672 | + return CCS_MODE_STICKY; | |
1673 | + if (!strcmp(word, "owner_read")) | |
1674 | + return CCS_MODE_OWNER_READ; | |
1675 | + if (!strcmp(word, "owner_write")) | |
1676 | + return CCS_MODE_OWNER_WRITE; | |
1677 | + if (!strcmp(word, "owner_execute")) | |
1678 | + return CCS_MODE_OWNER_EXECUTE; | |
1679 | + if (!strcmp(word, "group_read")) | |
1680 | + return CCS_MODE_GROUP_READ; | |
1681 | + if (!strcmp(word, "group_write")) | |
1682 | + return CCS_MODE_GROUP_WRITE; | |
1683 | + if (!strcmp(word, "group_execute")) | |
1684 | + return CCS_MODE_GROUP_EXECUTE; | |
1685 | + if (!strcmp(word, "others_read")) | |
1686 | + return CCS_MODE_OTHERS_READ; | |
1687 | + if (!strcmp(word, "others_write")) | |
1688 | + return CCS_MODE_OTHERS_WRITE; | |
1689 | + if (!strcmp(word, "others_execute")) | |
1690 | + return CCS_MODE_OTHERS_EXECUTE; | |
1691 | + return CCS_MAX_CONDITION_KEYWORD; | |
1692 | +} | |
1693 | + | |
1694 | +/** | |
1695 | + * ccs_parse_cond - Parse single condition. | |
1696 | + * | |
1697 | + * @tmp: Pointer to "struct ccs_cond_tmp". | |
1698 | + * @head: Pointer to "struct ccs_io_buffer". | |
1699 | + * | |
1700 | + * Returns true on success, false otherwise. | |
1701 | + */ | |
1702 | +static bool ccs_parse_cond(struct ccs_cond_tmp *tmp, | |
1703 | + struct ccs_io_buffer *head) | |
1704 | +{ | |
1705 | + enum ccs_group_id g; | |
1706 | + char *left = head->w.data; | |
1707 | + char *right; | |
1708 | + const enum ccs_mac_index type = head->w.acl_index; | |
1709 | + right = strchr(left, '='); | |
1710 | + if (!right || right == left) | |
1711 | + return false; | |
1712 | + *right++ = '\0'; | |
1713 | + tmp->is_not = (*(right - 2) == '!'); | |
1714 | + if (tmp->is_not) | |
1715 | + *(right - 2) = '\0'; | |
1716 | + if (!*left || !*right) | |
1717 | + return false; | |
1718 | + if (type == CCS_MAC_EXECUTE | |
1719 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
1720 | + || type == CCS_MAC_ENVIRON | |
1721 | +#endif | |
1722 | + ) { | |
1723 | + if (ccs_str_starts(&left, "argv[")) { | |
1724 | + tmp->left = CCS_ARGV_ENTRY; | |
1725 | + if (ccs_parse_ulong(&tmp->argv, &left) != | |
1726 | + CCS_VALUE_TYPE_DECIMAL || *left++ != ']' || *left) | |
1727 | + return false; | |
1728 | + } else if (ccs_str_starts(&left, "envp[")) { | |
1729 | + char *cp = left + strlen(left) - 1; | |
1730 | + tmp->left = CCS_ENVP_ENTRY; | |
1731 | + if (*cp != ']') | |
1732 | + return false; | |
1733 | + *cp = '\0'; | |
1734 | + tmp->envp = ccs_get_dqword(left); | |
1735 | + if (!tmp->envp) | |
1736 | + return false; | |
1737 | + } else if (!strcmp(left, "argc")) | |
1738 | + tmp->left = CCS_EXEC_ARGC; | |
1739 | + else if (!strcmp(left, "envc")) | |
1740 | + tmp->left = CCS_EXEC_ENVC; | |
1741 | + } | |
1742 | + if (tmp->left == CCS_MAX_CONDITION_KEYWORD) | |
1743 | + tmp->left = ccs_parse_syscall_arg(left, type); | |
1744 | + if (tmp->left == CCS_MAX_CONDITION_KEYWORD) | |
1745 | + tmp->left = ccs_parse_task_cond(left); | |
1746 | + if (tmp->left == CCS_MAX_CONDITION_KEYWORD) | |
1747 | + tmp->left = ccs_parse_path_attribute(left, type); | |
1748 | + if (tmp->left == CCS_MAX_CONDITION_KEYWORD) { | |
1749 | + /* | |
1750 | + * CCS_HANDLER_PATH and CCS_TRANSIT_DOMAIN are not for | |
1751 | + * comparison. | |
1752 | + */ | |
1753 | + if (tmp->is_not) | |
1754 | + return false; | |
1755 | + if (!strcmp(left, "handler")) | |
1756 | + tmp->left = CCS_HANDLER_PATH; | |
1757 | + else if (!strcmp(left, "transition")) | |
1758 | + tmp->left = CCS_TRANSIT_DOMAIN; | |
1759 | + else | |
1760 | + return false; | |
1761 | + tmp->right = CCS_IMM_NAME_ENTRY; | |
1762 | + if (!strcmp(right, "NULL")) { | |
1763 | + tmp->path = &ccs_null_name; | |
1764 | + } else { | |
1765 | + tmp->path = ccs_get_dqword(right); | |
1766 | + if (!tmp->path || | |
1767 | + tmp->path->const_len != tmp->path->total_len) | |
1768 | + return false; | |
1769 | + } | |
1770 | + return true; | |
1771 | + } | |
1772 | + switch (tmp->left) { | |
1773 | + case CCS_COND_DOMAIN: | |
1774 | + case CCS_SELF_DOMAIN: | |
1775 | + case CCS_ARGV_ENTRY: | |
1776 | + case CCS_ENVP_ENTRY: | |
1777 | + case CCS_COND_SARG0: | |
1778 | + case CCS_COND_SARG1: | |
1779 | + case CCS_COND_SARG2: | |
1780 | + case CCS_COND_SARG3: | |
1781 | + case CCS_SELF_EXE: | |
1782 | + g = CCS_STRING_GROUP; | |
1783 | + break; | |
1784 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
1785 | + case CCS_COND_IPARG: | |
1786 | + g = CCS_IP_GROUP; | |
1787 | + break; | |
1788 | +#endif | |
1789 | + case CCS_TASK_TYPE: | |
1790 | + tmp->right = CCS_TASK_EXECUTE_HANDLER; | |
1791 | + return !strcmp(right, "execute_handler"); | |
1792 | + case CCS_PATH_ATTRIBUTE_START + CCS_PATH_ATTRIBUTE_TYPE: | |
1793 | + case CCS_PATH_ATTRIBUTE_START + 16 + CCS_PATH_ATTRIBUTE_TYPE: | |
1794 | + case CCS_PATH_ATTRIBUTE_START + 32 + CCS_PATH_ATTRIBUTE_TYPE: | |
1795 | + case CCS_PATH_ATTRIBUTE_START + 48 + CCS_PATH_ATTRIBUTE_TYPE: | |
1796 | + tmp->right = ccs_find_path_type(right); | |
1797 | + return tmp->right != CCS_MAX_CONDITION_KEYWORD; | |
1798 | + case CCS_PATH_ATTRIBUTE_START + CCS_PATH_ATTRIBUTE_PERM: | |
1799 | + case CCS_PATH_ATTRIBUTE_START + 16 + CCS_PATH_ATTRIBUTE_PERM: | |
1800 | + case CCS_PATH_ATTRIBUTE_START + 32 + CCS_PATH_ATTRIBUTE_PERM: | |
1801 | + case CCS_PATH_ATTRIBUTE_START + 48 + CCS_PATH_ATTRIBUTE_PERM: | |
1802 | + tmp->right = ccs_find_path_perm(right); | |
1803 | + if (tmp->right != CCS_MAX_CONDITION_KEYWORD) | |
1804 | + return true; | |
1805 | + /* fall through */ | |
1806 | + default: | |
1807 | + g = CCS_NUMBER_GROUP; | |
1808 | + } | |
1809 | + if (*right == '@') { | |
1810 | + tmp->right = CCS_IMM_GROUP; | |
1811 | + head->w.data = ++right; | |
1812 | + tmp->group = ccs_get_group(head, g); | |
1813 | + return tmp->group != NULL; | |
1814 | + } | |
1815 | + if (*right == '"') { | |
1816 | + if (g != CCS_STRING_GROUP) | |
1817 | + return false; | |
1818 | + tmp->right = CCS_IMM_NAME_ENTRY; | |
1819 | + tmp->path = ccs_get_dqword(right); | |
1820 | + return tmp->path != NULL; | |
1821 | + } | |
1822 | + if (tmp->left == CCS_ENVP_ENTRY) { | |
1823 | + tmp->right = CCS_IMM_NAME_ENTRY; | |
1824 | + tmp->path = &ccs_null_name; | |
1825 | + return !strcmp(right, "NULL"); | |
1826 | + } | |
1827 | + if (g == CCS_NUMBER_GROUP) { | |
1828 | + tmp->right = ccs_parse_task_cond(right); | |
1829 | + if (tmp->right == CCS_SELF_DOMAIN || | |
1830 | + tmp->right == CCS_SELF_EXE) | |
1831 | + return false; | |
1832 | + if (tmp->right == CCS_MAX_CONDITION_KEYWORD) | |
1833 | + tmp->right = ccs_parse_path_attribute(right, type); | |
1834 | + if (tmp->right != CCS_MAX_CONDITION_KEYWORD) | |
1835 | + return true; | |
1836 | + tmp->radix = ccs_parse_values(right, tmp->value); | |
1837 | + if (tmp->radix == CCS_VALUE_TYPE_INVALID) | |
1838 | + return false; | |
1839 | + if (tmp->radix >> 2) | |
1840 | + tmp->right = CCS_IMM_NUMBER_ENTRY2; | |
1841 | + else | |
1842 | + tmp->right = CCS_IMM_NUMBER_ENTRY1; | |
1843 | + return true; | |
1844 | + } | |
1845 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
1846 | + if (g == CCS_IP_GROUP) { | |
1847 | + switch (ccs_parse_ipaddr(right, tmp->ipv6)) { | |
1848 | + case CCS_ADDRESS_TYPE_IPV4: | |
1849 | + tmp->right = CCS_IMM_IPV4ADDR_ENTRY1; | |
1850 | + break; | |
1851 | + case CCS_ADDRESS_TYPE_IPV4_RANGE: | |
1852 | + tmp->right = CCS_IMM_IPV4ADDR_ENTRY2; | |
1853 | + break; | |
1854 | + case CCS_ADDRESS_TYPE_IPV6: | |
1855 | + tmp->right = CCS_IMM_IPV6ADDR_ENTRY1; | |
1856 | + break; | |
1857 | + case CCS_ADDRESS_TYPE_IPV6_RANGE: | |
1858 | + tmp->right = CCS_IMM_IPV6ADDR_ENTRY2; | |
1859 | + break; | |
1860 | + default: | |
1861 | + return false; | |
1862 | + } | |
1863 | + return true; | |
1864 | + } | |
1865 | +#endif | |
1866 | + return false; | |
1867 | +} | |
1868 | + | |
1869 | +/** | |
1870 | + * ccs_get_condition - Parse condition part. | |
1871 | + * | |
1872 | + * @head: Pointer to "struct ccs_io_buffer". | |
1873 | + * | |
1874 | + * Returns pointer to "struct ccs_condition" on success, NULL otherwise. | |
1875 | + */ | |
1876 | +struct ccs_condition *ccs_get_condition(struct ccs_io_buffer *head) | |
1877 | +{ | |
1878 | + struct ccs_condition *entry = kmalloc(PAGE_SIZE, GFP_NOFS); | |
1879 | + union ccs_condition_element *condp; | |
1880 | + struct ccs_cond_tmp tmp; | |
1881 | + const enum ccs_mac_index type = head->w.acl_index; | |
1882 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
1883 | + bool handler_path_done = head->w.is_deny || | |
1884 | + type != CCS_MAC_EXECUTE; | |
1885 | +#else | |
1886 | + bool handler_path_done = true; | |
1887 | +#endif | |
1888 | + bool transit_domain_done = head->w.is_deny || | |
1889 | + (type != CCS_MAC_EXECUTE | |
1890 | +#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
1891 | + && type != CCS_MAC_AUTO_DOMAIN_TRANSITION | |
1892 | +#endif | |
1893 | + ); | |
1894 | + char *pos = head->w.data; | |
1895 | + if (!entry) | |
1896 | + return NULL; | |
1897 | + condp = (union ccs_condition_element *) (entry + 1); | |
1898 | + while (1) { | |
1899 | + memset(&tmp, 0, sizeof(tmp)); | |
1900 | + tmp.left = CCS_MAX_CONDITION_KEYWORD; | |
1901 | + tmp.right = CCS_MAX_CONDITION_KEYWORD; | |
1902 | + while (*pos == ' ') | |
1903 | + pos++; | |
1904 | + if (!*pos) | |
1905 | + break; | |
1906 | + if ((u8 *) condp >= ((u8 *) entry) + PAGE_SIZE | |
1907 | + - (sizeof(*condp) + sizeof(struct in6_addr) * 2)) | |
1908 | + goto out; | |
1909 | + { | |
1910 | + char *next = strchr(pos, ' '); | |
1911 | + if (next) | |
1912 | + *next++ = '\0'; | |
1913 | + else | |
1914 | + next = ""; | |
1915 | + head->w.data = pos; | |
1916 | + pos = next; | |
1917 | + } | |
1918 | + if (!ccs_parse_cond(&tmp, head)) | |
1919 | + goto out; | |
1920 | + if (tmp.left == CCS_HANDLER_PATH) { | |
1921 | + if (handler_path_done) | |
1922 | + goto out; | |
1923 | + handler_path_done = true; | |
1924 | + } | |
1925 | + if (tmp.left == CCS_TRANSIT_DOMAIN) { | |
1926 | + if (transit_domain_done) | |
1927 | + goto out; | |
1928 | + transit_domain_done = true; | |
1929 | + } | |
1930 | + condp->is_not = tmp.is_not; | |
1931 | + condp->left = tmp.left; | |
1932 | + condp->right = tmp.right; | |
1933 | + condp->radix = tmp.radix; | |
1934 | + condp++; | |
1935 | + if (tmp.left == CCS_ARGV_ENTRY) { | |
1936 | + condp->value = tmp.argv; | |
1937 | + condp++; | |
1938 | + } else if (tmp.left == CCS_ENVP_ENTRY) { | |
1939 | + condp->path = tmp.envp; | |
1940 | + condp++; | |
1941 | + } | |
1942 | + if (tmp.right == CCS_IMM_GROUP) { | |
1943 | + condp->group = tmp.group; | |
1944 | + condp++; | |
1945 | + } else if (tmp.right == CCS_IMM_NAME_ENTRY) { | |
1946 | + condp->path = tmp.path; | |
1947 | + condp++; | |
1948 | + } else if (tmp.right == CCS_IMM_NUMBER_ENTRY1 || | |
1949 | + tmp.right == CCS_IMM_NUMBER_ENTRY2) { | |
1950 | + condp->value = tmp.value[0]; | |
1951 | + condp++; | |
1952 | + if (tmp.right == CCS_IMM_NUMBER_ENTRY2) { | |
1953 | + condp->value = tmp.value[1]; | |
1954 | + condp++; | |
1955 | + } | |
1956 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
1957 | + } else if (tmp.right == CCS_IMM_IPV4ADDR_ENTRY1 || | |
1958 | + tmp.right == CCS_IMM_IPV4ADDR_ENTRY2) { | |
1959 | + condp->ip = * (u32 *) &tmp.ipv6[0]; | |
1960 | + condp++; | |
1961 | + if (tmp.right == CCS_IMM_IPV4ADDR_ENTRY2) { | |
1962 | + condp->ip = * (u32 *) &tmp.ipv6[1]; | |
1963 | + condp++; | |
1964 | + } | |
1965 | + } else if (tmp.right == CCS_IMM_IPV6ADDR_ENTRY1 || | |
1966 | + tmp.right == CCS_IMM_IPV6ADDR_ENTRY2) { | |
1967 | + * (struct in6_addr *) condp = tmp.ipv6[0]; | |
1968 | + condp = (void *) (((u8 *) condp) + | |
1969 | + sizeof(struct in6_addr)); | |
1970 | + if (tmp.right == CCS_IMM_IPV6ADDR_ENTRY2) { | |
1971 | + * (struct in6_addr *) condp = tmp.ipv6[1]; | |
1972 | + condp = (void *) (((u8 *) condp) + | |
1973 | + sizeof(struct in6_addr)); | |
1974 | + } | |
1975 | +#endif | |
1976 | + } | |
1977 | + } | |
1978 | +#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
1979 | + if (!transit_domain_done && type == CCS_MAC_AUTO_DOMAIN_TRANSITION) | |
1980 | + goto out; | |
1981 | +#endif | |
1982 | + entry->size = (void *) condp - (void *) entry; | |
1983 | + return ccs_commit_condition(entry); | |
1984 | +out: | |
1985 | + dprintk(KERN_WARNING "%u: type=%u env='%s' path='%s' group='%s'\n", | |
1986 | + __LINE__, type, tmp.envp ? tmp.envp->name : "", | |
1987 | + tmp.path ? tmp.path->name : "", | |
1988 | + tmp.group ? tmp.group->group_name->name : ""); | |
1989 | + ccs_put_name(tmp.envp); | |
1990 | + if (tmp.path != &ccs_null_name) | |
1991 | + ccs_put_name(tmp.path); | |
1992 | + ccs_put_group(tmp.group); | |
1993 | + entry->size = (void *) condp - (void *) entry; | |
1994 | + ccs_del_condition(&entry->head.list); | |
1995 | + kfree(entry); | |
1996 | + return NULL; | |
1997 | +} | |
1998 | + | |
1999 | +/** | |
2000 | + * ccs_yesno - Return "yes" or "no". | |
2001 | + * | |
2002 | + * @value: Bool value. | |
2003 | + * | |
2004 | + * Returns "yes" if @value is not 0, "no" otherwise. | |
2005 | + */ | |
2006 | +static const char *ccs_yesno(const unsigned int value) | |
2007 | +{ | |
2008 | + return value ? "yes" : "no"; | |
2009 | +} | |
2010 | + | |
2011 | +/** | |
2012 | + * ccs_flush - Flush queued string to userspace's buffer. | |
2013 | + * | |
2014 | + * @head: Pointer to "struct ccs_io_buffer". | |
2015 | + * | |
2016 | + * Returns true if all data was flushed, false otherwise. | |
2017 | + */ | |
2018 | +static bool ccs_flush(struct ccs_io_buffer *head) | |
2019 | +{ | |
2020 | + while (head->r.w_pos) { | |
2021 | + const char *w = head->r.w[0]; | |
2022 | + size_t len = strlen(w); | |
2023 | + if (len) { | |
2024 | + if (len > head->read_user_buf_avail) | |
2025 | + len = head->read_user_buf_avail; | |
2026 | + if (!len) | |
2027 | + return false; | |
2028 | + if (copy_to_user(head->read_user_buf, w, len)) | |
2029 | + return false; | |
2030 | + head->read_user_buf_avail -= len; | |
2031 | + head->read_user_buf += len; | |
2032 | + w += len; | |
2033 | + } | |
2034 | + head->r.w[0] = w; | |
2035 | + if (*w) | |
2036 | + return false; | |
2037 | + /* Add '\0' for audit logs and query. */ | |
2038 | + if (head->type == CCS_AUDIT || head->type == CCS_QUERY) { | |
2039 | + if (!head->read_user_buf_avail || | |
2040 | + copy_to_user(head->read_user_buf, "", 1)) | |
2041 | + return false; | |
2042 | + head->read_user_buf_avail--; | |
2043 | + head->read_user_buf++; | |
2044 | + } | |
2045 | + head->r.w_pos--; | |
2046 | + for (len = 0; len < head->r.w_pos; len++) | |
2047 | + head->r.w[len] = head->r.w[len + 1]; | |
2048 | + } | |
2049 | + head->r.avail = 0; | |
2050 | + return true; | |
2051 | +} | |
2052 | + | |
2053 | +/** | |
2054 | + * ccs_set_string - Queue string to "struct ccs_io_buffer" structure. | |
2055 | + * | |
2056 | + * @head: Pointer to "struct ccs_io_buffer". | |
2057 | + * @string: String to print. | |
2058 | + * | |
2059 | + * Returns nothing. | |
2060 | + * | |
2061 | + * Note that @string has to be kept valid until @head is kfree()d. | |
2062 | + * This means that char[] allocated on stack memory cannot be passed to | |
2063 | + * this function. Use ccs_io_printf() for char[] allocated on stack memory. | |
2064 | + */ | |
2065 | +static void ccs_set_string(struct ccs_io_buffer *head, const char *string) | |
2066 | +{ | |
2067 | + if (head->r.w_pos < CCS_MAX_IO_READ_QUEUE) { | |
2068 | + head->r.w[head->r.w_pos++] = string; | |
2069 | + ccs_flush(head); | |
2070 | + } else | |
2071 | + printk(KERN_WARNING "Too many words in a line.\n"); | |
2072 | +} | |
2073 | + | |
2074 | +/** | |
2075 | + * ccs_io_printf - printf() to "struct ccs_io_buffer" structure. | |
2076 | + * | |
2077 | + * @head: Pointer to "struct ccs_io_buffer". | |
2078 | + * @fmt: The printf()'s format string, followed by parameters. | |
2079 | + * | |
2080 | + * Returns nothing. | |
2081 | + */ | |
2082 | +static void ccs_io_printf(struct ccs_io_buffer *head, const char *fmt, ...) | |
2083 | +{ | |
2084 | + va_list args; | |
2085 | + size_t len; | |
2086 | + size_t pos = head->r.avail; | |
2087 | + int size = head->readbuf_size - pos; | |
2088 | + if (size <= 0) | |
2089 | + return; | |
2090 | + va_start(args, fmt); | |
2091 | + len = vsnprintf(head->read_buf + pos, size, fmt, args) + 1; | |
2092 | + va_end(args); | |
2093 | + if (pos + len >= head->readbuf_size) { | |
2094 | + printk(KERN_WARNING "Too many words in a line.\n"); | |
2095 | + return; | |
2096 | + } | |
2097 | + head->r.avail += len; | |
2098 | + ccs_set_string(head, head->read_buf + pos); | |
2099 | +} | |
2100 | + | |
2101 | +/** | |
2102 | + * ccs_set_space - Put a space to "struct ccs_io_buffer" structure. | |
2103 | + * | |
2104 | + * @head: Pointer to "struct ccs_io_buffer". | |
2105 | + * | |
2106 | + * Returns nothing. | |
2107 | + */ | |
2108 | +static void ccs_set_space(struct ccs_io_buffer *head) | |
2109 | +{ | |
2110 | + ccs_set_string(head, " "); | |
2111 | +} | |
2112 | + | |
2113 | +/** | |
2114 | + * ccs_set_lf - Put a line feed to "struct ccs_io_buffer" structure. | |
2115 | + * | |
2116 | + * @head: Pointer to "struct ccs_io_buffer". | |
2117 | + * | |
2118 | + * Returns true if all data was flushed, false otherwise. | |
2119 | + */ | |
2120 | +static bool ccs_set_lf(struct ccs_io_buffer *head) | |
2121 | +{ | |
2122 | + ccs_set_string(head, "\n"); | |
2123 | + return !head->r.w_pos; | |
2124 | +} | |
2125 | + | |
2126 | +/** | |
2127 | + * ccs_check_profile - Check policy is loaded. | |
2128 | + * | |
2129 | + * Returns nothing. | |
2130 | + */ | |
2131 | +static void ccs_check_profile(void) | |
2132 | +{ | |
2133 | + ccs_policy_loaded = true; | |
2134 | + printk(KERN_INFO "CaitSith: 0.1 2012/04/01\n"); | |
2135 | + if (ccs_policy_version == 20120401) { | |
2136 | + printk(KERN_INFO "CaitSith module activated.\n"); | |
2137 | + return; | |
2138 | + } | |
2139 | + printk(KERN_ERR "Policy version %u is not supported.\n", | |
2140 | + ccs_policy_version); | |
2141 | + printk(KERN_ERR "Userland tools for CaitSith must be installed and " | |
2142 | + "policy must be initialized.\n"); | |
2143 | + printk(KERN_ERR "Please see http://caitsith.sourceforge.jp/ " | |
2144 | + "for more information.\n"); | |
2145 | + panic("STOP!"); | |
2146 | +} | |
2147 | + | |
2148 | +/** | |
2149 | + * ccs_str_starts - Check whether the given string starts with the given keyword. | |
2150 | + * | |
2151 | + * @src: Pointer to pointer to the string. | |
2152 | + * @find: Pointer to the keyword. | |
2153 | + * | |
2154 | + * Returns true if @src starts with @find, false otherwise. | |
2155 | + * | |
2156 | + * The @src is updated to point the first character after the @find | |
2157 | + * if @src starts with @find. | |
2158 | + */ | |
2159 | +static bool ccs_str_starts(char **src, const char *find) | |
2160 | +{ | |
2161 | + const int len = strlen(find); | |
2162 | + char *tmp = *src; | |
2163 | + if (strncmp(tmp, find, len)) | |
2164 | + return false; | |
2165 | + tmp += len; | |
2166 | + *src = tmp; | |
2167 | + return true; | |
2168 | +} | |
2169 | + | |
2170 | +/** | |
2171 | + * ccs_find_domain - Find a domain by the given name. | |
2172 | + * | |
2173 | + * @domainname: The domainname to find. | |
2174 | + * | |
2175 | + * Returns pointer to "struct ccs_domain_info" if found, NULL otherwise. | |
2176 | + * | |
2177 | + * Caller holds ccs_read_lock(). | |
2178 | + */ | |
2179 | +static struct ccs_domain_info *ccs_find_domain(const char *domainname) | |
2180 | +{ | |
2181 | + struct ccs_domain_info *domain; | |
2182 | + struct ccs_path_info name; | |
2183 | + name.name = domainname; | |
2184 | + ccs_fill_path_info(&name); | |
2185 | + list_for_each_entry_srcu(domain, &ccs_domain_list, list, &ccs_ss) { | |
2186 | + if (!ccs_pathcmp(&name, domain->domainname)) | |
2187 | + return domain; | |
2188 | + } | |
2189 | + return NULL; | |
2190 | +} | |
2191 | + | |
2192 | +/** | |
2193 | + * ccs_select_acl - Parse select command. | |
2194 | + * | |
2195 | + * @head: Pointer to "struct ccs_io_buffer". | |
2196 | + * @data: String to parse. | |
2197 | + * | |
2198 | + * Returns true on success, false otherwise. | |
2199 | + * | |
2200 | + * Caller holds ccs_read_lock(). | |
2201 | + */ | |
2202 | +static bool ccs_select_acl(struct ccs_io_buffer *head, const char *data) | |
2203 | +{ | |
2204 | + unsigned int qid; | |
2205 | + struct ccs_acl_info *acl; | |
2206 | + if (sscanf(data, "Q=%u", &qid) != 1) | |
2207 | + return false; | |
2208 | + acl = ccs_find_acl_by_qid(qid); | |
2209 | + head->w.acl = acl; | |
2210 | + /* Accessing read_buf is safe because head->io_sem is held. */ | |
2211 | + if (!head->read_buf) | |
2212 | + return true; /* Do nothing if open(O_WRONLY). */ | |
2213 | + memset(&head->r, 0, sizeof(head->r)); | |
2214 | + head->r.print_this_acl_only = true; | |
2215 | + if (acl) | |
2216 | + head->r.acl = &acl->list; | |
2217 | + else | |
2218 | + head->r.eof = true; | |
2219 | + ccs_io_printf(head, "# Q=%u\n", qid); | |
2220 | + return true; | |
2221 | +} | |
2222 | + | |
2223 | +/** | |
2224 | + * ccs_update_acl - Update "struct ccs_acl_info" entry. | |
2225 | + * | |
2226 | + * @list: Pointer to "struct list_head". | |
2227 | + * @head: Pointer to "struct ccs_io_buffer". | |
2228 | + * @update: True to store matching entry, false otherwise. | |
2229 | + * | |
2230 | + * Returns 0 on success, negative value otherwise. | |
2231 | + * | |
2232 | + * Caller holds ccs_read_lock(). | |
2233 | + */ | |
2234 | +static int ccs_update_acl(struct list_head * const list, | |
2235 | + struct ccs_io_buffer *head, const bool update) | |
2236 | +{ | |
2237 | + struct ccs_acl_info *ptr; | |
2238 | + struct ccs_acl_info new_entry = { }; | |
2239 | + const bool is_delete = head->w.is_delete; | |
2240 | + int error = is_delete ? -ENOENT : -ENOMEM; | |
2241 | + new_entry.priority = head->w.priority; | |
2242 | + new_entry.is_deny = head->w.is_deny; | |
2243 | + if (head->w.data[0]) { | |
2244 | + new_entry.cond = ccs_get_condition(head); | |
2245 | + if (!new_entry.cond) | |
2246 | + return -EINVAL; | |
2247 | + } | |
2248 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | |
2249 | + goto out; | |
2250 | + list_for_each_entry_srcu(ptr, list, list, &ccs_ss) { | |
2251 | + if (ptr->priority > new_entry.priority) | |
2252 | + break; | |
2253 | + /* | |
2254 | + * We cannot reuse deleted "struct ccs_acl_info" entry because | |
2255 | + * somebody might be referencing children of this deleted entry | |
2256 | + * from srcu section. We cannot delete children of this deleted | |
2257 | + * entry until all children are no longer referenced. Thus, let | |
2258 | + * the garbage collector wait and delete rather than trying to | |
2259 | + * reuse this deleted entry. | |
2260 | + */ | |
2261 | + if (ptr->is_deleted || ptr->cond != new_entry.cond || | |
2262 | + ptr->priority != new_entry.priority || | |
2263 | + ptr->is_deny != new_entry.is_deny) | |
2264 | + continue; | |
2265 | + ptr->is_deleted = is_delete; | |
2266 | + if (!is_delete && update) | |
2267 | + head->w.acl = ptr; | |
2268 | + error = 0; | |
2269 | + break; | |
2270 | + } | |
2271 | + if (error && !is_delete) { | |
2272 | + struct ccs_acl_info *entry = | |
2273 | + ccs_commit_ok(&new_entry, sizeof(new_entry)); | |
2274 | + if (entry) { | |
2275 | + INIT_LIST_HEAD(&entry->acl_info_list); | |
2276 | + list_add_tail_rcu(&entry->list, &ptr->list); | |
2277 | + if (update) | |
2278 | + head->w.acl = entry; | |
2279 | + } | |
2280 | + } | |
2281 | + mutex_unlock(&ccs_policy_lock); | |
2282 | +out: | |
2283 | + ccs_put_condition(new_entry.cond); | |
2284 | + return error; | |
2285 | +} | |
2286 | + | |
2287 | +/** | |
2288 | + * ccs_parse_entry - Update ACL entry. | |
2289 | + * | |
2290 | + * @head: Pointer to "struct ccs_io_buffer". | |
2291 | + * | |
2292 | + * Returns 0 on success, negative value otherwise. | |
2293 | + * | |
2294 | + * Caller holds ccs_read_lock(). | |
2295 | + */ | |
2296 | +static int ccs_parse_entry(struct ccs_io_buffer *head) | |
2297 | +{ | |
2298 | + enum ccs_mac_index type; | |
2299 | + const char *operation = ccs_read_token(head); | |
2300 | + for (type = 0; type < CCS_MAX_MAC_INDEX; type++) { | |
2301 | + if (strcmp(operation, ccs_mac_keywords[type])) | |
2302 | + continue; | |
2303 | + head->w.acl_index = type; | |
2304 | + /* | |
2305 | + * This is_deny is for rejecting handler= and transition= | |
2306 | + * arguments in "acl" line. handler= and transition= arguments | |
2307 | + * are accepted for only "allow" line. | |
2308 | + */ | |
2309 | + head->w.is_deny = true; | |
2310 | + return ccs_update_acl(&ccs_acl_list[type], head, true); | |
2311 | + } | |
2312 | + return -EINVAL; | |
2313 | +} | |
2314 | + | |
2315 | +/** | |
2316 | + * ccs_print_number - Print number argument. | |
2317 | + * | |
2318 | + * @head: Pointer to "struct ccs_io_buffer". | |
2319 | + * @radix: One of values in "enum ccs_value_type". | |
2320 | + * @value: Value to print. | |
2321 | + * | |
2322 | + * Returns nothing. | |
2323 | + */ | |
2324 | +static void ccs_print_number(struct ccs_io_buffer *head, | |
2325 | + const enum ccs_value_type radix, | |
2326 | + const unsigned long value) | |
2327 | +{ | |
2328 | + switch (radix) { | |
2329 | + case CCS_VALUE_TYPE_HEXADECIMAL: | |
2330 | + ccs_io_printf(head, "0x%lX", value); | |
2331 | + break; | |
2332 | + case CCS_VALUE_TYPE_OCTAL: | |
2333 | + ccs_io_printf(head, "0%lo", value); | |
2334 | + break; | |
2335 | + default: | |
2336 | + ccs_io_printf(head, "%lu", value); | |
2337 | + } | |
2338 | +} | |
2339 | + | |
2340 | +/** | |
2341 | + * ccs_print_misc_attribute - Print misc attribute's name. | |
2342 | + * | |
2343 | + * @head: Pointer to "struct ccs_io_buffer". | |
2344 | + * @type: One of values in "enum ccs_mac_index". | |
2345 | + * @cond: One of values in "enum ccs_condition_index". | |
2346 | + * | |
2347 | + * Returns nothing. | |
2348 | + */ | |
2349 | +static void ccs_print_misc_attribute(struct ccs_io_buffer *head, | |
2350 | + const enum ccs_mac_index type, | |
2351 | + const enum ccs_conditions_index cond) | |
2352 | +{ | |
2353 | + if (cond >= CCS_PATH_ATTRIBUTE_START) { | |
2354 | + const u8 pos = cond - CCS_PATH_ATTRIBUTE_START; | |
2355 | + ccs_io_printf(head, "%s.%s%s", ccs_get_sarg(type, pos >= 32), | |
2356 | + pos % 32 >= 16 ? "parent." : "", | |
2357 | + ccs_path_attribute[pos % 16]); | |
2358 | + return; | |
2359 | + } | |
2360 | + if (cond == CCS_COND_DOMAIN) { | |
2361 | + ccs_set_string(head, "domain"); | |
2362 | + return; | |
2363 | + } | |
2364 | + switch (cond) { | |
2365 | + case CCS_SELF_UID: | |
2366 | + case CCS_SELF_EUID: | |
2367 | + case CCS_SELF_SUID: | |
2368 | + case CCS_SELF_FSUID: | |
2369 | + case CCS_SELF_GID: | |
2370 | + case CCS_SELF_EGID: | |
2371 | + case CCS_SELF_SGID: | |
2372 | + case CCS_SELF_FSGID: | |
2373 | + case CCS_SELF_PID: | |
2374 | + case CCS_SELF_PPID: | |
2375 | + case CCS_TASK_TYPE: | |
2376 | + case CCS_SELF_DOMAIN: | |
2377 | + case CCS_SELF_EXE: | |
2378 | + ccs_set_string(head, "task."); | |
2379 | + /* fall through */ | |
2380 | + default: | |
2381 | + if (cond < CCS_MAX_CONDITION_KEYWORD) | |
2382 | + ccs_set_string(head, ccs_condition_keyword[cond]); | |
2383 | + else | |
2384 | + ccs_io_printf(head, "unknown(%u)", cond); | |
2385 | + } | |
2386 | +} | |
2387 | + | |
2388 | +/** | |
2389 | + * ccs_print_condition_loop - Print condition part. | |
2390 | + * | |
2391 | + * @head: Pointer to "struct ccs_io_buffer". | |
2392 | + * @cond: Pointer to "struct ccs_condition". | |
2393 | + * | |
2394 | + * Returns true on success, false otherwise. | |
2395 | + */ | |
2396 | +static bool ccs_print_condition_loop(struct ccs_io_buffer *head, | |
2397 | + const struct ccs_condition *cond) | |
2398 | +{ | |
2399 | + const enum ccs_mac_index type = head->r.acl_index; | |
2400 | + const union ccs_condition_element *condp = head->r.cond; | |
2401 | + while ((void *) condp < (void *) ((u8 *) cond) + cond->size) { | |
2402 | + const bool is_not = condp->is_not; | |
2403 | + const enum ccs_conditions_index left = condp->left; | |
2404 | + const enum ccs_conditions_index right = condp->right; | |
2405 | + const u8 radix = condp->radix; | |
2406 | + if (!ccs_flush(head)) { | |
2407 | + head->r.cond = condp; | |
2408 | + return false; | |
2409 | + } | |
2410 | + condp++; | |
2411 | + ccs_set_space(head); | |
2412 | + switch (left) { | |
2413 | + case CCS_ARGV_ENTRY: | |
2414 | + ccs_io_printf(head, "argv[%lu]", condp->value); | |
2415 | + condp++; | |
2416 | + break; | |
2417 | + case CCS_ENVP_ENTRY: | |
2418 | + ccs_set_string(head, "envp[\""); | |
2419 | + ccs_set_string(head, condp->path->name); | |
2420 | + condp++; | |
2421 | + ccs_set_string(head, "\"]"); | |
2422 | + break; | |
2423 | + case CCS_COND_SARG0: | |
2424 | + ccs_set_string(head, ccs_get_sarg(type, 0)); | |
2425 | + break; | |
2426 | + case CCS_COND_SARG1: | |
2427 | + ccs_set_string(head, ccs_get_sarg(type, 1)); | |
2428 | + break; | |
2429 | + case CCS_COND_SARG2: | |
2430 | + ccs_set_string(head, ccs_get_sarg(type, 2)); | |
2431 | + break; | |
2432 | + case CCS_COND_SARG3: | |
2433 | + ccs_set_string(head, ccs_get_sarg(type, 3)); | |
2434 | + break; | |
2435 | + case CCS_COND_NARG0: | |
2436 | + ccs_set_string(head, ccs_get_narg(type, 0)); | |
2437 | + break; | |
2438 | + case CCS_COND_NARG1: | |
2439 | + ccs_set_string(head, ccs_get_narg(type, 1)); | |
2440 | + break; | |
2441 | + case CCS_COND_NARG2: | |
2442 | + ccs_set_string(head, ccs_get_narg(type, 2)); | |
2443 | + break; | |
2444 | + case CCS_COND_IPARG: | |
2445 | + ccs_set_string(head, "ip"); | |
2446 | + break; | |
2447 | + default: | |
2448 | + ccs_print_misc_attribute(head, type, left); | |
2449 | + } | |
2450 | + ccs_set_string(head, is_not ? "!=" : "="); | |
2451 | + switch (right) { | |
2452 | + case CCS_IMM_GROUP: | |
2453 | + ccs_set_string(head, "@"); | |
2454 | + ccs_set_string(head, condp->group->group_name->name); | |
2455 | + condp++; | |
2456 | + break; | |
2457 | + case CCS_IMM_NAME_ENTRY: | |
2458 | + if (condp->path != &ccs_null_name) { | |
2459 | + ccs_set_string(head, "\""); | |
2460 | + ccs_set_string(head, condp->path->name); | |
2461 | + ccs_set_string(head, "\""); | |
2462 | + } else { | |
2463 | + ccs_set_string(head, "NULL"); | |
2464 | + } | |
2465 | + condp++; | |
2466 | + break; | |
2467 | + case CCS_IMM_NUMBER_ENTRY1: | |
2468 | + case CCS_IMM_NUMBER_ENTRY2: | |
2469 | + ccs_print_number(head, radix & 3, condp->value); | |
2470 | + condp++; | |
2471 | + if (right == CCS_IMM_NUMBER_ENTRY1) | |
2472 | + break; | |
2473 | + ccs_set_string(head, "-"); | |
2474 | + ccs_print_number(head, (radix >> 2) & 3, condp->value); | |
2475 | + condp++; | |
2476 | + break; | |
2477 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
2478 | + case CCS_IMM_IPV4ADDR_ENTRY1: | |
2479 | + case CCS_IMM_IPV4ADDR_ENTRY2: | |
2480 | + ccs_print_ipv4(head, &condp->ip); | |
2481 | + condp++; | |
2482 | + if (right == CCS_IMM_IPV4ADDR_ENTRY1) | |
2483 | + break; | |
2484 | + ccs_set_string(head, "-"); | |
2485 | + ccs_print_ipv4(head, &condp->ip); | |
2486 | + condp++; | |
2487 | + break; | |
2488 | + case CCS_IMM_IPV6ADDR_ENTRY1: | |
2489 | + case CCS_IMM_IPV6ADDR_ENTRY2: | |
2490 | + ccs_print_ipv6(head, (const struct in6_addr *) condp); | |
2491 | + condp = (void *) | |
2492 | + ((u8 *) condp) + sizeof(struct in6_addr); | |
2493 | + if (right == CCS_IMM_IPV6ADDR_ENTRY1) | |
2494 | + break; | |
2495 | + ccs_set_string(head, "-"); | |
2496 | + ccs_print_ipv6(head, (const struct in6_addr *) condp); | |
2497 | + condp = (void *) | |
2498 | + ((u8 *) condp) + sizeof(struct in6_addr); | |
2499 | + break; | |
2500 | +#endif | |
2501 | + default: | |
2502 | + ccs_print_misc_attribute(head, type, right); | |
2503 | + } | |
2504 | + } | |
2505 | + head->r.cond = NULL; | |
2506 | + return true; | |
2507 | +} | |
2508 | + | |
2509 | +/** | |
2510 | + * ccs_print_condition - Print condition part. | |
2511 | + * | |
2512 | + * @head: Pointer to "struct ccs_io_buffer". | |
2513 | + * @cond: Pointer to "struct ccs_condition". | |
2514 | + * | |
2515 | + * Returns true on success, false otherwise. | |
2516 | + */ | |
2517 | +static bool ccs_print_condition(struct ccs_io_buffer *head, | |
2518 | + const struct ccs_condition *cond) | |
2519 | +{ | |
2520 | + switch (head->r.cond_step) { | |
2521 | + case 0: | |
2522 | + head->r.cond = (const union ccs_condition_element *) | |
2523 | + (cond + 1); | |
2524 | + head->r.cond_step++; | |
2525 | + /* fall through */ | |
2526 | + case 1: | |
2527 | + if (!ccs_print_condition_loop(head, cond)) | |
2528 | + return false; | |
2529 | + head->r.cond_step++; | |
2530 | + /* fall through */ | |
2531 | + case 2: | |
2532 | + head->r.cond = NULL; | |
2533 | + return true; | |
2534 | + } | |
2535 | + return false; | |
2536 | +} | |
2537 | + | |
2538 | +/** | |
2539 | + * ccs_read_acl - Print an ACL entry. | |
2540 | + * | |
2541 | + * @head: Pointer to "struct ccs_io_buffer". | |
2542 | + * @acl: Pointer to an ACL entry. | |
2543 | + * | |
2544 | + * Returns true on success, false otherwise. | |
2545 | + */ | |
2546 | +static bool ccs_read_acl(struct ccs_io_buffer *head, | |
2547 | + const struct ccs_acl_info *acl) | |
2548 | +{ | |
2549 | + const enum ccs_mac_index type = head->r.acl_index; | |
2550 | + if (head->r.cond) | |
2551 | + goto print_cond_part; | |
2552 | + if (acl->is_deleted) | |
2553 | + return true; | |
2554 | + if (!ccs_flush(head)) | |
2555 | + return false; | |
2556 | + ccs_io_printf(head, "%u ", acl->priority); | |
2557 | + ccs_set_string(head, "acl "); | |
2558 | + ccs_set_string(head, ccs_mac_keywords[type]); | |
2559 | + if (acl->cond) { | |
2560 | + head->r.cond_step = 0; | |
2561 | +print_cond_part: | |
2562 | + if (!ccs_print_condition(head, acl->cond)) | |
2563 | + return false; | |
2564 | + } | |
2565 | + ccs_set_lf(head); | |
2566 | + return true; | |
2567 | +} | |
2568 | + | |
2569 | +/** | |
2570 | + * ccs_write_pid - Specify PID to obtain domainname. | |
2571 | + * | |
2572 | + * @head: Pointer to "struct ccs_io_buffer". | |
2573 | + * | |
2574 | + * Returns 0. | |
2575 | + */ | |
2576 | +static int ccs_write_pid(struct ccs_io_buffer *head) | |
2577 | +{ | |
2578 | + head->r.eof = false; | |
2579 | + return 0; | |
2580 | +} | |
2581 | + | |
2582 | +/** | |
2583 | + * ccs_read_pid - Read information of a process. | |
2584 | + * | |
2585 | + * @head: Pointer to "struct ccs_io_buffer". | |
2586 | + * | |
2587 | + * Returns nothing. | |
2588 | + * | |
2589 | + * Reads the domainname which the specified PID is in or | |
2590 | + * process information of the specified PID on success. | |
2591 | + * | |
2592 | + * Caller holds ccs_read_lock(). | |
2593 | + */ | |
2594 | +static void ccs_read_pid(struct ccs_io_buffer *head) | |
2595 | +{ | |
2596 | + char *buf = head->write_buf; | |
2597 | + bool task_info = false; | |
2598 | + bool global_pid = false; | |
2599 | + unsigned int pid; | |
2600 | + struct task_struct *p; | |
2601 | + struct ccs_domain_info *domain = NULL; | |
2602 | + u32 ccs_flags = 0; | |
2603 | + /* Accessing write_buf is safe because head->io_sem is held. */ | |
2604 | + if (!buf) { | |
2605 | + head->r.eof = true; | |
2606 | + return; /* Do nothing if open(O_RDONLY). */ | |
2607 | + } | |
2608 | + if (head->r.w_pos || head->r.eof) | |
2609 | + return; | |
2610 | + head->r.eof = true; | |
2611 | + if (ccs_str_starts(&buf, "info ")) | |
2612 | + task_info = true; | |
2613 | + if (ccs_str_starts(&buf, "global-pid ")) | |
2614 | + global_pid = true; | |
2615 | + pid = (unsigned int) simple_strtoul(buf, NULL, 10); | |
2616 | + ccs_tasklist_lock(); | |
2617 | + if (global_pid) | |
2618 | + p = ccsecurity_exports.find_task_by_pid_ns(pid, &init_pid_ns); | |
2619 | + else | |
2620 | + p = ccsecurity_exports.find_task_by_vpid(pid); | |
2621 | + if (p) { | |
2622 | + domain = ccs_task_domain(p); | |
2623 | + ccs_flags = ccs_task_flags(p); | |
2624 | + } | |
2625 | + ccs_tasklist_unlock(); | |
2626 | + if (!domain) | |
2627 | + return; | |
2628 | + if (!task_info) { | |
2629 | + ccs_io_printf(head, "%u ", pid); | |
2630 | + ccs_set_string(head, domain->domainname->name); | |
2631 | + } else { | |
2632 | + ccs_io_printf(head, "%u manager=%s execute_handler=%s ", pid, | |
2633 | + ccs_yesno(ccs_flags & | |
2634 | + CCS_TASK_IS_MANAGER), | |
2635 | + ccs_yesno(ccs_flags & | |
2636 | + CCS_TASK_IS_EXECUTE_HANDLER)); | |
2637 | + } | |
2638 | +} | |
2639 | + | |
2640 | +/** | |
2641 | + * ccs_update_group - Update "struct ccs_string_group"/"struct ccs_number_group"/"struct ccs_ip_group" list. | |
2642 | + * | |
2643 | + * @head: Pointer to "struct ccs_io_buffer". | |
2644 | + * @type: Type of this group. | |
2645 | + * | |
2646 | + * Returns 0 on success, negative value otherwise. | |
2647 | + */ | |
2648 | +static int ccs_update_group(struct ccs_io_buffer *head, | |
2649 | + const enum ccs_group_id type) | |
2650 | +{ | |
2651 | + u8 size; | |
2652 | + const bool is_delete = head->w.is_delete; | |
2653 | + int error = is_delete ? -ENOENT : -ENOMEM; | |
2654 | + struct ccs_group *group = ccs_get_group(head, type); | |
2655 | + char *word = ccs_read_token(head); | |
2656 | + union { | |
2657 | + struct ccs_acl_head head; | |
2658 | + struct ccs_string_group path; | |
2659 | + struct ccs_number_group number; | |
2660 | + struct ccs_ip_group address; | |
2661 | + } e = { }; | |
2662 | + if (!group) | |
2663 | + return -ENOMEM; | |
2664 | + if (!*word) { | |
2665 | + error = -EINVAL; | |
2666 | + goto out; | |
2667 | + } | |
2668 | + if (type == CCS_STRING_GROUP) { | |
2669 | + if (!ccs_correct_word(word)) { | |
2670 | + error = -EINVAL; | |
2671 | + goto out; | |
2672 | + } | |
2673 | + e.path.member_name = ccs_get_name(word); | |
2674 | + if (!e.path.member_name) { | |
2675 | + error = -ENOMEM; | |
2676 | + goto out; | |
2677 | + } | |
2678 | + size = sizeof(e.path); | |
2679 | + } else if (type == CCS_NUMBER_GROUP) { | |
2680 | + e.number.radix = ccs_parse_values(word, e.number.value); | |
2681 | + if (e.number.radix == CCS_VALUE_TYPE_INVALID) | |
2682 | + goto out; | |
2683 | + size = sizeof(e.number); | |
2684 | + } else { | |
2685 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
2686 | + switch (ccs_parse_ipaddr(word, e.address.ip)) { | |
2687 | + case CCS_ADDRESS_TYPE_IPV4: | |
2688 | + case CCS_ADDRESS_TYPE_IPV4_RANGE: | |
2689 | + e.address.is_ipv6 = false; | |
2690 | + break; | |
2691 | + case CCS_ADDRESS_TYPE_IPV6: | |
2692 | + case CCS_ADDRESS_TYPE_IPV6_RANGE: | |
2693 | + e.address.is_ipv6 = true; | |
2694 | + break; | |
2695 | + default: | |
2696 | + goto out; | |
2697 | + } | |
2698 | + size = sizeof(e.address); | |
2699 | +#else | |
2700 | + goto out; | |
2701 | +#endif | |
2702 | + } | |
2703 | + if (mutex_lock_interruptible(&ccs_policy_lock) == 0) { | |
2704 | + struct ccs_acl_head *entry; | |
2705 | + list_for_each_entry_srcu(entry, &group->member_list, | |
2706 | + list, &ccs_ss) { | |
2707 | + if (entry->is_deleted == CCS_GC_IN_PROGRESS || | |
2708 | + memcmp(entry + 1, &e.head + 1, | |
2709 | + size - sizeof(*entry))) | |
2710 | + continue; | |
2711 | + entry->is_deleted = is_delete; | |
2712 | + error = 0; | |
2713 | + break; | |
2714 | + } | |
2715 | + if (error && !is_delete) { | |
2716 | + entry = ccs_commit_ok(&e, size); | |
2717 | + if (entry) { | |
2718 | + list_add_tail_rcu(&entry->list, | |
2719 | + &group->member_list); | |
2720 | + error = 0; | |
2721 | + } | |
2722 | + } | |
2723 | + mutex_unlock(&ccs_policy_lock); | |
2724 | + } | |
2725 | + if (type == CCS_STRING_GROUP) | |
2726 | + ccs_put_name(e.path.member_name); | |
2727 | +out: | |
2728 | + ccs_put_group(group); | |
2729 | + return error; | |
2730 | +} | |
2731 | + | |
2732 | +/** | |
2733 | + * ccs_write_policy - Write policy. | |
2734 | + * | |
2735 | + * @head: Pointer to "struct ccs_io_buffer". | |
2736 | + * | |
2737 | + * Returns 0 on success, negative value otherwise. | |
2738 | + */ | |
2739 | +static int ccs_write_policy(struct ccs_io_buffer *head) | |
2740 | +{ | |
2741 | + enum ccs_group_id i; | |
2742 | + unsigned int priority; | |
2743 | + char *word = ccs_read_token(head); | |
2744 | + if (sscanf(word, "%u", &priority) == 1) | |
2745 | + word = ccs_read_token(head); | |
2746 | + else | |
2747 | + priority = 1000; | |
2748 | + if (priority >= 65536 || !*word) | |
2749 | + return -EINVAL; | |
2750 | + head->w.priority = priority; | |
2751 | + if (!head->w.acl) | |
2752 | + goto no_acl_selected; | |
2753 | + head->w.is_deny = !strcmp(word, "deny"); | |
2754 | + if (head->w.is_deny || !strcmp(word, "allow")) | |
2755 | + return ccs_update_acl(&head->w.acl->acl_info_list, head, | |
2756 | + false); | |
2757 | + if (!strcmp(word, "audit")) { | |
2758 | + head->w.acl->audit = simple_strtoul(head->w.data, NULL, 10); | |
2759 | + return 0; | |
2760 | + } | |
2761 | + head->w.acl = NULL; | |
2762 | +no_acl_selected: | |
2763 | + if (ccs_select_acl(head, word)) | |
2764 | + return 0; | |
2765 | + if (!strcmp(word, "acl")) | |
2766 | + return ccs_parse_entry(head); | |
2767 | + for (i = 0; i < CCS_MAX_GROUP; i++) | |
2768 | + if (!strcmp(word, ccs_group_name[i])) | |
2769 | + return ccs_update_group(head, i); | |
2770 | + if (sscanf(word, "POLICY_VERSION=%u", &ccs_policy_version) == 1) | |
2771 | + return 0; | |
2772 | + if (strcmp(word, "quota")) | |
2773 | + return -EINVAL; | |
2774 | + if (ccs_str_starts(&head->w.data, "memory ")) | |
2775 | + return ccs_write_memory_quota(head->w.data); | |
2776 | + return ccs_write_audit_quota(head->w.data); | |
2777 | +} | |
2778 | + | |
2779 | +/** | |
2780 | + * ccs_read_subgroup - Read "struct ccs_string_group"/"struct ccs_number_group"/"struct ccs_ip_group" list. | |
2781 | + * | |
2782 | + * @head: Pointer to "struct ccs_io_buffer". | |
2783 | + * @group: Pointer to "struct ccs_group". | |
2784 | + * @idx: One of values in "enum ccs_group_id". | |
2785 | + * | |
2786 | + * Returns true on success, false otherwise. | |
2787 | + * | |
2788 | + * Caller holds ccs_read_lock(). | |
2789 | + */ | |
2790 | +static bool ccs_read_subgroup(struct ccs_io_buffer *head, | |
2791 | + struct ccs_group *group, | |
2792 | + const enum ccs_group_id idx) | |
2793 | +{ | |
2794 | + list_for_each_cookie(head->r.acl, &group->member_list) { | |
2795 | + struct ccs_acl_head *ptr = | |
2796 | + list_entry(head->r.acl, typeof(*ptr), list); | |
2797 | + if (ptr->is_deleted) | |
2798 | + continue; | |
2799 | + if (!ccs_flush(head)) | |
2800 | + return false; | |
2801 | + ccs_set_string(head, ccs_group_name[idx]); | |
2802 | + ccs_set_space(head); | |
2803 | + ccs_set_string(head, group->group_name->name); | |
2804 | + ccs_set_space(head); | |
2805 | + if (idx == CCS_STRING_GROUP) { | |
2806 | + ccs_set_string(head, container_of | |
2807 | + (ptr, struct ccs_string_group, | |
2808 | + head)->member_name->name); | |
2809 | + } else if (idx == CCS_NUMBER_GROUP) { | |
2810 | + struct ccs_number_group *e = | |
2811 | + container_of(ptr, typeof(*e), head); | |
2812 | + ccs_print_number(head, e->radix & 3, e->value[0]); | |
2813 | + if (e->radix >> 2) { | |
2814 | + ccs_set_string(head, "-"); | |
2815 | + ccs_print_number(head, (e->radix >> 2) & 3, | |
2816 | + e->value[1]); | |
2817 | + } | |
2818 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
2819 | + } else if (idx == CCS_IP_GROUP) { | |
2820 | + ccs_print_ip(head, container_of | |
2821 | + (ptr, struct ccs_ip_group, head)); | |
2822 | +#endif | |
2823 | + } | |
2824 | + ccs_set_lf(head); | |
2825 | + } | |
2826 | + head->r.acl = NULL; | |
2827 | + return true; | |
2828 | +} | |
2829 | + | |
2830 | +/** | |
2831 | + * ccs_read_group - Read "struct ccs_string_group"/"struct ccs_number_group"/"struct ccs_ip_group" list. | |
2832 | + * | |
2833 | + * @head: Pointer to "struct ccs_io_buffer". | |
2834 | + * | |
2835 | + * Returns true on success, false otherwise. | |
2836 | + * | |
2837 | + * Caller holds ccs_read_lock(). | |
2838 | + */ | |
2839 | +static bool ccs_read_group(struct ccs_io_buffer *head) | |
2840 | +{ | |
2841 | + while (head->r.step < CCS_MAX_GROUP) { | |
2842 | + const enum ccs_group_id idx = head->r.step; | |
2843 | + struct list_head *list = &ccs_group_list[idx]; | |
2844 | + list_for_each_cookie(head->r.group, list) { | |
2845 | + struct ccs_group *group = | |
2846 | + list_entry(head->r.group, typeof(*group), | |
2847 | + head.list); | |
2848 | + if (!ccs_read_subgroup(head, group, idx)) | |
2849 | + return false; | |
2850 | + } | |
2851 | + head->r.group = NULL; | |
2852 | + head->r.step++; | |
2853 | + } | |
2854 | + head->r.step = 0; | |
2855 | + return true; | |
2856 | +} | |
2857 | + | |
2858 | +/** | |
2859 | + * ccs_supervisor - Ask for the supervisor's decision. | |
2860 | + * | |
2861 | + * @r: Pointer to "struct ccs_request_info". | |
2862 | + * | |
2863 | + * Returns 0 if the supervisor decided to permit the access request, | |
2864 | + * CCS_RETRY_REQUEST if the supervisor decided to retry the access request, | |
2865 | + * -EPERM otherwise. | |
2866 | + */ | |
2867 | +static int ccs_supervisor(struct ccs_request_info *r) | |
2868 | +{ | |
2869 | + int error = -EPERM; | |
2870 | + int len; | |
2871 | + static unsigned int ccs_serial; | |
2872 | + struct ccs_query entry = { }; | |
2873 | + bool quota_exceeded = false; | |
2874 | + if (!r->matched_acl) | |
2875 | + return -EPERM; | |
2876 | + /* Get message. */ | |
2877 | + entry.query = ccs_init_log(r); | |
2878 | + if (!entry.query) | |
2879 | + return -EPERM; | |
2880 | + entry.query_len = strlen(entry.query) + 1; | |
2881 | + len = ccs_round2(entry.query_len); | |
2882 | + entry.acl = r->matched_acl; | |
2883 | + spin_lock(&ccs_query_list_lock); | |
2884 | + if (ccs_memory_quota[CCS_MEMORY_QUERY] && | |
2885 | + ccs_memory_used[CCS_MEMORY_QUERY] + len | |
2886 | + >= ccs_memory_quota[CCS_MEMORY_QUERY]) { | |
2887 | + quota_exceeded = true; | |
2888 | + } else { | |
2889 | + entry.serial = ccs_serial++; | |
2890 | + entry.retry = r->retry; | |
2891 | + ccs_memory_used[CCS_MEMORY_QUERY] += len; | |
2892 | + list_add_tail(&entry.list, &ccs_query_list); | |
2893 | + } | |
2894 | + spin_unlock(&ccs_query_list_lock); | |
2895 | + if (quota_exceeded) | |
2896 | + goto out; | |
2897 | + /* Give 10 seconds for supervisor's opinion. */ | |
2898 | + while (entry.timer < 10) { | |
2899 | + wake_up_all(&ccs_query_wait); | |
2900 | + if (wait_event_interruptible_timeout | |
2901 | + (ccs_answer_wait, entry.answer || | |
2902 | + !atomic_read(&ccs_query_observers), HZ)) | |
2903 | + break; | |
2904 | + else | |
2905 | + entry.timer++; | |
2906 | + } | |
2907 | + spin_lock(&ccs_query_list_lock); | |
2908 | + list_del(&entry.list); | |
2909 | + ccs_memory_used[CCS_MEMORY_QUERY] -= len; | |
2910 | + spin_unlock(&ccs_query_list_lock); | |
2911 | + switch (entry.answer) { | |
2912 | + case 3: /* Asked to retry by administrator. */ | |
2913 | + error = CCS_RETRY_REQUEST; | |
2914 | + r->retry++; | |
2915 | + break; | |
2916 | + case 1: | |
2917 | + /* Granted by administrator. */ | |
2918 | + error = 0; | |
2919 | + break; | |
2920 | + default: | |
2921 | + /* Timed out or rejected by administrator. */ | |
2922 | + break; | |
2923 | + } | |
2924 | +out: | |
2925 | + kfree(entry.query); | |
2926 | + return error; | |
2927 | +} | |
2928 | + | |
2929 | +/** | |
2930 | + * ccs_audit_log - Audit permission check log. | |
2931 | + * | |
2932 | + * @r: Pointer to "struct ccs_request_info". | |
2933 | + * | |
2934 | + * Returns 0 to grant the request, CCS_RETRY_REQUEST to retry the permission | |
2935 | + * check, -EPERM otherwise. | |
2936 | + */ | |
2937 | +int ccs_audit_log(struct ccs_request_info *r) | |
2938 | +{ | |
2939 | + /* Do not reject if not yet activated. */ | |
2940 | + if (!ccs_policy_loaded) | |
2941 | + return 0; | |
2942 | + /* Write /proc/ccs/audit unless quota exceeded. */ | |
2943 | + if (ccs_log_count[r->result] < ccs_log_quota[r->audit][r->result]) | |
2944 | + ccs_write_log(r); | |
2945 | + /* Nothing more to do unless denied. */ | |
2946 | + if (r->result != CCS_MATCHING_DENIED) | |
2947 | + return 0; | |
2948 | + /* Update policy violation counter if denied. */ | |
2949 | + ccs_update_stat(CCS_STAT_REQUEST_DENIED); | |
2950 | + /* Nothing more to do unless ccs-queryd is running. */ | |
2951 | + if (!atomic_read(&ccs_query_observers)) | |
2952 | + return -EPERM; | |
2953 | + /* Ask the ccs-queryd for decision. */ | |
2954 | + return ccs_supervisor(r); | |
2955 | +} | |
2956 | + | |
2957 | +/** | |
2958 | + * ccs_find_acl_by_qid - Get ACL by query id. | |
2959 | + * | |
2960 | + * @serial: Query ID assigned by ccs_supervisor(). | |
2961 | + * | |
2962 | + * Returns pointer to "struct ccs_acl_info" if found, NULL otherwise. | |
2963 | + */ | |
2964 | +static struct ccs_acl_info *ccs_find_acl_by_qid(unsigned int serial) | |
2965 | +{ | |
2966 | + struct ccs_query *ptr; | |
2967 | + struct ccs_acl_info *acl = NULL; | |
2968 | + spin_lock(&ccs_query_list_lock); | |
2969 | + list_for_each_entry(ptr, &ccs_query_list, list) { | |
2970 | + if (ptr->serial != serial) | |
2971 | + continue; | |
2972 | + acl = ptr->acl; | |
2973 | + break; | |
2974 | + } | |
2975 | + spin_unlock(&ccs_query_list_lock); | |
2976 | + return acl; | |
2977 | +} | |
2978 | + | |
2979 | +/** | |
2980 | + * ccs_read_query - Read access requests which violated policy in enforcing mode. | |
2981 | + * | |
2982 | + * @head: Pointer to "struct ccs_io_buffer". | |
2983 | + * | |
2984 | + * Returns nothing. | |
2985 | + */ | |
2986 | +static void ccs_read_query(struct ccs_io_buffer *head) | |
2987 | +{ | |
2988 | + struct list_head *tmp; | |
2989 | + unsigned int pos = 0; | |
2990 | + size_t len = 0; | |
2991 | + char *buf; | |
2992 | + if (head->r.w_pos) | |
2993 | + return; | |
2994 | + kfree(head->read_buf); | |
2995 | + head->read_buf = NULL; | |
2996 | + spin_lock(&ccs_query_list_lock); | |
2997 | + list_for_each(tmp, &ccs_query_list) { | |
2998 | + struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | |
2999 | + if (pos++ != head->r.query_index) | |
3000 | + continue; | |
3001 | + len = ptr->query_len; | |
3002 | + break; | |
3003 | + } | |
3004 | + spin_unlock(&ccs_query_list_lock); | |
3005 | + if (!len) { | |
3006 | + head->r.query_index = 0; | |
3007 | + return; | |
3008 | + } | |
3009 | + buf = kzalloc(len + 32, GFP_NOFS); | |
3010 | + if (!buf) | |
3011 | + return; | |
3012 | + pos = 0; | |
3013 | + spin_lock(&ccs_query_list_lock); | |
3014 | + list_for_each(tmp, &ccs_query_list) { | |
3015 | + struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | |
3016 | + if (pos++ != head->r.query_index) | |
3017 | + continue; | |
3018 | + /* | |
3019 | + * Some query can be skipped because ccs_query_list | |
3020 | + * can change, but I don't care. | |
3021 | + */ | |
3022 | + if (len == ptr->query_len) | |
3023 | + snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial, | |
3024 | + ptr->retry, ptr->query); | |
3025 | + break; | |
3026 | + } | |
3027 | + spin_unlock(&ccs_query_list_lock); | |
3028 | + if (buf[0]) { | |
3029 | + head->read_buf = buf; | |
3030 | + head->r.w[head->r.w_pos++] = buf; | |
3031 | + head->r.query_index++; | |
3032 | + } else { | |
3033 | + kfree(buf); | |
3034 | + } | |
3035 | +} | |
3036 | + | |
3037 | +/** | |
3038 | + * ccs_write_answer - Write the supervisor's decision. | |
3039 | + * | |
3040 | + * @head: Pointer to "struct ccs_io_buffer". | |
3041 | + * | |
3042 | + * Returns 0 on success, -EINVAL otherwise. | |
3043 | + */ | |
3044 | +static int ccs_write_answer(struct ccs_io_buffer *head) | |
3045 | +{ | |
3046 | + char *data = head->write_buf; | |
3047 | + struct list_head *tmp; | |
3048 | + unsigned int serial; | |
3049 | + unsigned int answer; | |
3050 | + spin_lock(&ccs_query_list_lock); | |
3051 | + list_for_each(tmp, &ccs_query_list) { | |
3052 | + struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | |
3053 | + ptr->timer = 0; | |
3054 | + } | |
3055 | + spin_unlock(&ccs_query_list_lock); | |
3056 | + if (sscanf(data, "A%u=%u", &serial, &answer) != 2) | |
3057 | + return -EINVAL; | |
3058 | + spin_lock(&ccs_query_list_lock); | |
3059 | + list_for_each(tmp, &ccs_query_list) { | |
3060 | + struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | |
3061 | + if (ptr->serial != serial) | |
3062 | + continue; | |
3063 | + ptr->answer = (u8) answer; | |
3064 | + /* Remove from ccs_query_list. */ | |
3065 | + if (ptr->answer) { | |
3066 | + list_del(&ptr->list); | |
3067 | + INIT_LIST_HEAD(&ptr->list); | |
3068 | + } | |
3069 | + break; | |
3070 | + } | |
3071 | + spin_unlock(&ccs_query_list_lock); | |
3072 | + wake_up_all(&ccs_answer_wait); | |
3073 | + return 0; | |
3074 | +} | |
3075 | + | |
3076 | +/** | |
3077 | + * ccs_read_version - Get version. | |
3078 | + * | |
3079 | + * @head: Pointer to "struct ccs_io_buffer". | |
3080 | + * | |
3081 | + * Returns nothing. | |
3082 | + */ | |
3083 | +static void ccs_read_version(struct ccs_io_buffer *head) | |
3084 | +{ | |
3085 | + if (head->r.eof) | |
3086 | + return; | |
3087 | + ccs_set_string(head, "1.8.3"); | |
3088 | + head->r.eof = true; | |
3089 | +} | |
3090 | + | |
3091 | +/** | |
3092 | + * ccs_update_stat - Update statistic counters. | |
3093 | + * | |
3094 | + * @index: Index for policy type. | |
3095 | + * | |
3096 | + * Returns nothing. | |
3097 | + */ | |
3098 | +static void ccs_update_stat(const u8 index) | |
3099 | +{ | |
3100 | + struct timeval tv; | |
3101 | + do_gettimeofday(&tv); | |
3102 | + /* | |
3103 | + * I don't use atomic operations because race condition is not fatal. | |
3104 | + */ | |
3105 | + ccs_stat_updated[index]++; | |
3106 | + ccs_stat_modified[index] = tv.tv_sec; | |
3107 | +} | |
3108 | + | |
3109 | +/** | |
3110 | + * ccs_read_stat - Read statistic data. | |
3111 | + * | |
3112 | + * @head: Pointer to "struct ccs_io_buffer". | |
3113 | + * | |
3114 | + * Returns nothing. | |
3115 | + */ | |
3116 | +static void ccs_read_stat(struct ccs_io_buffer *head) | |
3117 | +{ | |
3118 | + u8 i; | |
3119 | + for (i = 0; i < CCS_MAX_POLICY_STAT; i++) { | |
3120 | + static const char * const k[CCS_MAX_POLICY_STAT] = { | |
3121 | + [CCS_STAT_POLICY_UPDATES] = "Policy updated:", | |
3122 | + [CCS_STAT_REQUEST_DENIED] = "Requests denied:", | |
3123 | + }; | |
3124 | + ccs_io_printf(head, "stat %s %u", k[i], ccs_stat_updated[i]); | |
3125 | + if (ccs_stat_modified[i]) { | |
3126 | + struct ccs_time stamp; | |
3127 | + ccs_convert_time(ccs_stat_modified[i], &stamp); | |
3128 | + ccs_io_printf(head, " (Last: %04u/%02u/%02u " | |
3129 | + "%02u:%02u:%02u)", | |
3130 | + stamp.year, stamp.month, stamp.day, | |
3131 | + stamp.hour, stamp.min, stamp.sec); | |
3132 | + } | |
3133 | + ccs_set_lf(head); | |
3134 | + } | |
3135 | + for (i = 0; i < CCS_MAX_MEMORY_STAT; i++) | |
3136 | + ccs_io_printf(head, "stat Memory used by %s: %u\n", | |
3137 | + ccs_memory_headers[i], ccs_memory_used[i]); | |
3138 | +} | |
3139 | + | |
3140 | +/** | |
3141 | + * ccs_read_quota - Read quota data. | |
3142 | + * | |
3143 | + * @head: Pointer to "struct ccs_io_buffer". | |
3144 | + * | |
3145 | + * Returns true on success, false otherwise. | |
3146 | + */ | |
3147 | +static bool ccs_read_quota(struct ccs_io_buffer *head) | |
3148 | +{ | |
3149 | + unsigned int i; | |
3150 | + while (head->r.step < CCS_MAX_MEMORY_STAT) { | |
3151 | + i = head->r.step++; | |
3152 | + if (!ccs_memory_quota[i]) | |
3153 | + continue; | |
3154 | + ccs_io_printf(head, "quota memory %s %u\n", | |
3155 | + ccs_memory_headers[i], ccs_memory_quota[i]); | |
3156 | + } | |
3157 | + while (head->r.step < CCS_MAX_GROUP + CCS_MAX_MEMORY_STAT) { | |
3158 | + unsigned int a; | |
3159 | + unsigned int d; | |
3160 | + unsigned int u; | |
3161 | + if (!ccs_flush(head)) | |
3162 | + return false; | |
3163 | + i = head->r.step - CCS_MAX_MEMORY_STAT; | |
3164 | + a = ccs_log_quota[i][CCS_MATCHING_ALLOWED]; | |
3165 | + d = ccs_log_quota[i][CCS_MATCHING_DENIED]; | |
3166 | + u = ccs_log_quota[i][CCS_MATCHING_UNMATCHED]; | |
3167 | + if (a || d || u) | |
3168 | + ccs_io_printf(head, "quota audit[%u] allowed=%u" | |
3169 | + " denied=%u unmatched=%u\n", i, a, d, u); | |
3170 | + head->r.step++; | |
3171 | + } | |
3172 | + head->r.step = 0; | |
3173 | + return true; | |
3174 | +} | |
3175 | + | |
3176 | +/** | |
3177 | + * ccs_write_memory_quota - Set memory quota. | |
3178 | + * | |
3179 | + * @data: Line to parse. | |
3180 | + * | |
3181 | + * Returns 0 on success, -EINVAL otherwise. | |
3182 | + */ | |
3183 | +static int ccs_write_memory_quota(char *data) | |
3184 | +{ | |
3185 | + u8 i; | |
3186 | + for (i = 0; i < CCS_MAX_MEMORY_STAT; i++) | |
3187 | + if (ccs_str_starts(&data, ccs_memory_headers[i])) { | |
3188 | + if (*data == ' ') | |
3189 | + data++; | |
3190 | + ccs_memory_quota[i] = | |
3191 | + simple_strtoul(data, NULL, 10); | |
3192 | + return 0; | |
3193 | + } | |
3194 | + return -EINVAL; | |
3195 | +} | |
3196 | + | |
3197 | +/** | |
3198 | + * ccs_write_audit_quota - Set audit log quota. | |
3199 | + * | |
3200 | + * @data: Line to parse. | |
3201 | + * | |
3202 | + * Returns 0 on success, -EINVAL otherwise. | |
3203 | + */ | |
3204 | +static int ccs_write_audit_quota(char *data) | |
3205 | +{ | |
3206 | + unsigned int i; | |
3207 | + if (sscanf(data, "audit[%u]", &i) != 1 || i >= CCS_MAX_LOG_QUOTA) | |
3208 | + return -EINVAL; | |
3209 | + data = strchr(data, ' '); | |
3210 | + if (!data++) | |
3211 | + return -EINVAL; | |
3212 | + while (1) { | |
3213 | + unsigned int logs; | |
3214 | + char *cp = strchr(data, ' '); | |
3215 | + if (cp) | |
3216 | + *cp++ = '\0'; | |
3217 | + if (sscanf(data, "allowed=%u", &logs) == 1) | |
3218 | + ccs_log_quota[i][CCS_MATCHING_ALLOWED] = logs; | |
3219 | + else if (sscanf(data, "denied=%u", &logs) == 1) | |
3220 | + ccs_log_quota[i][CCS_MATCHING_DENIED] = logs; | |
3221 | + else if (sscanf(data, "unmatched=%u", &logs) == 1) | |
3222 | + ccs_log_quota[i][CCS_MATCHING_UNMATCHED] = logs; | |
3223 | + if (!cp) | |
3224 | + break; | |
3225 | + data = cp; | |
3226 | + } | |
3227 | + return 0; | |
3228 | +} | |
3229 | + | |
3230 | +/** | |
3231 | + * ccs_print_bprm - Print "struct linux_binprm" for auditing. | |
3232 | + * | |
3233 | + * @bprm: Pointer to "struct linux_binprm". | |
3234 | + * @dump: Pointer to "struct ccs_page_dump". | |
3235 | + * | |
3236 | + * Returns the contents of @bprm on success, NULL otherwise. | |
3237 | + * | |
3238 | + * This function uses kzalloc(), so caller must kfree() if this function | |
3239 | + * didn't return NULL. | |
3240 | + */ | |
3241 | +static char *ccs_print_bprm(struct linux_binprm *bprm, | |
3242 | + struct ccs_page_dump *dump) | |
3243 | +{ | |
3244 | + static const int ccs_buffer_len = 4096 * 2; | |
3245 | + char *buffer = kzalloc(ccs_buffer_len, GFP_NOFS); | |
3246 | + char *cp; | |
3247 | + char *last_start; | |
3248 | + unsigned long pos = bprm->p; | |
3249 | + int offset = pos % PAGE_SIZE; | |
3250 | + int argv_count = bprm->argc; | |
3251 | + int envp_count = bprm->envc; | |
3252 | + bool skip = false; | |
3253 | + bool env_value = false; | |
3254 | + if (!buffer) | |
3255 | + return NULL; | |
3256 | + cp = buffer + snprintf(buffer, ccs_buffer_len - 1, " argc=%d envc=%d", | |
3257 | + argv_count, envp_count); | |
3258 | + last_start = cp; | |
3259 | + while (argv_count || envp_count) { | |
3260 | + if (!ccs_dump_page(bprm, pos, dump)) { | |
3261 | + kfree(buffer); | |
3262 | + return NULL; | |
3263 | + } | |
3264 | + pos += PAGE_SIZE - offset; | |
3265 | + /* Read. */ | |
3266 | + while (offset < PAGE_SIZE) { | |
3267 | + const char *kaddr = dump->data; | |
3268 | + const unsigned char c = kaddr[offset++]; | |
3269 | + int len; | |
3270 | + /* Check for end of buffer. */ | |
3271 | + if (skip) { | |
3272 | + if (c) | |
3273 | + continue; | |
3274 | + goto reset; | |
3275 | + } | |
3276 | + len = buffer + ccs_buffer_len - cp - 1; | |
3277 | + if (len <= 32 && c) { | |
3278 | + cp = last_start; | |
3279 | + skip = true; | |
3280 | + continue; | |
3281 | + } | |
3282 | + /* Print argv[$index]=" or envp[" part. */ | |
3283 | + if (cp == last_start) { | |
3284 | + int l; | |
3285 | + if (argv_count) | |
3286 | + l = snprintf(cp, len, " argv[%u]=\"", | |
3287 | + bprm->argc - argv_count); | |
3288 | + else | |
3289 | + l = snprintf(cp, len, " envp[\""); | |
3290 | + cp += l; | |
3291 | + len -= l; | |
3292 | + } | |
3293 | + if (c > ' ' && c < 127 && c != '\\') { | |
3294 | + /* Print "]=" part if printing environ. */ | |
3295 | + if (c == '=' && !argv_count && !env_value) { | |
3296 | + cp += snprintf(cp, len, "\"]=\""); | |
3297 | + env_value = true; | |
3298 | + } else | |
3299 | + *cp++ = c; | |
3300 | + continue; | |
3301 | + } | |
3302 | + if (c) { | |
3303 | + *cp++ = '\\'; | |
3304 | + *cp++ = (c >> 6) + '0'; | |
3305 | + *cp++ = ((c >> 3) & 7) + '0'; | |
3306 | + *cp++ = (c & 7) + '0'; | |
3307 | + continue; | |
3308 | + } | |
3309 | + /* Print "]=" part if not yet printed. */ | |
3310 | + if (!argv_count && !env_value) | |
3311 | + cp += snprintf(cp, len, "\"]=\""); | |
3312 | + *cp++ = '"'; | |
3313 | + last_start = cp; | |
3314 | +reset: | |
3315 | + skip = false; | |
3316 | + env_value = false; | |
3317 | + if (argv_count) | |
3318 | + argv_count--; | |
3319 | + else if (envp_count) | |
3320 | + envp_count--; | |
3321 | + if (!argv_count && !envp_count) | |
3322 | + break; | |
3323 | + } | |
3324 | + offset = 0; | |
3325 | + } | |
3326 | + *cp = '\0'; | |
3327 | + return buffer; | |
3328 | +} | |
3329 | + | |
3330 | +/** | |
3331 | + * ccs_filetype - Get string representation of file type. | |
3332 | + * | |
3333 | + * @mode: Mode value for stat(). | |
3334 | + * | |
3335 | + * Returns file type string. | |
3336 | + */ | |
3337 | +static inline const char *ccs_filetype(const umode_t mode) | |
3338 | +{ | |
3339 | + switch (mode & S_IFMT) { | |
3340 | + case S_IFREG: | |
3341 | + case 0: | |
3342 | + return "file"; | |
3343 | + case S_IFDIR: | |
3344 | + return "directory"; | |
3345 | + case S_IFLNK: | |
3346 | + return "symlink"; | |
3347 | + case S_IFIFO: | |
3348 | + return "fifo"; | |
3349 | + case S_IFSOCK: | |
3350 | + return "socket"; | |
3351 | + case S_IFBLK: | |
3352 | + return "block"; | |
3353 | + case S_IFCHR: | |
3354 | + return "char"; | |
3355 | + } | |
3356 | + return "unknown"; /* This should not happen. */ | |
3357 | +} | |
3358 | + | |
3359 | +/** | |
3360 | + * ccs_print_trailer - Get misc info of audit log. | |
3361 | + * | |
3362 | + * @r: Pointer to "struct ccs_request_info". | |
3363 | + * | |
3364 | + * Returns string representation. | |
3365 | + * | |
3366 | + * This function uses kmalloc(), so caller must kfree() if this function | |
3367 | + * didn't return NULL. | |
3368 | + */ | |
3369 | +static char *ccs_print_trailer(struct ccs_request_info *r) | |
3370 | +{ | |
3371 | + const char *handler = | |
3372 | + ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER ? "" : "!"; | |
3373 | + const char *exe = r->exename.name; | |
3374 | + const char *domain = ccs_current_domain()->domainname->name; | |
3375 | + const int ccs_buffer_len = 2000 + strlen(exe) + strlen(domain); | |
3376 | + char *buffer = kmalloc(ccs_buffer_len, GFP_NOFS); | |
3377 | + int pos; | |
3378 | + u8 i; | |
3379 | + if (!buffer) | |
3380 | + return NULL; | |
3381 | + pos = snprintf(buffer, ccs_buffer_len - 1, " task.pid=%u task.ppid=%u" | |
3382 | + " task.uid=%u task.gid=%u task.euid=%u task.egid=%u" | |
3383 | + " task.suid=%u task.sgid=%u task.fsuid=%u task.fsgid=%u" | |
3384 | + " task.type%s=execute_handler task.exe=\"%s\"" | |
3385 | + " task.domain=\"%s\"", ccs_sys_getpid(), | |
3386 | + ccs_sys_getppid(), current_uid(), current_gid(), | |
3387 | + current_euid(), current_egid(), current_suid(), | |
3388 | + current_sgid(), current_fsuid(), current_fsgid(), | |
3389 | + handler, exe, domain); | |
3390 | + if (!r->obj.path[0].dentry && !r->obj.path[1].dentry) | |
3391 | + goto no_obj_info; | |
3392 | + ccs_get_attributes(r); | |
3393 | + for (i = 0; i < CCS_MAX_PATH_STAT; i++) { | |
3394 | + char objname[32]; | |
3395 | + struct ccs_mini_stat *stat; | |
3396 | + unsigned int dev; | |
3397 | + umode_t mode; | |
3398 | + if (!r->obj.stat_valid[i]) | |
3399 | + continue; | |
3400 | + stat = &r->obj.stat[i]; | |
3401 | + dev = stat->dev; | |
3402 | + mode = stat->mode; | |
3403 | + memset(objname, 0, sizeof(objname)); | |
3404 | + snprintf(objname, sizeof(objname) - 1, "%s%s.", | |
3405 | + ccs_get_sarg(r->type, (i >> 1)), | |
3406 | + i & 1 ? ".parent" : ""); | |
3407 | + pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, | |
3408 | + " %suid=%u %sgid=%u %sino=%lu %smajor=%u" | |
3409 | + " %sminor=%u %sperm=0%o %stype=%s" | |
3410 | + " %sfsmagic=0x%lX", objname, stat->uid, | |
3411 | + objname, stat->gid, objname, (unsigned long) | |
3412 | + stat->ino, objname, MAJOR(dev), objname, | |
3413 | + MINOR(dev), objname, mode & S_IALLUGO, objname, | |
3414 | + ccs_filetype(mode), objname, stat->fsmagic); | |
3415 | + if (S_ISCHR(mode) || S_ISBLK(mode)) { | |
3416 | + dev = stat->rdev; | |
3417 | + pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, | |
3418 | + " %sdev_major=%u %sdev_minor=%u", | |
3419 | + objname, MAJOR(dev), objname, | |
3420 | + MINOR(dev)); | |
3421 | + } | |
3422 | + } | |
3423 | +no_obj_info: | |
3424 | + if (pos < ccs_buffer_len - 1) | |
3425 | + return buffer; | |
3426 | + kfree(buffer); | |
3427 | + return NULL; | |
3428 | +} | |
3429 | + | |
3430 | +/** | |
3431 | + * ccs_print_param - Get arg info of audit log. | |
3432 | + * | |
3433 | + * @r: Pointer to "struct ccs_request_info". | |
3434 | + * @buf: Buffer to write. | |
3435 | + * @len: Size of @buf in bytes. | |
3436 | + */ | |
3437 | +static int ccs_print_param(struct ccs_request_info *r, char *buf, int len) | |
3438 | +{ | |
3439 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
3440 | + /* Make sure that IP address argument is ready. */ | |
3441 | + char ip[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")]; | |
3442 | + switch (r->type) { | |
3443 | + case CCS_MAC_INET_STREAM_BIND: | |
3444 | + case CCS_MAC_INET_STREAM_LISTEN: | |
3445 | + case CCS_MAC_INET_STREAM_CONNECT: | |
3446 | + case CCS_MAC_INET_STREAM_ACCEPT: | |
3447 | + case CCS_MAC_INET_DGRAM_BIND: | |
3448 | + case CCS_MAC_INET_DGRAM_SEND: | |
3449 | + case CCS_MAC_INET_RAW_BIND: | |
3450 | + case CCS_MAC_INET_RAW_SEND: | |
3451 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
3452 | + case CCS_MAC_INET_DGRAM_RECV: | |
3453 | + case CCS_MAC_INET_RAW_RECV: | |
3454 | +#endif | |
3455 | + if (!r->param.ip) | |
3456 | + return 0; | |
3457 | + if (r->param.is_ipv6) { | |
3458 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | |
3459 | + snprintf(ip, sizeof(ip), "%pI6c", | |
3460 | + (const struct in6_addr *) r->param.ip); | |
3461 | +#else | |
3462 | + ip6_compressed_string(ip, (const u8 *) r->param.ip); | |
3463 | +#endif | |
3464 | + } else { | |
3465 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | |
3466 | + snprintf(ip, sizeof(ip), "%pI4", r->param.ip); | |
3467 | +#else | |
3468 | + ip4_string(ip, r->param.ip); | |
3469 | +#endif | |
3470 | + } | |
3471 | + break; | |
3472 | + default: | |
3473 | + break; | |
3474 | + } | |
3475 | +#endif | |
3476 | + /* Make sure that string arguments are ready. */ | |
3477 | + if (!r->param.s[0] && r->obj.path[0].dentry) { | |
3478 | + ccs_populate_patharg(r, true); | |
3479 | + if (!r->param.s[0]) | |
3480 | + return 0; | |
3481 | + } | |
3482 | + if (!r->param.s[1] && r->obj.path[1].dentry) { | |
3483 | + ccs_populate_patharg(r, false); | |
3484 | + if (!r->param.s[1]) | |
3485 | + return 0; | |
3486 | + } | |
3487 | + switch (r->type) { | |
3488 | + int pos; | |
3489 | + u8 i; | |
3490 | + case CCS_MAC_EXECUTE: | |
3491 | + return snprintf(buf, len, " exec=\"%s\" path=\"%s\"", | |
3492 | + r->param.s[1]->name, r->param.s[0]->name); | |
3493 | + case CCS_MAC_READ: | |
3494 | + case CCS_MAC_WRITE: | |
3495 | + case CCS_MAC_APPEND: | |
3496 | + case CCS_MAC_UNLINK: | |
3497 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
3498 | + case CCS_MAC_GETATTR: | |
3499 | +#endif | |
3500 | + case CCS_MAC_RMDIR: | |
3501 | + case CCS_MAC_TRUNCATE: | |
3502 | + case CCS_MAC_CHROOT: | |
3503 | + return snprintf(buf, len, " path=\"%s\"", r->param.s[0]->name); | |
3504 | + case CCS_MAC_CREATE: | |
3505 | + case CCS_MAC_MKDIR: | |
3506 | + case CCS_MAC_MKFIFO: | |
3507 | + case CCS_MAC_MKSOCK: | |
3508 | + return snprintf(buf, len, " path=\"%s\" perm=0%lo", | |
3509 | + r->param.s[0]->name, r->param.i[0]); | |
3510 | + case CCS_MAC_SYMLINK: | |
3511 | + return snprintf(buf, len, " path=\"%s\" target=\"%s\"", | |
3512 | + r->param.s[0]->name, r->param.s[1]->name); | |
3513 | + case CCS_MAC_MKBLOCK: | |
3514 | + case CCS_MAC_MKCHAR: | |
3515 | + return snprintf(buf, len, " path=\"%s\" perm=0%lo " | |
3516 | + "dev_major=%lu dev_minor=%lu", | |
3517 | + r->param.s[0]->name, r->param.i[0], | |
3518 | + r->param.i[1], r->param.i[2]); | |
3519 | + case CCS_MAC_LINK: | |
3520 | + case CCS_MAC_RENAME: | |
3521 | + return snprintf(buf, len, " old_path=\"%s\" new_path=\"%s\"", | |
3522 | + r->param.s[0]->name, r->param.s[1]->name); | |
3523 | + case CCS_MAC_CHMOD: | |
3524 | + return snprintf(buf, len, " path=\"%s\" perm=0%lo", | |
3525 | + r->param.s[0]->name, r->param.i[0]); | |
3526 | + case CCS_MAC_CHOWN: | |
3527 | + return snprintf(buf, len, " path=\"%s\" uid=%lu", | |
3528 | + r->param.s[0]->name, r->param.i[0]); | |
3529 | + case CCS_MAC_CHGRP: | |
3530 | + return snprintf(buf, len, " path=\"%s\" gid=%lu", | |
3531 | + r->param.s[0]->name, r->param.i[0]); | |
3532 | + case CCS_MAC_IOCTL: | |
3533 | + return snprintf(buf, len, " path=\"%s\" cmd=0x%lX", | |
3534 | + r->param.s[0]->name, r->param.i[0]); | |
3535 | + case CCS_MAC_MOUNT: | |
3536 | + pos = 0; | |
3537 | + for (i = 0; i < 4; i++) { | |
3538 | + if (i == 3) | |
3539 | + pos += snprintf(buf + pos, pos < len ? | |
3540 | + len - pos : 0, " flags=0x%lX", | |
3541 | + r->param.i[0]); | |
3542 | + if (!r->param.s[i]) | |
3543 | + continue; | |
3544 | + pos += snprintf(buf + pos, pos < len ? len - pos : 0, | |
3545 | + " %s=\"%s\"", | |
3546 | + ccs_get_sarg(CCS_MAC_MOUNT, i), | |
3547 | + r->param.s[i]->name); | |
3548 | + } | |
3549 | + return pos; | |
3550 | + case CCS_MAC_UMOUNT: | |
3551 | + return snprintf(buf, len, " path=\"%s\" flags=0x%lX", | |
3552 | + r->param.s[0]->name, r->param.i[0]); | |
3553 | + case CCS_MAC_PIVOT_ROOT: | |
3554 | + return snprintf(buf, len, " new_root=\"%s\" put_old=\"%s\"", | |
3555 | + r->param.s[0]->name, r->param.s[1]->name); | |
3556 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
3557 | + case CCS_MAC_ENVIRON: | |
3558 | + return snprintf(buf, len, " name=\"%s\" value=\"%s\"" | |
3559 | + " exec=\"%s\" path=\"%s\"", | |
3560 | + r->param.s[2]->name, r->param.s[3]->name, | |
3561 | + r->param.s[1]->name, r->param.s[0]->name); | |
3562 | +#endif | |
3563 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
3564 | + case CCS_MAC_INET_STREAM_BIND: | |
3565 | + case CCS_MAC_INET_STREAM_LISTEN: | |
3566 | + case CCS_MAC_INET_STREAM_CONNECT: | |
3567 | + case CCS_MAC_INET_STREAM_ACCEPT: | |
3568 | + case CCS_MAC_INET_DGRAM_BIND: | |
3569 | + case CCS_MAC_INET_DGRAM_SEND: | |
3570 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
3571 | + case CCS_MAC_INET_DGRAM_RECV: | |
3572 | +#endif | |
3573 | + return snprintf(buf, len, " ip=%s port=%lu", ip, | |
3574 | + r->param.i[0]); | |
3575 | + case CCS_MAC_INET_RAW_BIND: | |
3576 | + case CCS_MAC_INET_RAW_SEND: | |
3577 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
3578 | + case CCS_MAC_INET_RAW_RECV: | |
3579 | +#endif | |
3580 | + return snprintf(buf, len, " ip=%s proto=%lu", ip, | |
3581 | + r->param.i[0]); | |
3582 | + case CCS_MAC_UNIX_STREAM_BIND: | |
3583 | + case CCS_MAC_UNIX_STREAM_LISTEN: | |
3584 | + case CCS_MAC_UNIX_STREAM_CONNECT: | |
3585 | + case CCS_MAC_UNIX_STREAM_ACCEPT: | |
3586 | + case CCS_MAC_UNIX_DGRAM_BIND: | |
3587 | + case CCS_MAC_UNIX_DGRAM_SEND: | |
3588 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
3589 | + case CCS_MAC_UNIX_DGRAM_RECV: | |
3590 | +#endif | |
3591 | + case CCS_MAC_UNIX_SEQPACKET_BIND: | |
3592 | + case CCS_MAC_UNIX_SEQPACKET_LISTEN: | |
3593 | + case CCS_MAC_UNIX_SEQPACKET_CONNECT: | |
3594 | + case CCS_MAC_UNIX_SEQPACKET_ACCEPT: | |
3595 | + return snprintf(buf, len, " addr=\"%s\"", r->param.s[0]->name); | |
3596 | +#endif | |
3597 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
3598 | + case CCS_MAC_PTRACE: | |
3599 | + return snprintf(buf, len, " cmd=%lu domain=\"%s\"", | |
3600 | + r->param.i[0], r->param.s[0]->name); | |
3601 | +#endif | |
3602 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
3603 | + case CCS_MAC_SIGNAL: | |
3604 | + return snprintf(buf, len, " sig=%lu", r->param.i[0]); | |
3605 | +#endif | |
3606 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
3607 | + case CCS_MAC_MANUAL_DOMAIN_TRANSITION: | |
3608 | + return snprintf(buf, len, " domain=\"%s\"", | |
3609 | + r->param.s[0]->name); | |
3610 | +#endif | |
3611 | + default: | |
3612 | + break; | |
3613 | + } | |
3614 | + return 0; | |
3615 | +} | |
3616 | + | |
3617 | +/** | |
3618 | + * ccs_init_log - Allocate buffer for audit logs. | |
3619 | + * | |
3620 | + * @r: Pointer to "struct ccs_request_info". | |
3621 | + * | |
3622 | + * Returns pointer to allocated memory. | |
3623 | + * | |
3624 | + * This function uses kzalloc(), so caller must kfree() if this function | |
3625 | + * didn't return NULL. | |
3626 | + */ | |
3627 | +static char *ccs_init_log(struct ccs_request_info *r) | |
3628 | +{ | |
3629 | + const pid_t gpid = task_pid_nr(current); | |
3630 | + struct timeval tv; | |
3631 | + struct ccs_time stamp; | |
3632 | + static const char * const k[CCS_MAX_MATCHING] = { | |
3633 | + [CCS_MATCHING_UNMATCHED] = "unmatched", | |
3634 | + [CCS_MATCHING_ALLOWED] = "allowed", | |
3635 | + [CCS_MATCHING_DENIED] = "denied", | |
3636 | + }; | |
3637 | + char *buf; | |
3638 | + const char *bprm_info; | |
3639 | + const char *trailer; | |
3640 | + int len; | |
3641 | + if (!r->exename.name && !ccs_get_exename(&r->exename)) | |
3642 | + return NULL; | |
3643 | + do_gettimeofday(&tv); | |
3644 | + ccs_convert_time(tv.tv_sec, &stamp); | |
3645 | + trailer = ccs_print_trailer(r); | |
3646 | + if (r->bprm) | |
3647 | + bprm_info = ccs_print_bprm(r->bprm, &r->dump); | |
3648 | + else | |
3649 | + bprm_info = NULL; | |
3650 | + len = 0; | |
3651 | + while (1) { | |
3652 | + int pos; | |
3653 | + buf = kzalloc(len, GFP_NOFS); | |
3654 | + if (!buf) | |
3655 | + break; | |
3656 | + pos = snprintf(buf, len, "#%04u/%02u/%02u %02u:%02u:%02u# " | |
3657 | + "global-pid=%u result=%s priority=%u / %s", | |
3658 | + stamp.year, stamp.month, stamp.day, stamp.hour, | |
3659 | + stamp.min, stamp.sec, gpid, k[r->result], | |
3660 | + r->matched_acl ? r->matched_acl->priority : 0, | |
3661 | + ccs_mac_keywords[r->type]); | |
3662 | + pos += ccs_print_param(r, buf + pos, | |
3663 | + pos < len ? len - pos : 0); | |
3664 | + if (bprm_info) | |
3665 | + pos += snprintf(buf + pos, pos < len ? len - pos : 0, | |
3666 | + "%s", bprm_info); | |
3667 | + if (trailer) | |
3668 | + pos += snprintf(buf + pos, pos < len ? len - pos : 0, | |
3669 | + "%s", trailer); | |
3670 | + pos += snprintf(buf + pos, pos < len ? len - pos : 0, | |
3671 | + "\n") + 1; | |
3672 | + if (pos <= len) | |
3673 | + break; | |
3674 | + kfree(buf); | |
3675 | + len = pos; | |
3676 | + } | |
3677 | + kfree(bprm_info); | |
3678 | + kfree(trailer); | |
3679 | + return buf; | |
3680 | +} | |
3681 | + | |
3682 | +/** | |
3683 | + * ccs_write_log - Write an audit log. | |
3684 | + * | |
3685 | + * @r: Pointer to "struct ccs_request_info". | |
3686 | + * | |
3687 | + * Returns nothing. | |
3688 | + */ | |
3689 | +static void ccs_write_log(struct ccs_request_info *r) | |
3690 | +{ | |
3691 | + struct ccs_log *entry; | |
3692 | + bool quota_exceeded = false; | |
3693 | + int len; | |
3694 | + char *buf = ccs_init_log(r); | |
3695 | + if (!buf) | |
3696 | + return; | |
3697 | + entry = kzalloc(sizeof(*entry), GFP_NOFS); | |
3698 | + if (!entry) { | |
3699 | + kfree(buf); | |
3700 | + return; | |
3701 | + } | |
3702 | + entry->log = buf; | |
3703 | + len = ccs_round2(strlen(buf) + 1); | |
3704 | + /* | |
3705 | + * The entry->size is used for memory quota checks. | |
3706 | + * Don't go beyond strlen(entry->log). | |
3707 | + */ | |
3708 | + entry->size = len + ccs_round2(sizeof(*entry)); | |
3709 | + entry->result = r->result; | |
3710 | + spin_lock(&ccs_log_lock); | |
3711 | + if (ccs_memory_quota[CCS_MEMORY_AUDIT] && | |
3712 | + ccs_memory_used[CCS_MEMORY_AUDIT] + entry->size >= | |
3713 | + ccs_memory_quota[CCS_MEMORY_AUDIT]) { | |
3714 | + quota_exceeded = true; | |
3715 | + } else { | |
3716 | + ccs_memory_used[CCS_MEMORY_AUDIT] += entry->size; | |
3717 | + list_add_tail(&entry->list, &ccs_log); | |
3718 | + ccs_log_count[entry->result]++; | |
3719 | + } | |
3720 | + spin_unlock(&ccs_log_lock); | |
3721 | + if (quota_exceeded) { | |
3722 | + kfree(buf); | |
3723 | + kfree(entry); | |
3724 | + return; | |
3725 | + } | |
3726 | + wake_up(&ccs_log_wait); | |
3727 | +} | |
3728 | + | |
3729 | +/** | |
3730 | + * ccs_read_log - Read an audit log. | |
3731 | + * | |
3732 | + * @head: Pointer to "struct ccs_io_buffer". | |
3733 | + * | |
3734 | + * Returns nothing. | |
3735 | + */ | |
3736 | +static void ccs_read_log(struct ccs_io_buffer *head) | |
3737 | +{ | |
3738 | + struct ccs_log *ptr = NULL; | |
3739 | + if (head->r.w_pos) | |
3740 | + return; | |
3741 | + kfree(head->read_buf); | |
3742 | + head->read_buf = NULL; | |
3743 | + spin_lock(&ccs_log_lock); | |
3744 | + if (!list_empty(&ccs_log)) { | |
3745 | + ptr = list_entry(ccs_log.next, typeof(*ptr), list); | |
3746 | + list_del(&ptr->list); | |
3747 | + ccs_log_count[ptr->result]--; | |
3748 | + ccs_memory_used[CCS_MEMORY_AUDIT] -= ptr->size; | |
3749 | + } | |
3750 | + spin_unlock(&ccs_log_lock); | |
3751 | + if (ptr) { | |
3752 | + head->read_buf = ptr->log; | |
3753 | + head->r.w[head->r.w_pos++] = head->read_buf; | |
3754 | + kfree(ptr); | |
3755 | + } | |
3756 | +} | |
3757 | + | |
3758 | +/** | |
3759 | + * ccs_transit_domain - Transit to other domain. | |
3760 | + * | |
3761 | + * @domainname: The name of domain. | |
3762 | + * | |
3763 | + * Returns true on success, false otherwise. | |
3764 | + * | |
3765 | + * Caller holds ccs_read_lock(). | |
3766 | + */ | |
3767 | +bool ccs_transit_domain(const char *domainname) | |
3768 | +{ | |
3769 | + struct ccs_security *security = ccs_current_security(); | |
3770 | + struct ccs_domain_info e = { }; | |
3771 | + struct ccs_domain_info *entry = ccs_find_domain(domainname); | |
3772 | + if (entry) { | |
3773 | + security->ccs_domain_info = entry; | |
3774 | + return true; | |
3775 | + } | |
3776 | + /* Requested domain does not exist. */ | |
3777 | + /* Don't create requested domain if domainname is invalid. */ | |
3778 | + if (!ccs_correct_domain(domainname)) | |
3779 | + return false; | |
3780 | + e.domainname = ccs_get_name(domainname); | |
3781 | + if (!e.domainname) | |
3782 | + return false; | |
3783 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | |
3784 | + goto out; | |
3785 | + entry = ccs_find_domain(domainname); | |
3786 | + if (entry) | |
3787 | + goto done; | |
3788 | + entry = ccs_commit_ok(&e, sizeof(e)); | |
3789 | + if (!entry) | |
3790 | + goto done; | |
3791 | + list_add_tail_rcu(&entry->list, &ccs_domain_list); | |
3792 | +done: | |
3793 | + mutex_unlock(&ccs_policy_lock); | |
3794 | +out: | |
3795 | + ccs_put_name(e.domainname); | |
3796 | + if (entry) | |
3797 | + security->ccs_domain_info = entry; | |
3798 | + return entry != NULL; | |
3799 | +} | |
3800 | + | |
3801 | +/** | |
3802 | + * ccs_parse_policy - Parse a policy line. | |
3803 | + * | |
3804 | + * @head: Poiter to "struct ccs_io_buffer". | |
3805 | + * @line: Line to parse. | |
3806 | + * | |
3807 | + * Returns 0 on success, negative value otherwise. | |
3808 | + * | |
3809 | + * Caller holds ccs_read_lock(). | |
3810 | + */ | |
3811 | +static int ccs_parse_policy(struct ccs_io_buffer *head, char *line) | |
3812 | +{ | |
3813 | + /* Set current line's content. */ | |
3814 | + head->w.data = line; | |
3815 | + head->w.is_deny = false; | |
3816 | + head->w.priority = 0; | |
3817 | + /* Delete request? */ | |
3818 | + head->w.is_delete = !strncmp(line, "delete ", 7); | |
3819 | + if (head->w.is_delete) | |
3820 | + memmove(line, line + 7, strlen(line + 7) + 1); | |
3821 | + /* Do the update. */ | |
3822 | + switch (head->type) { | |
3823 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
3824 | + case CCS_EXECUTE_HANDLER: | |
3825 | +#endif | |
3826 | + case CCS_PROCESS_STATUS: | |
3827 | + return ccs_write_pid(head); | |
3828 | + case CCS_QUERY: | |
3829 | + return ccs_write_answer(head); | |
3830 | + case CCS_POLICY: | |
3831 | + return ccs_write_policy(head); | |
3832 | + default: | |
3833 | + return -ENOSYS; | |
3834 | + } | |
3835 | +} | |
3836 | + | |
3837 | +/** | |
3838 | + * ccs_policy_io_init - Register hooks for policy I/O. | |
3839 | + * | |
3840 | + * Returns nothing. | |
3841 | + */ | |
3842 | +static void __init ccs_policy_io_init(void) | |
3843 | +{ | |
3844 | + ccsecurity_ops.check_profile = ccs_check_profile; | |
3845 | +} | |
3846 | + | |
3847 | +/** | |
3848 | + * ccs_load_builtin_policy - Load built-in policy. | |
3849 | + * | |
3850 | + * Returns nothing. | |
3851 | + */ | |
3852 | +static void __init ccs_load_builtin_policy(void) | |
3853 | +{ | |
3854 | + /* | |
3855 | + * This include file is manually created and contains built-in policy. | |
3856 | + * | |
3857 | + * static char [] __initdata ccs_builtin_policy = { ... }; | |
3858 | + */ | |
3859 | +#include "builtin-policy.h" | |
3860 | + const int idx = ccs_read_lock(); | |
3861 | + struct ccs_io_buffer head = { }; | |
3862 | + char *start = ccs_builtin_policy; | |
3863 | + head.type = CCS_POLICY; | |
3864 | + while (1) { | |
3865 | + char *end = strchr(start, '\n'); | |
3866 | + if (!end) | |
3867 | + break; | |
3868 | + *end = '\0'; | |
3869 | + ccs_normalize_line(start); | |
3870 | + head.write_buf = start; | |
3871 | + ccs_parse_policy(&head, start); | |
3872 | + start = end + 1; | |
3873 | + } | |
3874 | + ccs_read_unlock(idx); | |
3875 | +#ifdef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
3876 | + ccs_check_profile(); | |
3877 | +#endif | |
3878 | +} | |
3879 | + | |
3880 | +/** | |
3881 | + * ccs_read_self - read() for /proc/ccs/self_domain interface. | |
3882 | + * | |
3883 | + * @file: Pointer to "struct file". | |
3884 | + * @buf: Domainname which current thread belongs to. | |
3885 | + * @count: Size of @buf. | |
3886 | + * @ppos: Bytes read by now. | |
3887 | + * | |
3888 | + * Returns read size on success, negative value otherwise. | |
3889 | + */ | |
3890 | +static ssize_t ccs_read_self(struct file *file, char __user *buf, size_t count, | |
3891 | + loff_t *ppos) | |
3892 | +{ | |
3893 | + const char *domain = ccs_current_domain()->domainname->name; | |
3894 | + loff_t len = strlen(domain); | |
3895 | + loff_t pos = *ppos; | |
3896 | + if (pos >= len || !count) | |
3897 | + return 0; | |
3898 | + len -= pos; | |
3899 | + if (count < len) | |
3900 | + len = count; | |
3901 | + if (copy_to_user(buf, domain + pos, len)) | |
3902 | + return -EFAULT; | |
3903 | + *ppos += len; | |
3904 | + return len; | |
3905 | +} | |
3906 | + | |
3907 | +/** | |
3908 | + * ccs_read_subacl - Read sub ACL in ACL entry. | |
3909 | + * | |
3910 | + * @head: Pointer to "struct ccs_io_buffer". | |
3911 | + * @list: Pointer to "struct list_head". | |
3912 | + * | |
3913 | + * Returns true on success, false otherwise. | |
3914 | + * | |
3915 | + * Caller holds ccs_read_lock(). | |
3916 | + */ | |
3917 | +static bool ccs_read_subacl(struct ccs_io_buffer *head, | |
3918 | + const struct list_head *list) | |
3919 | +{ | |
3920 | + list_for_each_cookie(head->r.subacl, list) { | |
3921 | + struct ccs_acl_info *acl = | |
3922 | + list_entry(head->r.subacl, typeof(*acl), list); | |
3923 | + switch (head->r.step) { | |
3924 | + case 3: | |
3925 | + if (acl->is_deleted) | |
3926 | + continue; | |
3927 | + if (!ccs_flush(head)) | |
3928 | + return false; | |
3929 | + ccs_io_printf(head, " %u ", acl->priority); | |
3930 | + if (acl->is_deny) | |
3931 | + ccs_set_string(head, "deny"); | |
3932 | + else | |
3933 | + ccs_set_string(head, "allow"); | |
3934 | + head->r.cond_step = 0; | |
3935 | + head->r.step++; | |
3936 | + /* fall through */ | |
3937 | + case 4: | |
3938 | + if (!ccs_flush(head)) | |
3939 | + return false; | |
3940 | + if (acl->cond && | |
3941 | + !ccs_print_condition(head, acl->cond)) | |
3942 | + return false; | |
3943 | + ccs_set_lf(head); | |
3944 | + head->r.step--; | |
3945 | + } | |
3946 | + } | |
3947 | + head->r.subacl = NULL; | |
3948 | + return true; | |
3949 | +} | |
3950 | + | |
3951 | +/** | |
3952 | + * ccs_read_policy - Read policy. | |
3953 | + * | |
3954 | + * @head: Pointer to "struct ccs_io_buffer". | |
3955 | + * | |
3956 | + * Caller holds ccs_read_lock(). | |
3957 | + */ | |
3958 | +static void ccs_read_policy(struct ccs_io_buffer *head) | |
3959 | +{ | |
3960 | + if (head->r.eof) | |
3961 | + return; | |
3962 | + if (head->r.print_this_acl_only) | |
3963 | + goto skip; | |
3964 | + if (!head->r.version_done) { | |
3965 | + ccs_io_printf(head, "POLICY_VERSION=%u\n", ccs_policy_version); | |
3966 | + head->r.version_done = true; | |
3967 | + } | |
3968 | + if (!head->r.stat_done) { | |
3969 | + ccs_read_stat(head); | |
3970 | + head->r.stat_done = true; | |
3971 | + } | |
3972 | + if (!head->r.quota_done) { | |
3973 | + if (!ccs_read_quota(head)) | |
3974 | + return; | |
3975 | + head->r.quota_done = true; | |
3976 | + } | |
3977 | + if (!head->r.group_done) { | |
3978 | + if (!ccs_read_group(head)) | |
3979 | + return; | |
3980 | + head->r.group_done = true; | |
3981 | + ccs_set_lf(head); | |
3982 | + } | |
3983 | + while (head->r.acl_index < CCS_MAX_MAC_INDEX) { | |
3984 | + struct list_head * const list = | |
3985 | + &ccs_acl_list[head->r.acl_index]; | |
3986 | + list_for_each_cookie(head->r.acl, list) { | |
3987 | + struct ccs_acl_info *ptr; | |
3988 | +skip: | |
3989 | + ptr = list_entry(head->r.acl, typeof(*ptr), list); | |
3990 | + switch (head->r.step) { | |
3991 | + case 0: | |
3992 | + if (ptr->is_deleted && | |
3993 | + !head->r.print_this_acl_only) | |
3994 | + continue; | |
3995 | + head->r.step++; | |
3996 | + /* fall through */ | |
3997 | + case 1: | |
3998 | + if (!ccs_read_acl(head, ptr)) | |
3999 | + return; | |
4000 | + head->r.step++; | |
4001 | + /* fall through */ | |
4002 | + case 2: | |
4003 | + if (!ccs_flush(head)) | |
4004 | + return; | |
4005 | + ccs_io_printf(head, " audit %u\n", | |
4006 | + ptr->audit); | |
4007 | + head->r.step++; | |
4008 | + /* fall through */ | |
4009 | + case 3: | |
4010 | + case 4: | |
4011 | + if (!ccs_read_subacl(head, | |
4012 | + &ptr->acl_info_list)) | |
4013 | + return; | |
4014 | + head->r.step = 5; | |
4015 | + /* fall through */ | |
4016 | + case 5: | |
4017 | + if (!ccs_flush(head)) | |
4018 | + return; | |
4019 | + ccs_set_lf(head); | |
4020 | + head->r.step = 0; | |
4021 | + if (head->r.print_this_acl_only) | |
4022 | + goto done; | |
4023 | + } | |
4024 | + } | |
4025 | + head->r.acl = NULL; | |
4026 | + head->r.acl_index++; | |
4027 | + } | |
4028 | +done: | |
4029 | + head->r.eof = true; | |
4030 | +} | |
4031 | + | |
4032 | +/** | |
4033 | + * ccs_open - open() for /proc/ccs/ interface. | |
4034 | + * | |
4035 | + * @inode: Pointer to "struct inode". | |
4036 | + * @file: Pointer to "struct file". | |
4037 | + * | |
4038 | + * Returns 0 on success, negative value otherwise. | |
4039 | + */ | |
4040 | +static int ccs_open(struct inode *inode, struct file *file) | |
4041 | +{ | |
4042 | + const u8 type = (unsigned long) PDE(inode)->data; | |
4043 | + struct ccs_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS); | |
4044 | + if (!head) | |
4045 | + return -ENOMEM; | |
4046 | + mutex_init(&head->io_sem); | |
4047 | + head->type = type; | |
4048 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
4049 | + if (type == CCS_EXECUTE_HANDLER) { | |
4050 | + /* Allow execute_handler to read process's status. */ | |
4051 | + if (!(ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER)) { | |
4052 | + kfree(head); | |
4053 | + return -EPERM; | |
4054 | + } | |
4055 | + } | |
4056 | +#endif | |
4057 | + if ((file->f_mode & FMODE_READ) && type != CCS_AUDIT && | |
4058 | + type != CCS_QUERY) { | |
4059 | + /* Don't allocate read_buf for poll() access. */ | |
4060 | + head->readbuf_size = 4096; | |
4061 | + head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS); | |
4062 | + if (!head->read_buf) { | |
4063 | + kfree(head); | |
4064 | + return -ENOMEM; | |
4065 | + } | |
4066 | + } | |
4067 | + if (file->f_mode & FMODE_WRITE) { | |
4068 | + head->writebuf_size = 4096; | |
4069 | + head->write_buf = kzalloc(head->writebuf_size, GFP_NOFS); | |
4070 | + if (!head->write_buf) { | |
4071 | + kfree(head->read_buf); | |
4072 | + kfree(head); | |
4073 | + return -ENOMEM; | |
4074 | + } | |
4075 | + } | |
4076 | + /* | |
4077 | + * If the file is /proc/ccs/query, increment the observer counter. | |
4078 | + * The obserber counter is used by ccs_supervisor() to see if | |
4079 | + * there is some process monitoring /proc/ccs/query. | |
4080 | + */ | |
4081 | + if (type == CCS_QUERY) | |
4082 | + atomic_inc(&ccs_query_observers); | |
4083 | + file->private_data = head; | |
4084 | + ccs_notify_gc(head, true); | |
4085 | + return 0; | |
4086 | +} | |
4087 | + | |
4088 | +/** | |
4089 | + * ccs_release - close() for /proc/ccs/ interface. | |
4090 | + * | |
4091 | + * @inode: Pointer to "struct inode". | |
4092 | + * @file: Pointer to "struct file". | |
4093 | + * | |
4094 | + * Returns 0. | |
4095 | + */ | |
4096 | +static int ccs_release(struct inode *inode, struct file *file) | |
4097 | +{ | |
4098 | + struct ccs_io_buffer *head = file->private_data; | |
4099 | + /* | |
4100 | + * If the file is /proc/ccs/query, decrement the observer counter. | |
4101 | + */ | |
4102 | + if (head->type == CCS_QUERY && | |
4103 | + atomic_dec_and_test(&ccs_query_observers)) | |
4104 | + wake_up_all(&ccs_answer_wait); | |
4105 | + ccs_notify_gc(head, false); | |
4106 | + return 0; | |
4107 | +} | |
4108 | + | |
4109 | +/** | |
4110 | + * ccs_poll - poll() for /proc/ccs/ interface. | |
4111 | + * | |
4112 | + * @file: Pointer to "struct file". | |
4113 | + * @wait: Pointer to "poll_table". Maybe NULL. | |
4114 | + * | |
4115 | + * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, | |
4116 | + * POLLOUT | POLLWRNORM otherwise. | |
4117 | + */ | |
4118 | +static unsigned int ccs_poll(struct file *file, poll_table *wait) | |
4119 | +{ | |
4120 | + struct ccs_io_buffer *head = file->private_data; | |
4121 | + if (head->type == CCS_AUDIT) { | |
4122 | + if (!ccs_memory_used[CCS_MEMORY_AUDIT]) { | |
4123 | + poll_wait(file, &ccs_log_wait, wait); | |
4124 | + if (!ccs_memory_used[CCS_MEMORY_AUDIT]) | |
4125 | + return POLLOUT | POLLWRNORM; | |
4126 | + } | |
4127 | + } else if (head->type == CCS_QUERY) { | |
4128 | + if (list_empty(&ccs_query_list)) { | |
4129 | + poll_wait(file, &ccs_query_wait, wait); | |
4130 | + if (list_empty(&ccs_query_list)) | |
4131 | + return POLLOUT | POLLWRNORM; | |
4132 | + } | |
4133 | + } | |
4134 | + return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; | |
4135 | +} | |
4136 | + | |
4137 | +/** | |
4138 | + * ccs_read - read() for /proc/ccs/ interface. | |
4139 | + * | |
4140 | + * @file: Pointer to "struct file". | |
4141 | + * @buf: Pointer to buffer. | |
4142 | + * @count: Size of @buf. | |
4143 | + * @ppos: Unused. | |
4144 | + * | |
4145 | + * Returns bytes read on success, negative value otherwise. | |
4146 | + */ | |
4147 | +static ssize_t ccs_read(struct file *file, char __user *buf, size_t count, | |
4148 | + loff_t *ppos) | |
4149 | +{ | |
4150 | + struct ccs_io_buffer *head = file->private_data; | |
4151 | + int len; | |
4152 | + int idx; | |
4153 | + if (mutex_lock_interruptible(&head->io_sem)) | |
4154 | + return -EINTR; | |
4155 | + head->read_user_buf = buf; | |
4156 | + head->read_user_buf_avail = count; | |
4157 | + idx = ccs_read_lock(); | |
4158 | + if (ccs_flush(head)) { | |
4159 | + /* Call the policy handler. */ | |
4160 | + switch (head->type) { | |
4161 | + case CCS_AUDIT: | |
4162 | + ccs_read_log(head); | |
4163 | + break; | |
4164 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
4165 | + case CCS_EXECUTE_HANDLER: | |
4166 | +#endif | |
4167 | + case CCS_PROCESS_STATUS: | |
4168 | + ccs_read_pid(head); | |
4169 | + break; | |
4170 | + case CCS_VERSION: | |
4171 | + ccs_read_version(head); | |
4172 | + break; | |
4173 | + case CCS_QUERY: | |
4174 | + ccs_read_query(head); | |
4175 | + break; | |
4176 | + case CCS_POLICY: | |
4177 | + ccs_read_policy(head); | |
4178 | + break; | |
4179 | + } | |
4180 | + ccs_flush(head); | |
4181 | + } | |
4182 | + ccs_read_unlock(idx); | |
4183 | + len = head->read_user_buf - buf; | |
4184 | + mutex_unlock(&head->io_sem); | |
4185 | + return len; | |
4186 | +} | |
4187 | + | |
4188 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
4189 | + | |
4190 | +/** | |
4191 | + * ccs_write_self - write() for /proc/ccs/self_domain interface. | |
4192 | + * | |
4193 | + * @file: Pointer to "struct file". | |
4194 | + * @buf: Domainname to transit to. | |
4195 | + * @count: Size of @buf. | |
4196 | + * @ppos: Unused. | |
4197 | + * | |
4198 | + * Returns @count on success, negative value otherwise. | |
4199 | + * | |
4200 | + * If domain transition was permitted but the domain transition failed, this | |
4201 | + * function returns error rather than terminating current thread with SIGKILL. | |
4202 | + */ | |
4203 | +static ssize_t ccs_write_self(struct file *file, const char __user *buf, | |
4204 | + size_t count, loff_t *ppos) | |
4205 | +{ | |
4206 | + char *data; | |
4207 | + int error; | |
4208 | + if (!count || count >= CCS_EXEC_TMPSIZE - 10) | |
4209 | + return -ENOMEM; | |
4210 | + data = kzalloc(count + 1, GFP_NOFS); | |
4211 | + if (!data) | |
4212 | + return -ENOMEM; | |
4213 | + if (copy_from_user(data, buf, count)) { | |
4214 | + error = -EFAULT; | |
4215 | + goto out; | |
4216 | + } | |
4217 | + ccs_normalize_line(data); | |
4218 | + if (ccs_correct_domain(data)) { | |
4219 | + const int idx = ccs_read_lock(); | |
4220 | + struct ccs_path_info name; | |
4221 | + struct ccs_request_info r = { }; | |
4222 | + name.name = data; | |
4223 | + ccs_fill_path_info(&name); | |
4224 | + /* Check "manual_domain_transition" permission. */ | |
4225 | + r.type = CCS_MAC_MANUAL_DOMAIN_TRANSITION; | |
4226 | + r.param.s[0] = &name; | |
4227 | + ccs_check_acl(&r, true); | |
4228 | + if (r.result != CCS_MATCHING_ALLOWED) | |
4229 | + error = -EPERM; | |
4230 | + else | |
4231 | + error = ccs_transit_domain(data) ? 0 : -ENOENT; | |
4232 | + ccs_read_unlock(idx); | |
4233 | + } else | |
4234 | + error = -EINVAL; | |
4235 | +out: | |
4236 | + kfree(data); | |
4237 | + return error ? error : count; | |
4238 | +} | |
4239 | + | |
4240 | +#endif | |
4241 | + | |
4242 | +/** | |
4243 | + * ccs_write - write() for /proc/ccs/ interface. | |
4244 | + * | |
4245 | + * @file: Pointer to "struct file". | |
4246 | + * @buf: Pointer to buffer. | |
4247 | + * @count: Size of @buf. | |
4248 | + * @ppos: Unused. | |
4249 | + * | |
4250 | + * Returns @count on success, negative value otherwise. | |
4251 | + */ | |
4252 | +static ssize_t ccs_write(struct file *file, const char __user *buf, | |
4253 | + size_t count, loff_t *ppos) | |
4254 | +{ | |
4255 | + struct ccs_io_buffer *head = file->private_data; | |
4256 | + int error = count; | |
4257 | + char *cp0 = head->write_buf; | |
4258 | + int idx; | |
4259 | + if (mutex_lock_interruptible(&head->io_sem)) | |
4260 | + return -EINTR; | |
4261 | + head->read_user_buf_avail = 0; | |
4262 | + idx = ccs_read_lock(); | |
4263 | + /* Read a line and dispatch it to the policy handler. */ | |
4264 | + while (count) { | |
4265 | + char c; | |
4266 | + if (head->w.avail >= head->writebuf_size - 1) { | |
4267 | + const int len = head->writebuf_size * 2; | |
4268 | + char *cp = kzalloc(len, GFP_NOFS); | |
4269 | + if (!cp) { | |
4270 | + error = -ENOMEM; | |
4271 | + break; | |
4272 | + } | |
4273 | + memmove(cp, cp0, head->w.avail); | |
4274 | + kfree(cp0); | |
4275 | + head->write_buf = cp; | |
4276 | + cp0 = cp; | |
4277 | + head->writebuf_size = len; | |
4278 | + } | |
4279 | + if (get_user(c, buf)) { | |
4280 | + error = -EFAULT; | |
4281 | + break; | |
4282 | + } | |
4283 | + buf++; | |
4284 | + count--; | |
4285 | + cp0[head->w.avail++] = c; | |
4286 | + if (c != '\n') | |
4287 | + continue; | |
4288 | + cp0[head->w.avail - 1] = '\0'; | |
4289 | + head->w.avail = 0; | |
4290 | + ccs_normalize_line(cp0); | |
4291 | + /* Don't allow updating policies by non manager programs. */ | |
4292 | + if (head->type != CCS_PROCESS_STATUS && !ccs_manager()) { | |
4293 | + error = -EPERM; | |
4294 | + goto out; | |
4295 | + } | |
4296 | + switch (ccs_parse_policy(head, cp0)) { | |
4297 | + case -EPERM: | |
4298 | + error = -EPERM; | |
4299 | + goto out; | |
4300 | + case 0: | |
4301 | + /* Update statistics. */ | |
4302 | + if (head->type == CCS_POLICY) | |
4303 | + ccs_update_stat(CCS_STAT_POLICY_UPDATES); | |
4304 | + break; | |
4305 | + } | |
4306 | + } | |
4307 | +out: | |
4308 | + ccs_read_unlock(idx); | |
4309 | + mutex_unlock(&head->io_sem); | |
4310 | + return error; | |
4311 | +} | |
4312 | + | |
4313 | +/** | |
4314 | + * ccs_create_entry - Create interface files under /proc/ccs/ directory. | |
4315 | + * | |
4316 | + * @name: The name of the interface file. | |
4317 | + * @mode: The permission of the interface file. | |
4318 | + * @parent: The parent directory. | |
4319 | + * @key: Type of interface. | |
4320 | + * | |
4321 | + * Returns nothing. | |
4322 | + */ | |
4323 | +static void __init ccs_create_entry(const char *name, const umode_t mode, | |
4324 | + struct proc_dir_entry *parent, | |
4325 | + const u8 key) | |
4326 | +{ | |
4327 | + struct proc_dir_entry *entry = create_proc_entry(name, mode, parent); | |
4328 | + if (entry) { | |
4329 | + entry->proc_fops = &ccs_operations; | |
4330 | + entry->data = ((u8 *) NULL) + key; | |
4331 | + } | |
4332 | +} | |
4333 | + | |
4334 | +/** | |
4335 | + * ccs_proc_init - Initialize /proc/ccs/ interface. | |
4336 | + * | |
4337 | + * Returns nothing. | |
4338 | + */ | |
4339 | +static void __init ccs_proc_init(void) | |
4340 | +{ | |
4341 | + struct proc_dir_entry *ccs_dir = proc_mkdir("ccs", NULL); | |
4342 | + ccs_create_entry("query", 0600, ccs_dir, CCS_QUERY); | |
4343 | + ccs_create_entry("audit", 0400, ccs_dir, CCS_AUDIT); | |
4344 | + ccs_create_entry(".process_status", 0600, ccs_dir, | |
4345 | + CCS_PROCESS_STATUS); | |
4346 | + ccs_create_entry("version", 0400, ccs_dir, CCS_VERSION); | |
4347 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
4348 | + ccs_create_entry(".execute_handler", 0666, ccs_dir, | |
4349 | + CCS_EXECUTE_HANDLER); | |
4350 | +#endif | |
4351 | + ccs_create_entry("policy", 0600, ccs_dir, CCS_POLICY); | |
4352 | + { | |
4353 | + struct proc_dir_entry *e = create_proc_entry("self_domain", | |
4354 | + 0666, ccs_dir); | |
4355 | + if (e) | |
4356 | + e->proc_fops = &ccs_self_operations; | |
4357 | + } | |
4358 | +} | |
4359 | + | |
4360 | +/** | |
4361 | + * ccs_init_module - Initialize this module. | |
4362 | + * | |
4363 | + * Returns 0 on success, negative value otherwise. | |
4364 | + */ | |
4365 | +static int __init ccs_init_module(void) | |
4366 | +{ | |
4367 | + u16 idx; | |
4368 | + if (ccsecurity_ops.disabled) | |
4369 | + return -EINVAL; | |
4370 | +#ifdef DEBUG_CONDITION | |
4371 | + for (idx = 0; idx < CCS_MAX_MAC_INDEX; idx++) { | |
4372 | + if (ccs_mac_keywords[idx]) | |
4373 | + continue; | |
4374 | + printk(KERN_INFO "ccs_mac_keywords[%u]==NULL\n", idx); | |
4375 | + return -EINVAL; | |
4376 | + } | |
4377 | +#endif | |
4378 | + if (init_srcu_struct(&ccs_ss)) | |
4379 | + panic("Out of memory."); | |
4380 | + for (idx = 0; idx < CCS_MAX_MAC_INDEX; idx++) | |
4381 | + INIT_LIST_HEAD(&ccs_acl_list[idx]); | |
4382 | + for (idx = 0; idx < CCS_MAX_GROUP; idx++) | |
4383 | + INIT_LIST_HEAD(&ccs_group_list[idx]); | |
4384 | + for (idx = 0; idx < CCS_MAX_HASH; idx++) | |
4385 | + INIT_LIST_HEAD(&ccs_name_list[idx]); | |
4386 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | |
4387 | + ccs_mm_init(); | |
4388 | +#endif | |
4389 | + ccs_null_name.name = "NULL"; | |
4390 | + ccs_fill_path_info(&ccs_null_name); | |
4391 | + ccs_kernel_domain.domainname = ccs_get_name("<kernel>"); | |
4392 | + list_add_tail_rcu(&ccs_kernel_domain.list, &ccs_domain_list); | |
4393 | + ccs_policy_io_init(); | |
4394 | + ccs_permission_init(); | |
4395 | + ccs_proc_init(); | |
4396 | + ccs_load_builtin_policy(); | |
4397 | + return 0; | |
4398 | +} | |
4399 | + | |
4400 | +MODULE_LICENSE("GPL"); | |
4401 | +module_init(ccs_init_module); |
@@ -0,0 +1,179 @@ | ||
1 | +/* | |
2 | + * security/caitsith/memory.c | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#include "internal.h" | |
10 | + | |
11 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | |
12 | + | |
13 | +/***** SECTION1: Constants definition *****/ | |
14 | + | |
15 | +/***** SECTION2: Structure definition *****/ | |
16 | + | |
17 | +/***** SECTION3: Prototype definition section *****/ | |
18 | + | |
19 | +struct ccs_security *ccs_find_task_security(const struct task_struct *task); | |
20 | +void __init ccs_mm_init(void); | |
21 | + | |
22 | +static int __ccs_alloc_task_security(const struct task_struct *task); | |
23 | +static void __ccs_free_task_security(const struct task_struct *task); | |
24 | +static void ccs_add_task_security(struct ccs_security *ptr, | |
25 | + struct list_head *list); | |
26 | +static void ccs_rcu_free(struct rcu_head *rcu); | |
27 | + | |
28 | +/***** SECTION4: Standalone functions section *****/ | |
29 | + | |
30 | +/***** SECTION5: Variables definition section *****/ | |
31 | + | |
32 | +/* Dummy security context for avoiding NULL pointer dereference. */ | |
33 | +static struct ccs_security ccs_oom_security = { | |
34 | + .ccs_domain_info = &ccs_kernel_domain | |
35 | +}; | |
36 | + | |
37 | +/* Dummy security context for avoiding NULL pointer dereference. */ | |
38 | +static struct ccs_security ccs_default_security = { | |
39 | + .ccs_domain_info = &ccs_kernel_domain | |
40 | +}; | |
41 | + | |
42 | +/* List of "struct ccs_security". */ | |
43 | +struct list_head ccs_task_security_list[CCS_MAX_TASK_SECURITY_HASH]; | |
44 | +/* Lock for protecting ccs_task_security_list[]. */ | |
45 | +static DEFINE_SPINLOCK(ccs_task_security_list_lock); | |
46 | + | |
47 | +/***** SECTION6: Dependent functions section *****/ | |
48 | + | |
49 | +/** | |
50 | + * ccs_add_task_security - Add "struct ccs_security" to list. | |
51 | + * | |
52 | + * @ptr: Pointer to "struct ccs_security". | |
53 | + * @list: Pointer to "struct list_head". | |
54 | + * | |
55 | + * Returns nothing. | |
56 | + */ | |
57 | +static void ccs_add_task_security(struct ccs_security *ptr, | |
58 | + struct list_head *list) | |
59 | +{ | |
60 | + unsigned long flags; | |
61 | + spin_lock_irqsave(&ccs_task_security_list_lock, flags); | |
62 | + list_add_rcu(&ptr->list, list); | |
63 | + spin_unlock_irqrestore(&ccs_task_security_list_lock, flags); | |
64 | +} | |
65 | + | |
66 | +/** | |
67 | + * __ccs_alloc_task_security - Allocate memory for new tasks. | |
68 | + * | |
69 | + * @task: Pointer to "struct task_struct". | |
70 | + * | |
71 | + * Returns 0 on success, negative value otherwise. | |
72 | + */ | |
73 | +static int __ccs_alloc_task_security(const struct task_struct *task) | |
74 | +{ | |
75 | + struct ccs_security *old_security = ccs_current_security(); | |
76 | + struct ccs_security *new_security = kzalloc(sizeof(*new_security), | |
77 | + GFP_KERNEL); | |
78 | + struct list_head *list = &ccs_task_security_list | |
79 | + [hash_ptr((void *) task, CCS_TASK_SECURITY_HASH_BITS)]; | |
80 | + if (!new_security) | |
81 | + return -ENOMEM; | |
82 | + *new_security = *old_security; | |
83 | + new_security->task = task; | |
84 | + ccs_add_task_security(new_security, list); | |
85 | + return 0; | |
86 | +} | |
87 | + | |
88 | +/** | |
89 | + * ccs_find_task_security - Find "struct ccs_security" for given task. | |
90 | + * | |
91 | + * @task: Pointer to "struct task_struct". | |
92 | + * | |
93 | + * Returns pointer to "struct ccs_security" on success, &ccs_oom_security on | |
94 | + * out of memory, &ccs_default_security otherwise. | |
95 | + * | |
96 | + * If @task is current thread and "struct ccs_security" for current thread was | |
97 | + * not found, I try to allocate it. But if allocation failed, current thread | |
98 | + * will be killed by SIGKILL. Note that if current->pid == 1, sending SIGKILL | |
99 | + * won't work. | |
100 | + */ | |
101 | +struct ccs_security *ccs_find_task_security(const struct task_struct *task) | |
102 | +{ | |
103 | + struct ccs_security *ptr; | |
104 | + struct list_head *list = &ccs_task_security_list | |
105 | + [hash_ptr((void *) task, CCS_TASK_SECURITY_HASH_BITS)]; | |
106 | + /* Make sure INIT_LIST_HEAD() in ccs_mm_init() takes effect. */ | |
107 | + while (!list->next); | |
108 | + rcu_read_lock(); | |
109 | + list_for_each_entry_rcu(ptr, list, list) { | |
110 | + if (ptr->task != task) | |
111 | + continue; | |
112 | + rcu_read_unlock(); | |
113 | + return ptr; | |
114 | + } | |
115 | + rcu_read_unlock(); | |
116 | + if (task != current) | |
117 | + return &ccs_default_security; | |
118 | + /* Use GFP_ATOMIC because caller may have called rcu_read_lock(). */ | |
119 | + ptr = kzalloc(sizeof(*ptr), GFP_ATOMIC); | |
120 | + if (!ptr) { | |
121 | + printk(KERN_WARNING "Unable to allocate memory for pid=%u\n", | |
122 | + task->pid); | |
123 | + send_sig(SIGKILL, current, 0); | |
124 | + return &ccs_oom_security; | |
125 | + } | |
126 | + *ptr = ccs_default_security; | |
127 | + ptr->task = task; | |
128 | + ccs_add_task_security(ptr, list); | |
129 | + return ptr; | |
130 | +} | |
131 | + | |
132 | +/** | |
133 | + * ccs_rcu_free - RCU callback for releasing "struct ccs_security". | |
134 | + * | |
135 | + * @rcu: Pointer to "struct rcu_head". | |
136 | + * | |
137 | + * Returns nothing. | |
138 | + */ | |
139 | +static void ccs_rcu_free(struct rcu_head *rcu) | |
140 | +{ | |
141 | + struct ccs_security *ptr = container_of(rcu, typeof(*ptr), rcu); | |
142 | + kfree(ptr); | |
143 | +} | |
144 | + | |
145 | +/** | |
146 | + * __ccs_free_task_security - Release memory associated with "struct task_struct". | |
147 | + * | |
148 | + * @task: Pointer to "struct task_struct". | |
149 | + * | |
150 | + * Returns nothing. | |
151 | + */ | |
152 | +static void __ccs_free_task_security(const struct task_struct *task) | |
153 | +{ | |
154 | + unsigned long flags; | |
155 | + struct ccs_security *ptr = ccs_find_task_security(task); | |
156 | + if (ptr == &ccs_default_security || ptr == &ccs_oom_security) | |
157 | + return; | |
158 | + spin_lock_irqsave(&ccs_task_security_list_lock, flags); | |
159 | + list_del_rcu(&ptr->list); | |
160 | + spin_unlock_irqrestore(&ccs_task_security_list_lock, flags); | |
161 | + call_rcu(&ptr->rcu, ccs_rcu_free); | |
162 | +} | |
163 | + | |
164 | +/** | |
165 | + * ccs_mm_init - Initialize mm related code. | |
166 | + * | |
167 | + * Returns nothing. | |
168 | + */ | |
169 | +void __init ccs_mm_init(void) | |
170 | +{ | |
171 | + int idx; | |
172 | + for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) | |
173 | + INIT_LIST_HEAD(&ccs_task_security_list[idx]); | |
174 | + smp_wmb(); /* Avoid out of order execution. */ | |
175 | + ccsecurity_ops.alloc_task_security = __ccs_alloc_task_security; | |
176 | + ccsecurity_ops.free_task_security = __ccs_free_task_security; | |
177 | +} | |
178 | + | |
179 | +#endif |
@@ -0,0 +1,3752 @@ | ||
1 | +/* | |
2 | + * security/caitsith/permission.c | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#include "internal.h" | |
10 | + | |
11 | +/***** SECTION1: Constants definition *****/ | |
12 | + | |
13 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
14 | + | |
15 | +/* | |
16 | + * may_open() receives open flags modified by open_to_namei_flags() until | |
17 | + * 2.6.32. We stop here in case some distributions backported ACC_MODE changes, | |
18 | + * for we can't determine whether may_open() receives open flags modified by | |
19 | + * open_to_namei_flags() or not. | |
20 | + */ | |
21 | +#ifdef ACC_MODE | |
22 | +#error ACC_MODE already defined. | |
23 | +#endif | |
24 | +#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) | |
25 | + | |
26 | +#if defined(RHEL_MAJOR) && RHEL_MAJOR == 6 | |
27 | +/* RHEL6 passes unmodified flags since 2.6.32-71.14.1.el6 . */ | |
28 | +#undef ACC_MODE | |
29 | +#define ACC_MODE(x) ("\004\002\006"[(x)&O_ACCMODE]) | |
30 | +#endif | |
31 | + | |
32 | +#endif | |
33 | + | |
34 | +/* String table for special mount operations. */ | |
35 | +static const char * const ccs_mounts[CCS_MAX_SPECIAL_MOUNT] = { | |
36 | + [CCS_MOUNT_BIND] = "--bind", | |
37 | + [CCS_MOUNT_MOVE] = "--move", | |
38 | + [CCS_MOUNT_REMOUNT] = "--remount", | |
39 | + [CCS_MOUNT_MAKE_UNBINDABLE] = "--make-unbindable", | |
40 | + [CCS_MOUNT_MAKE_PRIVATE] = "--make-private", | |
41 | + [CCS_MOUNT_MAKE_SLAVE] = "--make-slave", | |
42 | + [CCS_MOUNT_MAKE_SHARED] = "--make-shared", | |
43 | +}; | |
44 | + | |
45 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
46 | + | |
47 | +/* | |
48 | + * Mapping table from "enum ccs_capability_acl_index" to "enum ccs_mac_index". | |
49 | + */ | |
50 | +static const u8 ccs_c2mac[CCS_MAX_CAPABILITY_INDEX] = { | |
51 | + [CCS_USE_ROUTE_SOCKET] = CCS_MAC_USE_NETLINK_SOCKET, | |
52 | + [CCS_USE_PACKET_SOCKET] = CCS_MAC_USE_PACKET_SOCKET, | |
53 | + [CCS_SYS_REBOOT] = CCS_MAC_USE_REBOOT, | |
54 | + [CCS_SYS_VHANGUP] = CCS_MAC_USE_VHANGUP, | |
55 | + [CCS_SYS_SETTIME] = CCS_MAC_SET_TIME, | |
56 | + [CCS_SYS_NICE] = CCS_MAC_SET_PRIORITY, | |
57 | + [CCS_SYS_SETHOSTNAME] = CCS_MAC_SET_HOSTNAME, | |
58 | + [CCS_USE_KERNEL_MODULE] = CCS_MAC_USE_KERNEL_MODULE, | |
59 | + [CCS_SYS_KEXEC_LOAD] = CCS_MAC_USE_NEW_KERNEL, | |
60 | +}; | |
61 | + | |
62 | +#endif | |
63 | + | |
64 | +/* Type of condition argument. */ | |
65 | +enum ccs_arg_type { | |
66 | + CCS_ARG_TYPE_NONE, | |
67 | + CCS_ARG_TYPE_NUMBER, | |
68 | + CCS_ARG_TYPE_NAME, | |
69 | + CCS_ARG_TYPE_GROUP, | |
70 | + CCS_ARG_TYPE_BITOP, | |
71 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
72 | + CCS_ARG_TYPE_IPV4ADDR, | |
73 | + CCS_ARG_TYPE_IPV6ADDR, | |
74 | +#endif | |
75 | +} __packed; | |
76 | + | |
77 | +/***** SECTION2: Structure definition *****/ | |
78 | + | |
79 | +/* Structure for holding inet domain socket's address. */ | |
80 | +struct ccs_inet_addr_info { | |
81 | + u16 port; /* In network byte order. */ | |
82 | + const u8 *address; /* In network byte order. */ | |
83 | + bool is_ipv6; | |
84 | +}; | |
85 | + | |
86 | +/* Structure for holding unix domain socket's address. */ | |
87 | +struct ccs_unix_addr_info { | |
88 | + u8 *addr; /* This may not be '\0' terminated string. */ | |
89 | + unsigned int addr_len; | |
90 | +}; | |
91 | + | |
92 | +/* Structure for holding socket address. */ | |
93 | +struct ccs_addr_info { | |
94 | + u8 operation; | |
95 | + struct ccs_inet_addr_info inet; | |
96 | + struct ccs_unix_addr_info unix0; | |
97 | +}; | |
98 | + | |
99 | +/* Structure for holding single condition component. */ | |
100 | +struct ccs_cond_arg { | |
101 | + enum ccs_arg_type type; | |
102 | + unsigned long value[2]; | |
103 | + const struct ccs_path_info *name; | |
104 | + const struct ccs_group *group; | |
105 | + struct in6_addr ip[2]; | |
106 | +}; | |
107 | + | |
108 | +/***** SECTION3: Prototype definition section *****/ | |
109 | + | |
110 | +static bool ccs_alphabet_char(const char c); | |
111 | +static bool ccs_byte_range(const char *str); | |
112 | +static bool ccs_check_entry(struct ccs_request_info *r, | |
113 | + const struct ccs_acl_info *ptr); | |
114 | +static bool ccs_condition(struct ccs_request_info *r, | |
115 | + const struct ccs_condition *cond); | |
116 | +static bool ccs_decimal(const char c); | |
117 | +static bool ccs_file_matches_pattern(const char *filename, | |
118 | + const char *filename_end, | |
119 | + const char *pattern, | |
120 | + const char *pattern_end); | |
121 | +static bool ccs_file_matches_pattern2(const char *filename, | |
122 | + const char *filename_end, | |
123 | + const char *pattern, | |
124 | + const char *pattern_end); | |
125 | +static bool ccs_hexadecimal(const char c); | |
126 | +static bool ccs_number_matches_group(const unsigned long min, | |
127 | + const unsigned long max, | |
128 | + const struct ccs_group *group); | |
129 | +static bool ccs_path_matches_pattern(const struct ccs_path_info *filename, | |
130 | + const struct ccs_path_info *pattern); | |
131 | +static bool ccs_path_matches_pattern2(const char *f, const char *p); | |
132 | +static bool ccs_path_matches_group(const struct ccs_path_info *pathname, | |
133 | + const struct ccs_group *group); | |
134 | +static int __ccs_chmod_permission(struct dentry *dentry, | |
135 | + struct vfsmount *vfsmnt, mode_t mode); | |
136 | +static int __ccs_chown_permission(struct dentry *dentry, | |
137 | + struct vfsmount *vfsmnt, uid_t user, | |
138 | + gid_t group); | |
139 | +static int __ccs_chroot_permission(struct path *path); | |
140 | +static int __ccs_fcntl_permission(struct file *file, unsigned int cmd, | |
141 | + unsigned long arg); | |
142 | +static int __ccs_ioctl_permission(struct file *filp, unsigned int cmd, | |
143 | + unsigned long arg); | |
144 | +static int __ccs_link_permission(struct dentry *old_dentry, | |
145 | + struct dentry *new_dentry, | |
146 | + struct vfsmount *mnt); | |
147 | +static int __ccs_mkdir_permission(struct dentry *dentry, struct vfsmount *mnt, | |
148 | + unsigned int mode); | |
149 | +static int __ccs_mknod_permission(struct dentry *dentry, struct vfsmount *mnt, | |
150 | + const unsigned int mode, unsigned int dev); | |
151 | +static int __ccs_mount_permission(char *dev_name, struct path *path, | |
152 | + const char *type, unsigned long flags, | |
153 | + void *data_page); | |
154 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | |
155 | +static int __ccs_open_exec_permission(struct dentry *dentry, | |
156 | + struct vfsmount *mnt); | |
157 | +#endif | |
158 | +static int __ccs_open_permission(struct dentry *dentry, struct vfsmount *mnt, | |
159 | + const int flag); | |
160 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL) | |
161 | +static int ccs_sysctl_permission(enum ccs_mac_index type, | |
162 | + const struct ccs_path_info *filename); | |
163 | +static int __ccs_parse_table(int __user *name, int nlen, void __user *oldval, | |
164 | + void __user *newval, struct ctl_table *table); | |
165 | +#endif | |
166 | +static int __ccs_pivot_root_permission(struct path *old_path, | |
167 | + struct path *new_path); | |
168 | +static int __ccs_rename_permission(struct dentry *old_dentry, | |
169 | + struct dentry *new_dentry, | |
170 | + struct vfsmount *mnt); | |
171 | +static int __ccs_rmdir_permission(struct dentry *dentry, struct vfsmount *mnt); | |
172 | +static int __ccs_search_binary_handler(struct linux_binprm *bprm, | |
173 | + struct pt_regs *regs); | |
174 | +static int __ccs_symlink_permission(struct dentry *dentry, | |
175 | + struct vfsmount *mnt, const char *from); | |
176 | +static int __ccs_truncate_permission(struct dentry *dentry, | |
177 | + struct vfsmount *mnt); | |
178 | +static int __ccs_umount_permission(struct vfsmount *mnt, int flags); | |
179 | +static int __ccs_unlink_permission(struct dentry *dentry, | |
180 | + struct vfsmount *mnt); | |
181 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | |
182 | +static int __ccs_uselib_permission(struct dentry *dentry, | |
183 | + struct vfsmount *mnt); | |
184 | +#endif | |
185 | +static int ccs_execute_path(struct linux_binprm *bprm, struct path *path); | |
186 | +static int ccs_execute(struct ccs_request_info *r); | |
187 | +static int ccs_kern_path(const char *pathname, int flags, struct path *path); | |
188 | +static int ccs_mkdev_perm(const u8 operation, struct dentry *dentry, | |
189 | + struct vfsmount *mnt, const unsigned int mode, | |
190 | + unsigned int dev); | |
191 | +static int ccs_mount_acl(const char *dev_name, struct path *dir, | |
192 | + const char *type, unsigned long flags, | |
193 | + const char *data); | |
194 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | |
195 | +static int ccs_new_open_permission(struct file *filp); | |
196 | +#endif | |
197 | +static int ccs_path2_perm(const enum ccs_mac_index operation, | |
198 | + struct dentry *dentry1, struct vfsmount *mnt1, | |
199 | + struct dentry *dentry2, struct vfsmount *mnt2); | |
200 | +static int ccs_path_number_perm(const enum ccs_mac_index type, | |
201 | + struct dentry *dentry, struct vfsmount *vfsmnt, | |
202 | + unsigned long number); | |
203 | +static int ccs_path_perm(const enum ccs_mac_index operation, | |
204 | + struct dentry *dentry, struct vfsmount *mnt); | |
205 | +static int ccs_start_execve(struct linux_binprm *bprm, | |
206 | + struct ccs_request_info **rp); | |
207 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
208 | +static void __ccs_clear_open_mode(void); | |
209 | +static void __ccs_save_open_mode(int mode); | |
210 | +#endif | |
211 | +static void ccs_check_auto_domain_transition(void); | |
212 | +static void ccs_clear_request_info(struct ccs_request_info *r); | |
213 | +static void ccs_finish_execve(int retval, struct ccs_request_info *r); | |
214 | + | |
215 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
216 | +static int ccs_env_perm(struct ccs_request_info *r, const char *name, | |
217 | + const char *value); | |
218 | +static int ccs_environ(struct ccs_request_info *r); | |
219 | +#endif | |
220 | + | |
221 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
222 | +static bool __ccs_capable(const u8 operation); | |
223 | +static bool ccs_kernel_service(void); | |
224 | +static int __ccs_socket_create_permission(int family, int type, int protocol); | |
225 | +#endif | |
226 | + | |
227 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
228 | +static bool ccs_ip_matches_group(const bool is_ipv6, const u8 *address, | |
229 | + const struct ccs_group *group); | |
230 | +static bool ccs_kernel_service(void); | |
231 | +static int __ccs_socket_bind_permission(struct socket *sock, | |
232 | + struct sockaddr *addr, int addr_len); | |
233 | +static int __ccs_socket_connect_permission(struct socket *sock, | |
234 | + struct sockaddr *addr, | |
235 | + int addr_len); | |
236 | +static int __ccs_socket_listen_permission(struct socket *sock); | |
237 | +static int __ccs_socket_post_accept_permission(struct socket *sock, | |
238 | + struct socket *newsock); | |
239 | +static int __ccs_socket_sendmsg_permission(struct socket *sock, | |
240 | + struct msghdr *msg, int size); | |
241 | +static int ccs_check_inet_address(const struct sockaddr *addr, | |
242 | + const unsigned int addr_len, const u16 port, | |
243 | + struct ccs_addr_info *address); | |
244 | +static int ccs_check_unix_address(struct sockaddr *addr, | |
245 | + const unsigned int addr_len, | |
246 | + struct ccs_addr_info *address); | |
247 | +static int ccs_inet_entry(const struct ccs_addr_info *address); | |
248 | +static int ccs_unix_entry(const struct ccs_addr_info *address); | |
249 | +static u8 ccs_sock_family(struct sock *sk); | |
250 | +#endif | |
251 | + | |
252 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
253 | +static int __ccs_socket_post_recvmsg_permission(struct sock *sk, | |
254 | + struct sk_buff *skb, | |
255 | + int flags); | |
256 | +#endif | |
257 | + | |
258 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
259 | +static int __ccs_ptrace_permission(long request, long pid); | |
260 | +#endif | |
261 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
262 | +static int __ccs_signal_permission(const int sig); | |
263 | +static int ccs_signal_permission0(const int pid, const int sig); | |
264 | +static int ccs_signal_permission1(pid_t tgid, pid_t pid, int sig); | |
265 | +#endif | |
266 | + | |
267 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
268 | +static int __ccs_getattr_permission(struct vfsmount *mnt, | |
269 | + struct dentry *dentry); | |
270 | +#endif | |
271 | + | |
272 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
273 | +static int ccs_try_alt_exec(struct ccs_request_info *r); | |
274 | +static void ccs_unescape(unsigned char *dest); | |
275 | +#endif | |
276 | + | |
277 | +/***** SECTION4: Standalone functions section *****/ | |
278 | + | |
279 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
280 | + | |
281 | +/** | |
282 | + * ccs_copy_argv - Wrapper for copy_strings_kernel(). | |
283 | + * | |
284 | + * @arg: String to copy. | |
285 | + * @bprm: Pointer to "struct linux_binprm". | |
286 | + * | |
287 | + * Returns return value of copy_strings_kernel(). | |
288 | + */ | |
289 | +static inline int ccs_copy_argv(const char *arg, struct linux_binprm *bprm) | |
290 | +{ | |
291 | + const int ret = copy_strings_kernel(1, &arg, bprm); | |
292 | + if (ret >= 0) | |
293 | + bprm->argc++; | |
294 | + return ret; | |
295 | +} | |
296 | + | |
297 | +#else | |
298 | + | |
299 | +/** | |
300 | + * ccs_copy_argv - Wrapper for copy_strings_kernel(). | |
301 | + * | |
302 | + * @arg: String to copy. | |
303 | + * @bprm: Pointer to "struct linux_binprm". | |
304 | + * | |
305 | + * Returns return value of copy_strings_kernel(). | |
306 | + */ | |
307 | +static inline int ccs_copy_argv(char *arg, struct linux_binprm *bprm) | |
308 | +{ | |
309 | + const int ret = copy_strings_kernel(1, &arg, bprm); | |
310 | + if (ret >= 0) | |
311 | + bprm->argc++; | |
312 | + return ret; | |
313 | +} | |
314 | + | |
315 | +#endif | |
316 | + | |
317 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) | |
318 | + | |
319 | +/** | |
320 | + * get_fs_root - Get reference on root directory. | |
321 | + * | |
322 | + * @fs: Pointer to "struct fs_struct". | |
323 | + * @root: Pointer to "struct path". | |
324 | + * | |
325 | + * Returns nothing. | |
326 | + * | |
327 | + * This is for compatibility with older kernels. | |
328 | + */ | |
329 | +static inline void get_fs_root(struct fs_struct *fs, struct path *root) | |
330 | +{ | |
331 | + read_lock(&fs->lock); | |
332 | + *root = fs->root; | |
333 | + path_get(root); | |
334 | + read_unlock(&fs->lock); | |
335 | +} | |
336 | + | |
337 | +#endif | |
338 | + | |
339 | +/** | |
340 | + * ccs_put_filesystem - Wrapper for put_filesystem(). | |
341 | + * | |
342 | + * @fstype: Pointer to "struct file_system_type". | |
343 | + * | |
344 | + * Returns nothing. | |
345 | + * | |
346 | + * Since put_filesystem() is not exported, I embed put_filesystem() here. | |
347 | + */ | |
348 | +static inline void ccs_put_filesystem(struct file_system_type *fstype) | |
349 | +{ | |
350 | + module_put(fstype->owner); | |
351 | +} | |
352 | + | |
353 | +/***** SECTION5: Variables definition section *****/ | |
354 | + | |
355 | +/* The initial domain. */ | |
356 | +struct ccs_domain_info ccs_kernel_domain; | |
357 | + | |
358 | +/* The list for "struct ccs_domain_info". */ | |
359 | +LIST_HEAD(ccs_domain_list); | |
360 | + | |
361 | +/* The list for ACL policy. */ | |
362 | +struct list_head ccs_acl_list[CCS_MAX_MAC_INDEX]; | |
363 | + | |
364 | +/* NULL value. */ | |
365 | +struct ccs_path_info ccs_null_name; | |
366 | + | |
367 | +/***** SECTION6: Dependent functions section *****/ | |
368 | + | |
369 | +/** | |
370 | + * ccs_path_matches_group - Check whether the given pathname matches members of the given pathname group. | |
371 | + * | |
372 | + * @pathname: The name of pathname. | |
373 | + * @group: Pointer to "struct ccs_string_group". | |
374 | + * | |
375 | + * Returns true if @pathname matches pathnames in @group, false otherwise. | |
376 | + * | |
377 | + * Caller holds ccs_read_lock(). | |
378 | + */ | |
379 | +static bool ccs_path_matches_group(const struct ccs_path_info *pathname, | |
380 | + const struct ccs_group *group) | |
381 | +{ | |
382 | + struct ccs_string_group *member; | |
383 | + list_for_each_entry_srcu(member, &group->member_list, head.list, | |
384 | + &ccs_ss) { | |
385 | + if (member->head.is_deleted) | |
386 | + continue; | |
387 | + if (!ccs_path_matches_pattern(pathname, member->member_name)) | |
388 | + continue; | |
389 | + return true; | |
390 | + } | |
391 | + return false; | |
392 | +} | |
393 | + | |
394 | +/** | |
395 | + * ccs_number_matches_group - Check whether the given number matches members of the given number group. | |
396 | + * | |
397 | + * @min: Min number. | |
398 | + * @max: Max number. | |
399 | + * @group: Pointer to "struct ccs_number_group". | |
400 | + * | |
401 | + * Returns true if @min and @max partially overlaps @group, false otherwise. | |
402 | + * | |
403 | + * Caller holds ccs_read_lock(). | |
404 | + */ | |
405 | +static bool ccs_number_matches_group(const unsigned long min, | |
406 | + const unsigned long max, | |
407 | + const struct ccs_group *group) | |
408 | +{ | |
409 | + struct ccs_number_group *member; | |
410 | + bool matched = false; | |
411 | + list_for_each_entry_srcu(member, &group->member_list, head.list, | |
412 | + &ccs_ss) { | |
413 | + if (member->head.is_deleted) | |
414 | + continue; | |
415 | + if (min > member->value[1] || max < member->value[0]) | |
416 | + continue; | |
417 | + matched = true; | |
418 | + break; | |
419 | + } | |
420 | + return matched; | |
421 | +} | |
422 | + | |
423 | +/** | |
424 | + * ccs_check_entry - Do permission check. | |
425 | + * | |
426 | + * @r: Pointer to "struct ccs_request_info". | |
427 | + * @ptr: Pointer to "struct ccs_acl_info". | |
428 | + * | |
429 | + * Returns true on match, false otherwise. | |
430 | + * | |
431 | + * Caller holds ccs_read_lock(). | |
432 | + */ | |
433 | +static bool ccs_check_entry(struct ccs_request_info *r, | |
434 | + const struct ccs_acl_info *ptr) | |
435 | +{ | |
436 | + return !ptr->is_deleted && ccs_condition(r, ptr->cond); | |
437 | +} | |
438 | + | |
439 | +/** | |
440 | + * ccs_check_acl_list - Do permission check. | |
441 | + * | |
442 | + * @r: Pointer to "struct ccs_request_info". | |
443 | + * | |
444 | + * Returns 0 on success, negative value otherwise. | |
445 | + * | |
446 | + * Caller holds ccs_read_lock(). | |
447 | + */ | |
448 | +static int ccs_check_acl_list(struct ccs_request_info *r) | |
449 | +{ | |
450 | + struct ccs_acl_info *ptr; | |
451 | + int error = 0; | |
452 | + struct list_head * const list = &ccs_acl_list[r->type]; | |
453 | + r->matched_acl = NULL; | |
454 | + list_for_each_entry_srcu(ptr, list, list, &ccs_ss) { | |
455 | + struct ccs_acl_info *ptr2; | |
456 | +retry: | |
457 | + if (!ccs_check_entry(r, ptr)) { | |
458 | + if (unlikely(r->failed_by_oom)) | |
459 | + goto oom; | |
460 | + continue; | |
461 | + } | |
462 | + r->matched_acl = ptr; | |
463 | + r->audit = ptr->audit; | |
464 | + r->result = CCS_MATCHING_UNMATCHED; | |
465 | + list_for_each_entry_srcu(ptr2, &ptr->acl_info_list, list, | |
466 | + &ccs_ss) { | |
467 | + r->transition_candidate = NULL; | |
468 | + r->handler_path_candidate = NULL; | |
469 | + if (!ccs_check_entry(r, ptr2)) { | |
470 | + if (unlikely(r->failed_by_oom)) | |
471 | + goto oom; | |
472 | + continue; | |
473 | + } | |
474 | + if (ptr2->is_deny) { | |
475 | + r->result = CCS_MATCHING_DENIED; | |
476 | + break; | |
477 | + } | |
478 | + r->result = CCS_MATCHING_ALLOWED; | |
479 | + /* Set the first matching domain transition entry. */ | |
480 | + if (r->transition_candidate && !r->transition) | |
481 | + r->transition = r->transition_candidate; | |
482 | + /* Set the first matching execute handler entry. */ | |
483 | + if (r->handler_path_candidate && !r->handler_path) | |
484 | + r->handler_path = r->handler_path_candidate; | |
485 | + break; | |
486 | + } | |
487 | + error = ccs_audit_log(r); | |
488 | + /* Ignore out of memory during audit. */ | |
489 | + r->failed_by_oom = false; | |
490 | + if (!error) | |
491 | + continue; | |
492 | + if (error == CCS_RETRY_REQUEST) | |
493 | + goto retry; | |
494 | + break; | |
495 | + } | |
496 | + return error; | |
497 | +oom: | |
498 | + /* | |
499 | + * If conditions could not be checked due to out of memory, | |
500 | + * reject the request with -ENOMEM, for we don't know whether | |
501 | + * there was a possibility of matching "deny" lines or not. | |
502 | + */ | |
503 | + { | |
504 | + static struct timeval ccs_last_tv; | |
505 | + struct timeval tv; | |
506 | + do_gettimeofday(&tv); | |
507 | + if (tv.tv_sec != ccs_last_tv.tv_sec) { | |
508 | + ccs_last_tv = tv; | |
509 | + printk(KERN_INFO "CaitSith: Rejecting access " | |
510 | + "request due to out of memory.\n"); | |
511 | + } | |
512 | + } | |
513 | + return -ENOMEM; | |
514 | +} | |
515 | + | |
516 | +/** | |
517 | + * ccs_check_acl - Do permission check. | |
518 | + * | |
519 | + * @r: Pointer to "struct ccs_request_info". | |
520 | + * @clear: True to cleanup @r before return, false otherwise. | |
521 | + * | |
522 | + * Returns 0 on success, negative value otherwise. | |
523 | + */ | |
524 | +int ccs_check_acl(struct ccs_request_info *r, const bool clear) | |
525 | +{ | |
526 | + int error; | |
527 | + const int idx = ccs_read_lock(); | |
528 | + error = ccs_check_acl_list(r); | |
529 | + ccs_read_unlock(idx); | |
530 | + if (clear) | |
531 | + ccs_clear_request_info(r); | |
532 | + return error; | |
533 | +} | |
534 | + | |
535 | +/** | |
536 | + * ccs_execute - Check permission for "execute". | |
537 | + * | |
538 | + * @r: Pointer to "struct ccs_request_info". | |
539 | + * | |
540 | + * Returns 0 on success, negative value otherwise. | |
541 | + * | |
542 | + * Caller holds ccs_read_lock(). | |
543 | + */ | |
544 | +static int ccs_execute(struct ccs_request_info *r) | |
545 | +{ | |
546 | + int retval; | |
547 | + | |
548 | + /* Get symlink's dentry/vfsmount. */ | |
549 | + retval = ccs_execute_path(r->bprm, &r->obj.path[1]); | |
550 | + if (retval < 0) | |
551 | + return retval; | |
552 | + ccs_populate_patharg(r, false); | |
553 | + if (!r->param.s[1]) | |
554 | + return -ENOMEM; | |
555 | + | |
556 | + /* Check execute permission. */ | |
557 | + r->type = CCS_MAC_EXECUTE; | |
558 | + retval = ccs_check_acl(r, false); | |
559 | + if (retval < 0) | |
560 | + return retval; | |
561 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
562 | + /* | |
563 | + * Switch to execute handler if matched. To avoid infinite execute | |
564 | + * handler loop, don't use execute handler if the current process is | |
565 | + * marked as execute handler. | |
566 | + */ | |
567 | + if (r->handler_path && r->handler_path != &ccs_null_name && | |
568 | + !(ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER)) { | |
569 | + retval = ccs_try_alt_exec(r); | |
570 | + if (retval < 0) | |
571 | + return retval; | |
572 | + } | |
573 | +#endif | |
574 | + /* | |
575 | + * Tell GC that I started execve(). | |
576 | + * Also, tell open_exec() to check read permission. | |
577 | + */ | |
578 | + ccs_current_security()->ccs_flags |= CCS_TASK_IS_IN_EXECVE; | |
579 | + if (!r->transition || r->transition == &ccs_null_name) | |
580 | + /* Keep current domain. */ | |
581 | + return 0; | |
582 | + /* | |
583 | + * Make ccs_current_security()->ccs_flags visible to GC before changing | |
584 | + * ccs_current_security()->ccs_domain_info. | |
585 | + */ | |
586 | + smp_wmb(); | |
587 | + /* | |
588 | + * Transit to the specified domain. | |
589 | + * It will be reverted if execve() failed. | |
590 | + */ | |
591 | + if (ccs_transit_domain(r->transition->name)) | |
592 | + return 0; | |
593 | + printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n", | |
594 | + r->transition->name); | |
595 | + return -ENOMEM; | |
596 | +} | |
597 | + | |
598 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
599 | + | |
600 | +/** | |
601 | + * ccs_unescape - Unescape escaped string. | |
602 | + * | |
603 | + * @dest: String to unescape. | |
604 | + * | |
605 | + * Returns nothing. | |
606 | + */ | |
607 | +static void ccs_unescape(unsigned char *dest) | |
608 | +{ | |
609 | + unsigned char *src = dest; | |
610 | + unsigned char c; | |
611 | + unsigned char d; | |
612 | + unsigned char e; | |
613 | + while (1) { | |
614 | + c = *src++; | |
615 | + if (!c) | |
616 | + break; | |
617 | + if (c != '\\') { | |
618 | + *dest++ = c; | |
619 | + continue; | |
620 | + } | |
621 | + c = *src++; | |
622 | + if (c < '0' || c > '3') | |
623 | + break; | |
624 | + d = *src++; | |
625 | + if (d < '0' || d > '7') | |
626 | + break; | |
627 | + e = *src++; | |
628 | + if (e < '0' || e > '7') | |
629 | + break; | |
630 | + *dest++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0'); | |
631 | + } | |
632 | + *dest = '\0'; | |
633 | +} | |
634 | + | |
635 | +/** | |
636 | + * ccs_try_alt_exec - Try to start execute handler. | |
637 | + * | |
638 | + * @r: Pointer to "struct ccs_request_info". | |
639 | + * | |
640 | + * Returns 0 on success, negative value otherwise. | |
641 | + */ | |
642 | +static int ccs_try_alt_exec(struct ccs_request_info *r) | |
643 | +{ | |
644 | + /* | |
645 | + * Contents of modified bprm. | |
646 | + * The envp[] in original bprm is moved to argv[] so that | |
647 | + * the alternatively executed program won't be affected by | |
648 | + * some dangerous environment variables like LD_PRELOAD. | |
649 | + * | |
650 | + * modified bprm->argc | |
651 | + * = original bprm->argc + original bprm->envc + 7 | |
652 | + * modified bprm->envc | |
653 | + * = 0 | |
654 | + * | |
655 | + * modified bprm->argv[0] | |
656 | + * = the program's name specified by *_execute_handler | |
657 | + * modified bprm->argv[1] | |
658 | + * = ccs_current_domain()->domainname->name | |
659 | + * modified bprm->argv[2] | |
660 | + * = the current process's name | |
661 | + * modified bprm->argv[3] | |
662 | + * = the current process's information (e.g. uid/gid). | |
663 | + * modified bprm->argv[4] | |
664 | + * = original bprm->filename | |
665 | + * modified bprm->argv[5] | |
666 | + * = original bprm->argc in string expression | |
667 | + * modified bprm->argv[6] | |
668 | + * = original bprm->envc in string expression | |
669 | + * modified bprm->argv[7] | |
670 | + * = original bprm->argv[0] | |
671 | + * ... | |
672 | + * modified bprm->argv[bprm->argc + 6] | |
673 | + * = original bprm->argv[bprm->argc - 1] | |
674 | + * modified bprm->argv[bprm->argc + 7] | |
675 | + * = original bprm->envp[0] | |
676 | + * ... | |
677 | + * modified bprm->argv[bprm->envc + bprm->argc + 6] | |
678 | + * = original bprm->envp[bprm->envc - 1] | |
679 | + */ | |
680 | + struct linux_binprm *bprm = r->bprm; | |
681 | + struct file *filp; | |
682 | + int retval; | |
683 | + const int original_argc = bprm->argc; | |
684 | + const int original_envc = bprm->envc; | |
685 | + | |
686 | + ccs_clear_request_info(r); | |
687 | + | |
688 | + /* Close the requested program's dentry. */ | |
689 | + r->obj.path[0].dentry = NULL; | |
690 | + r->obj.path[0].mnt = NULL; | |
691 | + r->obj.validate_done = false; | |
692 | + allow_write_access(bprm->file); | |
693 | + fput(bprm->file); | |
694 | + bprm->file = NULL; | |
695 | + | |
696 | + /* Invalidate page dump cache. */ | |
697 | + r->dump.page = NULL; | |
698 | + | |
699 | + /* Move envp[] to argv[] */ | |
700 | + bprm->argc += bprm->envc; | |
701 | + bprm->envc = 0; | |
702 | + | |
703 | + /* Set argv[6] */ | |
704 | + { | |
705 | + snprintf(r->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_envc); | |
706 | + retval = ccs_copy_argv(r->tmp, bprm); | |
707 | + if (retval < 0) | |
708 | + goto out; | |
709 | + } | |
710 | + | |
711 | + /* Set argv[5] */ | |
712 | + { | |
713 | + snprintf(r->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_argc); | |
714 | + retval = ccs_copy_argv(r->tmp, bprm); | |
715 | + if (retval < 0) | |
716 | + goto out; | |
717 | + } | |
718 | + | |
719 | + /* Set argv[4] */ | |
720 | + { | |
721 | + retval = ccs_copy_argv(bprm->filename, bprm); | |
722 | + if (retval < 0) | |
723 | + goto out; | |
724 | + } | |
725 | + | |
726 | + /* Set argv[3] */ | |
727 | + { | |
728 | + snprintf(r->tmp, CCS_EXEC_TMPSIZE - 1, | |
729 | + "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d " | |
730 | + "sgid=%d fsuid=%d fsgid=%d", ccs_sys_getpid(), | |
731 | + current_uid(), current_gid(), current_euid(), | |
732 | + current_egid(), current_suid(), current_sgid(), | |
733 | + current_fsuid(), current_fsgid()); | |
734 | + retval = ccs_copy_argv(r->tmp, bprm); | |
735 | + if (retval < 0) | |
736 | + goto out; | |
737 | + } | |
738 | + | |
739 | + /* Set argv[2] */ | |
740 | + { | |
741 | + char *exe = ccs_get_exe(); | |
742 | + if (exe) { | |
743 | + retval = ccs_copy_argv(exe, bprm); | |
744 | + kfree(exe); | |
745 | + } else { | |
746 | + retval = -ENOMEM; | |
747 | + } | |
748 | + if (retval < 0) | |
749 | + goto out; | |
750 | + } | |
751 | + | |
752 | + /* Set argv[1] */ | |
753 | + { | |
754 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
755 | + retval = ccs_copy_argv(ccs_current_domain()->domainname->name, | |
756 | + bprm); | |
757 | +#else | |
758 | + snprintf(r->tmp, CCS_EXEC_TMPSIZE - 1, "%s", | |
759 | + ccs_current_domain()->domainname->name); | |
760 | + retval = ccs_copy_argv(r->tmp, bprm); | |
761 | +#endif | |
762 | + if (retval < 0) | |
763 | + goto out; | |
764 | + } | |
765 | + | |
766 | + /* Set argv[0] */ | |
767 | + { | |
768 | + struct path root; | |
769 | + char *cp; | |
770 | + int root_len; | |
771 | + int handler_len; | |
772 | + get_fs_root(current->fs, &root); | |
773 | + cp = ccs_realpath(&root); | |
774 | + path_put(&root); | |
775 | + if (!cp) { | |
776 | + retval = -ENOMEM; | |
777 | + goto out; | |
778 | + } | |
779 | + root_len = strlen(cp); | |
780 | + retval = strncmp(r->handler_path->name, cp, root_len); | |
781 | + root_len--; | |
782 | + kfree(cp); | |
783 | + if (retval) { | |
784 | + retval = -ENOENT; | |
785 | + goto out; | |
786 | + } | |
787 | + handler_len = r->handler_path->total_len + 1; | |
788 | + /* r->handler is released by ccs_finish_execve(). */ | |
789 | + r->handler = kmalloc(handler_len, GFP_NOFS); | |
790 | + if (!r->handler) { | |
791 | + retval = -ENOMEM; | |
792 | + goto out; | |
793 | + } | |
794 | + /* Adjust root directory for open_exec(). */ | |
795 | + memmove(r->handler, r->handler_path->name + root_len, | |
796 | + handler_len - root_len); | |
797 | + ccs_unescape(r->handler); | |
798 | + retval = -ENOENT; | |
799 | + if (*r->handler != '/') | |
800 | + goto out; | |
801 | + retval = ccs_copy_argv(r->handler, bprm); | |
802 | + if (retval < 0) | |
803 | + goto out; | |
804 | + } | |
805 | + | |
806 | + /* | |
807 | + * OK, now restart the process with execute handler program's dentry. | |
808 | + */ | |
809 | + filp = open_exec(r->handler); | |
810 | + if (IS_ERR(filp)) { | |
811 | + retval = PTR_ERR(filp); | |
812 | + goto out; | |
813 | + } | |
814 | + r->obj.path[0].dentry = filp->f_dentry; | |
815 | + r->obj.path[0].mnt = filp->f_vfsmnt; | |
816 | + bprm->file = filp; | |
817 | + bprm->filename = r->handler; | |
818 | + bprm->interp = bprm->filename; | |
819 | + retval = prepare_binprm(bprm); | |
820 | + if (retval < 0) | |
821 | + goto out; | |
822 | + ccs_populate_patharg(r, true); | |
823 | + if (!r->param.s[0]) | |
824 | + retval = -ENOMEM; | |
825 | + else if (ccs_pathcmp(r->param.s[0], r->handler_path)) { | |
826 | + /* Failed to verify execute handler. */ | |
827 | + static u8 counter = 20; | |
828 | + if (counter) { | |
829 | + counter--; | |
830 | + printk(KERN_WARNING "Failed to verify: %s\n", | |
831 | + r->handler_path->name); | |
832 | + } | |
833 | + retval = -EINVAL; | |
834 | + } | |
835 | +out: | |
836 | + return retval; | |
837 | +} | |
838 | + | |
839 | +#endif | |
840 | + | |
841 | +/** | |
842 | + * ccs_dump_page - Dump a page to buffer. | |
843 | + * | |
844 | + * @bprm: Pointer to "struct linux_binprm". | |
845 | + * @pos: Location to dump. | |
846 | + * @dump: Poiner to "struct ccs_page_dump". | |
847 | + * | |
848 | + * Returns true on success, false otherwise. | |
849 | + */ | |
850 | +bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos, | |
851 | + struct ccs_page_dump *dump) | |
852 | +{ | |
853 | + struct page *page; | |
854 | + /* dump->data is released by ccs_start_execve(). */ | |
855 | + if (!dump->data) { | |
856 | + dump->data = kzalloc(PAGE_SIZE, GFP_NOFS); | |
857 | + if (!dump->data) | |
858 | + return false; | |
859 | + } | |
860 | + /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */ | |
861 | +#ifdef CONFIG_MMU | |
862 | + if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) | |
863 | + return false; | |
864 | +#else | |
865 | + page = bprm->page[pos / PAGE_SIZE]; | |
866 | +#endif | |
867 | + if (page != dump->page) { | |
868 | + const unsigned int offset = pos % PAGE_SIZE; | |
869 | + /* | |
870 | + * Maybe kmap()/kunmap() should be used here. | |
871 | + * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic(). | |
872 | + * So do I. | |
873 | + */ | |
874 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) | |
875 | + char *kaddr = kmap_atomic(page); | |
876 | +#else | |
877 | + char *kaddr = kmap_atomic(page, KM_USER0); | |
878 | +#endif | |
879 | + dump->page = page; | |
880 | + memcpy(dump->data + offset, kaddr + offset, | |
881 | + PAGE_SIZE - offset); | |
882 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) | |
883 | + kunmap_atomic(kaddr); | |
884 | +#else | |
885 | + kunmap_atomic(kaddr, KM_USER0); | |
886 | +#endif | |
887 | + } | |
888 | + /* Same with put_arg_page(page) in fs/exec.c */ | |
889 | +#ifdef CONFIG_MMU | |
890 | + put_page(page); | |
891 | +#endif | |
892 | + return true; | |
893 | +} | |
894 | + | |
895 | +/** | |
896 | + * ccs_start_execve - Prepare for execve() operation. | |
897 | + * | |
898 | + * @bprm: Pointer to "struct linux_binprm". | |
899 | + * @rp: Pointer to "struct ccs_request_info *". | |
900 | + * | |
901 | + * Returns 0 on success, negative value otherwise. | |
902 | + */ | |
903 | +static int ccs_start_execve(struct linux_binprm *bprm, | |
904 | + struct ccs_request_info **rp) | |
905 | +{ | |
906 | + int retval; | |
907 | + struct ccs_security *task = ccs_current_security(); | |
908 | + struct ccs_request_info *r; | |
909 | + int idx; | |
910 | + *rp = NULL; | |
911 | + r = kzalloc(sizeof(*r), GFP_NOFS); | |
912 | + if (!r) | |
913 | + return -ENOMEM; | |
914 | + r->tmp = kzalloc(CCS_EXEC_TMPSIZE, GFP_NOFS); | |
915 | + if (!r->tmp) { | |
916 | + kfree(r); | |
917 | + return -ENOMEM; | |
918 | + } | |
919 | + idx = ccs_read_lock(); | |
920 | + /* r->dump->data is allocated by ccs_dump_page(). */ | |
921 | + r->previous_domain = task->ccs_domain_info; | |
922 | + /* Clear manager flag. */ | |
923 | + task->ccs_flags &= ~CCS_TASK_IS_MANAGER; | |
924 | + *rp = r; | |
925 | + r->bprm = bprm; | |
926 | + r->obj.path[0].dentry = bprm->file->f_dentry; | |
927 | + r->obj.path[0].mnt = bprm->file->f_vfsmnt; | |
928 | + retval = ccs_execute(r); | |
929 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
930 | + if (!retval && bprm->envc) | |
931 | + retval = ccs_environ(r); | |
932 | +#endif | |
933 | + ccs_clear_request_info(r); | |
934 | + /* Drop refcount obtained by ccs_execute_path(). */ | |
935 | + if (r->obj.path[1].dentry) { | |
936 | + path_put(&r->obj.path[1]); | |
937 | + r->obj.path[1].dentry = NULL; | |
938 | + } | |
939 | + ccs_read_unlock(idx); | |
940 | + kfree(r->tmp); | |
941 | + r->tmp = NULL; | |
942 | + kfree(r->dump.data); | |
943 | + r->dump.data = NULL; | |
944 | + return retval; | |
945 | +} | |
946 | + | |
947 | +/** | |
948 | + * ccs_finish_execve - Clean up execve() operation. | |
949 | + * | |
950 | + * @retval: Return code of an execve() operation. | |
951 | + * @r: Pointer to "struct ccs_request_info". | |
952 | + * | |
953 | + * Returns nothing. | |
954 | + */ | |
955 | +static void ccs_finish_execve(int retval, struct ccs_request_info *r) | |
956 | +{ | |
957 | + struct ccs_security *task; | |
958 | + if (!r) | |
959 | + return; | |
960 | + task = ccs_current_security(); | |
961 | + if (retval < 0) { | |
962 | + task->ccs_domain_info = r->previous_domain; | |
963 | + /* | |
964 | + * Make task->ccs_domain_info visible to GC before changing | |
965 | + * task->ccs_flags. | |
966 | + */ | |
967 | + smp_wmb(); | |
968 | + } else { | |
969 | + /* Mark the current process as execute handler. */ | |
970 | + if (r->handler) | |
971 | + task->ccs_flags |= CCS_TASK_IS_EXECUTE_HANDLER; | |
972 | + /* Mark the current process as normal process. */ | |
973 | + else | |
974 | + task->ccs_flags &= ~CCS_TASK_IS_EXECUTE_HANDLER; | |
975 | + } | |
976 | + /* Tell GC that I finished execve(). */ | |
977 | + task->ccs_flags &= ~CCS_TASK_IS_IN_EXECVE; | |
978 | + ccs_clear_request_info(r); | |
979 | + kfree(r->handler); | |
980 | + kfree(r); | |
981 | +} | |
982 | + | |
983 | +/** | |
984 | + * __ccs_search_binary_handler - Main routine for do_execve(). | |
985 | + * | |
986 | + * @bprm: Pointer to "struct linux_binprm". | |
987 | + * @regs: Pointer to "struct pt_regs". | |
988 | + * | |
989 | + * Returns 0 on success, negative value otherwise. | |
990 | + * | |
991 | + * Performs permission checks for do_execve() and domain transition. | |
992 | + * Domain transition by "struct ccs_acl_info" will be reverted | |
993 | + * if do_execve() failed. | |
994 | + * Garbage collector does not remove "struct ccs_domain_info" from | |
995 | + * ccs_domain_list nor kfree("struct ccs_domain_info") if the current thread is | |
996 | + * marked as CCS_TASK_IS_IN_EXECVE. | |
997 | + */ | |
998 | +static int __ccs_search_binary_handler(struct linux_binprm *bprm, | |
999 | + struct pt_regs *regs) | |
1000 | +{ | |
1001 | + struct ccs_request_info *r; | |
1002 | + int retval; | |
1003 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
1004 | + if (!ccs_policy_loaded) | |
1005 | + ccsecurity_exports.load_policy(bprm->filename); | |
1006 | +#endif | |
1007 | + retval = ccs_start_execve(bprm, &r); | |
1008 | + if (!retval) | |
1009 | + retval = search_binary_handler(bprm, regs); | |
1010 | + ccs_finish_execve(retval, r); | |
1011 | + return retval; | |
1012 | +} | |
1013 | + | |
1014 | +/** | |
1015 | + * ccs_permission_init - Register permission check hooks. | |
1016 | + * | |
1017 | + * Returns nothing. | |
1018 | + */ | |
1019 | +void __init ccs_permission_init(void) | |
1020 | +{ | |
1021 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
1022 | + ccsecurity_ops.save_open_mode = __ccs_save_open_mode; | |
1023 | + ccsecurity_ops.clear_open_mode = __ccs_clear_open_mode; | |
1024 | + ccsecurity_ops.open_permission = __ccs_open_permission; | |
1025 | +#else | |
1026 | + ccsecurity_ops.open_permission = ccs_new_open_permission; | |
1027 | +#endif | |
1028 | + ccsecurity_ops.fcntl_permission = __ccs_fcntl_permission; | |
1029 | + ccsecurity_ops.ioctl_permission = __ccs_ioctl_permission; | |
1030 | + ccsecurity_ops.chmod_permission = __ccs_chmod_permission; | |
1031 | + ccsecurity_ops.chown_permission = __ccs_chown_permission; | |
1032 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
1033 | + ccsecurity_ops.getattr_permission = __ccs_getattr_permission; | |
1034 | +#endif | |
1035 | + ccsecurity_ops.pivot_root_permission = __ccs_pivot_root_permission; | |
1036 | + ccsecurity_ops.chroot_permission = __ccs_chroot_permission; | |
1037 | + ccsecurity_ops.umount_permission = __ccs_umount_permission; | |
1038 | + ccsecurity_ops.mknod_permission = __ccs_mknod_permission; | |
1039 | + ccsecurity_ops.mkdir_permission = __ccs_mkdir_permission; | |
1040 | + ccsecurity_ops.rmdir_permission = __ccs_rmdir_permission; | |
1041 | + ccsecurity_ops.unlink_permission = __ccs_unlink_permission; | |
1042 | + ccsecurity_ops.symlink_permission = __ccs_symlink_permission; | |
1043 | + ccsecurity_ops.truncate_permission = __ccs_truncate_permission; | |
1044 | + ccsecurity_ops.rename_permission = __ccs_rename_permission; | |
1045 | + ccsecurity_ops.link_permission = __ccs_link_permission; | |
1046 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | |
1047 | + ccsecurity_ops.open_exec_permission = __ccs_open_exec_permission; | |
1048 | + ccsecurity_ops.uselib_permission = __ccs_uselib_permission; | |
1049 | +#endif | |
1050 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL) | |
1051 | + ccsecurity_ops.parse_table = __ccs_parse_table; | |
1052 | +#endif | |
1053 | + ccsecurity_ops.mount_permission = __ccs_mount_permission; | |
1054 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
1055 | + ccsecurity_ops.capable = __ccs_capable; | |
1056 | + ccsecurity_ops.socket_create_permission = | |
1057 | + __ccs_socket_create_permission; | |
1058 | +#endif | |
1059 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
1060 | + ccsecurity_ops.socket_listen_permission = | |
1061 | + __ccs_socket_listen_permission; | |
1062 | + ccsecurity_ops.socket_connect_permission = | |
1063 | + __ccs_socket_connect_permission; | |
1064 | + ccsecurity_ops.socket_bind_permission = __ccs_socket_bind_permission; | |
1065 | + ccsecurity_ops.socket_post_accept_permission = | |
1066 | + __ccs_socket_post_accept_permission; | |
1067 | + ccsecurity_ops.socket_sendmsg_permission = | |
1068 | + __ccs_socket_sendmsg_permission; | |
1069 | +#endif | |
1070 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
1071 | + ccsecurity_ops.socket_post_recvmsg_permission = | |
1072 | + __ccs_socket_post_recvmsg_permission; | |
1073 | +#endif | |
1074 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
1075 | + ccsecurity_ops.ptrace_permission = __ccs_ptrace_permission; | |
1076 | +#endif | |
1077 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
1078 | + ccsecurity_ops.kill_permission = ccs_signal_permission0; | |
1079 | + ccsecurity_ops.tgkill_permission = ccs_signal_permission1; | |
1080 | + ccsecurity_ops.tkill_permission = ccs_signal_permission0; | |
1081 | + ccsecurity_ops.sigqueue_permission = ccs_signal_permission0; | |
1082 | + ccsecurity_ops.tgsigqueue_permission = ccs_signal_permission1; | |
1083 | +#endif | |
1084 | + ccsecurity_ops.search_binary_handler = __ccs_search_binary_handler; | |
1085 | +} | |
1086 | + | |
1087 | +/** | |
1088 | + * ccs_kern_path - Wrapper for kern_path(). | |
1089 | + * | |
1090 | + * @pathname: Pathname to resolve. Maybe NULL. | |
1091 | + * @flags: Lookup flags. | |
1092 | + * @path: Pointer to "struct path". | |
1093 | + * | |
1094 | + * Returns 0 on success, negative value otherwise. | |
1095 | + */ | |
1096 | +static int ccs_kern_path(const char *pathname, int flags, struct path *path) | |
1097 | +{ | |
1098 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) | |
1099 | + if (!pathname || kern_path(pathname, flags, path)) | |
1100 | + return -ENOENT; | |
1101 | +#else | |
1102 | + struct nameidata nd; | |
1103 | + if (!pathname || path_lookup(pathname, flags, &nd)) | |
1104 | + return -ENOENT; | |
1105 | + *path = nd.path; | |
1106 | +#endif | |
1107 | + return 0; | |
1108 | +} | |
1109 | + | |
1110 | +/** | |
1111 | + * ccs_execute_path - Get dentry/vfsmount of a program. | |
1112 | + * | |
1113 | + * @bprm: Pointer to "struct linux_binprm". | |
1114 | + * @path: Pointer to "struct path". | |
1115 | + * | |
1116 | + * Returns 0 on success, negative value otherwise. | |
1117 | + */ | |
1118 | +static int ccs_execute_path(struct linux_binprm *bprm, struct path *path) | |
1119 | +{ | |
1120 | + /* | |
1121 | + * Follow symlinks if the requested pathname is on procfs, for | |
1122 | + * /proc/\$/exe is meaningless. | |
1123 | + */ | |
1124 | + const unsigned int follow = | |
1125 | + (bprm->file->f_dentry->d_sb->s_magic == PROC_SUPER_MAGIC) ? | |
1126 | + LOOKUP_FOLLOW : 0; | |
1127 | + if (ccs_kern_path(bprm->filename, follow, path)) | |
1128 | + return -ENOENT; | |
1129 | + return 0; | |
1130 | +} | |
1131 | + | |
1132 | +/** | |
1133 | + * ccs_mount_acl - Check permission for mount() operation. | |
1134 | + * | |
1135 | + * @dev_name: Name of device file or mount source. Maybe NULL. | |
1136 | + * @dir: Pointer to "struct path". | |
1137 | + * @type: Name of filesystem type. Maybe NULL. | |
1138 | + * @flags: Mount options. | |
1139 | + * @data: Mount options not in @flags. Maybe NULL. | |
1140 | + * | |
1141 | + * Returns 0 on success, negative value otherwise. | |
1142 | + */ | |
1143 | +static int ccs_mount_acl(const char *dev_name, struct path *dir, | |
1144 | + const char *type, unsigned long flags, | |
1145 | + const char *data) | |
1146 | +{ | |
1147 | + struct ccs_request_info r = { }; | |
1148 | + struct ccs_path_info rtype = { }; | |
1149 | + struct ccs_path_info rdata = { }; | |
1150 | + bool check_dev = false; | |
1151 | + bool check_data = false; | |
1152 | + int error; | |
1153 | + | |
1154 | + /* Compare fstype in order to determine type of dev_name argument. */ | |
1155 | + if (type == ccs_mounts[CCS_MOUNT_REMOUNT]) { | |
1156 | + /* do_remount() case. */ | |
1157 | + if (data && !(dir->mnt->mnt_sb->s_type->fs_flags & | |
1158 | + FS_BINARY_MOUNTDATA)) | |
1159 | + check_data = true; | |
1160 | + } else if (type == ccs_mounts[CCS_MOUNT_BIND]) { | |
1161 | + /* do_loopback() case. */ | |
1162 | + check_dev = true; | |
1163 | + } else if (type == ccs_mounts[CCS_MOUNT_MAKE_UNBINDABLE] || | |
1164 | + type == ccs_mounts[CCS_MOUNT_MAKE_PRIVATE] || | |
1165 | + type == ccs_mounts[CCS_MOUNT_MAKE_SLAVE] || | |
1166 | + type == ccs_mounts[CCS_MOUNT_MAKE_SHARED]) { | |
1167 | + /* do_change_type() case. */ | |
1168 | + } else if (type == ccs_mounts[CCS_MOUNT_MOVE]) { | |
1169 | + /* do_move_mount() case. */ | |
1170 | + check_dev = true; | |
1171 | + } else { | |
1172 | + /* do_new_mount() case. */ | |
1173 | + struct file_system_type *fstype; | |
1174 | + if (!type) | |
1175 | + return -EINVAL; | |
1176 | + fstype = get_fs_type(type); | |
1177 | + if (!fstype) | |
1178 | + return -ENODEV; | |
1179 | + if (fstype->fs_flags & FS_REQUIRES_DEV) | |
1180 | + check_dev = true; | |
1181 | + if (data && !(fstype->fs_flags & FS_BINARY_MOUNTDATA)) | |
1182 | + check_data = true; | |
1183 | + ccs_put_filesystem(fstype); | |
1184 | + } | |
1185 | + /* Start filling arguments. */ | |
1186 | + r.type = CCS_MAC_MOUNT; | |
1187 | + /* Remember mount options. */ | |
1188 | + r.param.i[0] = flags; | |
1189 | + /* | |
1190 | + * Remember mount point. | |
1191 | + * r.param.s[1] is calculated from r.obj.path[1] as needed. | |
1192 | + */ | |
1193 | + r.obj.path[1] = *dir; | |
1194 | + /* Remember fstype. */ | |
1195 | + rtype.name = ccs_encode(type); | |
1196 | + if (!rtype.name) | |
1197 | + return -ENOMEM; | |
1198 | + ccs_fill_path_info(&rtype); | |
1199 | + r.param.s[2] = &rtype; | |
1200 | + if (check_data) { | |
1201 | + /* Remember data argument. */ | |
1202 | + rdata.name = ccs_encode(data); | |
1203 | + if (!rdata.name) { | |
1204 | + error = -ENOMEM; | |
1205 | + goto out; | |
1206 | + } | |
1207 | + ccs_fill_path_info(&rdata); | |
1208 | + r.param.s[3] = &rdata; | |
1209 | + } | |
1210 | + if (check_dev) { | |
1211 | + /* | |
1212 | + * Remember device file or mount source. | |
1213 | + * r.param.s[0] is calculated from r.obj.path[0] as needed. | |
1214 | + */ | |
1215 | + if (ccs_kern_path(dev_name, LOOKUP_FOLLOW, &r.obj.path[0])) { | |
1216 | + error = -ENOENT; | |
1217 | + goto out; | |
1218 | + } | |
1219 | + } | |
1220 | + error = ccs_check_acl(&r, false); | |
1221 | + /* Drop refcount obtained by ccs_kern_path(). */ | |
1222 | + if (check_dev) | |
1223 | + path_put(&r.obj.path[0]); | |
1224 | +out: | |
1225 | + kfree(rtype.name); | |
1226 | + kfree(rdata.name); | |
1227 | + ccs_clear_request_info(&r); | |
1228 | + return error; | |
1229 | +} | |
1230 | + | |
1231 | +/** | |
1232 | + * __ccs_mount_permission - Check permission for mount() operation. | |
1233 | + * | |
1234 | + * @dev_name: Name of device file. Maybe NULL. | |
1235 | + * @path: Pointer to "struct path". | |
1236 | + * @type: Name of filesystem type. Maybe NULL. | |
1237 | + * @flags: Mount options. | |
1238 | + * @data_page: Mount options not in @flags. Maybe NULL. | |
1239 | + * | |
1240 | + * Returns 0 on success, negative value otherwise. | |
1241 | + */ | |
1242 | +static int __ccs_mount_permission(char *dev_name, struct path *path, | |
1243 | + const char *type, unsigned long flags, | |
1244 | + void *data_page) | |
1245 | +{ | |
1246 | + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | |
1247 | + flags &= ~MS_MGC_MSK; | |
1248 | + if (flags & MS_REMOUNT) { | |
1249 | + type = ccs_mounts[CCS_MOUNT_REMOUNT]; | |
1250 | + flags &= ~MS_REMOUNT; | |
1251 | + } else if (flags & MS_BIND) { | |
1252 | + type = ccs_mounts[CCS_MOUNT_BIND]; | |
1253 | + flags &= ~MS_BIND; | |
1254 | + } else if (flags & MS_SHARED) { | |
1255 | + if (flags & (MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) | |
1256 | + return -EINVAL; | |
1257 | + type = ccs_mounts[CCS_MOUNT_MAKE_SHARED]; | |
1258 | + flags &= ~MS_SHARED; | |
1259 | + } else if (flags & MS_PRIVATE) { | |
1260 | + if (flags & (MS_SHARED | MS_SLAVE | MS_UNBINDABLE)) | |
1261 | + return -EINVAL; | |
1262 | + type = ccs_mounts[CCS_MOUNT_MAKE_PRIVATE]; | |
1263 | + flags &= ~MS_PRIVATE; | |
1264 | + } else if (flags & MS_SLAVE) { | |
1265 | + if (flags & (MS_SHARED | MS_PRIVATE | MS_UNBINDABLE)) | |
1266 | + return -EINVAL; | |
1267 | + type = ccs_mounts[CCS_MOUNT_MAKE_SLAVE]; | |
1268 | + flags &= ~MS_SLAVE; | |
1269 | + } else if (flags & MS_UNBINDABLE) { | |
1270 | + if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE)) | |
1271 | + return -EINVAL; | |
1272 | + type = ccs_mounts[CCS_MOUNT_MAKE_UNBINDABLE]; | |
1273 | + flags &= ~MS_UNBINDABLE; | |
1274 | + } else if (flags & MS_MOVE) { | |
1275 | + type = ccs_mounts[CCS_MOUNT_MOVE]; | |
1276 | + flags &= ~MS_MOVE; | |
1277 | + } | |
1278 | + /* | |
1279 | + * do_mount() terminates data_page with '\0' if data_page != NULL. | |
1280 | + * Therefore, it is safe to pass data_page argument to ccs_mount_acl() | |
1281 | + * as "const char *" rather than "void *". | |
1282 | + */ | |
1283 | + ccs_check_auto_domain_transition(); | |
1284 | + return ccs_mount_acl(dev_name, path, type, flags, data_page); | |
1285 | +} | |
1286 | + | |
1287 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
1288 | + | |
1289 | +/** | |
1290 | + * __ccs_save_open_mode - Remember original flags passed to sys_open(). | |
1291 | + * | |
1292 | + * @mode: Flags passed to sys_open(). | |
1293 | + * | |
1294 | + * Returns nothing. | |
1295 | + * | |
1296 | + * TOMOYO does not check "file write" if open(path, O_TRUNC | O_RDONLY) was | |
1297 | + * requested because write() is not permitted. Instead, TOMOYO checks | |
1298 | + * "file truncate" if O_TRUNC is passed. | |
1299 | + * | |
1300 | + * TOMOYO does not check "file read" and "file write" if open(path, 3) was | |
1301 | + * requested because read()/write() are not permitted. Instead, TOMOYO checks | |
1302 | + * "file ioctl" when ioctl() is requested. | |
1303 | + */ | |
1304 | +static void __ccs_save_open_mode(int mode) | |
1305 | +{ | |
1306 | + if ((mode & 3) == 3) | |
1307 | + ccs_current_security()->ccs_flags |= CCS_OPEN_FOR_IOCTL_ONLY; | |
1308 | +} | |
1309 | + | |
1310 | +/** | |
1311 | + * __ccs_clear_open_mode - Forget original flags passed to sys_open(). | |
1312 | + * | |
1313 | + * Returns nothing. | |
1314 | + */ | |
1315 | +static void __ccs_clear_open_mode(void) | |
1316 | +{ | |
1317 | + ccs_current_security()->ccs_flags &= ~CCS_OPEN_FOR_IOCTL_ONLY; | |
1318 | +} | |
1319 | + | |
1320 | +#endif | |
1321 | + | |
1322 | +/** | |
1323 | + * __ccs_open_permission - Check permission for "read" and "write". | |
1324 | + * | |
1325 | + * @dentry: Pointer to "struct dentry". | |
1326 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1327 | + * @flag: Flags for open(). | |
1328 | + * | |
1329 | + * Returns 0 on success, negative value otherwise. | |
1330 | + */ | |
1331 | +static int __ccs_open_permission(struct dentry *dentry, struct vfsmount *mnt, | |
1332 | + const int flag) | |
1333 | +{ | |
1334 | + struct ccs_request_info r = { }; | |
1335 | + const u32 ccs_flags = ccs_current_flags(); | |
1336 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | |
1337 | + const u8 acc_mode = (flag & 3) == 3 ? 0 : ACC_MODE(flag); | |
1338 | +#else | |
1339 | + const u8 acc_mode = (ccs_flags & CCS_OPEN_FOR_IOCTL_ONLY) ? 0 : | |
1340 | + ACC_MODE(flag); | |
1341 | +#endif | |
1342 | + int error = 0; | |
1343 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) | |
1344 | + if (current->in_execve && !(ccs_flags & CCS_TASK_IS_IN_EXECVE)) | |
1345 | + return 0; | |
1346 | +#endif | |
1347 | +#ifndef CONFIG_CCSECURITY_GETATTR | |
1348 | + if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) | |
1349 | + return 0; | |
1350 | +#endif | |
1351 | + r.obj.path[0].dentry = dentry; | |
1352 | + r.obj.path[0].mnt = mnt; | |
1353 | + if (!(ccs_flags & CCS_TASK_IS_IN_EXECVE)) | |
1354 | + ccs_check_auto_domain_transition(); | |
1355 | + if (acc_mode & MAY_READ) { | |
1356 | + r.type = CCS_MAC_READ; | |
1357 | + error = ccs_check_acl(&r, false); | |
1358 | + } | |
1359 | + if (!error && (acc_mode & MAY_WRITE)) { | |
1360 | + r.type = (flag & O_APPEND) ? CCS_MAC_APPEND : | |
1361 | + CCS_MAC_WRITE; | |
1362 | + error = ccs_check_acl(&r, false); | |
1363 | + } | |
1364 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
1365 | + if (!error && (flag & O_TRUNC)) { | |
1366 | + r.type = CCS_MAC_TRUNCATE; | |
1367 | + error = ccs_check_acl(&r, false); | |
1368 | + } | |
1369 | +#endif | |
1370 | + ccs_clear_request_info(&r); | |
1371 | + return error; | |
1372 | +} | |
1373 | + | |
1374 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | |
1375 | + | |
1376 | +/** | |
1377 | + * ccs_new_open_permission - Check permission for "read" and "write". | |
1378 | + * | |
1379 | + * @filp: Pointer to "struct file". | |
1380 | + * | |
1381 | + * Returns 0 on success, negative value otherwise. | |
1382 | + */ | |
1383 | +static int ccs_new_open_permission(struct file *filp) | |
1384 | +{ | |
1385 | + return __ccs_open_permission(filp->f_path.dentry, filp->f_path.mnt, | |
1386 | + filp->f_flags); | |
1387 | +} | |
1388 | + | |
1389 | +#endif | |
1390 | + | |
1391 | +/** | |
1392 | + * ccs_path_perm - Check permission for "unlink", "rmdir", "truncate", "append", "getattr" and "chroot". | |
1393 | + * | |
1394 | + * @operation: One of values in "enum ccs_mac_index". | |
1395 | + * @dentry: Pointer to "struct dentry". | |
1396 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1397 | + * | |
1398 | + * Returns 0 on success, negative value otherwise. | |
1399 | + */ | |
1400 | +static int ccs_path_perm(const enum ccs_mac_index operation, | |
1401 | + struct dentry *dentry, struct vfsmount *mnt) | |
1402 | +{ | |
1403 | + struct ccs_request_info r = { }; | |
1404 | + ccs_check_auto_domain_transition(); | |
1405 | + r.type = operation; | |
1406 | + r.obj.path[0].dentry = dentry; | |
1407 | + r.obj.path[0].mnt = mnt; | |
1408 | + return ccs_check_acl(&r, true); | |
1409 | +} | |
1410 | + | |
1411 | +/** | |
1412 | + * ccs_mkdev_perm - Check permission for "mkblock" and "mkchar". | |
1413 | + * | |
1414 | + * @operation: Type of operation. (CCS_MAC_MKCHAR or CCS_MAC_MKBLOCK) | |
1415 | + * @dentry: Pointer to "struct dentry". | |
1416 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1417 | + * @mode: Create mode. | |
1418 | + * @dev: Device number. | |
1419 | + * | |
1420 | + * Returns 0 on success, negative value otherwise. | |
1421 | + */ | |
1422 | +static int ccs_mkdev_perm(const u8 operation, struct dentry *dentry, | |
1423 | + struct vfsmount *mnt, const unsigned int mode, | |
1424 | + unsigned int dev) | |
1425 | +{ | |
1426 | + struct ccs_request_info r = { }; | |
1427 | + ccs_check_auto_domain_transition(); | |
1428 | + r.obj.path[0].dentry = dentry; | |
1429 | + r.obj.path[0].mnt = mnt; | |
1430 | + dev = new_decode_dev(dev); | |
1431 | + r.type = operation; | |
1432 | + r.param.i[0] = mode; | |
1433 | + r.param.i[1] = MAJOR(dev); | |
1434 | + r.param.i[2] = MINOR(dev); | |
1435 | + return ccs_check_acl(&r, true); | |
1436 | +} | |
1437 | + | |
1438 | +/** | |
1439 | + * ccs_path2_perm - Check permission for "rename", "link" and "pivot_root". | |
1440 | + * | |
1441 | + * @operation: One of values in "enum ccs_mac_index". | |
1442 | + * @dentry1: Pointer to "struct dentry". | |
1443 | + * @mnt1: Pointer to "struct vfsmount". Maybe NULL. | |
1444 | + * @dentry2: Pointer to "struct dentry". | |
1445 | + * @mnt2: Pointer to "struct vfsmount". Maybe NULL. | |
1446 | + * | |
1447 | + * Returns 0 on success, negative value otherwise. | |
1448 | + */ | |
1449 | +static int ccs_path2_perm(const enum ccs_mac_index operation, | |
1450 | + struct dentry *dentry1, struct vfsmount *mnt1, | |
1451 | + struct dentry *dentry2, struct vfsmount *mnt2) | |
1452 | +{ | |
1453 | + struct ccs_request_info r = { }; | |
1454 | + ccs_check_auto_domain_transition(); | |
1455 | + r.type = operation; | |
1456 | + r.obj.path[0].dentry = dentry1; | |
1457 | + r.obj.path[0].mnt = mnt1; | |
1458 | + r.obj.path[1].dentry = dentry2; | |
1459 | + r.obj.path[1].mnt = mnt2; | |
1460 | + return ccs_check_acl(&r, true); | |
1461 | +} | |
1462 | + | |
1463 | +/** | |
1464 | + * __ccs_symlink_permission - Check permission for "symlink". | |
1465 | + * | |
1466 | + * @dentry: Pointer to "struct dentry". | |
1467 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1468 | + * @target: Content of symlink. | |
1469 | + * | |
1470 | + * Returns 0 on success, negative value otherwise. | |
1471 | + */ | |
1472 | +static int __ccs_symlink_permission(struct dentry *dentry, | |
1473 | + struct vfsmount *mnt, const char *target) | |
1474 | +{ | |
1475 | + struct ccs_request_info r = { }; | |
1476 | + ccs_check_auto_domain_transition(); | |
1477 | + r.type = CCS_MAC_SYMLINK; | |
1478 | + r.obj.path[0].dentry = dentry; | |
1479 | + r.obj.path[0].mnt = mnt; | |
1480 | + r.obj.pathname[1].name = ccs_encode(target); | |
1481 | + if (!r.obj.pathname[1].name) | |
1482 | + return -ENOMEM; | |
1483 | + ccs_fill_path_info(&r.obj.pathname[1]); | |
1484 | + r.param.s[1] = &r.obj.pathname[1]; | |
1485 | + return ccs_check_acl(&r, true); | |
1486 | +} | |
1487 | + | |
1488 | +/** | |
1489 | + * ccs_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp" and "unmount". | |
1490 | + * | |
1491 | + * @type: One of values in "enum ccs_mac_index". | |
1492 | + * @dentry: Pointer to "struct dentry". | |
1493 | + * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. | |
1494 | + * @number: Number. | |
1495 | + * | |
1496 | + * Returns 0 on success, negative value otherwise. | |
1497 | + */ | |
1498 | +static int ccs_path_number_perm(const enum ccs_mac_index type, | |
1499 | + struct dentry *dentry, struct vfsmount *vfsmnt, | |
1500 | + unsigned long number) | |
1501 | +{ | |
1502 | + struct ccs_request_info r = { }; | |
1503 | + ccs_check_auto_domain_transition(); | |
1504 | + r.type = type; | |
1505 | + r.obj.path[0].dentry = dentry; | |
1506 | + r.obj.path[0].mnt = vfsmnt; | |
1507 | + r.param.i[0] = number; | |
1508 | + return ccs_check_acl(&r, true); | |
1509 | +} | |
1510 | + | |
1511 | +/** | |
1512 | + * __ccs_ioctl_permission - Check permission for "ioctl". | |
1513 | + * | |
1514 | + * @filp: Pointer to "struct file". | |
1515 | + * @cmd: Ioctl command number. | |
1516 | + * @arg: Param for @cmd. | |
1517 | + * | |
1518 | + * Returns 0 on success, negative value otherwise. | |
1519 | + */ | |
1520 | +static int __ccs_ioctl_permission(struct file *filp, unsigned int cmd, | |
1521 | + unsigned long arg) | |
1522 | +{ | |
1523 | + return ccs_path_number_perm(CCS_MAC_IOCTL, filp->f_dentry, | |
1524 | + filp->f_vfsmnt, cmd); | |
1525 | +} | |
1526 | + | |
1527 | +/** | |
1528 | + * __ccs_chmod_permission - Check permission for "chmod". | |
1529 | + * | |
1530 | + * @dentry: Pointer to "struct dentry". | |
1531 | + * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. | |
1532 | + * @mode: Mode. | |
1533 | + * | |
1534 | + * Returns 0 on success, negative value otherwise. | |
1535 | + */ | |
1536 | +static int __ccs_chmod_permission(struct dentry *dentry, | |
1537 | + struct vfsmount *vfsmnt, mode_t mode) | |
1538 | +{ | |
1539 | + if (mode == (mode_t) -1) | |
1540 | + return 0; | |
1541 | + return ccs_path_number_perm(CCS_MAC_CHMOD, dentry, vfsmnt, | |
1542 | + mode & S_IALLUGO); | |
1543 | +} | |
1544 | + | |
1545 | +/** | |
1546 | + * __ccs_chown_permission - Check permission for "chown/chgrp". | |
1547 | + * | |
1548 | + * @dentry: Pointer to "struct dentry". | |
1549 | + * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. | |
1550 | + * @user: User ID. | |
1551 | + * @group: Group ID. | |
1552 | + * | |
1553 | + * Returns 0 on success, negative value otherwise. | |
1554 | + */ | |
1555 | +static int __ccs_chown_permission(struct dentry *dentry, | |
1556 | + struct vfsmount *vfsmnt, uid_t user, | |
1557 | + gid_t group) | |
1558 | +{ | |
1559 | + int error = 0; | |
1560 | + if (user == (uid_t) -1 && group == (gid_t) -1) | |
1561 | + return 0; | |
1562 | + if (user != (uid_t) -1) | |
1563 | + error = ccs_path_number_perm(CCS_MAC_CHOWN, dentry, | |
1564 | + vfsmnt, user); | |
1565 | + if (!error && group != (gid_t) -1) | |
1566 | + error = ccs_path_number_perm(CCS_MAC_CHGRP, dentry, | |
1567 | + vfsmnt, group); | |
1568 | + return error; | |
1569 | +} | |
1570 | + | |
1571 | +/** | |
1572 | + * __ccs_fcntl_permission - Check permission for changing O_APPEND flag. | |
1573 | + * | |
1574 | + * @file: Pointer to "struct file". | |
1575 | + * @cmd: Command number. | |
1576 | + * @arg: Value for @cmd. | |
1577 | + * | |
1578 | + * Returns 0 on success, negative value otherwise. | |
1579 | + */ | |
1580 | +static int __ccs_fcntl_permission(struct file *file, unsigned int cmd, | |
1581 | + unsigned long arg) | |
1582 | +{ | |
1583 | + if (!(cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))) | |
1584 | + return 0; | |
1585 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | |
1586 | + return __ccs_open_permission(file->f_dentry, file->f_vfsmnt, | |
1587 | + O_WRONLY | (arg & O_APPEND)); | |
1588 | +#elif defined(RHEL_MAJOR) && RHEL_MAJOR == 6 | |
1589 | + return __ccs_open_permission(file->f_dentry, file->f_vfsmnt, | |
1590 | + O_WRONLY | (arg & O_APPEND)); | |
1591 | +#else | |
1592 | + return __ccs_open_permission(file->f_dentry, file->f_vfsmnt, | |
1593 | + (O_WRONLY + 1) | (arg & O_APPEND)); | |
1594 | +#endif | |
1595 | +} | |
1596 | + | |
1597 | +/** | |
1598 | + * __ccs_pivot_root_permission - Check permission for pivot_root(). | |
1599 | + * | |
1600 | + * @old_path: Pointer to "struct path". | |
1601 | + * @new_path: Pointer to "struct path". | |
1602 | + * | |
1603 | + * Returns 0 on success, negative value otherwise. | |
1604 | + */ | |
1605 | +static int __ccs_pivot_root_permission(struct path *old_path, | |
1606 | + struct path *new_path) | |
1607 | +{ | |
1608 | + return ccs_path2_perm(CCS_MAC_PIVOT_ROOT, new_path->dentry, | |
1609 | + new_path->mnt, old_path->dentry, old_path->mnt); | |
1610 | +} | |
1611 | + | |
1612 | +/** | |
1613 | + * __ccs_chroot_permission - Check permission for chroot(). | |
1614 | + * | |
1615 | + * @path: Pointer to "struct path". | |
1616 | + * | |
1617 | + * Returns 0 on success, negative value otherwise. | |
1618 | + */ | |
1619 | +static int __ccs_chroot_permission(struct path *path) | |
1620 | +{ | |
1621 | + return ccs_path_perm(CCS_MAC_CHROOT, path->dentry, path->mnt); | |
1622 | +} | |
1623 | + | |
1624 | +/** | |
1625 | + * __ccs_umount_permission - Check permission for unmount. | |
1626 | + * | |
1627 | + * @mnt: Pointer to "struct vfsmount". | |
1628 | + * @flags: Unmount flags. | |
1629 | + * | |
1630 | + * Returns 0 on success, negative value otherwise. | |
1631 | + */ | |
1632 | +static int __ccs_umount_permission(struct vfsmount *mnt, int flags) | |
1633 | +{ | |
1634 | + return ccs_path_number_perm(CCS_MAC_UMOUNT, mnt->mnt_root, mnt, flags); | |
1635 | +} | |
1636 | + | |
1637 | +/** | |
1638 | + * __ccs_mknod_permission - Check permission for vfs_mknod(). | |
1639 | + * | |
1640 | + * @dentry: Pointer to "struct dentry". | |
1641 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1642 | + * @mode: Device type and permission. | |
1643 | + * @dev: Device number for block or character device. | |
1644 | + * | |
1645 | + * Returns 0 on success, negative value otherwise. | |
1646 | + */ | |
1647 | +static int __ccs_mknod_permission(struct dentry *dentry, struct vfsmount *mnt, | |
1648 | + const unsigned int mode, unsigned int dev) | |
1649 | +{ | |
1650 | + int error = 0; | |
1651 | + const unsigned int perm = mode & S_IALLUGO; | |
1652 | + switch (mode & S_IFMT) { | |
1653 | + case S_IFCHR: | |
1654 | + error = ccs_mkdev_perm(CCS_MAC_MKCHAR, dentry, mnt, perm, | |
1655 | + dev); | |
1656 | + break; | |
1657 | + case S_IFBLK: | |
1658 | + error = ccs_mkdev_perm(CCS_MAC_MKBLOCK, dentry, mnt, perm, | |
1659 | + dev); | |
1660 | + break; | |
1661 | + case S_IFIFO: | |
1662 | + error = ccs_path_number_perm(CCS_MAC_MKFIFO, dentry, mnt, | |
1663 | + perm); | |
1664 | + break; | |
1665 | + case S_IFSOCK: | |
1666 | + error = ccs_path_number_perm(CCS_MAC_MKSOCK, dentry, mnt, | |
1667 | + perm); | |
1668 | + break; | |
1669 | + case 0: | |
1670 | + case S_IFREG: | |
1671 | + error = ccs_path_number_perm(CCS_MAC_CREATE, dentry, mnt, | |
1672 | + perm); | |
1673 | + break; | |
1674 | + } | |
1675 | + return error; | |
1676 | +} | |
1677 | + | |
1678 | +/** | |
1679 | + * __ccs_mkdir_permission - Check permission for vfs_mkdir(). | |
1680 | + * | |
1681 | + * @dentry: Pointer to "struct dentry". | |
1682 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1683 | + * @mode: Create mode. | |
1684 | + * | |
1685 | + * Returns 0 on success, negative value otherwise. | |
1686 | + */ | |
1687 | +static int __ccs_mkdir_permission(struct dentry *dentry, struct vfsmount *mnt, | |
1688 | + unsigned int mode) | |
1689 | +{ | |
1690 | + return ccs_path_number_perm(CCS_MAC_MKDIR, dentry, mnt, mode); | |
1691 | +} | |
1692 | + | |
1693 | +/** | |
1694 | + * __ccs_rmdir_permission - Check permission for vfs_rmdir(). | |
1695 | + * | |
1696 | + * @dentry: Pointer to "struct dentry". | |
1697 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1698 | + * | |
1699 | + * Returns 0 on success, negative value otherwise. | |
1700 | + */ | |
1701 | +static int __ccs_rmdir_permission(struct dentry *dentry, struct vfsmount *mnt) | |
1702 | +{ | |
1703 | + return ccs_path_perm(CCS_MAC_RMDIR, dentry, mnt); | |
1704 | +} | |
1705 | + | |
1706 | +/** | |
1707 | + * __ccs_unlink_permission - Check permission for vfs_unlink(). | |
1708 | + * | |
1709 | + * @dentry: Pointer to "struct dentry". | |
1710 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1711 | + * | |
1712 | + * Returns 0 on success, negative value otherwise. | |
1713 | + */ | |
1714 | +static int __ccs_unlink_permission(struct dentry *dentry, struct vfsmount *mnt) | |
1715 | +{ | |
1716 | + return ccs_path_perm(CCS_MAC_UNLINK, dentry, mnt); | |
1717 | +} | |
1718 | + | |
1719 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
1720 | + | |
1721 | +/** | |
1722 | + * __ccs_getattr_permission - Check permission for vfs_getattr(). | |
1723 | + * | |
1724 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1725 | + * @dentry: Pointer to "struct dentry". | |
1726 | + * | |
1727 | + * Returns 0 on success, negative value otherwise. | |
1728 | + */ | |
1729 | +static int __ccs_getattr_permission(struct vfsmount *mnt, | |
1730 | + struct dentry *dentry) | |
1731 | +{ | |
1732 | + return ccs_path_perm(CCS_MAC_GETATTR, dentry, mnt); | |
1733 | +} | |
1734 | + | |
1735 | +#endif | |
1736 | + | |
1737 | +/** | |
1738 | + * __ccs_truncate_permission - Check permission for notify_change(). | |
1739 | + * | |
1740 | + * @dentry: Pointer to "struct dentry". | |
1741 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1742 | + * | |
1743 | + * Returns 0 on success, negative value otherwise. | |
1744 | + */ | |
1745 | +static int __ccs_truncate_permission(struct dentry *dentry, | |
1746 | + struct vfsmount *mnt) | |
1747 | +{ | |
1748 | + return ccs_path_perm(CCS_MAC_TRUNCATE, dentry, mnt); | |
1749 | +} | |
1750 | + | |
1751 | +/** | |
1752 | + * __ccs_rename_permission - Check permission for vfs_rename(). | |
1753 | + * | |
1754 | + * @old_dentry: Pointer to "struct dentry". | |
1755 | + * @new_dentry: Pointer to "struct dentry". | |
1756 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1757 | + * | |
1758 | + * Returns 0 on success, negative value otherwise. | |
1759 | + */ | |
1760 | +static int __ccs_rename_permission(struct dentry *old_dentry, | |
1761 | + struct dentry *new_dentry, | |
1762 | + struct vfsmount *mnt) | |
1763 | +{ | |
1764 | + return ccs_path2_perm(CCS_MAC_RENAME, old_dentry, mnt, new_dentry, | |
1765 | + mnt); | |
1766 | +} | |
1767 | + | |
1768 | +/** | |
1769 | + * __ccs_link_permission - Check permission for vfs_link(). | |
1770 | + * | |
1771 | + * @old_dentry: Pointer to "struct dentry". | |
1772 | + * @new_dentry: Pointer to "struct dentry". | |
1773 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1774 | + * | |
1775 | + * Returns 0 on success, negative value otherwise. | |
1776 | + */ | |
1777 | +static int __ccs_link_permission(struct dentry *old_dentry, | |
1778 | + struct dentry *new_dentry, | |
1779 | + struct vfsmount *mnt) | |
1780 | +{ | |
1781 | + return ccs_path2_perm(CCS_MAC_LINK, old_dentry, mnt, new_dentry, | |
1782 | + mnt); | |
1783 | +} | |
1784 | + | |
1785 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | |
1786 | + | |
1787 | +/** | |
1788 | + * __ccs_open_exec_permission - Check permission for open_exec(). | |
1789 | + * | |
1790 | + * @dentry: Pointer to "struct dentry". | |
1791 | + * @mnt: Pointer to "struct vfsmount". | |
1792 | + * | |
1793 | + * Returns 0 on success, negative value otherwise. | |
1794 | + */ | |
1795 | +static int __ccs_open_exec_permission(struct dentry *dentry, | |
1796 | + struct vfsmount *mnt) | |
1797 | +{ | |
1798 | + return (ccs_current_flags() & CCS_TASK_IS_IN_EXECVE) ? | |
1799 | + __ccs_open_permission(dentry, mnt, O_RDONLY + 1) : 0; | |
1800 | +} | |
1801 | + | |
1802 | +/** | |
1803 | + * __ccs_uselib_permission - Check permission for sys_uselib(). | |
1804 | + * | |
1805 | + * @dentry: Pointer to "struct dentry". | |
1806 | + * @mnt: Pointer to "struct vfsmount". | |
1807 | + * | |
1808 | + * Returns 0 on success, negative value otherwise. | |
1809 | + */ | |
1810 | +static int __ccs_uselib_permission(struct dentry *dentry, struct vfsmount *mnt) | |
1811 | +{ | |
1812 | + return __ccs_open_permission(dentry, mnt, O_RDONLY + 1); | |
1813 | +} | |
1814 | + | |
1815 | +#endif | |
1816 | + | |
1817 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL) | |
1818 | + | |
1819 | +/** | |
1820 | + * ccs_sysctl_permission - Check permission for sysctl operation. | |
1821 | + * | |
1822 | + * @type: One of values in "enum ccs_mac_index". | |
1823 | + * @filename: Filename to check. | |
1824 | + * | |
1825 | + * Returns 0 on success, negative value otherwise. | |
1826 | + */ | |
1827 | +static int ccs_sysctl_permission(enum ccs_mac_index type, | |
1828 | + const struct ccs_path_info *filename) | |
1829 | +{ | |
1830 | + struct ccs_request_info r = { }; | |
1831 | + r.type = type; | |
1832 | + r.param.s[0] = filename; | |
1833 | + return ccs_check_acl(&r, true); | |
1834 | +} | |
1835 | + | |
1836 | +/** | |
1837 | + * __ccs_parse_table - Check permission for parse_table(). | |
1838 | + * | |
1839 | + * @name: Pointer to "int __user". | |
1840 | + * @nlen: Number of elements in @name. | |
1841 | + * @oldval: Pointer to "void __user". | |
1842 | + * @newval: Pointer to "void __user". | |
1843 | + * @table: Pointer to "struct ctl_table". | |
1844 | + * | |
1845 | + * Returns 0 on success, negative value otherwise. | |
1846 | + * | |
1847 | + * Note that this function is racy because this function checks values in | |
1848 | + * userspace memory which could be changed after permission check. | |
1849 | + */ | |
1850 | +static int __ccs_parse_table(int __user *name, int nlen, void __user *oldval, | |
1851 | + void __user *newval, struct ctl_table *table) | |
1852 | +{ | |
1853 | + int n; | |
1854 | + int error = -ENOMEM; | |
1855 | + int op = 0; | |
1856 | + struct ccs_path_info buf; | |
1857 | + char *buffer = NULL; | |
1858 | + if (oldval) | |
1859 | + op |= 004; | |
1860 | + if (newval) | |
1861 | + op |= 002; | |
1862 | + if (!op) /* Neither read nor write */ | |
1863 | + return 0; | |
1864 | + buffer = kmalloc(PAGE_SIZE, GFP_NOFS); | |
1865 | + if (!buffer) | |
1866 | + goto out; | |
1867 | + snprintf(buffer, PAGE_SIZE - 1, "proc:/sys"); | |
1868 | +repeat: | |
1869 | + if (!nlen) { | |
1870 | + error = -ENOTDIR; | |
1871 | + goto out; | |
1872 | + } | |
1873 | + if (get_user(n, name)) { | |
1874 | + error = -EFAULT; | |
1875 | + goto out; | |
1876 | + } | |
1877 | + for ( ; table->ctl_name || table->procname; table++) { | |
1878 | + int pos; | |
1879 | + const char *cp; | |
1880 | + if (!n || n != table->ctl_name) | |
1881 | + continue; | |
1882 | + pos = strlen(buffer); | |
1883 | + cp = table->procname; | |
1884 | + error = -ENOMEM; | |
1885 | + if (cp) { | |
1886 | + int len = strlen(cp); | |
1887 | + if (len + 2 > PAGE_SIZE - 1) | |
1888 | + goto out; | |
1889 | + buffer[pos++] = '/'; | |
1890 | + memmove(buffer + pos, cp, len + 1); | |
1891 | + } else { | |
1892 | + /* Assume nobody assigns "=\$=" for procname. */ | |
1893 | + snprintf(buffer + pos, PAGE_SIZE - pos - 1, | |
1894 | + "/=%d=", table->ctl_name); | |
1895 | + if (!memchr(buffer, '\0', PAGE_SIZE - 2)) | |
1896 | + goto out; | |
1897 | + } | |
1898 | + if (!table->child) | |
1899 | + goto no_child; | |
1900 | + name++; | |
1901 | + nlen--; | |
1902 | + table = table->child; | |
1903 | + goto repeat; | |
1904 | +no_child: | |
1905 | + /* printk("sysctl='%s'\n", buffer); */ | |
1906 | + buf.name = ccs_encode(buffer); | |
1907 | + if (!buf.name) | |
1908 | + goto out; | |
1909 | + ccs_fill_path_info(&buf); | |
1910 | + if (op & MAY_READ) | |
1911 | + error = ccs_sysctl_permission(CCS_MAC_READ, &buf); | |
1912 | + else | |
1913 | + error = 0; | |
1914 | + if (!error && (op & MAY_WRITE)) | |
1915 | + error = ccs_sysctl_permission(CCS_MAC_WRITE, | |
1916 | + &buf); | |
1917 | + kfree(buf.name); | |
1918 | + goto out; | |
1919 | + } | |
1920 | + error = -ENOTDIR; | |
1921 | +out: | |
1922 | + kfree(buffer); | |
1923 | + return error; | |
1924 | +} | |
1925 | + | |
1926 | +#endif | |
1927 | + | |
1928 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
1929 | + | |
1930 | +/** | |
1931 | + * ccs_ip_matches_group - Check whether the given IP address matches members of the given IP group. | |
1932 | + * | |
1933 | + * @is_ipv6: True if @address is an IPv6 address. | |
1934 | + * @address: An IPv4 or IPv6 address. | |
1935 | + * @group: Pointer to "struct ccs_ip_group". | |
1936 | + * | |
1937 | + * Returns true if @address matches addresses in @group group, false otherwise. | |
1938 | + * | |
1939 | + * Caller holds ccs_read_lock(). | |
1940 | + */ | |
1941 | +static bool ccs_ip_matches_group(const bool is_ipv6, const u8 *address, | |
1942 | + const struct ccs_group *group) | |
1943 | +{ | |
1944 | + struct ccs_ip_group *member; | |
1945 | + bool matched = false; | |
1946 | + const u8 size = is_ipv6 ? 16 : 4; | |
1947 | + list_for_each_entry_srcu(member, &group->member_list, head.list, | |
1948 | + &ccs_ss) { | |
1949 | + if (member->head.is_deleted) | |
1950 | + continue; | |
1951 | + if (member->is_ipv6 != is_ipv6) | |
1952 | + continue; | |
1953 | + if (memcmp(&member->ip[0], address, size) > 0 || | |
1954 | + memcmp(address, &member->ip[1], size) > 0) | |
1955 | + continue; | |
1956 | + matched = true; | |
1957 | + break; | |
1958 | + } | |
1959 | + return matched; | |
1960 | +} | |
1961 | + | |
1962 | +/** | |
1963 | + * ccs_inet_entry - Check permission for INET network operation. | |
1964 | + * | |
1965 | + * @address: Pointer to "struct ccs_addr_info". | |
1966 | + * | |
1967 | + * Returns 0 on success, negative value otherwise. | |
1968 | + */ | |
1969 | +static int ccs_inet_entry(const struct ccs_addr_info *address) | |
1970 | +{ | |
1971 | + struct ccs_request_info r = { }; | |
1972 | + ccs_check_auto_domain_transition(); | |
1973 | + r.type = address->operation; | |
1974 | + r.param.is_ipv6 = address->inet.is_ipv6; | |
1975 | + r.param.ip = address->inet.address; | |
1976 | + r.param.i[0] = ntohs(address->inet.port); | |
1977 | + return ccs_check_acl(&r, true); | |
1978 | +} | |
1979 | + | |
1980 | +/** | |
1981 | + * ccs_check_inet_address - Check permission for inet domain socket's operation. | |
1982 | + * | |
1983 | + * @addr: Pointer to "struct sockaddr". | |
1984 | + * @addr_len: Size of @addr. | |
1985 | + * @port: Port number. | |
1986 | + * @address: Pointer to "struct ccs_addr_info". | |
1987 | + * | |
1988 | + * Returns 0 on success, negative value otherwise. | |
1989 | + */ | |
1990 | +static int ccs_check_inet_address(const struct sockaddr *addr, | |
1991 | + const unsigned int addr_len, const u16 port, | |
1992 | + struct ccs_addr_info *address) | |
1993 | +{ | |
1994 | + struct ccs_inet_addr_info *i = &address->inet; | |
1995 | + switch (addr->sa_family) { | |
1996 | + case AF_INET6: | |
1997 | + if (addr_len < SIN6_LEN_RFC2133) | |
1998 | + goto skip; | |
1999 | + i->is_ipv6 = true; | |
2000 | + i->address = | |
2001 | + ((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr; | |
2002 | + i->port = ((struct sockaddr_in6 *) addr)->sin6_port; | |
2003 | + break; | |
2004 | + case AF_INET: | |
2005 | + if (addr_len < sizeof(struct sockaddr_in)) | |
2006 | + goto skip; | |
2007 | + i->is_ipv6 = false; | |
2008 | + i->address = (u8 *) &((struct sockaddr_in *) addr)->sin_addr; | |
2009 | + i->port = ((struct sockaddr_in *) addr)->sin_port; | |
2010 | + break; | |
2011 | + default: | |
2012 | + goto skip; | |
2013 | + } | |
2014 | + if (address->operation == CCS_MAC_INET_RAW_BIND || | |
2015 | + address->operation == CCS_MAC_INET_RAW_SEND || | |
2016 | + address->operation == CCS_MAC_INET_RAW_RECV) | |
2017 | + i->port = htons(port); | |
2018 | + return ccs_inet_entry(address); | |
2019 | +skip: | |
2020 | + return 0; | |
2021 | +} | |
2022 | + | |
2023 | +/** | |
2024 | + * ccs_unix_entry - Check permission for UNIX network operation. | |
2025 | + * | |
2026 | + * @address: Pointer to "struct ccs_addr_info". | |
2027 | + * | |
2028 | + * Returns 0 on success, negative value otherwise. | |
2029 | + */ | |
2030 | +static int ccs_unix_entry(const struct ccs_addr_info *address) | |
2031 | +{ | |
2032 | + int error; | |
2033 | + char *buf = address->unix0.addr; | |
2034 | + int len = address->unix0.addr_len - sizeof(sa_family_t); | |
2035 | + if (len <= 0) { | |
2036 | + buf = "anonymous"; | |
2037 | + len = 9; | |
2038 | + } else if (buf[0]) { | |
2039 | + len = strnlen(buf, len); | |
2040 | + } | |
2041 | + buf = ccs_encode2(buf, len); | |
2042 | + if (buf) { | |
2043 | + struct ccs_path_info addr; | |
2044 | + struct ccs_request_info r = { }; | |
2045 | + addr.name = buf; | |
2046 | + ccs_fill_path_info(&addr); | |
2047 | + r.type = address->operation; | |
2048 | + r.param.s[0] = &addr; | |
2049 | + error = ccs_check_acl(&r, true); | |
2050 | + kfree(buf); | |
2051 | + } else | |
2052 | + error = -ENOMEM; | |
2053 | + return error; | |
2054 | +} | |
2055 | + | |
2056 | +/** | |
2057 | + * ccs_check_unix_address - Check permission for unix domain socket's operation. | |
2058 | + * | |
2059 | + * @addr: Pointer to "struct sockaddr". | |
2060 | + * @addr_len: Size of @addr. | |
2061 | + * @address: Pointer to "struct ccs_addr_info". | |
2062 | + * | |
2063 | + * Returns 0 on success, negative value otherwise. | |
2064 | + */ | |
2065 | +static int ccs_check_unix_address(struct sockaddr *addr, | |
2066 | + const unsigned int addr_len, | |
2067 | + struct ccs_addr_info *address) | |
2068 | +{ | |
2069 | + struct ccs_unix_addr_info *u = &address->unix0; | |
2070 | + if (addr->sa_family != AF_UNIX) | |
2071 | + return 0; | |
2072 | + u->addr = ((struct sockaddr_un *) addr)->sun_path; | |
2073 | + u->addr_len = addr_len; | |
2074 | + return ccs_unix_entry(address); | |
2075 | +} | |
2076 | + | |
2077 | +/** | |
2078 | + * ccs_sock_family - Get socket's family. | |
2079 | + * | |
2080 | + * @sk: Pointer to "struct sock". | |
2081 | + * | |
2082 | + * Returns one of PF_INET, PF_INET6, PF_UNIX or 0. | |
2083 | + */ | |
2084 | +static u8 ccs_sock_family(struct sock *sk) | |
2085 | +{ | |
2086 | + u8 family; | |
2087 | + if (ccs_kernel_service()) | |
2088 | + return 0; | |
2089 | + family = sk->sk_family; | |
2090 | + switch (family) { | |
2091 | + case PF_INET: | |
2092 | + case PF_INET6: | |
2093 | + case PF_UNIX: | |
2094 | + return family; | |
2095 | + default: | |
2096 | + return 0; | |
2097 | + } | |
2098 | +} | |
2099 | + | |
2100 | +/** | |
2101 | + * __ccs_socket_listen_permission - Check permission for listening a socket. | |
2102 | + * | |
2103 | + * @sock: Pointer to "struct socket". | |
2104 | + * | |
2105 | + * Returns 0 on success, negative value otherwise. | |
2106 | + */ | |
2107 | +static int __ccs_socket_listen_permission(struct socket *sock) | |
2108 | +{ | |
2109 | + struct ccs_addr_info address; | |
2110 | + const u8 family = ccs_sock_family(sock->sk); | |
2111 | + const unsigned int type = sock->type; | |
2112 | + struct sockaddr_storage addr; | |
2113 | + int addr_len; | |
2114 | + if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET)) | |
2115 | + return 0; | |
2116 | + { | |
2117 | + const int error = sock->ops->getname(sock, (struct sockaddr *) | |
2118 | + &addr, &addr_len, 0); | |
2119 | + if (error) | |
2120 | + return error; | |
2121 | + } | |
2122 | + if (family == PF_INET || family == PF_INET6) | |
2123 | + address.operation = CCS_MAC_INET_STREAM_LISTEN; | |
2124 | + else if (type == SOCK_STREAM) | |
2125 | + address.operation = CCS_MAC_UNIX_STREAM_LISTEN; | |
2126 | + else | |
2127 | + address.operation = CCS_MAC_UNIX_SEQPACKET_LISTEN; | |
2128 | + if (family == PF_UNIX) | |
2129 | + return ccs_check_unix_address((struct sockaddr *) &addr, | |
2130 | + addr_len, &address); | |
2131 | + return ccs_check_inet_address((struct sockaddr *) &addr, addr_len, 0, | |
2132 | + &address); | |
2133 | +} | |
2134 | + | |
2135 | +/** | |
2136 | + * __ccs_socket_connect_permission - Check permission for setting the remote address of a socket. | |
2137 | + * | |
2138 | + * @sock: Pointer to "struct socket". | |
2139 | + * @addr: Pointer to "struct sockaddr". | |
2140 | + * @addr_len: Size of @addr. | |
2141 | + * | |
2142 | + * Returns 0 on success, negative value otherwise. | |
2143 | + */ | |
2144 | +static int __ccs_socket_connect_permission(struct socket *sock, | |
2145 | + struct sockaddr *addr, int addr_len) | |
2146 | +{ | |
2147 | + struct ccs_addr_info address; | |
2148 | + const u8 family = ccs_sock_family(sock->sk); | |
2149 | + if (!family) | |
2150 | + return 0; | |
2151 | + switch (sock->type) { | |
2152 | + case SOCK_DGRAM: | |
2153 | + address.operation = family == PF_UNIX ? | |
2154 | + CCS_MAC_UNIX_DGRAM_SEND : | |
2155 | + CCS_MAC_INET_DGRAM_SEND; | |
2156 | + break; | |
2157 | + case SOCK_RAW: | |
2158 | + address.operation = CCS_MAC_INET_RAW_SEND; | |
2159 | + break; | |
2160 | + case SOCK_STREAM: | |
2161 | + address.operation = family == PF_UNIX ? | |
2162 | + CCS_MAC_UNIX_STREAM_CONNECT : | |
2163 | + CCS_MAC_INET_STREAM_CONNECT; | |
2164 | + break; | |
2165 | + case SOCK_SEQPACKET: | |
2166 | + address.operation = CCS_MAC_UNIX_SEQPACKET_CONNECT; | |
2167 | + break; | |
2168 | + default: | |
2169 | + return 0; | |
2170 | + } | |
2171 | + if (family == PF_UNIX) | |
2172 | + return ccs_check_unix_address(addr, addr_len, &address); | |
2173 | + return ccs_check_inet_address(addr, addr_len, sock->sk->sk_protocol, | |
2174 | + &address); | |
2175 | +} | |
2176 | + | |
2177 | +/** | |
2178 | + * __ccs_socket_bind_permission - Check permission for setting the local address of a socket. | |
2179 | + * | |
2180 | + * @sock: Pointer to "struct socket". | |
2181 | + * @addr: Pointer to "struct sockaddr". | |
2182 | + * @addr_len: Size of @addr. | |
2183 | + * | |
2184 | + * Returns 0 on success, negative value otherwise. | |
2185 | + */ | |
2186 | +static int __ccs_socket_bind_permission(struct socket *sock, | |
2187 | + struct sockaddr *addr, int addr_len) | |
2188 | +{ | |
2189 | + struct ccs_addr_info address; | |
2190 | + const u8 family = ccs_sock_family(sock->sk); | |
2191 | + const unsigned int type = sock->type; | |
2192 | + if (!family) | |
2193 | + return 0; | |
2194 | + switch (type) { | |
2195 | + case SOCK_STREAM: | |
2196 | + address.operation = family == PF_UNIX ? | |
2197 | + CCS_MAC_UNIX_STREAM_BIND : | |
2198 | + CCS_MAC_INET_STREAM_BIND; | |
2199 | + break; | |
2200 | + case SOCK_DGRAM: | |
2201 | + address.operation = family == PF_UNIX ? | |
2202 | + CCS_MAC_UNIX_DGRAM_BIND : | |
2203 | + CCS_MAC_INET_DGRAM_BIND; | |
2204 | + break; | |
2205 | + case SOCK_RAW: | |
2206 | + address.operation = CCS_MAC_INET_RAW_BIND; | |
2207 | + break; | |
2208 | + case SOCK_SEQPACKET: | |
2209 | + address.operation = CCS_MAC_UNIX_SEQPACKET_BIND; | |
2210 | + break; | |
2211 | + default: | |
2212 | + return 0; | |
2213 | + } | |
2214 | + if (family == PF_UNIX) | |
2215 | + return ccs_check_unix_address(addr, addr_len, &address); | |
2216 | + return ccs_check_inet_address(addr, addr_len, sock->sk->sk_protocol, | |
2217 | + &address); | |
2218 | +} | |
2219 | + | |
2220 | +/** | |
2221 | + * __ccs_socket_sendmsg_permission - Check permission for sending a datagram. | |
2222 | + * | |
2223 | + * @sock: Pointer to "struct socket". | |
2224 | + * @msg: Pointer to "struct msghdr". | |
2225 | + * @size: Unused. | |
2226 | + * | |
2227 | + * Returns 0 on success, negative value otherwise. | |
2228 | + */ | |
2229 | +static int __ccs_socket_sendmsg_permission(struct socket *sock, | |
2230 | + struct msghdr *msg, int size) | |
2231 | +{ | |
2232 | + struct ccs_addr_info address; | |
2233 | + const u8 family = ccs_sock_family(sock->sk); | |
2234 | + const unsigned int type = sock->type; | |
2235 | + if (!msg->msg_name || !family || | |
2236 | + (type != SOCK_DGRAM && type != SOCK_RAW)) | |
2237 | + return 0; | |
2238 | + if (family == PF_UNIX) | |
2239 | + address.operation = CCS_MAC_UNIX_DGRAM_SEND; | |
2240 | + else if (type == SOCK_DGRAM) | |
2241 | + address.operation = CCS_MAC_INET_DGRAM_SEND; | |
2242 | + else | |
2243 | + address.operation = CCS_MAC_INET_RAW_SEND; | |
2244 | + if (family == PF_UNIX) | |
2245 | + return ccs_check_unix_address((struct sockaddr *) | |
2246 | + msg->msg_name, msg->msg_namelen, | |
2247 | + &address); | |
2248 | + return ccs_check_inet_address((struct sockaddr *) msg->msg_name, | |
2249 | + msg->msg_namelen, sock->sk->sk_protocol, | |
2250 | + &address); | |
2251 | +} | |
2252 | + | |
2253 | +/** | |
2254 | + * __ccs_socket_post_accept_permission - Check permission for accepting a socket. | |
2255 | + * | |
2256 | + * @sock: Pointer to "struct socket". | |
2257 | + * @newsock: Pointer to "struct socket". | |
2258 | + * | |
2259 | + * Returns 0 on success, negative value otherwise. | |
2260 | + */ | |
2261 | +static int __ccs_socket_post_accept_permission(struct socket *sock, | |
2262 | + struct socket *newsock) | |
2263 | +{ | |
2264 | + struct ccs_addr_info address; | |
2265 | + const u8 family = ccs_sock_family(sock->sk); | |
2266 | + const unsigned int type = sock->type; | |
2267 | + struct sockaddr_storage addr; | |
2268 | + int addr_len; | |
2269 | + if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET)) | |
2270 | + return 0; | |
2271 | + { | |
2272 | + const int error = newsock->ops->getname(newsock, | |
2273 | + (struct sockaddr *) | |
2274 | + &addr, &addr_len, 2); | |
2275 | + if (error) | |
2276 | + return error; | |
2277 | + } | |
2278 | + if (family == PF_INET || family == PF_INET6) | |
2279 | + address.operation = CCS_MAC_INET_STREAM_ACCEPT; | |
2280 | + else if (type == SOCK_STREAM) | |
2281 | + address.operation = CCS_MAC_UNIX_STREAM_ACCEPT; | |
2282 | + else | |
2283 | + address.operation = CCS_MAC_UNIX_SEQPACKET_ACCEPT; | |
2284 | + if (family == PF_UNIX) | |
2285 | + return ccs_check_unix_address((struct sockaddr *) &addr, | |
2286 | + addr_len, &address); | |
2287 | + return ccs_check_inet_address((struct sockaddr *) &addr, addr_len, 0, | |
2288 | + &address); | |
2289 | +} | |
2290 | + | |
2291 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
2292 | + | |
2293 | +/** | |
2294 | + * __ccs_socket_post_recvmsg_permission - Check permission for receiving a datagram. | |
2295 | + * | |
2296 | + * @sk: Pointer to "struct sock". | |
2297 | + * @skb: Pointer to "struct sk_buff". | |
2298 | + * @flags: Flags passed to skb_recv_datagram(). | |
2299 | + * | |
2300 | + * Returns 0 on success, negative value otherwise. | |
2301 | + */ | |
2302 | +static int __ccs_socket_post_recvmsg_permission(struct sock *sk, | |
2303 | + struct sk_buff *skb, int flags) | |
2304 | +{ | |
2305 | + struct ccs_addr_info address; | |
2306 | + const u8 family = ccs_sock_family(sk); | |
2307 | + const unsigned int type = sk->sk_type; | |
2308 | + struct sockaddr_storage addr; | |
2309 | + if (!family || (type != SOCK_DGRAM && type != SOCK_RAW)) | |
2310 | + return 0; | |
2311 | + if (family == PF_UNIX) | |
2312 | + address.operation = CCS_MAC_UNIX_DGRAM_RECV; | |
2313 | + else if (type == SOCK_DGRAM) | |
2314 | + address.operation = CCS_MAC_INET_DGRAM_RECV; | |
2315 | + else | |
2316 | + address.operation = CCS_MAC_INET_RAW_RECV; | |
2317 | + switch (family) { | |
2318 | + case PF_INET6: | |
2319 | + { | |
2320 | + struct in6_addr *sin6 = (struct in6_addr *) &addr; | |
2321 | + address.inet.is_ipv6 = true; | |
2322 | + if (type == SOCK_DGRAM && | |
2323 | + skb->protocol == htons(ETH_P_IP)) | |
2324 | + ipv6_addr_set(sin6, 0, 0, htonl(0xffff), | |
2325 | + ip_hdr(skb)->saddr); | |
2326 | + else | |
2327 | + *sin6 = ipv6_hdr(skb)->saddr; | |
2328 | + break; | |
2329 | + } | |
2330 | + case PF_INET: | |
2331 | + { | |
2332 | + struct in_addr *sin4 = (struct in_addr *) &addr; | |
2333 | + address.inet.is_ipv6 = false; | |
2334 | + sin4->s_addr = ip_hdr(skb)->saddr; | |
2335 | + break; | |
2336 | + } | |
2337 | + default: /* == PF_UNIX */ | |
2338 | + { | |
2339 | + struct unix_address *u = unix_sk(skb->sk)->addr; | |
2340 | + unsigned int addr_len; | |
2341 | + if (u && u->len <= sizeof(addr)) { | |
2342 | + addr_len = u->len; | |
2343 | + memcpy(&addr, u->name, addr_len); | |
2344 | + } else { | |
2345 | + addr_len = 0; | |
2346 | + addr.ss_family = AF_UNIX; | |
2347 | + } | |
2348 | + if (ccs_check_unix_address((struct sockaddr *) &addr, | |
2349 | + addr_len, &address)) | |
2350 | + goto out; | |
2351 | + return 0; | |
2352 | + } | |
2353 | + } | |
2354 | + address.inet.address = (u8 *) &addr; | |
2355 | + if (type == SOCK_DGRAM) | |
2356 | + address.inet.port = udp_hdr(skb)->source; | |
2357 | + else | |
2358 | + address.inet.port = htons(sk->sk_protocol); | |
2359 | + if (ccs_inet_entry(&address)) | |
2360 | + goto out; | |
2361 | + return 0; | |
2362 | +out: | |
2363 | + /* | |
2364 | + * Remove from queue if MSG_PEEK is used so that | |
2365 | + * the head message from unwanted source in receive queue will not | |
2366 | + * prevent the caller from picking up next message from wanted source | |
2367 | + * when the caller is using MSG_PEEK flag for picking up. | |
2368 | + */ | |
2369 | + { | |
2370 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) | |
2371 | + bool slow = false; | |
2372 | + if (type == SOCK_DGRAM && family != PF_UNIX) | |
2373 | + slow = lock_sock_fast(sk); | |
2374 | +#else | |
2375 | + if (type == SOCK_DGRAM && family != PF_UNIX) | |
2376 | + lock_sock(sk); | |
2377 | +#endif | |
2378 | + skb_kill_datagram(sk, skb, flags); | |
2379 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) | |
2380 | + if (type == SOCK_DGRAM && family != PF_UNIX) | |
2381 | + unlock_sock_fast(sk, slow); | |
2382 | +#else | |
2383 | + if (type == SOCK_DGRAM && family != PF_UNIX) | |
2384 | + release_sock(sk); | |
2385 | +#endif | |
2386 | + } | |
2387 | + return -EPERM; | |
2388 | +} | |
2389 | + | |
2390 | +#endif | |
2391 | + | |
2392 | +#endif | |
2393 | + | |
2394 | +#if defined(CONFIG_CCSECURITY_CAPABILITY) || defined(CONFIG_CCSECURITY_NETWORK) | |
2395 | + | |
2396 | +/** | |
2397 | + * ccs_kernel_service - Check whether I'm kernel service or not. | |
2398 | + * | |
2399 | + * Returns true if I'm kernel service, false otherwise. | |
2400 | + */ | |
2401 | +static bool ccs_kernel_service(void) | |
2402 | +{ | |
2403 | + /* Nothing to do if I am a kernel service. */ | |
2404 | + return segment_eq(get_fs(), KERNEL_DS); | |
2405 | +} | |
2406 | + | |
2407 | +#endif | |
2408 | + | |
2409 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
2410 | + | |
2411 | +/** | |
2412 | + * ccs_capable - Check permission for capability. | |
2413 | + * | |
2414 | + * @operation: Type of operation. | |
2415 | + * | |
2416 | + * Returns true on success, false otherwise. | |
2417 | + */ | |
2418 | +static bool __ccs_capable(const u8 operation) | |
2419 | +{ | |
2420 | + struct ccs_request_info r = { }; | |
2421 | + r.type = ccs_c2mac[operation]; | |
2422 | + return !ccs_check_acl(&r, true); | |
2423 | +} | |
2424 | + | |
2425 | +/** | |
2426 | + * __ccs_socket_create_permission - Check permission for creating a socket. | |
2427 | + * | |
2428 | + * @family: Protocol family. | |
2429 | + * @type: Unused. | |
2430 | + * @protocol: Unused. | |
2431 | + * | |
2432 | + * Returns 0 on success, negative value otherwise. | |
2433 | + */ | |
2434 | +static int __ccs_socket_create_permission(int family, int type, int protocol) | |
2435 | +{ | |
2436 | + if (ccs_kernel_service()) | |
2437 | + return 0; | |
2438 | + if (family == PF_PACKET && !ccs_capable(CCS_USE_PACKET_SOCKET)) | |
2439 | + return -EPERM; | |
2440 | + if (family == PF_NETLINK && !ccs_capable(CCS_USE_ROUTE_SOCKET)) | |
2441 | + return -EPERM; | |
2442 | + return 0; | |
2443 | +} | |
2444 | + | |
2445 | +#endif | |
2446 | + | |
2447 | +/** | |
2448 | + * ccs_manager - Check whether the current process is a policy manager. | |
2449 | + * | |
2450 | + * Returns true if the current process is permitted to modify policy | |
2451 | + * via /proc/ccs/ interface. | |
2452 | + * | |
2453 | + * Caller holds ccs_read_lock(). | |
2454 | + */ | |
2455 | +bool ccs_manager(void) | |
2456 | +{ | |
2457 | + struct ccs_security *task; | |
2458 | + if (!ccs_policy_loaded) | |
2459 | + return true; | |
2460 | + task = ccs_current_security(); | |
2461 | + if (task->ccs_flags & CCS_TASK_IS_MANAGER) | |
2462 | + return true; | |
2463 | + { | |
2464 | + struct ccs_request_info r = { }; | |
2465 | + r.type = CCS_MAC_MODIFY_POLICY; | |
2466 | + if (ccs_check_acl(&r, true) == 0) { | |
2467 | + /* Set manager flag. */ | |
2468 | + task->ccs_flags |= CCS_TASK_IS_MANAGER; | |
2469 | + return true; | |
2470 | + } | |
2471 | + } | |
2472 | + { /* Reduce error messages. */ | |
2473 | + static pid_t ccs_last_pid; | |
2474 | + const pid_t pid = current->pid; | |
2475 | + if (ccs_last_pid != pid) { | |
2476 | + const char *exe = ccs_get_exe(); | |
2477 | + printk(KERN_WARNING "'%s' (pid=%u domain='%s') is" | |
2478 | + " not permitted to update policies.\n", exe, | |
2479 | + pid, task->ccs_domain_info->domainname->name); | |
2480 | + ccs_last_pid = pid; | |
2481 | + kfree(exe); | |
2482 | + } | |
2483 | + } | |
2484 | + return false; | |
2485 | +} | |
2486 | + | |
2487 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
2488 | + | |
2489 | +/** | |
2490 | + * __ccs_ptrace_permission - Check permission for ptrace(). | |
2491 | + * | |
2492 | + * @request: Command number. | |
2493 | + * @pid: Target's PID. | |
2494 | + * | |
2495 | + * Returns 0 on success, negative value otherwise. | |
2496 | + */ | |
2497 | +static int __ccs_ptrace_permission(long request, long pid) | |
2498 | +{ | |
2499 | + struct ccs_domain_info *dest; | |
2500 | + int error = -ESRCH; | |
2501 | + const int idx = ccs_read_lock(); | |
2502 | + ccs_check_auto_domain_transition(); | |
2503 | + if (request == PTRACE_TRACEME) { | |
2504 | + dest = ccs_current_domain(); | |
2505 | + } else { | |
2506 | + struct task_struct *p; | |
2507 | + ccs_tasklist_lock(); | |
2508 | + p = ccsecurity_exports.find_task_by_vpid((pid_t) pid); | |
2509 | + if (p) | |
2510 | + dest = ccs_task_domain(p); | |
2511 | + else | |
2512 | + dest = NULL; | |
2513 | + ccs_tasklist_unlock(); | |
2514 | + } | |
2515 | + if (dest) { | |
2516 | + struct ccs_request_info r = { }; | |
2517 | + r.type = CCS_MAC_PTRACE; | |
2518 | + r.param.i[0] = request; | |
2519 | + r.param.s[0] = dest->domainname; | |
2520 | + error = ccs_check_acl(&r, true); | |
2521 | + } | |
2522 | + ccs_read_unlock(idx); | |
2523 | + return error; | |
2524 | +} | |
2525 | + | |
2526 | +#endif | |
2527 | + | |
2528 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
2529 | + | |
2530 | +/** | |
2531 | + * __ccs_signal_permission - Check permission for signal. | |
2532 | + * | |
2533 | + * @sig: Signal number. | |
2534 | + * | |
2535 | + * Returns 0 on success, negative value otherwise. | |
2536 | + * | |
2537 | + * Caller holds ccs_read_lock(). | |
2538 | + */ | |
2539 | +static int __ccs_signal_permission(const int sig) | |
2540 | +{ | |
2541 | + struct ccs_request_info r = { }; | |
2542 | + const int idx = ccs_read_lock(); | |
2543 | + int error; | |
2544 | + ccs_check_auto_domain_transition(); | |
2545 | + r.type = CCS_MAC_SIGNAL; | |
2546 | + r.param.i[0] = sig; | |
2547 | + error = ccs_check_acl(&r, true); | |
2548 | + ccs_read_unlock(idx); | |
2549 | + return error; | |
2550 | +} | |
2551 | + | |
2552 | +/** | |
2553 | + * ccs_signal_permission0 - Check permission for signal. | |
2554 | + * | |
2555 | + * @pid: Unused. | |
2556 | + * @sig: Signal number. | |
2557 | + * | |
2558 | + * Returns 0 on success, negative value otherwise. | |
2559 | + */ | |
2560 | +static int ccs_signal_permission0(const int pid, const int sig) | |
2561 | +{ | |
2562 | + return __ccs_signal_permission(sig); | |
2563 | +} | |
2564 | + | |
2565 | +/** | |
2566 | + * ccs_signal_permission1 - Permission check for signal(). | |
2567 | + * | |
2568 | + * @tgid: Unused. | |
2569 | + * @pid: Unused. | |
2570 | + * @sig: Signal number. | |
2571 | + * | |
2572 | + * Returns 0 on success, negative value otherwise. | |
2573 | + */ | |
2574 | +static int ccs_signal_permission1(pid_t tgid, pid_t pid, int sig) | |
2575 | +{ | |
2576 | + return __ccs_signal_permission(sig); | |
2577 | +} | |
2578 | + | |
2579 | +#endif | |
2580 | + | |
2581 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
2582 | + | |
2583 | +/** | |
2584 | + * ccs_env_perm - Check permission for environment variable's name. | |
2585 | + * | |
2586 | + * @r: Pointer to "struct ccs_request_info". | |
2587 | + * @name: Name of environment variable. Maybe "". | |
2588 | + * @value: Value of environment variable. Maybe "". | |
2589 | + * | |
2590 | + * Returns 0 on success, negative value otherwise. | |
2591 | + */ | |
2592 | +static int ccs_env_perm(struct ccs_request_info *r, const char *name, | |
2593 | + const char *value) | |
2594 | +{ | |
2595 | + struct ccs_path_info n; | |
2596 | + struct ccs_path_info v; | |
2597 | + n.name = name; | |
2598 | + ccs_fill_path_info(&n); | |
2599 | + v.name = value; | |
2600 | + ccs_fill_path_info(&v); | |
2601 | + r->type = CCS_MAC_ENVIRON; | |
2602 | + r->param.s[2] = &n; | |
2603 | + r->param.s[3] = &v; | |
2604 | + return ccs_check_acl(r, false); | |
2605 | +} | |
2606 | + | |
2607 | +/** | |
2608 | + * ccs_environ - Check permission for environment variable names. | |
2609 | + * | |
2610 | + * @r: Pointer to "struct ccs_request_info". | |
2611 | + * | |
2612 | + * Returns 0 on success, negative value otherwise. | |
2613 | + */ | |
2614 | +static int ccs_environ(struct ccs_request_info *r) | |
2615 | +{ | |
2616 | + struct linux_binprm *bprm = r->bprm; | |
2617 | + /* env_page.data is allocated by ccs_dump_page(). */ | |
2618 | + struct ccs_page_dump env_page = { }; | |
2619 | + char *arg_ptr; /* Size is CCS_EXEC_TMPSIZE bytes */ | |
2620 | + int arg_len = 0; | |
2621 | + unsigned long pos = bprm->p; | |
2622 | + int offset = pos % PAGE_SIZE; | |
2623 | + int argv_count = bprm->argc; | |
2624 | + int envp_count = bprm->envc; | |
2625 | + int error = -ENOMEM; | |
2626 | + arg_ptr = kzalloc(CCS_EXEC_TMPSIZE, GFP_NOFS); | |
2627 | + if (!arg_ptr) { | |
2628 | + r->failed_by_oom = true; | |
2629 | + goto out; | |
2630 | + } | |
2631 | + while (error == -ENOMEM) { | |
2632 | + if (!ccs_dump_page(bprm, pos, &env_page)) { | |
2633 | + r->failed_by_oom = true; | |
2634 | + goto out; | |
2635 | + } | |
2636 | + pos += PAGE_SIZE - offset; | |
2637 | + /* Read. */ | |
2638 | + while (argv_count && offset < PAGE_SIZE) { | |
2639 | + if (!env_page.data[offset++]) | |
2640 | + argv_count--; | |
2641 | + } | |
2642 | + if (argv_count) { | |
2643 | + offset = 0; | |
2644 | + continue; | |
2645 | + } | |
2646 | + while (offset < PAGE_SIZE) { | |
2647 | + char *value; | |
2648 | + const unsigned char c = env_page.data[offset++]; | |
2649 | + if (c && arg_len < CCS_EXEC_TMPSIZE - 10) { | |
2650 | + if (c > ' ' && c < 127 && c != '\\') { | |
2651 | + arg_ptr[arg_len++] = c; | |
2652 | + } else { | |
2653 | + arg_ptr[arg_len++] = '\\'; | |
2654 | + arg_ptr[arg_len++] = (c >> 6) + '0'; | |
2655 | + arg_ptr[arg_len++] | |
2656 | + = ((c >> 3) & 7) + '0'; | |
2657 | + arg_ptr[arg_len++] = (c & 7) + '0'; | |
2658 | + } | |
2659 | + } else { | |
2660 | + arg_ptr[arg_len] = '\0'; | |
2661 | + } | |
2662 | + if (c) | |
2663 | + continue; | |
2664 | + value = strchr(arg_ptr, '='); | |
2665 | + if (value) | |
2666 | + *value++ = '\0'; | |
2667 | + else | |
2668 | + value = ""; | |
2669 | + if (ccs_env_perm(r, arg_ptr, value)) { | |
2670 | + error = -EPERM; | |
2671 | + break; | |
2672 | + } | |
2673 | + if (!--envp_count) { | |
2674 | + error = 0; | |
2675 | + break; | |
2676 | + } | |
2677 | + arg_len = 0; | |
2678 | + } | |
2679 | + offset = 0; | |
2680 | + } | |
2681 | +out: | |
2682 | + kfree(env_page.data); | |
2683 | + kfree(arg_ptr); | |
2684 | + return error; | |
2685 | +} | |
2686 | + | |
2687 | +#endif | |
2688 | + | |
2689 | +/** | |
2690 | + * ccs_path_matches_group_or_pattern - Check whether the given pathname matches the given group or the given pattern. | |
2691 | + * | |
2692 | + * @path: Pointer to "struct ccs_path_info". | |
2693 | + * @group: Pointer to "struct ccs_group". Maybe NULL. | |
2694 | + * @pattern: Poiner to "struct ccs_path_info". Maybe NULL. | |
2695 | + * @match: True if positive match, false othwerwise. | |
2696 | + * | |
2697 | + * Returns true on success, false otherwise. | |
2698 | + */ | |
2699 | +static bool ccs_path_matches_group_or_pattern | |
2700 | +(const struct ccs_path_info *path, const struct ccs_group *group, | |
2701 | + const struct ccs_path_info *pattern, const bool match) | |
2702 | +{ | |
2703 | + if (group) | |
2704 | + return ccs_path_matches_group(path, group) == match; | |
2705 | + else if (pattern != &ccs_null_name) | |
2706 | + return ccs_path_matches_pattern(path, pattern) == match; | |
2707 | + else | |
2708 | + return !match; | |
2709 | +} | |
2710 | + | |
2711 | +/** | |
2712 | + * ccs_check_argv - Check argv[] in "struct linux_binbrm". | |
2713 | + * | |
2714 | + * @r: Pointer to "struct ccs_request_info". | |
2715 | + * @index: Index number to check. | |
2716 | + * @group: Pointer to "struct ccs_group". Maybe NULL. | |
2717 | + * @value: Poiner to "struct ccs_path_info". NULL if @group != NULL. | |
2718 | + * @match: True if positive match, false othwerwise. | |
2719 | + * | |
2720 | + * Returns true on success, false otherwise. | |
2721 | + */ | |
2722 | +static bool ccs_check_argv(struct ccs_request_info *r, unsigned long index, | |
2723 | + const struct ccs_group *group, | |
2724 | + const struct ccs_path_info *value, | |
2725 | + const bool match) | |
2726 | +{ | |
2727 | + struct linux_binprm *bprm = r->bprm; | |
2728 | + struct ccs_page_dump *dump = &r->dump; | |
2729 | + char *arg_ptr = r->tmp; | |
2730 | + int arg_len = 0; | |
2731 | + unsigned long pos = bprm->p; | |
2732 | + int offset = pos % PAGE_SIZE; | |
2733 | + struct ccs_path_info arg; | |
2734 | + if (index > bprm->argc) | |
2735 | + return false; | |
2736 | + while (1) { | |
2737 | + if (!ccs_dump_page(bprm, pos, dump)) { | |
2738 | + r->failed_by_oom = true; | |
2739 | + return false; | |
2740 | + } | |
2741 | + pos += PAGE_SIZE - offset; | |
2742 | + while (offset < PAGE_SIZE) { | |
2743 | + const unsigned char c = dump->data[offset++]; | |
2744 | + if (index) { | |
2745 | + if (!c) | |
2746 | + index--; | |
2747 | + continue; | |
2748 | + } | |
2749 | + if (c && arg_len < CCS_EXEC_TMPSIZE - 10) { | |
2750 | + if (c > ' ' && c < 127 && c != '\\') { | |
2751 | + arg_ptr[arg_len++] = c; | |
2752 | + } else { | |
2753 | + arg_ptr[arg_len++] = '\\'; | |
2754 | + arg_ptr[arg_len++] = (c >> 6) + '0'; | |
2755 | + arg_ptr[arg_len++] = | |
2756 | + ((c >> 3) & 7) + '0'; | |
2757 | + arg_ptr[arg_len++] = (c & 7) + '0'; | |
2758 | + } | |
2759 | + continue; | |
2760 | + } | |
2761 | + arg_ptr[arg_len] = '\0'; | |
2762 | + arg.name = arg_ptr; | |
2763 | + ccs_fill_path_info(&arg); | |
2764 | + return ccs_path_matches_group_or_pattern | |
2765 | + (&arg, group, value, match); | |
2766 | + } | |
2767 | + offset = 0; | |
2768 | + } | |
2769 | +} | |
2770 | + | |
2771 | +/** | |
2772 | + * ccs_check_envp - Check envp[] in "struct linux_binbrm". | |
2773 | + * | |
2774 | + * @r: Pointer to "struct ccs_request_info". | |
2775 | + * @name: Pointer to "struct ccs_path_info". | |
2776 | + * @group: Pointer to "struct ccs_group". Maybe NULL. | |
2777 | + * @value: Pointer to "struct ccs_path_info". NULL if @group != NULL. | |
2778 | + * @match: True if positive match, false othwerwise. | |
2779 | + * | |
2780 | + * Returns true on success, false otherwise. | |
2781 | + */ | |
2782 | +static bool ccs_check_envp(struct ccs_request_info *r, | |
2783 | + const struct ccs_path_info *name, | |
2784 | + const struct ccs_group *group, | |
2785 | + const struct ccs_path_info *value, | |
2786 | + const bool match) | |
2787 | +{ | |
2788 | + struct linux_binprm *bprm = r->bprm; | |
2789 | + struct ccs_page_dump *dump = &r->dump; | |
2790 | + char *arg_ptr = r->tmp; | |
2791 | + int arg_len = 0; | |
2792 | + unsigned long pos = bprm->p; | |
2793 | + int offset = pos % PAGE_SIZE; | |
2794 | + int argv_count = bprm->argc; | |
2795 | + int envp_count = bprm->envc; | |
2796 | + bool result = (value != &ccs_null_name) == match; | |
2797 | + struct ccs_path_info env; | |
2798 | + char *cp; | |
2799 | + while (envp_count) { | |
2800 | + if (!ccs_dump_page(bprm, pos, dump)) { | |
2801 | + r->failed_by_oom = true; | |
2802 | + return false; | |
2803 | + } | |
2804 | + pos += PAGE_SIZE - offset; | |
2805 | + while (envp_count && offset < PAGE_SIZE) { | |
2806 | + const unsigned char c = dump->data[offset++]; | |
2807 | + if (argv_count) { | |
2808 | + if (!c) | |
2809 | + argv_count--; | |
2810 | + continue; | |
2811 | + } | |
2812 | + if (c && arg_len < CCS_EXEC_TMPSIZE - 10) { | |
2813 | + if (c > ' ' && c < 127 && c != '\\') { | |
2814 | + arg_ptr[arg_len++] = c; | |
2815 | + } else { | |
2816 | + arg_ptr[arg_len++] = '\\'; | |
2817 | + arg_ptr[arg_len++] = (c >> 6) + '0'; | |
2818 | + arg_ptr[arg_len++] = | |
2819 | + ((c >> 3) & 7) + '0'; | |
2820 | + arg_ptr[arg_len++] = (c & 7) + '0'; | |
2821 | + } | |
2822 | + } else { | |
2823 | + arg_ptr[arg_len] = '\0'; | |
2824 | + } | |
2825 | + if (c) | |
2826 | + continue; | |
2827 | + arg_len = 0; | |
2828 | + envp_count--; | |
2829 | + /* Check. */ | |
2830 | + cp = strchr(arg_ptr, '='); | |
2831 | + if (!cp) | |
2832 | + cp = ""; | |
2833 | + else | |
2834 | + *cp++ = '\0'; | |
2835 | + env.name = arg_ptr; | |
2836 | + ccs_fill_path_info(&env); | |
2837 | + if (!ccs_path_matches_pattern(&env, name)) | |
2838 | + continue; | |
2839 | + result = true; | |
2840 | + env.name = cp; | |
2841 | + ccs_fill_path_info(&env); | |
2842 | + if (ccs_path_matches_group_or_pattern | |
2843 | + (&env, group, value, match)) | |
2844 | + continue; | |
2845 | + return false; | |
2846 | + } | |
2847 | + offset = 0; | |
2848 | + } | |
2849 | + return result; | |
2850 | +} | |
2851 | + | |
2852 | +/** | |
2853 | + * ccs_get_attributes - Revalidate "struct inode". | |
2854 | + * | |
2855 | + * @r: Pointer to "struct ccs_request_info". | |
2856 | + * | |
2857 | + * Returns nothing. | |
2858 | + */ | |
2859 | +void ccs_get_attributes(struct ccs_request_info *r) | |
2860 | +{ | |
2861 | + u8 i; | |
2862 | + struct dentry *dentry = NULL; | |
2863 | + | |
2864 | + if (r->obj.validate_done) | |
2865 | + return; | |
2866 | + for (i = 0; i < CCS_MAX_PATH_STAT; i++) { | |
2867 | + struct inode *inode; | |
2868 | + switch (i) { | |
2869 | + case CCS_PATH1: | |
2870 | + dentry = r->obj.path[0].dentry; | |
2871 | + if (!dentry) | |
2872 | + continue; | |
2873 | + break; | |
2874 | + case CCS_PATH2: | |
2875 | + dentry = r->obj.path[1].dentry; | |
2876 | + if (!dentry) | |
2877 | + continue; | |
2878 | + break; | |
2879 | + default: | |
2880 | + if (!dentry) | |
2881 | + continue; | |
2882 | + dentry = dget_parent(dentry); | |
2883 | + break; | |
2884 | + } | |
2885 | + inode = dentry->d_inode; | |
2886 | + if (inode) { | |
2887 | + struct ccs_mini_stat *stat = &r->obj.stat[i]; | |
2888 | + stat->uid = inode->i_uid; | |
2889 | + stat->gid = inode->i_gid; | |
2890 | + stat->ino = inode->i_ino; | |
2891 | + stat->mode = inode->i_mode; | |
2892 | + stat->dev = inode->i_sb->s_dev; | |
2893 | + stat->rdev = inode->i_rdev; | |
2894 | + stat->fsmagic = dentry->d_sb->s_magic; | |
2895 | + r->obj.stat_valid[i] = true; | |
2896 | + } | |
2897 | + if (i & 1) /* parent directory */ | |
2898 | + dput(dentry); | |
2899 | + } | |
2900 | + r->obj.validate_done = true; | |
2901 | +} | |
2902 | + | |
2903 | +/** | |
2904 | + * ccs_populate_patharg - Calculate pathname for permission check and audit logs. | |
2905 | + * | |
2906 | + * @r: Pointer to "struct ccs_request_info". | |
2907 | + * @first: True for first pathname, false for second pathname. | |
2908 | + * | |
2909 | + * Returns nothing. | |
2910 | + */ | |
2911 | +void ccs_populate_patharg(struct ccs_request_info *r, const bool first) | |
2912 | +{ | |
2913 | + struct ccs_path_info *buf = &r->obj.pathname[!first]; | |
2914 | + struct path *path = &r->obj.path[!first]; | |
2915 | + if (!buf->name && path->dentry) { | |
2916 | + buf->name = ccs_realpath(path); | |
2917 | + /* Set OOM flag if failed. */ | |
2918 | + if (!buf->name) { | |
2919 | + r->failed_by_oom = true; | |
2920 | + return; | |
2921 | + } | |
2922 | + ccs_fill_path_info(buf); | |
2923 | + } | |
2924 | + if (!r->param.s[!first] && buf->name) | |
2925 | + r->param.s[!first] = buf; | |
2926 | +} | |
2927 | + | |
2928 | +/** | |
2929 | + * ccs_cond2arg - Assign values to condition variables. | |
2930 | + * | |
2931 | + * @arg: Pointer to "struct ccs_cond_arg". | |
2932 | + * @cmd: One of values in "enum ccs_conditions_index". | |
2933 | + * @condp: Pointer to "union ccs_condition_element *". | |
2934 | + * @r: Pointer to "struct ccs_request_info". | |
2935 | + * | |
2936 | + * Returns true on success, false othwerwise. | |
2937 | + * | |
2938 | + * This function should not fail. But it can fail if (for example) out of | |
2939 | + * memory has occured while calculating ccs_populate_patharg() or | |
2940 | + * ccs_get_exename(). | |
2941 | + */ | |
2942 | +static bool ccs_cond2arg(struct ccs_cond_arg *arg, | |
2943 | + const enum ccs_conditions_index cmd, | |
2944 | + const union ccs_condition_element **condp, | |
2945 | + struct ccs_request_info *r) | |
2946 | +{ | |
2947 | + struct ccs_mini_stat *stat; | |
2948 | + unsigned long value; | |
2949 | + const struct linux_binprm *bprm = r->bprm; | |
2950 | + const struct ccs_request_param *param = &r->param; | |
2951 | + arg->type = CCS_ARG_TYPE_NUMBER; | |
2952 | + switch (cmd) { | |
2953 | + case CCS_SELF_UID: | |
2954 | + value = current_uid(); | |
2955 | + break; | |
2956 | + case CCS_SELF_EUID: | |
2957 | + value = current_euid(); | |
2958 | + break; | |
2959 | + case CCS_SELF_SUID: | |
2960 | + value = current_suid(); | |
2961 | + break; | |
2962 | + case CCS_SELF_FSUID: | |
2963 | + value = current_fsuid(); | |
2964 | + break; | |
2965 | + case CCS_SELF_GID: | |
2966 | + value = current_gid(); | |
2967 | + break; | |
2968 | + case CCS_SELF_EGID: | |
2969 | + value = current_egid(); | |
2970 | + break; | |
2971 | + case CCS_SELF_SGID: | |
2972 | + value = current_sgid(); | |
2973 | + break; | |
2974 | + case CCS_SELF_FSGID: | |
2975 | + value = current_fsgid(); | |
2976 | + break; | |
2977 | + case CCS_SELF_PID: | |
2978 | + value = ccs_sys_getpid(); | |
2979 | + break; | |
2980 | + case CCS_SELF_PPID: | |
2981 | + value = ccs_sys_getppid(); | |
2982 | + break; | |
2983 | + case CCS_OBJ_IS_SOCKET: | |
2984 | + value = S_IFSOCK; | |
2985 | + break; | |
2986 | + case CCS_OBJ_IS_SYMLINK: | |
2987 | + value = S_IFLNK; | |
2988 | + break; | |
2989 | + case CCS_OBJ_IS_FILE: | |
2990 | + value = S_IFREG; | |
2991 | + break; | |
2992 | + case CCS_OBJ_IS_BLOCK_DEV: | |
2993 | + value = S_IFBLK; | |
2994 | + break; | |
2995 | + case CCS_OBJ_IS_DIRECTORY: | |
2996 | + value = S_IFDIR; | |
2997 | + break; | |
2998 | + case CCS_OBJ_IS_CHAR_DEV: | |
2999 | + value = S_IFCHR; | |
3000 | + break; | |
3001 | + case CCS_OBJ_IS_FIFO: | |
3002 | + value = S_IFIFO; | |
3003 | + break; | |
3004 | + case CCS_EXEC_ARGC: | |
3005 | + if (!bprm) | |
3006 | + return false; | |
3007 | + value = bprm->argc; | |
3008 | + break; | |
3009 | + case CCS_EXEC_ENVC: | |
3010 | + if (!bprm) | |
3011 | + return false; | |
3012 | + value = bprm->envc; | |
3013 | + break; | |
3014 | + case CCS_TASK_TYPE: | |
3015 | + value = ((u8) ccs_current_flags()) & | |
3016 | + CCS_TASK_IS_EXECUTE_HANDLER; | |
3017 | + break; | |
3018 | + case CCS_TASK_EXECUTE_HANDLER: | |
3019 | + value = CCS_TASK_IS_EXECUTE_HANDLER; | |
3020 | + break; | |
3021 | + case CCS_ARGV_ENTRY: | |
3022 | + case CCS_IMM_NUMBER_ENTRY1: | |
3023 | + value = (*condp)->value; | |
3024 | + (*condp)++; | |
3025 | + break; | |
3026 | + case CCS_COND_NARG0: | |
3027 | + value = param->i[0]; | |
3028 | + break; | |
3029 | + case CCS_COND_NARG1: | |
3030 | + value = param->i[1]; | |
3031 | + break; | |
3032 | + case CCS_COND_NARG2: | |
3033 | + value = param->i[2]; | |
3034 | + break; | |
3035 | + case CCS_HANDLER_PATH: | |
3036 | + case CCS_TRANSIT_DOMAIN: | |
3037 | + case CCS_COND_IPARG: | |
3038 | + /* Values are loaded by caller. Just return a dummy. */ | |
3039 | + arg->type = CCS_ARG_TYPE_NONE; | |
3040 | + value = 0; | |
3041 | + break; | |
3042 | + default: | |
3043 | + goto not_single_value; | |
3044 | + } | |
3045 | + arg->value[0] = value; | |
3046 | + arg->value[1] = value; | |
3047 | + return true; | |
3048 | +not_single_value: | |
3049 | + if (cmd == CCS_IMM_NUMBER_ENTRY2) { | |
3050 | + arg->value[0] = (*condp)->value; | |
3051 | + (*condp)++; | |
3052 | + arg->value[1] = (*condp)->value; | |
3053 | + (*condp)++; | |
3054 | + return true; | |
3055 | + } | |
3056 | + switch (cmd) { | |
3057 | + case CCS_COND_SARG0: | |
3058 | + if (!r->param.s[0]) | |
3059 | + ccs_populate_patharg(r, true); | |
3060 | + arg->name = r->param.s[0]; | |
3061 | + break; | |
3062 | + case CCS_COND_SARG1: | |
3063 | + if (!r->param.s[1]) | |
3064 | + ccs_populate_patharg(r, false); | |
3065 | + arg->name = r->param.s[1]; | |
3066 | + break; | |
3067 | + case CCS_COND_SARG2: | |
3068 | + arg->name = r->param.s[2]; | |
3069 | + break; | |
3070 | + case CCS_COND_SARG3: | |
3071 | + arg->name = r->param.s[3]; | |
3072 | + break; | |
3073 | + case CCS_ENVP_ENTRY: | |
3074 | + case CCS_IMM_NAME_ENTRY: | |
3075 | + arg->name = (*condp)->path; | |
3076 | + (*condp)++; | |
3077 | + break; | |
3078 | + case CCS_SELF_EXE: | |
3079 | + if (!r->exename.name) { | |
3080 | + ccs_get_exename(&r->exename); | |
3081 | + /* Set OOM flag if failed. */ | |
3082 | + if (!r->exename.name) | |
3083 | + r->failed_by_oom = true; | |
3084 | + } | |
3085 | + arg->name = &r->exename; | |
3086 | + break; | |
3087 | + case CCS_COND_DOMAIN: | |
3088 | + arg->name = r->param.s[0]; | |
3089 | + break; | |
3090 | + case CCS_SELF_DOMAIN: | |
3091 | + arg->name = ccs_current_domain()->domainname; | |
3092 | + break; | |
3093 | + default: | |
3094 | + goto not_single_name; | |
3095 | + } | |
3096 | + if (!arg->name) | |
3097 | + return false; | |
3098 | + arg->type = CCS_ARG_TYPE_NAME; | |
3099 | + return true; | |
3100 | +not_single_name: | |
3101 | + if (cmd == CCS_IMM_GROUP) { | |
3102 | + arg->type = CCS_ARG_TYPE_GROUP; | |
3103 | + arg->group = (*condp)->group; | |
3104 | + (*condp)++; | |
3105 | + return true; | |
3106 | + } | |
3107 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
3108 | + if (cmd == CCS_IMM_IPV4ADDR_ENTRY1) { | |
3109 | + arg->type = CCS_ARG_TYPE_IPV4ADDR; | |
3110 | + memmove(&arg->ip[0], &(*condp)->ip, 4); | |
3111 | + memmove(&arg->ip[1], &(*condp)->ip, 4); | |
3112 | + (*condp)++; | |
3113 | + return true; | |
3114 | + } | |
3115 | + if (cmd == CCS_IMM_IPV4ADDR_ENTRY2) { | |
3116 | + arg->type = CCS_ARG_TYPE_IPV4ADDR; | |
3117 | + memmove(&arg->ip[0], &(*condp)->ip, 4); | |
3118 | + (*condp)++; | |
3119 | + memmove(&arg->ip[1], &(*condp)->ip, 4); | |
3120 | + (*condp)++; | |
3121 | + return true; | |
3122 | + } | |
3123 | + if (cmd == CCS_IMM_IPV6ADDR_ENTRY1) { | |
3124 | + arg->type = CCS_ARG_TYPE_IPV6ADDR; | |
3125 | + memmove(&arg->ip[0], &(*condp)->ip, 16); | |
3126 | + memmove(&arg->ip[1], &(*condp)->ip, 16); | |
3127 | + *condp = (void *) | |
3128 | + (((u8 *) *condp) + sizeof(struct in6_addr)); | |
3129 | + return true; | |
3130 | + } | |
3131 | + if (cmd == CCS_IMM_IPV6ADDR_ENTRY2) { | |
3132 | + arg->type = CCS_ARG_TYPE_IPV6ADDR; | |
3133 | + memmove(&arg->ip[0], &(*condp)->ip, 16); | |
3134 | + *condp = (void *) | |
3135 | + (((u8 *) *condp) + sizeof(struct in6_addr)); | |
3136 | + memmove(&arg->ip[1], &(*condp)->ip, 16); | |
3137 | + *condp = (void *) | |
3138 | + (((u8 *) *condp) + sizeof(struct in6_addr)); | |
3139 | + return true; | |
3140 | + } | |
3141 | +#endif | |
3142 | + switch (cmd) { | |
3143 | + case CCS_MODE_SETUID: | |
3144 | + value = S_ISUID; | |
3145 | + break; | |
3146 | + case CCS_MODE_SETGID: | |
3147 | + value = S_ISGID; | |
3148 | + break; | |
3149 | + case CCS_MODE_STICKY: | |
3150 | + value = S_ISVTX; | |
3151 | + break; | |
3152 | + case CCS_MODE_OWNER_READ: | |
3153 | + value = S_IRUSR; | |
3154 | + break; | |
3155 | + case CCS_MODE_OWNER_WRITE: | |
3156 | + value = S_IWUSR; | |
3157 | + break; | |
3158 | + case CCS_MODE_OWNER_EXECUTE: | |
3159 | + value = S_IXUSR; | |
3160 | + break; | |
3161 | + case CCS_MODE_GROUP_READ: | |
3162 | + value = S_IRGRP; | |
3163 | + break; | |
3164 | + case CCS_MODE_GROUP_WRITE: | |
3165 | + value = S_IWGRP; | |
3166 | + break; | |
3167 | + case CCS_MODE_GROUP_EXECUTE: | |
3168 | + value = S_IXGRP; | |
3169 | + break; | |
3170 | + case CCS_MODE_OTHERS_READ: | |
3171 | + value = S_IROTH; | |
3172 | + break; | |
3173 | + case CCS_MODE_OTHERS_WRITE: | |
3174 | + value = S_IWOTH; | |
3175 | + break; | |
3176 | + case CCS_MODE_OTHERS_EXECUTE: | |
3177 | + value = S_IXOTH; | |
3178 | + break; | |
3179 | + default: | |
3180 | + goto not_bitop; | |
3181 | + } | |
3182 | + arg->type = CCS_ARG_TYPE_BITOP; | |
3183 | + arg->value[0] = value; | |
3184 | + return true; | |
3185 | +not_bitop: | |
3186 | + arg->type = CCS_ARG_TYPE_NUMBER; | |
3187 | + if (!r->obj.path[0].dentry && !r->obj.path[1].dentry) | |
3188 | + return false; | |
3189 | + ccs_get_attributes(r); | |
3190 | + value = (cmd - CCS_PATH_ATTRIBUTE_START) >> 4; | |
3191 | + if (value > 3) | |
3192 | + return false; | |
3193 | + stat = &r->obj.stat[value]; | |
3194 | + if (!stat) | |
3195 | + return false; | |
3196 | + switch ((cmd - CCS_PATH_ATTRIBUTE_START) & 0xF) { | |
3197 | + case CCS_PATH_ATTRIBUTE_UID: | |
3198 | + value = stat->uid; | |
3199 | + break; | |
3200 | + case CCS_PATH_ATTRIBUTE_GID: | |
3201 | + value = stat->gid; | |
3202 | + break; | |
3203 | + case CCS_PATH_ATTRIBUTE_INO: | |
3204 | + value = stat->ino; | |
3205 | + break; | |
3206 | + case CCS_PATH_ATTRIBUTE_MAJOR: | |
3207 | + value = MAJOR(stat->dev); | |
3208 | + break; | |
3209 | + case CCS_PATH_ATTRIBUTE_MINOR: | |
3210 | + value = MINOR(stat->dev); | |
3211 | + break; | |
3212 | + case CCS_PATH_ATTRIBUTE_TYPE: | |
3213 | + value = stat->mode & S_IFMT; | |
3214 | + break; | |
3215 | + case CCS_PATH_ATTRIBUTE_DEV_MAJOR: | |
3216 | + value = MAJOR(stat->rdev); | |
3217 | + break; | |
3218 | + case CCS_PATH_ATTRIBUTE_DEV_MINOR: | |
3219 | + value = MINOR(stat->rdev); | |
3220 | + break; | |
3221 | + case CCS_PATH_ATTRIBUTE_PERM: | |
3222 | + value = stat->mode & S_IALLUGO; | |
3223 | + break; | |
3224 | + case CCS_PATH_ATTRIBUTE_FSMAGIC: | |
3225 | + value = stat->fsmagic; | |
3226 | + break; | |
3227 | + default: | |
3228 | + return false; | |
3229 | + } | |
3230 | + arg->value[0] = value; | |
3231 | + arg->value[1] = value; | |
3232 | + return true; | |
3233 | +} | |
3234 | + | |
3235 | +/** | |
3236 | + * ccs_condition - Check condition part. | |
3237 | + * | |
3238 | + * @r: Pointer to "struct ccs_request_info". | |
3239 | + * @cond: Pointer to "struct ccs_condition". Maybe NULL. | |
3240 | + * | |
3241 | + * Returns true on success, false otherwise. | |
3242 | + * | |
3243 | + * Caller holds ccs_read_lock(). | |
3244 | + */ | |
3245 | +static bool ccs_condition(struct ccs_request_info *r, | |
3246 | + const struct ccs_condition *cond) | |
3247 | +{ | |
3248 | + const union ccs_condition_element *condp; | |
3249 | + if (!cond) | |
3250 | + return true; | |
3251 | + condp = (typeof(condp)) (cond + 1); | |
3252 | + while ((void *) condp < (void *) ((u8 *) cond) + cond->size) { | |
3253 | + struct ccs_cond_arg left; | |
3254 | + struct ccs_cond_arg right; | |
3255 | + const enum ccs_conditions_index left_op = condp->left; | |
3256 | + const enum ccs_conditions_index right_op = condp->right; | |
3257 | + const bool match = !condp->is_not; | |
3258 | + condp++; | |
3259 | + if (!ccs_cond2arg(&left, left_op, &condp, r) || | |
3260 | + !ccs_cond2arg(&right, right_op, &condp, r)) { | |
3261 | + /* | |
3262 | + * Something wrong (e.g. out of memory or invalid | |
3263 | + * argument) occured. We can't check permission. | |
3264 | + */ | |
3265 | + return false; | |
3266 | + } | |
3267 | + if (left.type == CCS_ARG_TYPE_NUMBER) { | |
3268 | + if (left_op == CCS_ARGV_ENTRY) { | |
3269 | + if (!r->bprm) | |
3270 | + return false; | |
3271 | + else if (right.type == CCS_ARG_TYPE_NAME) | |
3272 | + right.group = NULL; | |
3273 | + else if (right.type == CCS_ARG_TYPE_GROUP) | |
3274 | + right.name = NULL; | |
3275 | + else | |
3276 | + return false; | |
3277 | + if (ccs_check_argv(r, left.value[0], | |
3278 | + right.group, right.name, | |
3279 | + match)) | |
3280 | + continue; | |
3281 | + return false; | |
3282 | + } | |
3283 | + if (right.type == CCS_ARG_TYPE_NUMBER) { | |
3284 | + if ((left.value[0] <= right.value[1] && | |
3285 | + left.value[1] >= right.value[0]) == match) | |
3286 | + continue; | |
3287 | + return false; | |
3288 | + } | |
3289 | + if (right.type == CCS_ARG_TYPE_GROUP) { | |
3290 | + if (ccs_number_matches_group | |
3291 | + (left.value[0], left.value[1], right.group) | |
3292 | + == match) | |
3293 | + continue; | |
3294 | + return false; | |
3295 | + } | |
3296 | + if (right.type == CCS_ARG_TYPE_BITOP) { | |
3297 | + if (!(left.value[0] & right.value[0]) == | |
3298 | + !match) | |
3299 | + continue; | |
3300 | + return false; | |
3301 | + } | |
3302 | + return false; | |
3303 | + } | |
3304 | + if (left.type == CCS_ARG_TYPE_NAME) { | |
3305 | + if (right.type == CCS_ARG_TYPE_NAME) | |
3306 | + right.group = NULL; | |
3307 | + else if (right.type == CCS_ARG_TYPE_GROUP) | |
3308 | + right.name = NULL; | |
3309 | + else | |
3310 | + return false; | |
3311 | + if (left_op == CCS_ENVP_ENTRY) { | |
3312 | + if (r->bprm && ccs_check_envp | |
3313 | + (r, left.name, right.group, right.name, | |
3314 | + match)) | |
3315 | + continue; | |
3316 | + } else if (ccs_path_matches_group_or_pattern | |
3317 | + (left.name, right.group, right.name, match)) | |
3318 | + continue; | |
3319 | + return false; | |
3320 | + } | |
3321 | + if (left.type != CCS_ARG_TYPE_NONE) | |
3322 | + return false; | |
3323 | + /* Check IPv4 or IPv6 address expressions. */ | |
3324 | + if (left_op == CCS_COND_IPARG) { | |
3325 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
3326 | + if (right.type == CCS_ARG_TYPE_GROUP) { | |
3327 | + if (ccs_ip_matches_group | |
3328 | + (r->param.is_ipv6, r->param.ip, | |
3329 | + right.group) == match) | |
3330 | + continue; | |
3331 | + } else if (right.type == CCS_ARG_TYPE_IPV6ADDR) { | |
3332 | + if (r->param.is_ipv6 && | |
3333 | + (memcmp(r->param.ip, &right.ip[0], | |
3334 | + 16) >= 0 && | |
3335 | + memcmp(r->param.ip, &right.ip[1], | |
3336 | + 16) <= 0) == match) | |
3337 | + continue; | |
3338 | + } else if (right.type == CCS_ARG_TYPE_IPV4ADDR) { | |
3339 | + if (!r->param.is_ipv6 && | |
3340 | + (memcmp(r->param.ip, &right.ip[0], | |
3341 | + 4) >= 0 && | |
3342 | + memcmp(r->param.ip, &right.ip[1], | |
3343 | + 4) <= 0) == match) | |
3344 | + continue; | |
3345 | + } | |
3346 | +#endif | |
3347 | + return false; | |
3348 | + } | |
3349 | + if (left_op == CCS_HANDLER_PATH) { | |
3350 | + r->handler_path_candidate = right.name; | |
3351 | + continue; | |
3352 | + } | |
3353 | + if (left_op == CCS_TRANSIT_DOMAIN) { | |
3354 | + r->transition_candidate = right.name; | |
3355 | + continue; | |
3356 | + } | |
3357 | + return false; | |
3358 | + } | |
3359 | + return true; | |
3360 | +} | |
3361 | + | |
3362 | +/** | |
3363 | + * ccs_check_auto_domain_transition - Check "auto_domain_transition" entry. | |
3364 | + * | |
3365 | + * Returns nothing. | |
3366 | + * | |
3367 | + * If "auto_domain_transition" keyword was specified and transition to that | |
3368 | + * domain failed, the current thread will be killed by SIGKILL. | |
3369 | + */ | |
3370 | +static void ccs_check_auto_domain_transition(void) | |
3371 | +{ | |
3372 | +#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
3373 | + struct ccs_request_info r = { }; | |
3374 | + const int idx = ccs_read_lock(); | |
3375 | + r.type = CCS_MAC_AUTO_DOMAIN_TRANSITION; | |
3376 | + ccs_check_acl(&r, true); | |
3377 | + if (r.result != CCS_MATCHING_ALLOWED || | |
3378 | + ccs_transit_domain(r.transition->name)) | |
3379 | + goto done; | |
3380 | + printk(KERN_WARNING "ERROR: Unable to transit to '%s' domain.\n", | |
3381 | + r.transition->name); | |
3382 | + force_sig(SIGKILL, current); | |
3383 | +done: | |
3384 | + ccs_read_unlock(idx); | |
3385 | +#endif | |
3386 | +} | |
3387 | + | |
3388 | +/** | |
3389 | + * ccs_byte_range - Check whether the string is a \ooo style octal value. | |
3390 | + * | |
3391 | + * @str: Pointer to the string. | |
3392 | + * | |
3393 | + * Returns true if @str is a \ooo style octal value, false otherwise. | |
3394 | + */ | |
3395 | +static bool ccs_byte_range(const char *str) | |
3396 | +{ | |
3397 | + return *str >= '0' && *str++ <= '3' && | |
3398 | + *str >= '0' && *str++ <= '7' && | |
3399 | + *str >= '0' && *str <= '7'; | |
3400 | +} | |
3401 | + | |
3402 | +/** | |
3403 | + * ccs_decimal - Check whether the character is a decimal character. | |
3404 | + * | |
3405 | + * @c: The character to check. | |
3406 | + * | |
3407 | + * Returns true if @c is a decimal character, false otherwise. | |
3408 | + */ | |
3409 | +static bool ccs_decimal(const char c) | |
3410 | +{ | |
3411 | + return c >= '0' && c <= '9'; | |
3412 | +} | |
3413 | + | |
3414 | +/** | |
3415 | + * ccs_hexadecimal - Check whether the character is a hexadecimal character. | |
3416 | + * | |
3417 | + * @c: The character to check. | |
3418 | + * | |
3419 | + * Returns true if @c is a hexadecimal character, false otherwise. | |
3420 | + */ | |
3421 | +static bool ccs_hexadecimal(const char c) | |
3422 | +{ | |
3423 | + return (c >= '0' && c <= '9') || | |
3424 | + (c >= 'A' && c <= 'F') || | |
3425 | + (c >= 'a' && c <= 'f'); | |
3426 | +} | |
3427 | + | |
3428 | +/** | |
3429 | + * ccs_alphabet_char - Check whether the character is an alphabet. | |
3430 | + * | |
3431 | + * @c: The character to check. | |
3432 | + * | |
3433 | + * Returns true if @c is an alphabet character, false otherwise. | |
3434 | + */ | |
3435 | +static bool ccs_alphabet_char(const char c) | |
3436 | +{ | |
3437 | + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); | |
3438 | +} | |
3439 | + | |
3440 | +/** | |
3441 | + * ccs_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern. | |
3442 | + * | |
3443 | + * @filename: The start of string to check. | |
3444 | + * @filename_end: The end of string to check. | |
3445 | + * @pattern: The start of pattern to compare. | |
3446 | + * @pattern_end: The end of pattern to compare. | |
3447 | + * | |
3448 | + * Returns true if @filename matches @pattern, false otherwise. | |
3449 | + */ | |
3450 | +static bool ccs_file_matches_pattern2(const char *filename, | |
3451 | + const char *filename_end, | |
3452 | + const char *pattern, | |
3453 | + const char *pattern_end) | |
3454 | +{ | |
3455 | + while (filename < filename_end && pattern < pattern_end) { | |
3456 | + char c; | |
3457 | + if (*pattern != '\\') { | |
3458 | + if (*filename++ != *pattern++) | |
3459 | + return false; | |
3460 | + continue; | |
3461 | + } | |
3462 | + c = *filename; | |
3463 | + pattern++; | |
3464 | + switch (*pattern) { | |
3465 | + int i; | |
3466 | + int j; | |
3467 | + case '?': | |
3468 | + if (c == '/') { | |
3469 | + return false; | |
3470 | + } else if (c == '\\') { | |
3471 | + if (ccs_byte_range(filename + 1)) | |
3472 | + filename += 3; | |
3473 | + else | |
3474 | + return false; | |
3475 | + } | |
3476 | + break; | |
3477 | + case '+': | |
3478 | + if (!ccs_decimal(c)) | |
3479 | + return false; | |
3480 | + break; | |
3481 | + case 'x': | |
3482 | + if (!ccs_hexadecimal(c)) | |
3483 | + return false; | |
3484 | + break; | |
3485 | + case 'a': | |
3486 | + if (!ccs_alphabet_char(c)) | |
3487 | + return false; | |
3488 | + break; | |
3489 | + case '0': | |
3490 | + case '1': | |
3491 | + case '2': | |
3492 | + case '3': | |
3493 | + if (c == '\\' && ccs_byte_range(filename + 1) | |
3494 | + && !strncmp(filename + 1, pattern, 3)) { | |
3495 | + filename += 3; | |
3496 | + pattern += 2; | |
3497 | + break; | |
3498 | + } | |
3499 | + return false; /* Not matched. */ | |
3500 | + case '*': | |
3501 | + case '@': | |
3502 | + for (i = 0; i <= filename_end - filename; i++) { | |
3503 | + if (ccs_file_matches_pattern2(filename + i, | |
3504 | + filename_end, | |
3505 | + pattern + 1, | |
3506 | + pattern_end)) | |
3507 | + return true; | |
3508 | + c = filename[i]; | |
3509 | + if (c == '.' && *pattern == '@') | |
3510 | + break; | |
3511 | + if (c != '\\') | |
3512 | + continue; | |
3513 | + if (ccs_byte_range(filename + i + 1)) | |
3514 | + i += 3; | |
3515 | + else | |
3516 | + break; /* Bad pattern. */ | |
3517 | + } | |
3518 | + return false; /* Not matched. */ | |
3519 | + default: | |
3520 | + j = 0; | |
3521 | + c = *pattern; | |
3522 | + if (c == '$') { | |
3523 | + while (ccs_decimal(filename[j])) | |
3524 | + j++; | |
3525 | + } else if (c == 'X') { | |
3526 | + while (ccs_hexadecimal(filename[j])) | |
3527 | + j++; | |
3528 | + } else if (c == 'A') { | |
3529 | + while (ccs_alphabet_char(filename[j])) | |
3530 | + j++; | |
3531 | + } | |
3532 | + for (i = 1; i <= j; i++) { | |
3533 | + if (ccs_file_matches_pattern2(filename + i, | |
3534 | + filename_end, | |
3535 | + pattern + 1, | |
3536 | + pattern_end)) | |
3537 | + return true; | |
3538 | + } | |
3539 | + return false; /* Not matched or bad pattern. */ | |
3540 | + } | |
3541 | + filename++; | |
3542 | + pattern++; | |
3543 | + } | |
3544 | + /* Ignore trailing "\*" and "\@" in @pattern. */ | |
3545 | + while (*pattern == '\\' && | |
3546 | + (*(pattern + 1) == '*' || *(pattern + 1) == '@')) | |
3547 | + pattern += 2; | |
3548 | + return filename == filename_end && pattern == pattern_end; | |
3549 | +} | |
3550 | + | |
3551 | +/** | |
3552 | + * ccs_file_matches_pattern - Pattern matching without '/' character. | |
3553 | + * | |
3554 | + * @filename: The start of string to check. | |
3555 | + * @filename_end: The end of string to check. | |
3556 | + * @pattern: The start of pattern to compare. | |
3557 | + * @pattern_end: The end of pattern to compare. | |
3558 | + * | |
3559 | + * Returns true if @filename matches @pattern, false otherwise. | |
3560 | + */ | |
3561 | +static bool ccs_file_matches_pattern(const char *filename, | |
3562 | + const char *filename_end, | |
3563 | + const char *pattern, | |
3564 | + const char *pattern_end) | |
3565 | +{ | |
3566 | + const char *pattern_start = pattern; | |
3567 | + bool first = true; | |
3568 | + bool result; | |
3569 | + while (pattern < pattern_end - 1) { | |
3570 | + /* Split at "\-" pattern. */ | |
3571 | + if (*pattern++ != '\\' || *pattern++ != '-') | |
3572 | + continue; | |
3573 | + result = ccs_file_matches_pattern2(filename, filename_end, | |
3574 | + pattern_start, pattern - 2); | |
3575 | + if (first) | |
3576 | + result = !result; | |
3577 | + if (result) | |
3578 | + return false; | |
3579 | + first = false; | |
3580 | + pattern_start = pattern; | |
3581 | + } | |
3582 | + result = ccs_file_matches_pattern2(filename, filename_end, | |
3583 | + pattern_start, pattern_end); | |
3584 | + return first ? result : !result; | |
3585 | +} | |
3586 | + | |
3587 | +/** | |
3588 | + * ccs_path_matches_pattern2 - Do pathname pattern matching. | |
3589 | + * | |
3590 | + * @f: The start of string to check. | |
3591 | + * @p: The start of pattern to compare. | |
3592 | + * | |
3593 | + * Returns true if @f matches @p, false otherwise. | |
3594 | + */ | |
3595 | +static bool ccs_path_matches_pattern2(const char *f, const char *p) | |
3596 | +{ | |
3597 | + const char *f_delimiter; | |
3598 | + const char *p_delimiter; | |
3599 | + char recursive_end; | |
3600 | + while (*f && *p) { | |
3601 | + f_delimiter = strchr(f, '/'); | |
3602 | + if (!f_delimiter) | |
3603 | + f_delimiter = f + strlen(f); | |
3604 | + p_delimiter = strchr(p, '/'); | |
3605 | + if (!p_delimiter) | |
3606 | + p_delimiter = p + strlen(p); | |
3607 | + if (*p == '\\') { | |
3608 | + if (*(p + 1) == '{') { | |
3609 | + recursive_end = '}'; | |
3610 | + goto recursive; | |
3611 | + } | |
3612 | + if (*(p + 1) == '(') { | |
3613 | + recursive_end = ')'; | |
3614 | + goto recursive; | |
3615 | + } | |
3616 | + } | |
3617 | + if (!ccs_file_matches_pattern(f, f_delimiter, p, p_delimiter)) | |
3618 | + return false; | |
3619 | + f = f_delimiter; | |
3620 | + if (*f) | |
3621 | + f++; | |
3622 | + p = p_delimiter; | |
3623 | + if (*p) | |
3624 | + p++; | |
3625 | + } | |
3626 | + /* Ignore trailing "\*" and "\@" in @pattern. */ | |
3627 | + while (*p == '\\' && | |
3628 | + (*(p + 1) == '*' || *(p + 1) == '@')) | |
3629 | + p += 2; | |
3630 | + return !*f && !*p; | |
3631 | +recursive: | |
3632 | + /* | |
3633 | + * The "\{" or "\(" pattern is permitted only after '/' character. | |
3634 | + * This guarantees that below "*(p - 1)" is safe. | |
3635 | + * Also, the "\}" or "\)" pattern is permitted only before '/' | |
3636 | + * character so that "\{" + "\}" or "\(" + "\)" pair will not break | |
3637 | + * the "\-" operator. | |
3638 | + */ | |
3639 | + if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || | |
3640 | + *(p_delimiter - 1) != recursive_end || *(p_delimiter - 2) != '\\') | |
3641 | + return false; /* Bad pattern. */ | |
3642 | + if (recursive_end == ')') { | |
3643 | + /* Check zero repetition. */ | |
3644 | + if (ccs_path_matches_pattern2(f, p_delimiter + 1)) | |
3645 | + return true; | |
3646 | + /* Fall back to one or more repetition. */ | |
3647 | + } | |
3648 | + do { | |
3649 | + /* Compare current component with pattern. */ | |
3650 | + if (!ccs_file_matches_pattern(f, f_delimiter, p + 2, | |
3651 | + p_delimiter - 2)) | |
3652 | + break; | |
3653 | + /* Proceed to next component. */ | |
3654 | + f = f_delimiter; | |
3655 | + if (!*f) | |
3656 | + break; | |
3657 | + f++; | |
3658 | + /* Continue comparison. */ | |
3659 | + if (ccs_path_matches_pattern2(f, p_delimiter + 1)) | |
3660 | + return true; | |
3661 | + f_delimiter = strchr(f, '/'); | |
3662 | + } while (f_delimiter); | |
3663 | + return false; /* Not matched. */ | |
3664 | +} | |
3665 | + | |
3666 | +/** | |
3667 | + * ccs_path_matches_pattern - Check whether the given filename matches the given pattern. | |
3668 | + * | |
3669 | + * @filename: The filename to check. | |
3670 | + * @pattern: The pattern to compare. | |
3671 | + * | |
3672 | + * Returns true if matches, false otherwise. | |
3673 | + * | |
3674 | + * The following patterns are available. | |
3675 | + * \ooo Octal representation of a byte. | |
3676 | + * \* Zero or more repetitions of characters other than '/'. | |
3677 | + * \@ Zero or more repetitions of characters other than '/' or '.'. | |
3678 | + * \? 1 byte character other than '/'. | |
3679 | + * \$ One or more repetitions of decimal digits. | |
3680 | + * \+ 1 decimal digit. | |
3681 | + * \X One or more repetitions of hexadecimal digits. | |
3682 | + * \x 1 hexadecimal digit. | |
3683 | + * \A One or more repetitions of alphabet characters. | |
3684 | + * \a 1 alphabet character. | |
3685 | + * | |
3686 | + * \- Subtraction operator. | |
3687 | + * | |
3688 | + * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ | |
3689 | + * /dir/dir/dir/ ). | |
3690 | + * | |
3691 | + * /\(dir\)/ '/' + 'Zero or more repetitions of dir/' (e.g. / /dir/ | |
3692 | + * /dir/dir/ ). | |
3693 | + */ | |
3694 | +static bool ccs_path_matches_pattern(const struct ccs_path_info *filename, | |
3695 | + const struct ccs_path_info *pattern) | |
3696 | +{ | |
3697 | + const char *f = filename->name; | |
3698 | + const char *p = pattern->name; | |
3699 | + const int len = pattern->const_len; | |
3700 | + /* If @pattern doesn't contain pattern, I can use strcmp(). */ | |
3701 | + if (len == pattern->total_len) | |
3702 | + return !ccs_pathcmp(filename, pattern); | |
3703 | + /* Compare the initial length without patterns. */ | |
3704 | + if (strncmp(f, p, len)) | |
3705 | + return false; | |
3706 | + f += len; | |
3707 | + p += len; | |
3708 | + /* Compare the last component first. */ | |
3709 | + { | |
3710 | + const char *f2 = strrchr(f, '/'); | |
3711 | + const char *p2 = strrchr(p, '/'); | |
3712 | + if (!f2++) | |
3713 | + f2 = f; | |
3714 | + if (!p2++) | |
3715 | + p2 = p; | |
3716 | + if (!ccs_file_matches_pattern(f2, filename->name | |
3717 | + + filename->total_len, | |
3718 | + p2, pattern->name | |
3719 | + + pattern->total_len)) | |
3720 | + return false; | |
3721 | + } | |
3722 | + return ccs_path_matches_pattern2(f, p); | |
3723 | +} | |
3724 | + | |
3725 | +/** | |
3726 | + * ccs_clear_request_info - Release memory allocated during permission check. | |
3727 | + * | |
3728 | + * @r: Pointer to "struct ccs_request_info". | |
3729 | + * | |
3730 | + * Returns nothing. | |
3731 | + */ | |
3732 | +static void ccs_clear_request_info(struct ccs_request_info *r) | |
3733 | +{ | |
3734 | + u8 i; | |
3735 | + /* | |
3736 | + * r->obj.pathname[0] (which is referenced by r->obj.s[0]) and | |
3737 | + * r->obj.pathname[1] (which is referenced by r->obj.s[1]) may contain | |
3738 | + * pathnames allocated using ccs_populate_patharg() or ccs_mount_acl(). | |
3739 | + * Their callers do not allocate memory until pathnames becomes needed | |
3740 | + * for checking condition or auditing requests. | |
3741 | + * | |
3742 | + * r->obj.s[2] and r->obj.s[3] are used by | |
3743 | + * ccs_mount_acl()/ccs_env_perm() and are allocated/released by their | |
3744 | + * callers. | |
3745 | + */ | |
3746 | + for (i = 0; i < 2; i++) { | |
3747 | + kfree(r->obj.pathname[i].name); | |
3748 | + r->obj.pathname[i].name = NULL; | |
3749 | + } | |
3750 | + kfree(r->exename.name); | |
3751 | + r->exename.name = NULL; | |
3752 | +} |
@@ -0,0 +1,581 @@ | ||
1 | +/* | |
2 | + * security/caitsith/gc.c | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#include "internal.h" | |
10 | + | |
11 | +/***** SECTION1: Constants definition *****/ | |
12 | + | |
13 | +/* For compatibility with older kernels. */ | |
14 | +#ifndef for_each_process | |
15 | +#define for_each_process for_each_task | |
16 | +#endif | |
17 | + | |
18 | +/* The list for "struct ccs_io_buffer". */ | |
19 | +static LIST_HEAD(ccs_io_buffer_list); | |
20 | +/* Lock for protecting ccs_io_buffer_list. */ | |
21 | +static DEFINE_SPINLOCK(ccs_io_buffer_list_lock); | |
22 | + | |
23 | +/***** SECTION2: Structure definition *****/ | |
24 | + | |
25 | +/***** SECTION3: Prototype definition section *****/ | |
26 | + | |
27 | +static bool ccs_domain_used_by_task(struct ccs_domain_info *domain); | |
28 | +static bool ccs_name_used_by_io_buffer(const char *string, const size_t size); | |
29 | +static bool ccs_struct_used_by_io_buffer(const struct list_head *element); | |
30 | +static int ccs_gc_thread(void *unused); | |
31 | +static void ccs_collect_acl(struct list_head *list); | |
32 | +static void ccs_collect_entry(void); | |
33 | +static void ccs_collect_member(const enum ccs_policy_id id, | |
34 | + struct list_head *member_list); | |
35 | +static void ccs_memory_free(const void *ptr, const enum ccs_policy_id type); | |
36 | +static void ccs_try_to_gc(const enum ccs_policy_id type, | |
37 | + struct list_head *element); | |
38 | + | |
39 | +/***** SECTION4: Standalone functions section *****/ | |
40 | + | |
41 | +/***** SECTION5: Variables definition section *****/ | |
42 | + | |
43 | +/* | |
44 | + * Lock for syscall users. | |
45 | + * | |
46 | + * This lock is held for only protecting single SRCU section. | |
47 | + */ | |
48 | +struct srcu_struct ccs_ss; | |
49 | + | |
50 | +/***** SECTION6: Dependent functions section *****/ | |
51 | + | |
52 | +/** | |
53 | + * ccs_memory_free - Free memory for elements. | |
54 | + * | |
55 | + * @ptr: Pointer to allocated memory. | |
56 | + * @type: One of values in "enum ccs_policy_id". | |
57 | + * | |
58 | + * Returns nothing. | |
59 | + * | |
60 | + * Caller holds ccs_policy_lock mutex. | |
61 | + */ | |
62 | +static void ccs_memory_free(const void *ptr, const enum ccs_policy_id type) | |
63 | +{ | |
64 | + /* Size of an element. */ | |
65 | + static const u8 e[CCS_MAX_POLICY] = { | |
66 | + [CCS_ID_GROUP] = sizeof(struct ccs_group), | |
67 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
68 | + [CCS_ID_IP_GROUP] = sizeof(struct ccs_ip_group), | |
69 | +#endif | |
70 | + [CCS_ID_STRING_GROUP] = sizeof(struct ccs_string_group), | |
71 | + [CCS_ID_NUMBER_GROUP] = sizeof(struct ccs_number_group), | |
72 | + /* [CCS_ID_CONDITION] = "struct ccs_condition"->size, */ | |
73 | + /* [CCS_ID_NAME] = "struct ccs_name"->size, */ | |
74 | + /* [CCS_ID_ACL] = sizeof(struct ccs_acl_info), */ | |
75 | + [CCS_ID_DOMAIN] = sizeof(struct ccs_domain_info), | |
76 | + }; | |
77 | + size_t size; | |
78 | + if (type == CCS_ID_ACL) | |
79 | + size = sizeof(struct ccs_acl_info); | |
80 | + else if (type == CCS_ID_NAME) | |
81 | + size = container_of(ptr, typeof(struct ccs_name), | |
82 | + head.list)->size; | |
83 | + else if (type == CCS_ID_CONDITION) | |
84 | + size = container_of(ptr, typeof(struct ccs_condition), | |
85 | + head.list)->size; | |
86 | + else | |
87 | + size = e[type]; | |
88 | + ccs_memory_used[CCS_MEMORY_POLICY] -= ccs_round2(size); | |
89 | + kfree(ptr); | |
90 | +} | |
91 | + | |
92 | +/** | |
93 | + * ccs_struct_used_by_io_buffer - Check whether the list element is used by /proc/ccs/ users or not. | |
94 | + * | |
95 | + * @element: Pointer to "struct list_head". | |
96 | + * | |
97 | + * Returns true if @element is used by /proc/ccs/ users, false otherwise. | |
98 | + */ | |
99 | +static bool ccs_struct_used_by_io_buffer(const struct list_head *element) | |
100 | +{ | |
101 | + struct ccs_io_buffer *head; | |
102 | + bool in_use = false; | |
103 | + spin_lock(&ccs_io_buffer_list_lock); | |
104 | + list_for_each_entry(head, &ccs_io_buffer_list, list) { | |
105 | + head->users++; | |
106 | + spin_unlock(&ccs_io_buffer_list_lock); | |
107 | + mutex_lock(&head->io_sem); | |
108 | + if (head->r.acl == element || head->r.subacl == element || | |
109 | + head->r.group == element || &head->w.acl->list == element) | |
110 | + in_use = true; | |
111 | + mutex_unlock(&head->io_sem); | |
112 | + spin_lock(&ccs_io_buffer_list_lock); | |
113 | + head->users--; | |
114 | + if (in_use) | |
115 | + break; | |
116 | + } | |
117 | + spin_unlock(&ccs_io_buffer_list_lock); | |
118 | + return in_use; | |
119 | +} | |
120 | + | |
121 | +/** | |
122 | + * ccs_name_used_by_io_buffer - Check whether the string is used by /proc/ccs/ users or not. | |
123 | + * | |
124 | + * @string: String to check. | |
125 | + * @size: Memory allocated for @string . | |
126 | + * | |
127 | + * Returns true if @string is used by /proc/ccs/ users, false otherwise. | |
128 | + */ | |
129 | +static bool ccs_name_used_by_io_buffer(const char *string, const size_t size) | |
130 | +{ | |
131 | + struct ccs_io_buffer *head; | |
132 | + bool in_use = false; | |
133 | + spin_lock(&ccs_io_buffer_list_lock); | |
134 | + list_for_each_entry(head, &ccs_io_buffer_list, list) { | |
135 | + int i; | |
136 | + head->users++; | |
137 | + spin_unlock(&ccs_io_buffer_list_lock); | |
138 | + mutex_lock(&head->io_sem); | |
139 | + for (i = 0; i < CCS_MAX_IO_READ_QUEUE; i++) { | |
140 | + const char *w = head->r.w[i]; | |
141 | + if (w < string || w > string + size) | |
142 | + continue; | |
143 | + in_use = true; | |
144 | + break; | |
145 | + } | |
146 | + mutex_unlock(&head->io_sem); | |
147 | + spin_lock(&ccs_io_buffer_list_lock); | |
148 | + head->users--; | |
149 | + if (in_use) | |
150 | + break; | |
151 | + } | |
152 | + spin_unlock(&ccs_io_buffer_list_lock); | |
153 | + return in_use; | |
154 | +} | |
155 | + | |
156 | +/** | |
157 | + * ccs_domain_used_by_task - Check whether the given pointer is referenced by a task. | |
158 | + * | |
159 | + * @domain: Pointer to "struct ccs_domain_info". | |
160 | + * | |
161 | + * Returns true if @domain is in use, false otherwise. | |
162 | + */ | |
163 | +static bool ccs_domain_used_by_task(struct ccs_domain_info *domain) | |
164 | +{ | |
165 | + bool in_use = false; | |
166 | + /* | |
167 | + * Don't delete this domain if somebody is doing execve(). | |
168 | + * | |
169 | + * Since ccs_finish_execve() first reverts ccs_domain_info and then | |
170 | + * updates ccs_flags, we need smp_rmb() to make sure that GC first | |
171 | + * checks ccs_flags and then checks ccs_domain_info. | |
172 | + */ | |
173 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | |
174 | + int idx; | |
175 | + rcu_read_lock(); | |
176 | + for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) { | |
177 | + struct ccs_security *ptr; | |
178 | + struct list_head *list = &ccs_task_security_list[idx]; | |
179 | + list_for_each_entry_rcu(ptr, list, list) { | |
180 | + if (!(ptr->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { | |
181 | + smp_rmb(); /* Avoid out of order execution. */ | |
182 | + if (ptr->ccs_domain_info != domain) | |
183 | + continue; | |
184 | + } | |
185 | + in_use = true; | |
186 | + goto out; | |
187 | + } | |
188 | + } | |
189 | +out: | |
190 | + rcu_read_unlock(); | |
191 | +#else | |
192 | + struct task_struct *g; | |
193 | + struct task_struct *t; | |
194 | + ccs_tasklist_lock(); | |
195 | + do_each_thread(g, t) { | |
196 | + if (!(t->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { | |
197 | + smp_rmb(); /* Avoid out of order execution. */ | |
198 | + if (t->ccs_domain_info != domain) | |
199 | + continue; | |
200 | + } | |
201 | + in_use = true; | |
202 | + goto out; | |
203 | + } while_each_thread(g, t); | |
204 | +out: | |
205 | + ccs_tasklist_unlock(); | |
206 | +#endif | |
207 | + return in_use; | |
208 | +} | |
209 | + | |
210 | +/** | |
211 | + * ccs_del_acl - Delete members in "struct ccs_acl_info". | |
212 | + * | |
213 | + * @element: Pointer to "struct list_head". | |
214 | + * | |
215 | + * Returns nothing. | |
216 | + */ | |
217 | +static inline void ccs_del_acl(struct list_head *element) | |
218 | +{ | |
219 | + struct ccs_acl_info *acl = container_of(element, typeof(*acl), list); | |
220 | + ccs_put_condition(acl->cond); | |
221 | +} | |
222 | + | |
223 | +/** | |
224 | + * ccs_del_domain - Delete members in "struct ccs_domain_info". | |
225 | + * | |
226 | + * @element: Pointer to "struct list_head". | |
227 | + * | |
228 | + * Returns nothing. | |
229 | + * | |
230 | + * Caller holds ccs_policy_lock mutex. | |
231 | + */ | |
232 | +static inline void ccs_del_domain(struct list_head *element) | |
233 | +{ | |
234 | + struct ccs_domain_info *domain = | |
235 | + container_of(element, typeof(*domain), list); | |
236 | + ccs_put_name(domain->domainname); | |
237 | +} | |
238 | + | |
239 | +/** | |
240 | + * ccs_del_string_group - Delete members in "struct ccs_string_group". | |
241 | + * | |
242 | + * @element: Pointer to "struct list_head". | |
243 | + * | |
244 | + * Returns nothing. | |
245 | + */ | |
246 | +static inline void ccs_del_string_group(struct list_head *element) | |
247 | +{ | |
248 | + struct ccs_string_group *member = | |
249 | + container_of(element, typeof(*member), head.list); | |
250 | + ccs_put_name(member->member_name); | |
251 | +} | |
252 | + | |
253 | +/** | |
254 | + * ccs_del_group - Delete "struct ccs_group". | |
255 | + * | |
256 | + * @element: Pointer to "struct list_head". | |
257 | + * | |
258 | + * Returns nothing. | |
259 | + */ | |
260 | +static inline void ccs_del_group(struct list_head *element) | |
261 | +{ | |
262 | + struct ccs_group *group = | |
263 | + container_of(element, typeof(*group), head.list); | |
264 | + ccs_put_name(group->group_name); | |
265 | +} | |
266 | + | |
267 | +/** | |
268 | + * ccs_del_condition - Delete members in "struct ccs_condition". | |
269 | + * | |
270 | + * @element: Pointer to "struct list_head". | |
271 | + * | |
272 | + * Returns nothing. | |
273 | + */ | |
274 | +void ccs_del_condition(struct list_head *element) | |
275 | +{ | |
276 | + struct ccs_condition *cond = container_of(element, typeof(*cond), | |
277 | + head.list); | |
278 | + const union ccs_condition_element *condp = (typeof(condp)) (cond + 1); | |
279 | + while ((void *) condp < (void *) ((u8 *) cond) + cond->size) { | |
280 | + const enum ccs_conditions_index left = condp->left; | |
281 | + const enum ccs_conditions_index right = condp->right; | |
282 | + condp++; | |
283 | + if (left == CCS_ARGV_ENTRY) | |
284 | + condp++; | |
285 | + else if (left == CCS_ENVP_ENTRY) { | |
286 | + ccs_put_name(condp->path); | |
287 | + condp++; | |
288 | + } | |
289 | + if (right == CCS_IMM_GROUP) { | |
290 | + ccs_put_group(condp->group); | |
291 | + condp++; | |
292 | + } else if (right == CCS_IMM_NAME_ENTRY) { | |
293 | + if (condp->path != &ccs_null_name) | |
294 | + ccs_put_name(condp->path); | |
295 | + condp++; | |
296 | + } else if (right == CCS_IMM_NUMBER_ENTRY1) | |
297 | + condp++; | |
298 | + else if (right == CCS_IMM_NUMBER_ENTRY2) | |
299 | + condp += 2; | |
300 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
301 | + else if (right == CCS_IMM_IPV6ADDR_ENTRY1) | |
302 | + condp = (void *) | |
303 | + (((u8 *) condp) + sizeof(struct in6_addr)); | |
304 | + else if (right == CCS_IMM_IPV6ADDR_ENTRY2) | |
305 | + condp = (void *) | |
306 | + (((u8 *) condp) + sizeof(struct in6_addr) * 2); | |
307 | +#endif | |
308 | + } | |
309 | +} | |
310 | + | |
311 | +/** | |
312 | + * ccs_try_to_gc - Try to kfree() an entry. | |
313 | + * | |
314 | + * @type: One of values in "enum ccs_policy_id". | |
315 | + * @element: Pointer to "struct list_head". | |
316 | + * | |
317 | + * Returns nothing. | |
318 | + * | |
319 | + * Caller holds ccs_policy_lock mutex. | |
320 | + */ | |
321 | +static void ccs_try_to_gc(const enum ccs_policy_id type, | |
322 | + struct list_head *element) | |
323 | +{ | |
324 | + /* | |
325 | + * __list_del_entry() guarantees that the list element became no longer | |
326 | + * reachable from the list which the element was originally on (e.g. | |
327 | + * ccs_domain_list). Also, synchronize_srcu() guarantees that the list | |
328 | + * element became no longer referenced by syscall users. | |
329 | + */ | |
330 | + __list_del_entry(element); | |
331 | + mutex_unlock(&ccs_policy_lock); | |
332 | + synchronize_srcu(&ccs_ss); | |
333 | + /* | |
334 | + * However, there are two users which may still be using the list | |
335 | + * element. We need to defer until both users forget this element. | |
336 | + * | |
337 | + * Don't kfree() until "struct ccs_io_buffer"->r.{group,acl,subacl} and | |
338 | + * "struct ccs_io_buffer"->w.acl forget this element. | |
339 | + */ | |
340 | + if (ccs_struct_used_by_io_buffer(element)) | |
341 | + goto reinject; | |
342 | + switch (type) { | |
343 | + case CCS_ID_GROUP: | |
344 | + ccs_del_group(element); | |
345 | + break; | |
346 | + case CCS_ID_STRING_GROUP: | |
347 | + ccs_del_string_group(element); | |
348 | + break; | |
349 | + case CCS_ID_CONDITION: | |
350 | + ccs_del_condition(element); | |
351 | + break; | |
352 | + case CCS_ID_NAME: | |
353 | + /* | |
354 | + * Don't kfree() until all "struct ccs_io_buffer"->r.w[] forget | |
355 | + * this element. | |
356 | + */ | |
357 | + if (ccs_name_used_by_io_buffer | |
358 | + (container_of(element, typeof(struct ccs_name), | |
359 | + head.list)->entry.name, | |
360 | + container_of(element, typeof(struct ccs_name), | |
361 | + head.list)->size)) | |
362 | + goto reinject; | |
363 | + break; | |
364 | + case CCS_ID_ACL: | |
365 | + ccs_del_acl(element); | |
366 | + break; | |
367 | + case CCS_ID_DOMAIN: | |
368 | + /* | |
369 | + * Don't kfree() until all "struct task_struct" forget this | |
370 | + * element. | |
371 | + */ | |
372 | + if (ccs_domain_used_by_task | |
373 | + (container_of(element, typeof(struct ccs_domain_info), | |
374 | + list))) | |
375 | + goto reinject; | |
376 | + ccs_del_domain(element); | |
377 | + break; | |
378 | + default: | |
379 | + break; | |
380 | + } | |
381 | + mutex_lock(&ccs_policy_lock); | |
382 | + ccs_memory_free(element, type); | |
383 | + return; | |
384 | +reinject: | |
385 | + /* | |
386 | + * We can safely reinject this element here bacause | |
387 | + * (1) Appending list elements and removing list elements are protected | |
388 | + * by ccs_policy_lock mutex. | |
389 | + * (2) Only this function removes list elements and this function is | |
390 | + * exclusively executed by ccs_gc_mutex mutex. | |
391 | + * are true. | |
392 | + */ | |
393 | + mutex_lock(&ccs_policy_lock); | |
394 | + list_add_rcu(element, element->prev); | |
395 | +} | |
396 | + | |
397 | +/** | |
398 | + * ccs_collect_member - Delete elements with "struct ccs_acl_head". | |
399 | + * | |
400 | + * @id: One of values in "enum ccs_policy_id". | |
401 | + * @member_list: Pointer to "struct list_head". | |
402 | + * | |
403 | + * Returns nothing. | |
404 | + * | |
405 | + * Caller holds ccs_policy_lock mutex. | |
406 | + */ | |
407 | +static void ccs_collect_member(const enum ccs_policy_id id, | |
408 | + struct list_head *member_list) | |
409 | +{ | |
410 | + struct ccs_acl_head *member; | |
411 | + struct ccs_acl_head *tmp; | |
412 | + list_for_each_entry_safe(member, tmp, member_list, list) { | |
413 | + if (!member->is_deleted) | |
414 | + continue; | |
415 | + member->is_deleted = CCS_GC_IN_PROGRESS; | |
416 | + ccs_try_to_gc(id, &member->list); | |
417 | + } | |
418 | +} | |
419 | + | |
420 | +/** | |
421 | + * ccs_collect_acl - Delete elements in "struct ccs_acl_info". | |
422 | + * | |
423 | + * @list: Pointer to "struct list_head". | |
424 | + * | |
425 | + * Returns nothing. | |
426 | + * | |
427 | + * Caller holds ccs_policy_lock mutex. | |
428 | + */ | |
429 | +static void ccs_collect_acl(struct list_head *list) | |
430 | +{ | |
431 | + struct ccs_acl_info *acl; | |
432 | + struct ccs_acl_info *tmp; | |
433 | + list_for_each_entry_safe(acl, tmp, list, list) { | |
434 | + if (!acl->is_deleted) | |
435 | + continue; | |
436 | + ccs_try_to_gc(CCS_ID_ACL, &acl->list); | |
437 | + } | |
438 | +} | |
439 | + | |
440 | +/** | |
441 | + * ccs_collect_entry - Try to kfree() deleted elements. | |
442 | + * | |
443 | + * Returns nothing. | |
444 | + */ | |
445 | +static void ccs_collect_entry(void) | |
446 | +{ | |
447 | + int i; | |
448 | + mutex_lock(&ccs_policy_lock); | |
449 | + { | |
450 | + struct ccs_domain_info *domain; | |
451 | + struct ccs_domain_info *tmp; | |
452 | + list_for_each_entry_safe(domain, tmp, &ccs_domain_list, list) { | |
453 | + if (ccs_domain_used_by_task(domain)) | |
454 | + continue; | |
455 | + ccs_try_to_gc(CCS_ID_DOMAIN, &domain->list); | |
456 | + } | |
457 | + } | |
458 | + for (i = 0; i < CCS_MAX_MAC_INDEX; i++) { | |
459 | + struct ccs_acl_info *ptr; | |
460 | + struct ccs_acl_info *tmp; | |
461 | + struct list_head * const list = &ccs_acl_list[i]; | |
462 | + list_for_each_entry_safe(ptr, tmp, list, list) { | |
463 | + ccs_collect_acl(&ptr->acl_info_list); | |
464 | + if (!ptr->is_deleted || | |
465 | + !list_empty(&ptr->acl_info_list)) | |
466 | + continue; | |
467 | + /* ptr->is_deleted = CCS_GC_IN_PROGRESS; */ | |
468 | + ccs_try_to_gc(CCS_ID_ACL, &ptr->list); | |
469 | + } | |
470 | + } | |
471 | + { | |
472 | + struct ccs_shared_acl_head *ptr; | |
473 | + struct ccs_shared_acl_head *tmp; | |
474 | + list_for_each_entry_safe(ptr, tmp, &ccs_condition_list, list) { | |
475 | + if (atomic_read(&ptr->users) > 0) | |
476 | + continue; | |
477 | + atomic_set(&ptr->users, CCS_GC_IN_PROGRESS); | |
478 | + ccs_try_to_gc(CCS_ID_CONDITION, &ptr->list); | |
479 | + } | |
480 | + } | |
481 | + for (i = 0; i < CCS_MAX_GROUP; i++) { | |
482 | + struct list_head *list = &ccs_group_list[i]; | |
483 | + struct ccs_group *group; | |
484 | + struct ccs_group *tmp; | |
485 | + enum ccs_policy_id id = CCS_ID_STRING_GROUP; | |
486 | + if (i == CCS_NUMBER_GROUP) | |
487 | + id = CCS_ID_NUMBER_GROUP; | |
488 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
489 | + else if (i == CCS_IP_GROUP) | |
490 | + id = CCS_ID_IP_GROUP; | |
491 | +#endif | |
492 | + list_for_each_entry_safe(group, tmp, list, head.list) { | |
493 | + ccs_collect_member(id, &group->member_list); | |
494 | + if (!list_empty(&group->member_list) || | |
495 | + atomic_read(&group->head.users) > 0) | |
496 | + continue; | |
497 | + atomic_set(&group->head.users, CCS_GC_IN_PROGRESS); | |
498 | + ccs_try_to_gc(CCS_ID_GROUP, &group->head.list); | |
499 | + } | |
500 | + } | |
501 | + for (i = 0; i < CCS_MAX_HASH; i++) { | |
502 | + struct list_head *list = &ccs_name_list[i]; | |
503 | + struct ccs_shared_acl_head *ptr; | |
504 | + struct ccs_shared_acl_head *tmp; | |
505 | + list_for_each_entry_safe(ptr, tmp, list, list) { | |
506 | + if (atomic_read(&ptr->users) > 0) | |
507 | + continue; | |
508 | + atomic_set(&ptr->users, CCS_GC_IN_PROGRESS); | |
509 | + ccs_try_to_gc(CCS_ID_NAME, &ptr->list); | |
510 | + } | |
511 | + } | |
512 | + mutex_unlock(&ccs_policy_lock); | |
513 | +} | |
514 | + | |
515 | +/** | |
516 | + * ccs_gc_thread - Garbage collector thread function. | |
517 | + * | |
518 | + * @unused: Unused. | |
519 | + * | |
520 | + * Returns 0. | |
521 | + */ | |
522 | +static int ccs_gc_thread(void *unused) | |
523 | +{ | |
524 | + /* Garbage collector thread is exclusive. */ | |
525 | + static DEFINE_MUTEX(ccs_gc_mutex); | |
526 | + if (!mutex_trylock(&ccs_gc_mutex)) | |
527 | + goto out; | |
528 | + ccs_collect_entry(); | |
529 | + { | |
530 | + struct ccs_io_buffer *head; | |
531 | + struct ccs_io_buffer *tmp; | |
532 | + spin_lock(&ccs_io_buffer_list_lock); | |
533 | + list_for_each_entry_safe(head, tmp, &ccs_io_buffer_list, | |
534 | + list) { | |
535 | + if (head->users) | |
536 | + continue; | |
537 | + list_del(&head->list); | |
538 | + kfree(head->read_buf); | |
539 | + kfree(head->write_buf); | |
540 | + kfree(head); | |
541 | + } | |
542 | + spin_unlock(&ccs_io_buffer_list_lock); | |
543 | + } | |
544 | + mutex_unlock(&ccs_gc_mutex); | |
545 | +out: | |
546 | + /* This acts as do_exit(0). */ | |
547 | + return 0; | |
548 | +} | |
549 | + | |
550 | +/** | |
551 | + * ccs_notify_gc - Register/unregister /proc/ccs/ users. | |
552 | + * | |
553 | + * @head: Pointer to "struct ccs_io_buffer". | |
554 | + * @is_register: True if register, false if unregister. | |
555 | + * | |
556 | + * Returns nothing. | |
557 | + */ | |
558 | +void ccs_notify_gc(struct ccs_io_buffer *head, const bool is_register) | |
559 | +{ | |
560 | + bool is_write = false; | |
561 | + spin_lock(&ccs_io_buffer_list_lock); | |
562 | + if (is_register) { | |
563 | + head->users = 1; | |
564 | + list_add(&head->list, &ccs_io_buffer_list); | |
565 | + } else { | |
566 | + is_write = head->write_buf != NULL; | |
567 | + if (!--head->users) { | |
568 | + list_del(&head->list); | |
569 | + kfree(head->read_buf); | |
570 | + kfree(head->write_buf); | |
571 | + kfree(head); | |
572 | + } | |
573 | + } | |
574 | + spin_unlock(&ccs_io_buffer_list_lock); | |
575 | + if (is_write) { | |
576 | + struct task_struct *task = kthread_create(ccs_gc_thread, NULL, | |
577 | + "GC for CCS"); | |
578 | + if (!IS_ERR(task)) | |
579 | + wake_up_process(task); | |
580 | + } | |
581 | +} |
@@ -0,0 +1,22 @@ | ||
1 | +ccsecurity-objs := permission.o gc.o memory.o policy_io.o realpath.o | |
2 | + | |
3 | +obj-y += load_policy.o | |
4 | +ifdef CONFIG_CCSECURITY_LKM | |
5 | +obj-m += ccsecurity.o | |
6 | +else | |
7 | +obj-y += ccsecurity.o | |
8 | +endif | |
9 | + | |
10 | +$(obj)/policy/policy.conf: | |
11 | + @mkdir -p $(obj)/policy/ | |
12 | + @echo Creating an empty policy/policy.conf | |
13 | + @touch $@ | |
14 | + | |
15 | +$(obj)/builtin-policy.h: $(obj)/policy/policy.conf | |
16 | + @echo Generating built-in policy for CaitSith 0.1. | |
17 | + @echo "static char ccs_builtin_policy[] __initdata =" > $@.tmp | |
18 | + @sed -e 's/\\/\\134/g' -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/policy.conf >> $@.tmp | |
19 | + @echo "\"\";" >> $@.tmp | |
20 | + @mv $@.tmp $@ | |
21 | + | |
22 | +$(obj)/policy_io.o: $(obj)/builtin-policy.h |
@@ -0,0 +1,55 @@ | ||
1 | +Summary: Userspace tools for CaitSith 0.1 | |
2 | + | |
3 | +Name: caitsith-tools | |
4 | +Version: 0.1 | |
5 | +Release: 1 | |
6 | +License: GPL | |
7 | +Group: System Environment/Kernel | |
8 | +ExclusiveOS: Linux | |
9 | +Autoreqprov: no | |
10 | +Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot | |
11 | +## | |
12 | +## This spec file is intended to be distribution independent. | |
13 | +## I don't enable "BuildRequires:" line because rpmbuild will fail on | |
14 | +## environments where packages are managed by (e.g.) apt. | |
15 | +## | |
16 | +# BuildRequires: ncurses-devel | |
17 | +Requires: ncurses | |
18 | +Conflicts: caitsith-tools < 0.1-1 | |
19 | + | |
20 | +Source0: http://osdn.dl.sourceforge.jp/caitsith/XXXXX/caitsith-tools-0.1-20120401.tar.gz | |
21 | + | |
22 | +%description | |
23 | +This package contains userspace tools for administrating CaitSith 0.1. | |
24 | +Please see http://caitsith.sourceforge.jp/ for documentation. | |
25 | + | |
26 | +%prep | |
27 | + | |
28 | +%setup -q -n caitsithtools | |
29 | + | |
30 | +%build | |
31 | + | |
32 | +make USRLIBDIR=%_libdir CFLAGS="-Wall $RPM_OPT_FLAGS" | |
33 | + | |
34 | +%install | |
35 | + | |
36 | +rm -rf $RPM_BUILD_ROOT | |
37 | +make INSTALLDIR=$RPM_BUILD_ROOT USRLIBDIR=%_libdir install | |
38 | + | |
39 | +%clean | |
40 | + | |
41 | +rm -rf $RPM_BUILD_ROOT | |
42 | + | |
43 | +%post | |
44 | +ldconfig || true | |
45 | + | |
46 | +%files | |
47 | +%defattr(-,root,root) | |
48 | +/sbin/ | |
49 | +%_libdir/caitsith/ | |
50 | +%_libdir/libcaitsith* | |
51 | +/usr/sbin/ | |
52 | + | |
53 | +%changelog | |
54 | +* Sun Apr 01 2012 0.1-1 | |
55 | +- First-release. |
@@ -0,0 +1,1764 @@ | ||
1 | +#include <stdio.h> | |
2 | +#include <stdlib.h> | |
3 | +#include <string.h> | |
4 | +#include <unistd.h> | |
5 | +#include <sys/types.h> | |
6 | +#include <sys/stat.h> | |
7 | +#include <sys/socket.h> | |
8 | +#include <netinet/in.h> | |
9 | +#include <sys/ptrace.h> | |
10 | +#include <sys/wait.h> | |
11 | +#include <linux/ip.h> | |
12 | +#include <fcntl.h> | |
13 | +#include <errno.h> | |
14 | + | |
15 | +static FILE *fp = NULL; | |
16 | + | |
17 | +static void set(const char *str) | |
18 | +{ | |
19 | + fprintf(fp, "%s\n", str); | |
20 | + fflush(fp); | |
21 | +} | |
22 | + | |
23 | +static void unset(const char *str) | |
24 | +{ | |
25 | + fprintf(fp, "delete %s\n", str); | |
26 | + fflush(fp); | |
27 | +} | |
28 | + | |
29 | +static void unset2(const char *str) | |
30 | +{ | |
31 | + const char *cp = str; | |
32 | + while (*cp) { | |
33 | + if (*cp++ != '\n') | |
34 | + continue; | |
35 | + fprintf(fp, "delete "); | |
36 | + fwrite(str, cp - str, 1, fp); | |
37 | + str = cp; | |
38 | + } | |
39 | + fprintf(fp, "delete %s\n", str); | |
40 | + fflush(fp); | |
41 | +} | |
42 | + | |
43 | +static void check(const char *prompt, int result) | |
44 | +{ | |
45 | + int err = errno; | |
46 | + printf("%s%s\n", prompt, result ? "Success" : "Failed"); | |
47 | + if (!result) { | |
48 | + fprintf(stderr, "Err: %s(%d)\n", strerror(err), err); | |
49 | + { | |
50 | + int fd2 = open("/proc/caitsith/self_domain", O_RDONLY); | |
51 | + char c; | |
52 | + fprintf(stderr, "task.domain=\""); | |
53 | + while (read(fd2, &c, 1) == 1) | |
54 | + fprintf(stderr, "%c", c); | |
55 | + close(fd2); | |
56 | + fprintf(stderr, "\"\n"); | |
57 | + } | |
58 | + exit(1); | |
59 | + } | |
60 | + printf("\n"); | |
61 | + fflush(stdout); | |
62 | +} | |
63 | + | |
64 | +static void check_init(const char *prompt, const char *expected) | |
65 | +{ | |
66 | + int result; | |
67 | + int fd = open("/proc/caitsith/.process_status", O_RDWR); | |
68 | + char buffer[1024]; | |
69 | + char *cp; | |
70 | + memset(buffer, 0, sizeof(buffer)); | |
71 | + kill(1, SIGHUP); | |
72 | + sleep(1); | |
73 | + write(fd, "1\n", 2); | |
74 | + read(fd, buffer, sizeof(buffer) - 1); | |
75 | + close(fd); | |
76 | + cp = strchr(buffer, ' '); | |
77 | + if (cp++) | |
78 | + memmove(buffer, cp, strlen(cp) + 1); | |
79 | + result = !strcmp(buffer, expected); | |
80 | + printf("%s%s\n", prompt, result ? "Success" : "Failed"); | |
81 | + if (!result) { | |
82 | + fprintf(stderr, "Err: expected='%s' result='%s'\n", | |
83 | + expected, buffer); | |
84 | + exit(1); | |
85 | + } | |
86 | + printf("\n"); | |
87 | + fflush(stdout); | |
88 | +} | |
89 | + | |
90 | +static void test_task_transition(void) | |
91 | +{ | |
92 | + int fd = open("/proc/caitsith/self_domain", O_WRONLY); | |
93 | + char *policy; | |
94 | + | |
95 | + policy = "100 acl manual_domain_transition\n" | |
96 | + "0 allow domain=\"domain\\$\"\n"; | |
97 | + set(policy); | |
98 | + check(policy, write(fd, "domain0", 7) != EOF); | |
99 | + check(policy, write(fd, "domain10", 8) != EOF); | |
100 | + check(policy, write(fd, "domainXYX", 9) == EOF); | |
101 | + check(policy, write(fd, "domain200", 9) != EOF); | |
102 | + unset(policy); | |
103 | + | |
104 | + policy = "100 acl auto_domain_transition\n" | |
105 | + "0 allow task.pid=1 transition=\"<init3>\"\n"; | |
106 | + set(policy); | |
107 | + check_init(policy, "<init3>"); | |
108 | + unset(policy); | |
109 | + | |
110 | + policy = "100 acl auto_domain_transition\n" | |
111 | + "0 allow task.pid=1 task.uid!=0 transition=\"<init2>\"\n"; | |
112 | + set(policy); | |
113 | + check_init(policy, "<init3>"); | |
114 | + unset(policy); | |
115 | + | |
116 | + policy = "100 acl auto_domain_transition\n" | |
117 | + "0 allow task.pid=1 transition=\"<init>\"\n"; | |
118 | + set(policy); | |
119 | + check_init(policy, "<init>"); | |
120 | + unset(policy); | |
121 | + | |
122 | + close(fd); | |
123 | +} | |
124 | + | |
125 | +static void test_file_read(void) | |
126 | +{ | |
127 | + int fd; | |
128 | + char *policy; | |
129 | + | |
130 | + policy = "100 acl read\n"; | |
131 | + set(policy); | |
132 | + fd = open("/dev/null", O_RDONLY); | |
133 | + check(policy, fd != EOF); | |
134 | + close(fd); | |
135 | + unset(policy); | |
136 | + | |
137 | + policy = "100 acl read\n" | |
138 | + "0 allow\n" | |
139 | + "1 deny\n"; | |
140 | + set(policy); | |
141 | + fd = open("/dev/null", O_RDONLY); | |
142 | + check(policy, fd != EOF); | |
143 | + close(fd); | |
144 | + unset(policy); | |
145 | + | |
146 | + policy = "100 acl read\n" | |
147 | + "0 deny\n" | |
148 | + "1 allow\n"; | |
149 | + set(policy); | |
150 | + fd = open("/dev/null", O_RDONLY); | |
151 | + check(policy, fd == EOF); | |
152 | + close(fd); | |
153 | + unset(policy); | |
154 | + | |
155 | + policy = "100 acl read path=\"/dev/null\"\n" | |
156 | + "0 allow\n" | |
157 | + "1 deny\n"; | |
158 | + set(policy); | |
159 | + fd = open("/dev/null", O_RDONLY); | |
160 | + check(policy, fd != EOF); | |
161 | + close(fd); | |
162 | + unset(policy); | |
163 | + | |
164 | + policy = "100 acl read path=\"/dev/null\"\n" | |
165 | + "0 deny\n" | |
166 | + "1 allow\n"; | |
167 | + set(policy); | |
168 | + fd = open("/dev/null", O_RDONLY); | |
169 | + check(policy, fd == EOF); | |
170 | + close(fd); | |
171 | + unset(policy); | |
172 | + | |
173 | + policy = "100 acl read\n" | |
174 | + "0 allow path=\"/dev/null\"\n" | |
175 | + "1 deny\n"; | |
176 | + set(policy); | |
177 | + fd = open("/dev/null", O_RDONLY); | |
178 | + check(policy, fd != EOF); | |
179 | + close(fd); | |
180 | + unset(policy); | |
181 | + | |
182 | + policy = "100 acl read\n" | |
183 | + "0 deny path=\"/dev/null\"\n" | |
184 | + "1 allow\n"; | |
185 | + set(policy); | |
186 | + fd = open("/dev/null", O_RDONLY); | |
187 | + check(policy, fd == EOF); | |
188 | + close(fd); | |
189 | + unset(policy); | |
190 | + | |
191 | + policy = "100 acl read\n" | |
192 | + "0 allow path.type=char path.dev_major=1 path.dev_minor=3\n" | |
193 | + "1 deny\n"; | |
194 | + set(policy); | |
195 | + fd = open("/dev/null", O_RDONLY); | |
196 | + check(policy, fd != EOF); | |
197 | + close(fd); | |
198 | + unset(policy); | |
199 | + | |
200 | + policy = "100 acl read\n" | |
201 | + "0 deny path.type=char path.dev_major=1 path.dev_minor=3\n" | |
202 | + "1 allow\n"; | |
203 | + set(policy); | |
204 | + fd = open("/dev/null", O_RDONLY); | |
205 | + check(policy, fd == EOF); | |
206 | + close(fd); | |
207 | + unset(policy); | |
208 | + | |
209 | + policy = "100 acl read\n" | |
210 | + "0 allow path.type=char path.dev_major=1 path.dev_minor!=3\n" | |
211 | + "1 deny\n"; | |
212 | + set(policy); | |
213 | + fd = open("/dev/null", O_RDONLY); | |
214 | + check(policy, fd == EOF); | |
215 | + close(fd); | |
216 | + unset(policy); | |
217 | + | |
218 | + policy = "100 acl read\n" | |
219 | + "0 deny path.type=char path.dev_major=1 path.dev_minor!=3\n" | |
220 | + "1 allow\n"; | |
221 | + set(policy); | |
222 | + fd = open("/dev/null", O_RDONLY); | |
223 | + check(policy, fd != EOF); | |
224 | + close(fd); | |
225 | + unset(policy); | |
226 | + | |
227 | + policy = "string_group GROUP1 /dev/null\n" | |
228 | + "100 acl read\n" | |
229 | + "0 allow path=@GROUP1\n" | |
230 | + "1 deny\n"; | |
231 | + set(policy); | |
232 | + fd = open("/dev/null", O_RDONLY); | |
233 | + check(policy, fd != EOF); | |
234 | + close(fd); | |
235 | + unset2(policy); | |
236 | + | |
237 | + policy = "string_group GROUP1 /dev/null\n" | |
238 | + "100 acl read\n" | |
239 | + "0 deny path=@GROUP1\n" | |
240 | + "1 allow\n"; | |
241 | + set(policy); | |
242 | + fd = open("/dev/null", O_RDONLY); | |
243 | + check(policy, fd == EOF); | |
244 | + close(fd); | |
245 | + unset2(policy); | |
246 | + | |
247 | + policy = "string_group GROUP1 /dev/null\n" | |
248 | + "100 acl read\n" | |
249 | + "0 allow path!=@GROUP1\n" | |
250 | + "1 deny\n"; | |
251 | + set(policy); | |
252 | + fd = open("/dev/null", O_RDONLY); | |
253 | + check(policy, fd == EOF); | |
254 | + close(fd); | |
255 | + unset2(policy); | |
256 | + | |
257 | + policy = "string_group GROUP1 /dev/null\n" | |
258 | + "100 acl read\n" | |
259 | + "0 deny path!=@GROUP1\n" | |
260 | + "1 allow\n"; | |
261 | + set(policy); | |
262 | + fd = open("/dev/null", O_RDONLY); | |
263 | + check(policy, fd != EOF); | |
264 | + close(fd); | |
265 | + unset2(policy); | |
266 | + | |
267 | + policy = "string_group GROUP1 /dev/null\n" | |
268 | + "number_group MAJOR 1\n" | |
269 | + "number_group MINOR 3\n" | |
270 | + "100 acl read\n" | |
271 | + "0 allow path=@GROUP1 path.dev_major=@MAJOR" | |
272 | + " path.dev_minor=@MINOR\n" | |
273 | + "1 deny\n"; | |
274 | + set(policy); | |
275 | + fd = open("/dev/null", O_RDONLY); | |
276 | + check(policy, fd != EOF); | |
277 | + close(fd); | |
278 | + unset2(policy); | |
279 | + | |
280 | + policy = "string_group GROUP1 /dev/null\n" | |
281 | + "number_group MAJOR 1\n" | |
282 | + "number_group MINOR 3\n" | |
283 | + "100 acl read\n" | |
284 | + "0 deny path=@GROUP1 path.dev_major=@MAJOR" | |
285 | + " path.dev_minor=@MINOR\n" | |
286 | + "1 allow\n"; | |
287 | + set(policy); | |
288 | + fd = open("/dev/null", O_RDONLY); | |
289 | + check(policy, fd == EOF); | |
290 | + close(fd); | |
291 | + unset2(policy); | |
292 | + | |
293 | + policy = "string_group GROUP1 /dev/zero\n" | |
294 | + "string_group GROUP1 /dev/null\n" | |
295 | + "string_group GROUP1 /dev/urandom\n" | |
296 | + "number_group MAJOR 0\n" | |
297 | + "number_group MAJOR 2-255\n" | |
298 | + "number_group MINOR 00-0x2\n" | |
299 | + "number_group MINOR 255\n" | |
300 | + "100 acl read\n" | |
301 | + "0 allow path=@GROUP1 path.dev_major=@MAJOR" | |
302 | + " path.dev_minor=@MINOR\n" | |
303 | + "1 deny\n"; | |
304 | + set(policy); | |
305 | + fd = open("/dev/null", O_RDONLY); | |
306 | + check(policy, fd == EOF); | |
307 | + close(fd); | |
308 | + unset2(policy); | |
309 | + | |
310 | + policy = "string_group GROUP1 /dev/zero\n" | |
311 | + "string_group GROUP1 /dev/null\n" | |
312 | + "string_group GROUP1 /dev/urandom\n" | |
313 | + "number_group MAJOR 0\n" | |
314 | + "number_group MAJOR 2-255\n" | |
315 | + "number_group MINOR 00-0x2\n" | |
316 | + "number_group MINOR 255\n" | |
317 | + "100 acl read\n" | |
318 | + "0 allow path=@GROUP1 path.dev_major!=@MAJOR" | |
319 | + " path.dev_minor!=@MINOR\n" | |
320 | + "1 deny\n"; | |
321 | + set(policy); | |
322 | + fd = open("/dev/null", O_RDONLY); | |
323 | + check(policy, fd != EOF); | |
324 | + close(fd); | |
325 | + unset2(policy); | |
326 | +} | |
327 | + | |
328 | +static void test_file_write(void) | |
329 | +{ | |
330 | + int fd; | |
331 | + char *policy; | |
332 | + | |
333 | + policy = "100 acl write\n" | |
334 | + "0 allow\n" | |
335 | + "100 acl append\n" | |
336 | + "0 deny\n"; | |
337 | + set(policy); | |
338 | + fd = open("/dev/null", O_WRONLY); | |
339 | + check(policy, fd != EOF); | |
340 | + close(fd); | |
341 | + unset2(policy); | |
342 | + | |
343 | + policy = "100 acl write\n" | |
344 | + "0 deny\n" | |
345 | + "100 acl append\n" | |
346 | + "0 allow\n"; | |
347 | + set(policy); | |
348 | + fd = open("/dev/null", O_WRONLY); | |
349 | + check(policy, fd == EOF); | |
350 | + close(fd); | |
351 | + unset2(policy); | |
352 | + | |
353 | + policy = "100 acl write\n" | |
354 | + "0 allow\n" | |
355 | + "100 acl append\n" | |
356 | + "0 deny\n"; | |
357 | + set(policy); | |
358 | + fd = open("/dev/null", O_WRONLY | O_APPEND); | |
359 | + check(policy, fd == EOF); | |
360 | + close(fd); | |
361 | + unset2(policy); | |
362 | + | |
363 | + policy = "100 acl write\n" | |
364 | + "0 deny\n" | |
365 | + "100 acl append\n" | |
366 | + "0 append\n"; | |
367 | + set(policy); | |
368 | + fd = open("/dev/null", O_WRONLY | O_APPEND); | |
369 | + check(policy, fd != EOF); | |
370 | + close(fd); | |
371 | + unset2(policy); | |
372 | + | |
373 | + policy = "100 acl write\n" | |
374 | + "0 allow path.type=char path.dev_major=1 path.dev_minor=3\n" | |
375 | + "1 deny\n"; | |
376 | + set(policy); | |
377 | + fd = open("/dev/null", O_WRONLY | O_TRUNC); | |
378 | + check(policy, fd != EOF); | |
379 | + close(fd); | |
380 | + unset(policy); | |
381 | + | |
382 | + policy = "100 acl write\n" | |
383 | + "0 allow path.type=char path.dev_major=1" | |
384 | + " path.dev_minor=@MINOR\n" | |
385 | + "1 deny\n"; | |
386 | + set(policy); | |
387 | + fd = open("/dev/null", O_WRONLY | O_TRUNC); | |
388 | + check(policy, fd == EOF); | |
389 | + close(fd); | |
390 | + unset(policy); | |
391 | + | |
392 | + policy = "100 acl write\n" | |
393 | + "0 allow path.parent.type=directory path.parent.uid=0" | |
394 | + " path.parent.perm=0755\n" | |
395 | + "1 deny\n"; | |
396 | + set(policy); | |
397 | + fd = open("/dev/null", O_WRONLY); | |
398 | + check(policy, fd != EOF); | |
399 | + close(fd); | |
400 | + unset(policy); | |
401 | + | |
402 | + policy = "100 acl write\n" | |
403 | + "0 allow path.parent.uid=task.uid path.parent.gid=task.gid\n" | |
404 | + "1 deny\n"; | |
405 | + set(policy); | |
406 | + fd = open("/dev/null", O_WRONLY); | |
407 | + check(policy, fd != EOF); | |
408 | + close(fd); | |
409 | + unset(policy); | |
410 | + | |
411 | + policy = "100 acl write\n" | |
412 | + "0 allow task.uid=path.parent.uid task.gid=path.parent.gid\n" | |
413 | + "1 deny\n"; | |
414 | + set(policy); | |
415 | + fd = open("/dev/null", O_WRONLY); | |
416 | + check(policy, fd != EOF); | |
417 | + close(fd); | |
418 | + unset(policy); | |
419 | +} | |
420 | + | |
421 | +static void test_file_create(void) | |
422 | +{ | |
423 | + int fd; | |
424 | + char *policy; | |
425 | + | |
426 | + policy = "100 acl create\n" | |
427 | + "0 allow path.uid=0\n" | |
428 | + "1 deny\n"; | |
429 | + set(policy); | |
430 | + unlink("/tmp/file"); | |
431 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
432 | + check(policy, fd == EOF); | |
433 | + close(fd); | |
434 | + unset(policy); | |
435 | + | |
436 | + policy = "100 acl create\n" | |
437 | + "0 allow path=\"/tmp/file\" path.parent.uid=0\n" | |
438 | + "1 deny\n"; | |
439 | + set(policy); | |
440 | + unlink("/tmp/file"); | |
441 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
442 | + check(policy, fd != EOF); | |
443 | + close(fd); | |
444 | + unset(policy); | |
445 | + | |
446 | + policy = "number_group GROUP1 1-0xFFFFFFFF\n" | |
447 | + "100 acl create\n" | |
448 | + "0 allow path.parent.uid!=@GROUP1 perm=0600\n" | |
449 | + "1 deny\n"; | |
450 | + set(policy); | |
451 | + unlink("/tmp/file"); | |
452 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
453 | + check(policy, fd != EOF); | |
454 | + close(fd); | |
455 | + unset2(policy); | |
456 | + | |
457 | + policy = "number_group GROUP1 1-0xFFFFFFFF\n" | |
458 | + "100 acl create\n" | |
459 | + "0 allow path.parent.uid!=@GROUP1 perm!=0600\n" | |
460 | + "1 deny\n"; | |
461 | + set(policy); | |
462 | + unlink("/tmp/file"); | |
463 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
464 | + check(policy, fd == EOF); | |
465 | + close(fd); | |
466 | + unset2(policy); | |
467 | + | |
468 | + policy = "100 acl create\n" | |
469 | + "0 allow path.parent.uid=task.uid\n" | |
470 | + "1 deny\n"; | |
471 | + set(policy); | |
472 | + unlink("/tmp/file"); | |
473 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
474 | + check(policy, fd != EOF); | |
475 | + close(fd); | |
476 | + unset(policy); | |
477 | +} | |
478 | + | |
479 | +static void test_file_unlink(void) | |
480 | +{ | |
481 | + char *policy; | |
482 | + | |
483 | + policy = "100 acl unlink\n" | |
484 | + "0 allow path.uid=0 path.uid=path.parent.uid\n" | |
485 | + "1 deny\n"; | |
486 | + set(policy); | |
487 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
488 | + check(policy, unlink("/tmp/file") == 0); | |
489 | + unset(policy); | |
490 | + | |
491 | + policy = "100 acl unlink\n" | |
492 | + "0 deny path.uid=0 path.uid=path.parent.uid\n" | |
493 | + "1 allow\n"; | |
494 | + set(policy); | |
495 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
496 | + check(policy, unlink("/tmp/file") == EOF); | |
497 | + unset(policy); | |
498 | +} | |
499 | + | |
500 | +static void test_file_link(void) | |
501 | +{ | |
502 | + char *policy; | |
503 | + | |
504 | + policy = "100 acl link\n" | |
505 | + "0 allow old_path.uid=0 old_path.uid=old_path.parent.uid" | |
506 | + " old_path.parent.ino=new_path.parent.ino\n" | |
507 | + "1 deny\n"; | |
508 | + set(policy); | |
509 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
510 | + unlink("/tmp/file2"); | |
511 | + check(policy, link("/tmp/file", "/tmp/file2") == 0); | |
512 | + unset(policy); | |
513 | + | |
514 | + policy = "100 acl link\n" | |
515 | + "0 deny old_path.uid=0 old_path.uid=old_path.parent.uid\n" | |
516 | + "1 allow\n"; | |
517 | + set(policy); | |
518 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
519 | + unlink("/tmp/file2"); | |
520 | + check(policy, link("/tmp/file", "/tmp/file2") == EOF); | |
521 | + unset(policy); | |
522 | +} | |
523 | + | |
524 | +static void test_file_rename(void) | |
525 | +{ | |
526 | + char *policy; | |
527 | + | |
528 | + policy = "100 acl rename\n" | |
529 | + "0 allow old_path.uid=0 old_path.uid=old_path.parent.uid" | |
530 | + " old_path.parent.ino=new_path.parent.ino\n" | |
531 | + "1 deny\n"; | |
532 | + set(policy); | |
533 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
534 | + unlink("/tmp/file2"); | |
535 | + check(policy, rename("/tmp/file", "/tmp/file2") == 0); | |
536 | + unset(policy); | |
537 | + | |
538 | + policy = "100 acl rename\n" | |
539 | + "0 deny old_path.uid=0 old_path.uid=old_path.parent.uid\n" | |
540 | + "1 allow\n"; | |
541 | + set(policy); | |
542 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
543 | + unlink("/tmp/file2"); | |
544 | + check(policy, rename("/tmp/file", "/tmp/file2") == EOF); | |
545 | + unset(policy); | |
546 | +} | |
547 | + | |
548 | +static void test_network_inet_stream(void) | |
549 | +{ | |
550 | + struct sockaddr_in addr1 = { }; | |
551 | + struct sockaddr_in addr2 = { }; | |
552 | + socklen_t size = sizeof(addr1); | |
553 | + int fd1; | |
554 | + int fd2; | |
555 | + int fd3; | |
556 | + char *policy; | |
557 | + char buffer[1024]; | |
558 | + memset(buffer, 0, sizeof(buffer)); | |
559 | + | |
560 | + fd1 = socket(PF_INET, SOCK_STREAM, 0); | |
561 | + fd2 = socket(PF_INET, SOCK_STREAM, 0); | |
562 | + addr1.sin_family = AF_INET; | |
563 | + addr1.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
564 | + | |
565 | + policy = "100 acl inet_stream_bind\n" | |
566 | + "0 allow ip=127.0.0.1 port!=0\n" | |
567 | + "1 deny\n"; | |
568 | + set(policy); | |
569 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
570 | + EOF); | |
571 | + unset(policy); | |
572 | + | |
573 | + policy = "100 acl inet_stream_bind\n" | |
574 | + "0 allow ip!=127.0.0.1 port=0\n" | |
575 | + "1 deny\n"; | |
576 | + set(policy); | |
577 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
578 | + EOF); | |
579 | + unset(policy); | |
580 | + | |
581 | + policy = "100 acl inet_stream_bind\n" | |
582 | + "0 allow ip=127.0.0.1 port=0 path.uid=task.uid\n" | |
583 | + "1 deny\n"; | |
584 | + set(policy); | |
585 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
586 | + EOF); | |
587 | + unset(policy); | |
588 | + | |
589 | + policy = "100 acl inet_stream_bind\n" | |
590 | + "0 allow ip=127.0.0.1 port=0\n" | |
591 | + "1 deny\n"; | |
592 | + set(policy); | |
593 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
594 | + 0); | |
595 | + unset(policy); | |
596 | + | |
597 | + getsockname(fd1, (struct sockaddr *) &addr1, &size); | |
598 | + | |
599 | + snprintf(buffer, sizeof(buffer) - 1, | |
600 | + "100 acl inet_stream_listen\n" | |
601 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
602 | + "1 deny\n", ntohs(addr1.sin_port)); | |
603 | + policy = buffer; | |
604 | + set(policy); | |
605 | + check(policy, listen(fd1, 5) == EOF); | |
606 | + unset(policy); | |
607 | + | |
608 | + snprintf(buffer, sizeof(buffer) - 1, | |
609 | + "100 acl inet_stream_listen\n" | |
610 | + "0 allow ip=127.0.0.1 port=%u\n" | |
611 | + "1 deny\n", ntohs(addr1.sin_port)); | |
612 | + policy = buffer; | |
613 | + set(policy); | |
614 | + check(policy, listen(fd1, 5) == 0); | |
615 | + unset(policy); | |
616 | + | |
617 | + snprintf(buffer, sizeof(buffer) - 1, | |
618 | + "100 acl inet_stream_connect\n" | |
619 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
620 | + "1 deny\n", ntohs(addr1.sin_port)); | |
621 | + policy = buffer; | |
622 | + set(policy); | |
623 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
624 | + == EOF); | |
625 | + unset(policy); | |
626 | + | |
627 | + snprintf(buffer, sizeof(buffer) - 1, | |
628 | + "100 acl inet_stream_connect\n" | |
629 | + "0 allow ip=127.0.0.1 port=%u\n" | |
630 | + "1 deny\n", ntohs(addr1.sin_port)); | |
631 | + policy = buffer; | |
632 | + set(policy); | |
633 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
634 | + == 0); | |
635 | + unset(policy); | |
636 | + | |
637 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
638 | + | |
639 | + snprintf(buffer, sizeof(buffer) - 1, | |
640 | + "100 acl inet_stream_accept\n" | |
641 | + "0 allow ip=127.0.0.1 port=%u\n" | |
642 | + "1 deny\n", ntohs(addr2.sin_port)); | |
643 | + policy = buffer; | |
644 | + set(policy); | |
645 | + fd3 = accept(fd1, NULL, 0); | |
646 | + check(policy, fd3 != EOF); | |
647 | + close(fd3); | |
648 | + unset(policy); | |
649 | + | |
650 | + snprintf(buffer, sizeof(buffer) - 1, | |
651 | + "100 acl inet_stream_connect\n" | |
652 | + "0 allow ip=127.0.0.1 port=%u\n" | |
653 | + "1 deny\n", ntohs(addr1.sin_port)); | |
654 | + policy = buffer; | |
655 | + set(policy); | |
656 | + close(fd2); | |
657 | + fd2 = socket(PF_INET, SOCK_STREAM, 0); | |
658 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
659 | + == 0); | |
660 | + unset(policy); | |
661 | + | |
662 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
663 | + | |
664 | + snprintf(buffer, sizeof(buffer) - 1, | |
665 | + "100 acl inet_stream_accept\n" | |
666 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
667 | + "1 deny\n", ntohs(addr2.sin_port)); | |
668 | + policy = buffer; | |
669 | + set(policy); | |
670 | + fd3 = accept(fd1, NULL, 0); | |
671 | + check(policy, fd3 == EOF); | |
672 | + close(fd3); | |
673 | + unset(policy); | |
674 | + | |
675 | + close(fd1); | |
676 | + close(fd2); | |
677 | +} | |
678 | + | |
679 | +static void test_network_inet_dgram(void) | |
680 | +{ | |
681 | + struct sockaddr_in addr1 = { }; | |
682 | + struct sockaddr_in addr2 = { }; | |
683 | + socklen_t size = sizeof(addr1); | |
684 | + int fd1; | |
685 | + int fd2; | |
686 | + char c; | |
687 | + char *policy; | |
688 | + char buffer[1024]; | |
689 | + memset(buffer, 0, sizeof(buffer)); | |
690 | + | |
691 | + fd1 = socket(PF_INET, SOCK_DGRAM, 0); | |
692 | + fd2 = socket(PF_INET, SOCK_DGRAM, 0); | |
693 | + addr1.sin_family = AF_INET; | |
694 | + addr1.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
695 | + | |
696 | + policy = "100 acl inet_dgram_bind\n" | |
697 | + "0 allow ip=127.0.0.1 port!=0\n" | |
698 | + "1 deny\n"; | |
699 | + set(policy); | |
700 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
701 | + EOF); | |
702 | + unset(policy); | |
703 | + | |
704 | + policy = "100 acl inet_dgram_bind\n" | |
705 | + "0 allow ip!=127.0.0.1 port=0\n" | |
706 | + "1 deny\n"; | |
707 | + set(policy); | |
708 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
709 | + EOF); | |
710 | + unset(policy); | |
711 | + | |
712 | + policy = "100 acl inet_dgram_bind\n" | |
713 | + "0 allow ip=127.0.0.1 port=0 path.uid=task.uid\n" | |
714 | + "1 deny\n"; | |
715 | + set(policy); | |
716 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
717 | + EOF); | |
718 | + unset(policy); | |
719 | + | |
720 | + policy = "100 acl inet_dgram_bind\n" | |
721 | + "0 allow ip=127.0.0.1 port=0\n" | |
722 | + "1 deny\n"; | |
723 | + set(policy); | |
724 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
725 | + 0); | |
726 | + unset(policy); | |
727 | + | |
728 | + getsockname(fd1, (struct sockaddr *) &addr1, &size); | |
729 | + | |
730 | + snprintf(buffer, sizeof(buffer) - 1, | |
731 | + "100 acl inet_dgram_send\n" | |
732 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
733 | + "1 deny\n", ntohs(addr1.sin_port)); | |
734 | + policy = buffer; | |
735 | + set(policy); | |
736 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
737 | + == EOF); | |
738 | + unset(policy); | |
739 | + | |
740 | + snprintf(buffer, sizeof(buffer) - 1, | |
741 | + "100 acl inet_dgram_send\n" | |
742 | + "0 allow ip=127.0.0.1 port=%u\n" | |
743 | + "1 deny\n", ntohs(addr1.sin_port)); | |
744 | + policy = buffer; | |
745 | + set(policy); | |
746 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
747 | + == 0); | |
748 | + unset(policy); | |
749 | + | |
750 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
751 | + | |
752 | + snprintf(buffer, sizeof(buffer) - 1, | |
753 | + "100 acl inet_dgram_send\n" | |
754 | + "0 allow ip=127.0.0.1 port=%u\n" | |
755 | + "1 deny\n", ntohs(addr1.sin_port)); | |
756 | + policy = buffer; | |
757 | + set(policy); | |
758 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
759 | + unset(policy); | |
760 | + | |
761 | + snprintf(buffer, sizeof(buffer) - 1, | |
762 | + "100 acl inet_dgram_recv\n" | |
763 | + "0 allow ip=127.0.0.1 port=%u\n" | |
764 | + "1 deny\n", ntohs(addr2.sin_port)); | |
765 | + policy = buffer; | |
766 | + set(policy); | |
767 | + check(policy, recv(fd1, &c, 1, 0) != EOF); | |
768 | + unset(policy); | |
769 | + | |
770 | + snprintf(buffer, sizeof(buffer) - 1, | |
771 | + "100 acl inet_dgram_send\n" | |
772 | + "0 allow ip=127.0.0.1 port=%u\n" | |
773 | + "1 deny\n", ntohs(addr1.sin_port)); | |
774 | + policy = buffer; | |
775 | + set(policy); | |
776 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
777 | + unset(policy); | |
778 | + | |
779 | + snprintf(buffer, sizeof(buffer) - 1, | |
780 | + "100 acl inet_dgram_recv\n" | |
781 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
782 | + "1 deny\n", ntohs(addr2.sin_port)); | |
783 | + policy = buffer; | |
784 | + set(policy); | |
785 | + check(policy, recv(fd1, &c, 1, 0) == EOF); | |
786 | + unset(policy); | |
787 | + | |
788 | + snprintf(buffer, sizeof(buffer) - 1, | |
789 | + "ip_group LOCALHOST 127.0.0.0-127.255.255.255\n" | |
790 | + "100 acl inet_dgram_send\n" | |
791 | + "0 allow ip=@LOCALHOST port=%u\n" | |
792 | + "1 deny\n", ntohs(addr1.sin_port)); | |
793 | + policy = buffer; | |
794 | + set(policy); | |
795 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
796 | + unset2(policy); | |
797 | + | |
798 | + snprintf(buffer, sizeof(buffer) - 1, | |
799 | + "ip_group LOCALHOST 127.0.0.0-127.255.255.255\n" | |
800 | + "100 acl inet_dgram_recv\n" | |
801 | + "0 allow ip!=@LOCALHOST port=%u\n" | |
802 | + "1 deny\n", ntohs(addr2.sin_port)); | |
803 | + policy = buffer; | |
804 | + set(policy); | |
805 | + check(policy, recv(fd1, &c, 1, 0) == EOF); | |
806 | + unset2(policy); | |
807 | + | |
808 | + close(fd1); | |
809 | + close(fd2); | |
810 | +} | |
811 | + | |
812 | +static void test_network_inet_raw(void) | |
813 | +{ | |
814 | + struct sockaddr_in addr = { }; | |
815 | + static struct iphdr ip = { }; | |
816 | + int fd1; | |
817 | + int fd2; | |
818 | + char *policy; | |
819 | + fd1 = socket(PF_INET, SOCK_RAW, 1); | |
820 | + fd2 = socket(PF_INET, SOCK_RAW, 1); | |
821 | + addr.sin_family = AF_INET; | |
822 | + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
823 | + ip.version = 4; | |
824 | + ip.ihl = sizeof(struct iphdr) / 4; | |
825 | + ip.protocol = IPPROTO_RAW; | |
826 | + ip.daddr = htonl(INADDR_LOOPBACK); | |
827 | + ip.saddr = ip.daddr; | |
828 | + | |
829 | + policy = "100 acl inet_raw_bind\n" | |
830 | + "0 allow ip=127.0.0.1 proto!=1\n" | |
831 | + "1 deny\n"; | |
832 | + set(policy); | |
833 | + check(policy, bind(fd1, (struct sockaddr *) &addr, sizeof(addr)) == | |
834 | + EOF); | |
835 | + unset(policy); | |
836 | + | |
837 | + policy = "100 acl inet_raw_bind\n" | |
838 | + "0 allow ip!=127.0.0.1 proto=1\n" | |
839 | + "1 deny\n"; | |
840 | + set(policy); | |
841 | + check(policy, bind(fd1, (struct sockaddr *) &addr, sizeof(addr)) == | |
842 | + EOF); | |
843 | + unset(policy); | |
844 | + | |
845 | + policy = "100 acl inet_raw_bind\n" | |
846 | + "0 allow ip=127.0.0.1 proto=1 path.uid=task.uid\n" | |
847 | + "1 deny\n"; | |
848 | + set(policy); | |
849 | + check(policy, bind(fd1, (struct sockaddr *) &addr, sizeof(addr)) == | |
850 | + EOF); | |
851 | + unset(policy); | |
852 | + | |
853 | + policy = "100 acl inet_raw_bind\n" | |
854 | + "0 allow ip=127.0.0.1 proto=1\n" | |
855 | + "1 deny\n"; | |
856 | + set(policy); | |
857 | + check(policy, bind(fd2, (struct sockaddr *) &addr, sizeof(addr)) == | |
858 | + 0); | |
859 | + unset(policy); | |
860 | + | |
861 | + policy = "100 acl inet_raw_send\n" | |
862 | + "0 allow ip=127.0.0.1 proto!=1\n" | |
863 | + "1 deny\n"; | |
864 | + set(policy); | |
865 | + check(policy, connect(fd2, (struct sockaddr *) &addr, sizeof(addr)) | |
866 | + == EOF); | |
867 | + unset(policy); | |
868 | + | |
869 | + policy = "100 acl inet_raw_send\n" | |
870 | + "0 allow ip=127.0.0.1 proto=1\n" | |
871 | + "1 deny\n"; | |
872 | + set(policy); | |
873 | + check(policy, connect(fd2, (struct sockaddr *) &addr, sizeof(addr)) | |
874 | + == 0); | |
875 | + unset(policy); | |
876 | + | |
877 | + policy = "100 acl inet_raw_send\n" | |
878 | + "0 allow ip=127.0.0.1 proto=1\n" | |
879 | + "1 deny\n"; | |
880 | + set(policy); | |
881 | + check(policy, send(fd2, &ip, sizeof(ip), 0) != EOF); | |
882 | + unset(policy); | |
883 | + | |
884 | + policy = "100 acl inet_raw_recv\n" | |
885 | + "0 allow ip=127.0.0.1 proto=1\n" | |
886 | + "1 deny\n"; | |
887 | + set(policy); | |
888 | + check(policy, recv(fd1, &ip, sizeof(ip), MSG_DONTWAIT) != EOF); | |
889 | + unset(policy); | |
890 | + | |
891 | + policy = "100 acl inet_raw_send\n" | |
892 | + "0 allow ip=127.0.0.1 proto=1\n" | |
893 | + "1 deny\n"; | |
894 | + set(policy); | |
895 | + check(policy, send(fd2, &ip, sizeof(ip), 0) != EOF); | |
896 | + unset(policy); | |
897 | + | |
898 | + policy = "100 acl inet_raw_recv\n" | |
899 | + "0 allow ip=127.0.0.1 proto!=1\n" | |
900 | + "1 deny\n"; | |
901 | + set(policy); | |
902 | + check(policy, recv(fd1, &ip, sizeof(ip), MSG_DONTWAIT) == EOF); | |
903 | + unset(policy); | |
904 | + | |
905 | + policy = "ip_group LOCALHOST 127.0.0.0-127.255.255.255\n" | |
906 | + "100 acl inet_raw_send\n" | |
907 | + "0 allow ip=@LOCALHOST proto=1\n" | |
908 | + "1 deny\n"; | |
909 | + set(policy); | |
910 | + check(policy, send(fd2, &ip, sizeof(ip), 0) != EOF); | |
911 | + unset2(policy); | |
912 | + | |
913 | + policy = "ip_group LOCALHOST 127.0.0.0-127.255.255.255\n" | |
914 | + "100 acl inet_raw_recv\n" | |
915 | + "0 allow ip!=@LOCALHOST proto=1\n" | |
916 | + "1 deny\n"; | |
917 | + set(policy); | |
918 | + check(policy, recv(fd1, &ip, sizeof(ip), MSG_DONTWAIT) == EOF); | |
919 | + unset2(policy); | |
920 | + | |
921 | + close(fd1); | |
922 | + close(fd2); | |
923 | +} | |
924 | + | |
925 | +static void test_network_inet6_stream(void) | |
926 | +{ | |
927 | + struct sockaddr_in6 addr1 = { }; | |
928 | + struct sockaddr_in6 addr2 = { }; | |
929 | + socklen_t size = sizeof(addr1); | |
930 | + int fd1; | |
931 | + int fd2; | |
932 | + int fd3; | |
933 | + char *policy; | |
934 | + char buffer[1024]; | |
935 | + memset(buffer, 0, sizeof(buffer)); | |
936 | + | |
937 | + fd1 = socket(PF_INET6, SOCK_STREAM, 0); | |
938 | + fd2 = socket(PF_INET6, SOCK_STREAM, 0); | |
939 | + addr1.sin6_family = AF_INET6; | |
940 | + addr1.sin6_addr = in6addr_loopback; | |
941 | + | |
942 | + policy = "100 acl inet_stream_bind\n" | |
943 | + "0 allow ip=::1 port!=0\n" | |
944 | + "1 deny\n"; | |
945 | + set(policy); | |
946 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
947 | + EOF); | |
948 | + unset(policy); | |
949 | + | |
950 | + policy = "100 acl inet_stream_bind\n" | |
951 | + "0 allow ip!=::1 port=0\n" | |
952 | + "1 deny\n"; | |
953 | + set(policy); | |
954 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
955 | + EOF); | |
956 | + unset(policy); | |
957 | + | |
958 | + policy = "100 acl inet_stream_bind\n" | |
959 | + "0 allow ip=::1 port=0 path.uid=task.uid\n" | |
960 | + "1 deny\n"; | |
961 | + set(policy); | |
962 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
963 | + EOF); | |
964 | + unset(policy); | |
965 | + | |
966 | + policy = "100 acl inet_stream_bind\n" | |
967 | + "0 allow ip=::1 port=0\n" | |
968 | + "1 deny\n"; | |
969 | + set(policy); | |
970 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
971 | + 0); | |
972 | + unset(policy); | |
973 | + | |
974 | + getsockname(fd1, (struct sockaddr *) &addr1, &size); | |
975 | + | |
976 | + snprintf(buffer, sizeof(buffer) - 1, | |
977 | + "100 acl inet_stream_listen\n" | |
978 | + "0 allow ip=::1 port!=%u\n" | |
979 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
980 | + policy = buffer; | |
981 | + set(policy); | |
982 | + check(policy, listen(fd1, 5) == EOF); | |
983 | + unset(policy); | |
984 | + | |
985 | + snprintf(buffer, sizeof(buffer) - 1, | |
986 | + "100 acl inet_stream_listen\n" | |
987 | + "0 allow ip=::1 port=%u\n" | |
988 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
989 | + policy = buffer; | |
990 | + set(policy); | |
991 | + check(policy, listen(fd1, 5) == 0); | |
992 | + unset(policy); | |
993 | + | |
994 | + snprintf(buffer, sizeof(buffer) - 1, | |
995 | + "100 acl inet_stream_connect\n" | |
996 | + "0 allow ip=::1 port!=%u\n" | |
997 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
998 | + policy = buffer; | |
999 | + set(policy); | |
1000 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
1001 | + == EOF); | |
1002 | + unset(policy); | |
1003 | + | |
1004 | + snprintf(buffer, sizeof(buffer) - 1, | |
1005 | + "100 acl inet_stream_connect\n" | |
1006 | + "0 allow ip=::1 port=%u\n" | |
1007 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1008 | + policy = buffer; | |
1009 | + set(policy); | |
1010 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
1011 | + == 0); | |
1012 | + unset(policy); | |
1013 | + | |
1014 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
1015 | + | |
1016 | + snprintf(buffer, sizeof(buffer) - 1, | |
1017 | + "100 acl inet_stream_accept\n" | |
1018 | + "0 allow ip=::1 port=%u\n" | |
1019 | + "1 deny\n", ntohs(addr2.sin6_port)); | |
1020 | + policy = buffer; | |
1021 | + set(policy); | |
1022 | + fd3 = accept(fd1, NULL, 0); | |
1023 | + check(policy, fd3 != EOF); | |
1024 | + close(fd3); | |
1025 | + unset(policy); | |
1026 | + | |
1027 | + snprintf(buffer, sizeof(buffer) - 1, | |
1028 | + "100 acl inet_stream_connect\n" | |
1029 | + "0 allow ip=::1 port=%u\n" | |
1030 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1031 | + policy = buffer; | |
1032 | + set(policy); | |
1033 | + close(fd2); | |
1034 | + fd2 = socket(PF_INET6, SOCK_STREAM, 0); | |
1035 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
1036 | + == 0); | |
1037 | + unset(policy); | |
1038 | + | |
1039 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
1040 | + | |
1041 | + snprintf(buffer, sizeof(buffer) - 1, | |
1042 | + "100 acl inet_stream_accept\n" | |
1043 | + "0 allow ip=::1 port!=%u\n" | |
1044 | + "1 deny\n", ntohs(addr2.sin6_port)); | |
1045 | + policy = buffer; | |
1046 | + set(policy); | |
1047 | + fd3 = accept(fd1, NULL, 0); | |
1048 | + check(policy, fd3 == EOF); | |
1049 | + close(fd3); | |
1050 | + unset(policy); | |
1051 | + | |
1052 | + close(fd1); | |
1053 | + close(fd2); | |
1054 | +} | |
1055 | + | |
1056 | +static void test_network_inet6_dgram(void) | |
1057 | +{ | |
1058 | + struct sockaddr_in6 addr1 = { }; | |
1059 | + struct sockaddr_in6 addr2 = { }; | |
1060 | + socklen_t size = sizeof(addr1); | |
1061 | + int fd1; | |
1062 | + int fd2; | |
1063 | + char c; | |
1064 | + char *policy; | |
1065 | + char buffer[1024]; | |
1066 | + memset(buffer, 0, sizeof(buffer)); | |
1067 | + | |
1068 | + fd1 = socket(PF_INET6, SOCK_DGRAM, 0); | |
1069 | + fd2 = socket(PF_INET6, SOCK_DGRAM, 0); | |
1070 | + addr1.sin6_family = AF_INET6; | |
1071 | + addr1.sin6_addr = in6addr_loopback; | |
1072 | + | |
1073 | + policy = "100 acl inet_dgram_bind\n" | |
1074 | + "0 allow ip=::1 port!=0\n" | |
1075 | + "1 deny\n"; | |
1076 | + set(policy); | |
1077 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
1078 | + EOF); | |
1079 | + unset(policy); | |
1080 | + | |
1081 | + policy = "100 acl inet_dgram_bind\n" | |
1082 | + "0 allow ip!=::1 port=0\n" | |
1083 | + "1 deny\n"; | |
1084 | + set(policy); | |
1085 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
1086 | + EOF); | |
1087 | + unset(policy); | |
1088 | + | |
1089 | + policy = "100 acl inet_dgram_bind\n" | |
1090 | + "0 allow ip=::1 port=0 path.uid=task.uid\n" | |
1091 | + "1 deny\n"; | |
1092 | + set(policy); | |
1093 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
1094 | + EOF); | |
1095 | + unset(policy); | |
1096 | + | |
1097 | + policy = "100 acl inet_dgram_bind\n" | |
1098 | + "0 allow ip=::1 port=0\n" | |
1099 | + "1 deny\n"; | |
1100 | + set(policy); | |
1101 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
1102 | + 0); | |
1103 | + unset(policy); | |
1104 | + | |
1105 | + getsockname(fd1, (struct sockaddr *) &addr1, &size); | |
1106 | + | |
1107 | + snprintf(buffer, sizeof(buffer) - 1, | |
1108 | + "100 acl inet_dgram_send\n" | |
1109 | + "0 allow ip=::1 port!=%u\n" | |
1110 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1111 | + policy = buffer; | |
1112 | + set(policy); | |
1113 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
1114 | + == EOF); | |
1115 | + unset(policy); | |
1116 | + | |
1117 | + snprintf(buffer, sizeof(buffer) - 1, | |
1118 | + "100 acl inet_dgram_send\n" | |
1119 | + "0 allow ip=::1 port=%u\n" | |
1120 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1121 | + policy = buffer; | |
1122 | + set(policy); | |
1123 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
1124 | + == 0); | |
1125 | + unset(policy); | |
1126 | + | |
1127 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
1128 | + | |
1129 | + snprintf(buffer, sizeof(buffer) - 1, | |
1130 | + "100 acl inet_dgram_send\n" | |
1131 | + "0 allow ip=::1 port=%u\n" | |
1132 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1133 | + policy = buffer; | |
1134 | + set(policy); | |
1135 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
1136 | + unset(policy); | |
1137 | + | |
1138 | + snprintf(buffer, sizeof(buffer) - 1, | |
1139 | + "100 acl inet_dgram_recv\n" | |
1140 | + "0 allow ip=::1 port=%u\n" | |
1141 | + "1 deny\n", ntohs(addr2.sin6_port)); | |
1142 | + policy = buffer; | |
1143 | + set(policy); | |
1144 | + check(policy, recv(fd1, &c, 1, 0) != EOF); | |
1145 | + unset(policy); | |
1146 | + | |
1147 | + snprintf(buffer, sizeof(buffer) - 1, | |
1148 | + "100 acl inet_dgram_send\n" | |
1149 | + "0 allow ip=::1 port=%u\n" | |
1150 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1151 | + policy = buffer; | |
1152 | + set(policy); | |
1153 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
1154 | + unset(policy); | |
1155 | + | |
1156 | + snprintf(buffer, sizeof(buffer) - 1, | |
1157 | + "100 acl inet_dgram_recv\n" | |
1158 | + "0 allow ip=::1 port!=%u\n" | |
1159 | + "1 deny\n", ntohs(addr2.sin6_port)); | |
1160 | + policy = buffer; | |
1161 | + set(policy); | |
1162 | + check(policy, recv(fd1, &c, 1, 0) == EOF); | |
1163 | + unset(policy); | |
1164 | + | |
1165 | + snprintf(buffer, sizeof(buffer) - 1, | |
1166 | + "ip_group LOCALHOST ::-::ffff\n" | |
1167 | + "100 acl inet_dgram_send\n" | |
1168 | + "0 allow ip=@LOCALHOST port=%u\n" | |
1169 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1170 | + policy = buffer; | |
1171 | + set(policy); | |
1172 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
1173 | + unset2(policy); | |
1174 | + | |
1175 | + snprintf(buffer, sizeof(buffer) - 1, | |
1176 | + "ip_group LOCALHOST ::-::ffff\n" | |
1177 | + "100 acl inet_dgram_recv\n" | |
1178 | + "0 allow ip!=@LOCALHOST port=%u\n" | |
1179 | + "1 deny\n", ntohs(addr2.sin6_port)); | |
1180 | + policy = buffer; | |
1181 | + set(policy); | |
1182 | + check(policy, recv(fd1, &c, 1, 0) == EOF); | |
1183 | + unset2(policy); | |
1184 | + | |
1185 | + close(fd1); | |
1186 | + close(fd2); | |
1187 | +} | |
1188 | + | |
1189 | +static void test_capability(void) | |
1190 | +{ | |
1191 | + char *policy; | |
1192 | + | |
1193 | + policy = "100 acl set_priority\n" | |
1194 | + "0 allow task.uid=0\n" | |
1195 | + "1 deny\n"; | |
1196 | + set(policy); | |
1197 | + check(policy, nice(0) == 0); | |
1198 | + unset(policy); | |
1199 | + | |
1200 | + policy = "100 acl set_priority\n" | |
1201 | + "0 allow task.uid=task.gid task.type!=execute_handler\n" | |
1202 | + "1 deny\n"; | |
1203 | + set(policy); | |
1204 | + check(policy, nice(0) == 0); | |
1205 | + unset(policy); | |
1206 | + | |
1207 | + policy = "100 acl set_priority\n" | |
1208 | + "0 deny task.uid=0\n" | |
1209 | + "1 allow\n"; | |
1210 | + set(policy); | |
1211 | + check(policy, nice(0) == EOF); | |
1212 | + unset(policy); | |
1213 | + | |
1214 | + policy = "100 acl set_priority\n" | |
1215 | + "0 allow task.uid=task.gid task.type=execute_handler\n" | |
1216 | + "1 deny\n"; | |
1217 | + set(policy); | |
1218 | + check(policy, nice(0) == EOF); | |
1219 | + unset(policy); | |
1220 | +} | |
1221 | + | |
1222 | +static void detach_init(void) | |
1223 | +{ | |
1224 | + ptrace(PTRACE_DETACH, 1, NULL, NULL); | |
1225 | + kill(1, SIGCONT); | |
1226 | + sleep(1); | |
1227 | +} | |
1228 | + | |
1229 | +static void test_ptrace(void) | |
1230 | +{ | |
1231 | + char *policy; | |
1232 | + | |
1233 | + policy = "100 acl ptrace\n" | |
1234 | + "0 allow cmd=1 domain!=\"foo\"\n" | |
1235 | + "0 allow cmd=17\n" | |
1236 | + "1 deny\n"; | |
1237 | + set(policy); | |
1238 | + check(policy, ptrace(PTRACE_ATTACH, 1, NULL, NULL) == EOF); | |
1239 | + unset(policy); | |
1240 | + detach_init(); | |
1241 | + | |
1242 | + policy = "100 acl ptrace\n" | |
1243 | + "0 allow cmd=16 domain=\"foo\"\n" | |
1244 | + "0 allow cmd=17\n" | |
1245 | + "1 deny\n"; | |
1246 | + set(policy); | |
1247 | + check(policy, ptrace(PTRACE_ATTACH, 1, NULL, NULL) == EOF); | |
1248 | + unset(policy); | |
1249 | + detach_init(); | |
1250 | + | |
1251 | + policy = "100 acl ptrace\n" | |
1252 | + "0 allow cmd=16 domain!=\"foo\"\n" | |
1253 | + "0 allow cmd=17\n" | |
1254 | + "1 deny\n"; | |
1255 | + set(policy); | |
1256 | + check(policy, ptrace(PTRACE_ATTACH, 1, NULL, NULL) == 0); | |
1257 | + unset(policy); | |
1258 | + detach_init(); | |
1259 | + | |
1260 | + policy = "string_group DOMAINS <init>\n" | |
1261 | + "100 acl ptrace\n" | |
1262 | + "0 allow cmd=16 domain=@DOMAINS\n" | |
1263 | + "0 allow cmd=17\n" | |
1264 | + "1 deny\n"; | |
1265 | + set(policy); | |
1266 | + check(policy, ptrace(PTRACE_ATTACH, 1, NULL, NULL) == 0); | |
1267 | + unset2(policy); | |
1268 | + detach_init(); | |
1269 | + | |
1270 | + policy = "string_group DOMAINS <init>\n" | |
1271 | + "100 acl ptrace\n" | |
1272 | + "0 allow cmd=16 domain!=@DOMAINS\n" | |
1273 | + "0 allow cmd=17\n" | |
1274 | + "1 deny\n"; | |
1275 | + set(policy); | |
1276 | + check(policy, ptrace(PTRACE_ATTACH, 1, NULL, NULL) == EOF); | |
1277 | + unset2(policy); | |
1278 | + detach_init(); | |
1279 | +} | |
1280 | + | |
1281 | +static void test_signal(void) | |
1282 | +{ | |
1283 | + char *policy; | |
1284 | + | |
1285 | + policy = "100 acl signal task.domain=\"domain200\"\n" | |
1286 | + "0 allow sig=1 task.uid=0\n" | |
1287 | + "1 deny\n"; | |
1288 | + set(policy); | |
1289 | + check(policy, kill(1, 1) == 0); | |
1290 | + unset(policy); | |
1291 | + | |
1292 | + policy = "100 acl signal task.domain=\"domain200\"\n" | |
1293 | + "0 allow sig!=1 task.uid=0\n" | |
1294 | + "1 deny\n"; | |
1295 | + set(policy); | |
1296 | + check(policy, kill(1, 1) == EOF); | |
1297 | + unset(policy); | |
1298 | + | |
1299 | + policy = "100 acl signal\n" | |
1300 | + "0 allow task.domain!=\"domain200\"\n" | |
1301 | + "0 allow sig=1\n" | |
1302 | + "1 deny\n"; | |
1303 | + set(policy); | |
1304 | + check(policy, kill(1, 1) == 0); | |
1305 | + unset(policy); | |
1306 | + | |
1307 | + policy = "100 acl signal\n" | |
1308 | + "0 deny task.domain=\"domain200\"\n" | |
1309 | + "0 allow\n"; | |
1310 | + set(policy); | |
1311 | + check(policy, kill(1, 1) == EOF); | |
1312 | + unset(policy); | |
1313 | + | |
1314 | + policy = "100 acl signal\n" | |
1315 | + "0 deny sig=1 task.domain=\"domain200\"\n" | |
1316 | + "0 allow\n"; | |
1317 | + set(policy); | |
1318 | + check(policy, kill(1, 1) == EOF); | |
1319 | + unset(policy); | |
1320 | +} | |
1321 | + | |
1322 | +static int fork_exec(char *envp[]) | |
1323 | +{ | |
1324 | + int ret_ignored; | |
1325 | + int pipe_fd[2] = { EOF, EOF }; | |
1326 | + int err = 0; | |
1327 | + pid_t pid; | |
1328 | + if (pipe(pipe_fd)) { | |
1329 | + fprintf(stderr, "Err: %s(%d)\n", strerror(err), err); | |
1330 | + exit(1); | |
1331 | + } | |
1332 | + pid = fork(); | |
1333 | + if (pid == 0) { | |
1334 | + char *argv[2] = { "/bin/true", NULL }; | |
1335 | + execve("/bin/true", argv, envp); | |
1336 | + err = errno; | |
1337 | + ret_ignored = write(pipe_fd[1], &err, sizeof(err)); | |
1338 | + _exit(0); | |
1339 | + } | |
1340 | + close(pipe_fd[1]); | |
1341 | + ret_ignored = read(pipe_fd[0], &err, sizeof(err)); | |
1342 | + close(pipe_fd[0]); | |
1343 | + wait(NULL); | |
1344 | + errno = err; | |
1345 | + return err ? EOF : 0; | |
1346 | +} | |
1347 | + | |
1348 | +static void test_environ(void) | |
1349 | +{ | |
1350 | + char *policy; | |
1351 | + char *envp[2]; | |
1352 | + envp[1] = NULL; | |
1353 | + | |
1354 | + policy = "100 acl environ name=\"PATH2\"\n" | |
1355 | + "0 allow value=\"/\"\n" | |
1356 | + "1 deny\n"; | |
1357 | + set(policy); | |
1358 | + envp[0] = "PATH2=/"; | |
1359 | + check(policy, fork_exec(envp) == 0); | |
1360 | + unset(policy); | |
1361 | + | |
1362 | + policy = "100 acl environ name=\"PATH2\"\n" | |
1363 | + "0 allow value!=\"/\"\n" | |
1364 | + "1 deny\n"; | |
1365 | + set(policy); | |
1366 | + envp[0] = "PATH2=/"; | |
1367 | + check(policy, fork_exec(envp) == EOF); | |
1368 | + unset(policy); | |
1369 | + | |
1370 | + policy = "100 acl environ name=\"PATH2\"\n" | |
1371 | + "0 deny value!=\"/\"\n" | |
1372 | + "1 allow\n"; | |
1373 | + set(policy); | |
1374 | + envp[0] = "PATH2=/"; | |
1375 | + check(policy, fork_exec(envp) == 0); | |
1376 | + unset(policy); | |
1377 | + | |
1378 | + policy = "100 acl environ name=\"PATH2\"\n" | |
1379 | + "0 deny value=\"/\"\n" | |
1380 | + "1 allow\n"; | |
1381 | + set(policy); | |
1382 | + envp[0] = "PATH2=/"; | |
1383 | + check(policy, fork_exec(envp) == EOF); | |
1384 | + unset(policy); | |
1385 | + | |
1386 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1387 | + "0 allow envp[\"PATH2\"]=\"/\"\n" | |
1388 | + "1 deny\n"; | |
1389 | + set(policy); | |
1390 | + envp[0] = "PATH2=/"; | |
1391 | + check(policy, fork_exec(envp) == 0); | |
1392 | + unset(policy); | |
1393 | + | |
1394 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1395 | + "0 allow envp[\"PATH2\"]!=\"/\"\n" | |
1396 | + "1 deny\n"; | |
1397 | + set(policy); | |
1398 | + envp[0] = "PATH2=/"; | |
1399 | + check(policy, fork_exec(envp) == EOF); | |
1400 | + unset(policy); | |
1401 | + | |
1402 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1403 | + "0 allow envp[\"PATH2\"]!=NULL\n" | |
1404 | + "1 deny\n"; | |
1405 | + set(policy); | |
1406 | + envp[0] = "PATH2"; | |
1407 | + check(policy, fork_exec(envp) == 0); | |
1408 | + unset(policy); | |
1409 | + | |
1410 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1411 | + "0 allow envp[\"PATH2\"]!=NULL\n" | |
1412 | + "1 deny\n"; | |
1413 | + set(policy); | |
1414 | + envp[0] = "PATH2="; | |
1415 | + check(policy, fork_exec(envp) == 0); | |
1416 | + unset(policy); | |
1417 | + | |
1418 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1419 | + "0 allow envp[\"PATH2\"]!=NULL\n" | |
1420 | + "1 deny\n"; | |
1421 | + set(policy); | |
1422 | + envp[0] = "PATH2=/"; | |
1423 | + check(policy, fork_exec(envp) == 0); | |
1424 | + unset(policy); | |
1425 | + | |
1426 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1427 | + "0 allow envp[\"PATH2\"]=NULL\n" | |
1428 | + "1 deny\n"; | |
1429 | + set(policy); | |
1430 | + envp[0] = "PATH2"; | |
1431 | + check(policy, fork_exec(envp) == EOF); | |
1432 | + unset(policy); | |
1433 | + | |
1434 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1435 | + "0 allow envp[\"PATH2\"]=NULL\n" | |
1436 | + "1 deny\n"; | |
1437 | + set(policy); | |
1438 | + envp[0] = "PATH2="; | |
1439 | + check(policy, fork_exec(envp) == EOF); | |
1440 | + unset(policy); | |
1441 | + | |
1442 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1443 | + "0 allow envp[\"PATH2\"]=NULL\n" | |
1444 | + "1 deny\n"; | |
1445 | + set(policy); | |
1446 | + envp[0] = "PATH2=/"; | |
1447 | + check(policy, fork_exec(envp) == EOF); | |
1448 | + unset(policy); | |
1449 | + | |
1450 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1451 | + "0 allow envp[\"\"]=NULL\n" | |
1452 | + "1 deny\n"; | |
1453 | + set(policy); | |
1454 | + envp[0] = ""; | |
1455 | + check(policy, fork_exec(envp) == EOF); | |
1456 | + unset(policy); | |
1457 | + | |
1458 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1459 | + "0 allow envp[\"\"]!=NULL\n" | |
1460 | + "1 deny\n"; | |
1461 | + set(policy); | |
1462 | + envp[0] = ""; | |
1463 | + check(policy, fork_exec(envp) == 0); | |
1464 | + unset(policy); | |
1465 | + | |
1466 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1467 | + "0 allow envp[\"\"]!=NULL\n" | |
1468 | + "1 deny\n"; | |
1469 | + set(policy); | |
1470 | + envp[0] = "="; | |
1471 | + check(policy, fork_exec(envp) == 0); | |
1472 | + unset(policy); | |
1473 | + | |
1474 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1475 | + "0 allow envp[\"\"]!=NULL\n" | |
1476 | + "1 deny\n"; | |
1477 | + set(policy); | |
1478 | + envp[0] = "=/"; | |
1479 | + check(policy, fork_exec(envp) == 0); | |
1480 | + unset(policy); | |
1481 | +} | |
1482 | + | |
1483 | +static int fork_exec2(char *argv[], char *envp[]) | |
1484 | +{ | |
1485 | + int ret_ignored; | |
1486 | + int pipe_fd[2] = { EOF, EOF }; | |
1487 | + int err = 0; | |
1488 | + pid_t pid; | |
1489 | + if (pipe(pipe_fd)) { | |
1490 | + fprintf(stderr, "Err: %s(%d)\n", strerror(err), err); | |
1491 | + exit(1); | |
1492 | + } | |
1493 | + pid = fork(); | |
1494 | + if (pid == 0) { | |
1495 | + execve("/bin/true", argv, envp); | |
1496 | + err = errno; | |
1497 | + ret_ignored = write(pipe_fd[1], &err, sizeof(err)); | |
1498 | + _exit(0); | |
1499 | + } | |
1500 | + close(pipe_fd[1]); | |
1501 | + ret_ignored = read(pipe_fd[0], &err, sizeof(err)); | |
1502 | + close(pipe_fd[0]); | |
1503 | + wait(NULL); | |
1504 | + errno = err; | |
1505 | + return err ? EOF : 0; | |
1506 | +} | |
1507 | + | |
1508 | +static void test_file_execute(void) | |
1509 | +{ | |
1510 | + char *policy; | |
1511 | + char *argv[5]; | |
1512 | + char *envp[5]; | |
1513 | + memset(argv, 0, sizeof(argv)); | |
1514 | + memset(envp, 0, sizeof(envp)); | |
1515 | + | |
1516 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1517 | + "0 allow argc=1\n" | |
1518 | + "1 deny\n"; | |
1519 | + set(policy); | |
1520 | + argv[0]="true"; | |
1521 | + check(policy, fork_exec2(argv, envp) == 0); | |
1522 | + unset(policy); | |
1523 | + | |
1524 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1525 | + "0 allow argc!=1\n" | |
1526 | + "1 deny\n"; | |
1527 | + set(policy); | |
1528 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1529 | + unset(policy); | |
1530 | + | |
1531 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1532 | + "0 deny argc!=1\n" | |
1533 | + "1 allow\n"; | |
1534 | + set(policy); | |
1535 | + check(policy, fork_exec2(argv, envp) == 0); | |
1536 | + unset(policy); | |
1537 | + | |
1538 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1539 | + "0 deny argc=1\n" | |
1540 | + "1 allow\n"; | |
1541 | + set(policy); | |
1542 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1543 | + unset(policy); | |
1544 | + | |
1545 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1546 | + "0 deny argv[0]!=\"true\"\n" | |
1547 | + "1 allow\n"; | |
1548 | + set(policy); | |
1549 | + check(policy, fork_exec2(argv, envp) == 0); | |
1550 | + unset(policy); | |
1551 | + | |
1552 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1553 | + "0 deny argv[0]=\"true\"\n" | |
1554 | + "1 allow\n"; | |
1555 | + set(policy); | |
1556 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1557 | + unset(policy); | |
1558 | + | |
1559 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1560 | + "0 allow argv[0]!=\"true\"\n" | |
1561 | + "1 deny\n"; | |
1562 | + set(policy); | |
1563 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1564 | + unset(policy); | |
1565 | + | |
1566 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1567 | + "0 allow argv[0]=\"true\"\n" | |
1568 | + "1 deny\n"; | |
1569 | + set(policy); | |
1570 | + check(policy, fork_exec2(argv, envp) == 0); | |
1571 | + unset(policy); | |
1572 | + | |
1573 | + policy = "string_group EXEC_ARGV0 false\n" | |
1574 | + "string_group EXEC_ARGV0 true\n" | |
1575 | + "100 acl execute path=\"/bin/true\"\n" | |
1576 | + "0 deny argv[0]!=@EXEC_ARGV0\n" | |
1577 | + "1 allow\n"; | |
1578 | + set(policy); | |
1579 | + check(policy, fork_exec2(argv, envp) == 0); | |
1580 | + unset2(policy); | |
1581 | + | |
1582 | + policy = "string_group EXEC_ARGV0 false\n" | |
1583 | + "string_group EXEC_ARGV0 true\n" | |
1584 | + "100 acl execute path=\"/bin/true\"\n" | |
1585 | + "0 deny argv[0]=@EXEC_ARGV0\n" | |
1586 | + "1 allow\n"; | |
1587 | + set(policy); | |
1588 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1589 | + unset2(policy); | |
1590 | + | |
1591 | + policy = "string_group EXEC_ARGV0 false\n" | |
1592 | + "string_group EXEC_ARGV0 true\n" | |
1593 | + "100 acl execute path=\"/bin/true\"\n" | |
1594 | + "0 allow argv[0]!=@EXEC_ARGV0\n" | |
1595 | + "1 deny\n"; | |
1596 | + set(policy); | |
1597 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1598 | + unset2(policy); | |
1599 | + | |
1600 | + policy = "string_group EXEC_ARGV0 false\n" | |
1601 | + "string_group EXEC_ARGV0 true\n" | |
1602 | + "100 acl execute path=\"/bin/true\"\n" | |
1603 | + "0 allow argv[0]=@EXEC_ARGV0\n" | |
1604 | + "1 deny\n"; | |
1605 | + set(policy); | |
1606 | + check(policy, fork_exec2(argv, envp) == 0); | |
1607 | + unset2(policy); | |
1608 | + | |
1609 | + | |
1610 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1611 | + "0 allow envc=1\n" | |
1612 | + "1 deny\n"; | |
1613 | + set(policy); | |
1614 | + envp[0]="PATH=/"; | |
1615 | + check(policy, fork_exec2(argv, envp) == 0); | |
1616 | + unset(policy); | |
1617 | + | |
1618 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1619 | + "0 allow envc!=1\n" | |
1620 | + "1 deny\n"; | |
1621 | + set(policy); | |
1622 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1623 | + unset(policy); | |
1624 | + | |
1625 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1626 | + "0 deny envc!=1\n" | |
1627 | + "1 allow\n"; | |
1628 | + set(policy); | |
1629 | + check(policy, fork_exec2(argv, envp) == 0); | |
1630 | + unset(policy); | |
1631 | + | |
1632 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1633 | + "0 deny envc=1\n" | |
1634 | + "1 allow\n"; | |
1635 | + set(policy); | |
1636 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1637 | + unset(policy); | |
1638 | + | |
1639 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1640 | + "0 deny envp[\"PATH\"]!=\"/\"\n" | |
1641 | + "1 allow\n"; | |
1642 | + set(policy); | |
1643 | + check(policy, fork_exec2(argv, envp) == 0); | |
1644 | + unset(policy); | |
1645 | + | |
1646 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1647 | + "0 deny envp[\"PATH\"]=\"/\"\n" | |
1648 | + "1 allow\n"; | |
1649 | + set(policy); | |
1650 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1651 | + unset(policy); | |
1652 | + | |
1653 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1654 | + "0 allow envp[\"PATH\"]!=\"/\"\n" | |
1655 | + "1 deny\n"; | |
1656 | + set(policy); | |
1657 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1658 | + unset(policy); | |
1659 | + | |
1660 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1661 | + "0 allow envp[\"PATH\"]=\"/\"\n" | |
1662 | + "1 deny\n"; | |
1663 | + set(policy); | |
1664 | + check(policy, fork_exec2(argv, envp) == 0); | |
1665 | + unset(policy); | |
1666 | + | |
1667 | + policy = "string_group PATH_VALUES /bin\n" | |
1668 | + "string_group PATH_VALUES /\n" | |
1669 | + "string_group PATH_VALUES /sbin\n" | |
1670 | + "100 acl execute path=\"/bin/true\"\n" | |
1671 | + "0 deny envp[\"PATH\"]!=@PATH_VALUES\n" | |
1672 | + "1 allow\n"; | |
1673 | + set(policy); | |
1674 | + check(policy, fork_exec2(argv, envp) == 0); | |
1675 | + unset2(policy); | |
1676 | + | |
1677 | + policy = "string_group PATH_VALUES /bin\n" | |
1678 | + "string_group PATH_VALUES /\n" | |
1679 | + "string_group PATH_VALUES /sbin\n" | |
1680 | + "100 acl execute path=\"/bin/true\"\n" | |
1681 | + "0 deny envp[\"PATH\"]=@PATH_VALUES\n" | |
1682 | + "1 allow\n"; | |
1683 | + set(policy); | |
1684 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1685 | + unset2(policy); | |
1686 | + | |
1687 | + policy = "string_group PATH_VALUES /bin\n" | |
1688 | + "string_group PATH_VALUES /\n" | |
1689 | + "string_group PATH_VALUES /sbin\n" | |
1690 | + "100 acl execute path=\"/bin/true\"\n" | |
1691 | + "0 allow envp[\"PATH\"]!=@PATH_VALUES\n" | |
1692 | + "1 deny\n"; | |
1693 | + set(policy); | |
1694 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1695 | + unset2(policy); | |
1696 | + | |
1697 | + policy = "string_group PATH_VALUES /bin\n" | |
1698 | + "string_group PATH_VALUES /\n" | |
1699 | + "string_group PATH_VALUES /sbin\n" | |
1700 | + "100 acl execute path=\"/bin/true\"\n" | |
1701 | + "0 allow envp[\"PATH\"]=@PATH_VALUES\n" | |
1702 | + "1 deny\n"; | |
1703 | + set(policy); | |
1704 | + check(policy, fork_exec2(argv, envp) == 0); | |
1705 | + unset2(policy); | |
1706 | + | |
1707 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1708 | + "0 deny envp[\"PATH\"]!=NULL\n" | |
1709 | + "1 allow\n"; | |
1710 | + set(policy); | |
1711 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1712 | + unset(policy); | |
1713 | + | |
1714 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1715 | + "0 deny envp[\"PATH\"]=NULL\n" | |
1716 | + "1 allow\n"; | |
1717 | + set(policy); | |
1718 | + check(policy, fork_exec2(argv, envp) == 0); | |
1719 | + unset(policy); | |
1720 | + | |
1721 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1722 | + "0 allow envp[\"PATH\"]!=NULL\n" | |
1723 | + "1 deny\n"; | |
1724 | + set(policy); | |
1725 | + check(policy, fork_exec2(argv, envp) == 0); | |
1726 | + unset(policy); | |
1727 | + | |
1728 | + policy = "100 acl execute path=\"/bin/true\"\n" | |
1729 | + "0 allow envp[\"PATH\"]=NULL\n" | |
1730 | + "1 deny\n"; | |
1731 | + set(policy); | |
1732 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1733 | + unset(policy); | |
1734 | +} | |
1735 | + | |
1736 | +int main(int argc, char *argv[]) | |
1737 | +{ | |
1738 | + fp = fopen("/proc/caitsith/policy", "w"); | |
1739 | + if (!fp) { | |
1740 | + fprintf(stderr, " Can't open /proc/caitsith/policy\n"); | |
1741 | + return 1; | |
1742 | + } | |
1743 | + fprintf(fp, "quota audit[0]" | |
1744 | + " allowed=1024 unmatched=1024 denied=1024\n"); | |
1745 | + | |
1746 | + test_task_transition(); | |
1747 | + test_file_read(); | |
1748 | + test_file_write(); | |
1749 | + test_file_create(); | |
1750 | + test_file_unlink(); | |
1751 | + test_file_link(); | |
1752 | + test_file_rename(); | |
1753 | + test_network_inet_stream(); | |
1754 | + test_network_inet_dgram(); | |
1755 | + test_network_inet_raw(); | |
1756 | + test_network_inet6_stream(); | |
1757 | + test_network_inet6_dgram(); | |
1758 | + test_capability(); | |
1759 | + test_ptrace(); | |
1760 | + test_signal(); | |
1761 | + test_environ(); | |
1762 | + test_file_execute(); | |
1763 | + return 0; | |
1764 | +} |
@@ -0,0 +1,394 @@ | ||
1 | +#include <stdio.h> | |
2 | +#include <string.h> | |
3 | +#include <sys/types.h> | |
4 | +#include <sys/stat.h> | |
5 | +#include <fcntl.h> | |
6 | +#include <unistd.h> | |
7 | +#include <linux/kdev_t.h> | |
8 | +#include <sys/mount.h> | |
9 | +#include <sys/ioctl.h> | |
10 | + | |
11 | +#ifndef MS_MOVE | |
12 | +#define MS_MOVE 8192 | |
13 | +#endif | |
14 | +#ifndef MS_UNBINDABLE | |
15 | +#define MS_UNBINDABLE (1<<17) | |
16 | +#endif | |
17 | +#ifndef MS_PRIVATE | |
18 | +#define MS_PRIVATE (1<<18) | |
19 | +#endif | |
20 | +#ifndef MS_SLAVE | |
21 | +#define MS_SLAVE (1<<19) | |
22 | +#endif | |
23 | +#ifndef MS_SHARED | |
24 | +#define MS_SHARED (1<<20) | |
25 | +#endif | |
26 | + | |
27 | +#include <asm/unistd.h> | |
28 | +static inline int pivot_root(const char *new_root, const char *put_old) | |
29 | +{ | |
30 | + return syscall(__NR_pivot_root, new_root, put_old); | |
31 | +} | |
32 | + | |
33 | +static _Bool debug = 0; | |
34 | + | |
35 | +static char *read_log(const char *expected_result, const char *expected_action) | |
36 | +{ | |
37 | + static int fd = EOF; | |
38 | + static char buffer[16384]; | |
39 | + if (fd == EOF) | |
40 | + fd = open("/proc/caitsith/audit", O_RDONLY); | |
41 | + memset(buffer, 0, sizeof(buffer)); | |
42 | + while (buffer[0] = '\0', read(fd, buffer, sizeof(buffer) - 1) > 0) { | |
43 | + char *cp1; | |
44 | + char *cp2; | |
45 | + if (debug) | |
46 | + printf("Got '%s'\n", buffer); | |
47 | + cp1 = strstr(buffer, " / "); | |
48 | + if (buffer[0] != '#' || !cp1 || !strchr(buffer, '\n')) { | |
49 | + fprintf(stderr, | |
50 | + "Expected complete audit log, got '%s'\n", | |
51 | + buffer); | |
52 | + return NULL; | |
53 | + } | |
54 | + *cp1 = '\0'; | |
55 | + if (!strstr(buffer, expected_result)) | |
56 | + continue; | |
57 | + cp1 += 3; | |
58 | + cp2 = strchr(cp1, ' '); | |
59 | + if (!cp2) { | |
60 | + fprintf(stderr, | |
61 | + "Expected complete audit log, got '%s'\n", | |
62 | + cp1); | |
63 | + return NULL; | |
64 | + } | |
65 | + *cp2++ = '\0'; | |
66 | + if (strcmp(expected_action, cp1)) | |
67 | + continue; | |
68 | + return cp2; | |
69 | + } | |
70 | + fprintf(stderr, "Expected '%s' '%s', found none\n", | |
71 | + expected_result, expected_action); | |
72 | + return NULL; | |
73 | +} | |
74 | + | |
75 | +static void create_dummy(const char *path) | |
76 | +{ | |
77 | + close(open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600)); | |
78 | +} | |
79 | + | |
80 | +static void test_execute(void) | |
81 | +{ | |
82 | + char *args[3] = { "null", "--help", NULL }; | |
83 | + char *envs[3] = { "PATH=/", "HOME=/", NULL }; | |
84 | + create_dummy("/tmp/null"); | |
85 | + chmod("/tmp/null", 0700); | |
86 | + execve("/tmp/null", args, envs); | |
87 | +} | |
88 | + | |
89 | +static void test_read(void) | |
90 | +{ | |
91 | + close(open("/dev/null", O_RDONLY)); | |
92 | +} | |
93 | + | |
94 | +static void test_write(void) | |
95 | +{ | |
96 | + close(open("/dev/null", O_WRONLY)); | |
97 | +} | |
98 | + | |
99 | +static void test_append(void) | |
100 | +{ | |
101 | + close(open("/dev/null", O_WRONLY | O_APPEND)); | |
102 | +} | |
103 | + | |
104 | +static void test_create(void) | |
105 | +{ | |
106 | + unlink("/tmp/null"); | |
107 | + create_dummy("/tmp/null"); | |
108 | +} | |
109 | + | |
110 | +static void test_unlink(void) | |
111 | +{ | |
112 | + create_dummy("/tmp/null"); | |
113 | + unlink("/tmp/null"); | |
114 | +} | |
115 | + | |
116 | +static void test_getattr(void) | |
117 | +{ | |
118 | + struct stat buf; | |
119 | + create_dummy("/tmp/null"); | |
120 | + stat("/tmp/null", &buf); | |
121 | +} | |
122 | + | |
123 | +static void test_mkdir(void) | |
124 | +{ | |
125 | + rmdir("/tmp/nulldir"); | |
126 | + mkdir("/tmp/nulldir", 0755); | |
127 | +} | |
128 | + | |
129 | +static void test_rmdir(void) | |
130 | +{ | |
131 | + mkdir("/tmp/nulldir", 0755); | |
132 | + rmdir("/tmp/nulldir"); | |
133 | +} | |
134 | + | |
135 | +static void test_mkfifo(void) | |
136 | +{ | |
137 | + unlink("/tmp/null"); | |
138 | + mknod("/tmp/null", S_IFIFO, 0); | |
139 | +} | |
140 | + | |
141 | +static void test_mksock(void) | |
142 | +{ | |
143 | + unlink("/tmp/null"); | |
144 | + mknod("/tmp/null", S_IFSOCK, 0); | |
145 | +} | |
146 | + | |
147 | +static void test_truncate(void) | |
148 | +{ | |
149 | + create_dummy("/tmp/null"); | |
150 | + truncate("/tmp/null", 0); | |
151 | +} | |
152 | + | |
153 | +static void test_symlink(void) | |
154 | +{ | |
155 | + unlink("/tmp/null"); | |
156 | + symlink("symlink'starget", "/tmp/null"); | |
157 | +} | |
158 | + | |
159 | +static void test_mkblock(void) | |
160 | +{ | |
161 | + unlink("/tmp/null"); | |
162 | + mknod("/tmp/null", S_IFBLK, MKDEV(1, 0)); | |
163 | +} | |
164 | + | |
165 | +static void test_mkchar(void) | |
166 | +{ | |
167 | + unlink("/tmp/null"); | |
168 | + mknod("/tmp/null", S_IFCHR, MKDEV(1, 3)); | |
169 | +} | |
170 | + | |
171 | +static void test_link(void) | |
172 | +{ | |
173 | + create_dummy("/tmp/link"); | |
174 | + unlink("/tmp/newlink"); | |
175 | + link("/tmp/link", "/tmp/newlink"); | |
176 | +} | |
177 | + | |
178 | +static void test_rename(void) | |
179 | +{ | |
180 | + link("/dev/null", "/dev/null0"); | |
181 | + rename("/dev/null0", "/dev/null1"); | |
182 | + unlink("/dev/null1"); | |
183 | +} | |
184 | + | |
185 | +static void test_chmod(void) | |
186 | +{ | |
187 | + chmod("/dev/null", 0666); | |
188 | +} | |
189 | + | |
190 | +static void test_chown(void) | |
191 | +{ | |
192 | + chown("/dev/null", 0, -1); | |
193 | +} | |
194 | + | |
195 | +static void test_chgrp(void) | |
196 | +{ | |
197 | + chown("/dev/null", -1, 0); | |
198 | +} | |
199 | + | |
200 | +static void test_ioctl(void) | |
201 | +{ | |
202 | + int fd = open("/dev/null", 3); | |
203 | + ioctl(fd, 0); | |
204 | + close(fd); | |
205 | +} | |
206 | + | |
207 | +static void test_chroot(void) | |
208 | +{ | |
209 | + chroot("/"); | |
210 | +} | |
211 | + | |
212 | +static void test_mount1(void) | |
213 | +{ | |
214 | + mount(NULL, "/tmp", "tmpfs", 0, "size=10%,uid=0,gid=0"); | |
215 | + umount("/tmp"); | |
216 | +} | |
217 | + | |
218 | +static void test_mount2(void) | |
219 | +{ | |
220 | + mount("/", "/", NULL, MS_BIND, NULL); | |
221 | +} | |
222 | + | |
223 | +static void test_mount3(void) | |
224 | +{ | |
225 | + mount("/", "/", NULL, MS_MOVE, NULL); | |
226 | +} | |
227 | + | |
228 | +static void test_mount4(void) | |
229 | +{ | |
230 | + mount(NULL, "/", NULL, MS_REMOUNT | MS_NOATIME, "errors=remount-ro"); | |
231 | +} | |
232 | + | |
233 | +static void test_unmount(void) | |
234 | +{ | |
235 | + umount2("/", 1); | |
236 | +} | |
237 | + | |
238 | +static void test_pivot_root(void) | |
239 | +{ | |
240 | + pivot_root("/", "/"); | |
241 | +} | |
242 | + | |
243 | +static _Bool check_policy(const char *policy, const char *decision, | |
244 | + const char *condition) | |
245 | +{ | |
246 | + static char buffer[16384]; | |
247 | + FILE *fp = fopen("/proc/caitsith/policy", "r"); | |
248 | + _Bool found = 0; | |
249 | + if (!fp) { | |
250 | + fprintf(stderr, "Can't read /proc/caitsith/policy interface.\n"); | |
251 | + return 0; | |
252 | + } | |
253 | + memset(buffer, 0, sizeof(buffer)); | |
254 | + while (fgets(buffer, sizeof(buffer) - 1, fp)) { | |
255 | + if (strstr(buffer, decision) && strstr(buffer, condition)) { | |
256 | + found = 1; | |
257 | + break; | |
258 | + } | |
259 | + } | |
260 | + fclose(fp); | |
261 | + if (found) { | |
262 | + printf("%s %s%s\n", policy, decision, condition); | |
263 | + return 1; | |
264 | + } | |
265 | + fprintf(stderr, "Can't find %s %s%s\n", | |
266 | + policy, decision, condition); | |
267 | + return 0; | |
268 | +} | |
269 | + | |
270 | +int main(int argc, char *argv[]) | |
271 | +{ | |
272 | + unsigned int i; | |
273 | + char buffer[16384]; | |
274 | + struct { | |
275 | + const char *action; | |
276 | + void (*func) (void); | |
277 | + } testcases[] = { | |
278 | + { "execute", test_execute }, | |
279 | + { "read", test_read }, | |
280 | + { "write", test_write }, | |
281 | + { "append", test_append }, | |
282 | + { "create", test_create }, | |
283 | + { "unlink", test_unlink }, | |
284 | + { "getattr", test_getattr }, | |
285 | + { "mkdir", test_mkdir }, | |
286 | + { "rmdir", test_rmdir }, | |
287 | + { "mkfifo", test_mkfifo }, | |
288 | + { "mksock", test_mksock }, | |
289 | + { "truncate", test_truncate }, | |
290 | + { "symlink", test_symlink }, | |
291 | + { "mkblock", test_mkblock }, | |
292 | + { "mkchar", test_mkchar }, | |
293 | + { "link", test_link }, | |
294 | + { "rename", test_rename }, | |
295 | + { "chmod", test_chmod }, | |
296 | + { "chown", test_chown }, | |
297 | + { "chgrp", test_chgrp }, | |
298 | + { "ioctl", test_ioctl }, | |
299 | + { "chroot", test_chroot }, | |
300 | + { "mount", test_mount1 }, | |
301 | + { "mount", test_mount2 }, | |
302 | + { "mount", test_mount3 }, | |
303 | + { "mount", test_mount4 }, | |
304 | + { "unmount", test_unmount }, | |
305 | + { "pivot_root", test_pivot_root }, | |
306 | + /* | |
307 | + acl inet_stream_bind | |
308 | + acl inet_stream_listen | |
309 | + acl inet_stream_connect | |
310 | + acl inet_stream_accept | |
311 | + acl inet_dgram_bind | |
312 | + acl inet_dgram_send | |
313 | + acl inet_dgram_recv | |
314 | + acl inet_raw_bind | |
315 | + acl inet_raw_send | |
316 | + acl inet_raw_recv | |
317 | + acl unix_stream_bind | |
318 | + acl unix_stream_listen | |
319 | + # acl unix_stream_connect | |
320 | + acl unix_stream_accept | |
321 | + acl unix_dgram_bind | |
322 | + acl unix_dgram_send | |
323 | + acl unix_dgram_recv | |
324 | + acl unix_seqpacket_bind | |
325 | + acl unix_seqpacket_listen | |
326 | + acl unix_seqpacket_connect | |
327 | + acl unix_seqpacket_accept | |
328 | + # acl environ | |
329 | + acl ptrace | |
330 | + acl signal | |
331 | + acl modify_policy | |
332 | + # acl use_netlink_socket | |
333 | + acl use_packet_socket | |
334 | + acl use_reboot | |
335 | + acl use_vhangup | |
336 | + acl set_time | |
337 | + acl set_priority | |
338 | + acl set_hostname | |
339 | + acl use_kernel_module | |
340 | + acl use_new_kernel | |
341 | + # acl auto_domain_transition | |
342 | + acl manual_domain_transition | |
343 | + */ | |
344 | + { NULL, NULL }, | |
345 | + }; | |
346 | + int fd_out = open("/proc/caitsith/policy", O_WRONLY); | |
347 | + char *cp1; | |
348 | + char *cp2; | |
349 | + memset(buffer, 0, sizeof(buffer)); | |
350 | + if (fd_out == EOF) { | |
351 | + fprintf(stderr, "Can't write /proc/caitsith/policy interface.\n"); | |
352 | + goto out; | |
353 | + } | |
354 | + cp1 = "POLICY_VERSION=20100903\n" | |
355 | + "quota memory audit 16777216\n" | |
356 | + "quota memory query 1048576\n" | |
357 | + "quota audit[1] allowed=1024 denied=1024 unmatched=1024\n"; | |
358 | + i = strlen(cp1); | |
359 | + if (write(fd_out, cp1, i) != i) { | |
360 | + fprintf(stderr, "Can't write /proc/caitsith/policy interface.\n"); | |
361 | + goto out; | |
362 | + } | |
363 | + for (i = 0; testcases[i].action; i++) { | |
364 | + int fd_in = open("/proc/caitsith/policy", O_RDONLY); | |
365 | + if (fd_in == EOF) { | |
366 | + fprintf(stderr, | |
367 | + "Can't read /proc/caitsith/policy interface.\n"); | |
368 | + goto out; | |
369 | + } | |
370 | + snprintf(buffer, sizeof(buffer) - 1, "0 acl %s task.pid=%u\n" | |
371 | + " audit 1\n", testcases[i].action, getpid()); | |
372 | + write(fd_out, buffer, strlen(buffer)); | |
373 | + testcases[i].func(); | |
374 | + cp2 = read_log("result=unmatched", testcases[i].action); | |
375 | + if (!cp2) | |
376 | + goto out; | |
377 | + cp1 = "0 deny "; | |
378 | + write(fd_out, cp1, strlen(cp1)); | |
379 | + write(fd_out, cp2, strlen(cp2)); | |
380 | + if (!check_policy(buffer, cp1, cp2)) | |
381 | + goto out; | |
382 | + testcases[i].func(); | |
383 | + cp2 = read_log("result=denied", testcases[i].action); | |
384 | + if (!cp2) | |
385 | + goto out; | |
386 | + snprintf(buffer, sizeof(buffer) - 1, | |
387 | + "delete 0 acl %s task.pid=%u\n", | |
388 | + testcases[i].action, getpid()); | |
389 | + write(fd_out, buffer, strlen(buffer)); | |
390 | + } | |
391 | + return 0; | |
392 | +out: | |
393 | + return 1; | |
394 | +} |
@@ -0,0 +1,21 @@ | ||
1 | +ALL_FILES = ccs_param_test ccs_log_test | |
2 | + | |
3 | +all: $(ALL_FILES) | |
4 | + | |
5 | +CC=gcc | |
6 | + | |
7 | +CFLAGS=-Wall -O2 | |
8 | + | |
9 | +# | |
10 | +# Tools for kernel testing. | |
11 | +# | |
12 | + | |
13 | +.c: | |
14 | + $(CC) $(CFLAGS) -o $@ $< | |
15 | + | |
16 | +# | |
17 | +# Delete all test programs. | |
18 | +# | |
19 | + | |
20 | +clean: | |
21 | + rm -f $(ALL_FILES) |
@@ -0,0 +1,86 @@ | ||
1 | +/* | |
2 | + * caitsithtools.h | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.1 2012/04/01 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#define _FILE_OFFSET_BITS 64 | |
24 | +#define _LARGEFILE_SOURCE | |
25 | +#define _LARGEFILE64_SOURCE | |
26 | +#define _GNU_SOURCE | |
27 | +#include <arpa/inet.h> | |
28 | +#include <asm/types.h> | |
29 | +#include <dirent.h> | |
30 | +#include <errno.h> | |
31 | +#include <fcntl.h> | |
32 | +#include <limits.h> | |
33 | +#include <stdio.h> | |
34 | +#include <stdlib.h> | |
35 | +#include <string.h> | |
36 | +#include <sys/file.h> | |
37 | +#include <sys/socket.h> | |
38 | +#include <sys/stat.h> | |
39 | +#include <sys/types.h> | |
40 | +#include <sys/un.h> | |
41 | +#include <time.h> | |
42 | +#include <unistd.h> | |
43 | +#include <stdarg.h> | |
44 | + | |
45 | +#define s8 __s8 | |
46 | +#define u8 __u8 | |
47 | +#define u16 __u16 | |
48 | +#define u32 __u32 | |
49 | +#define true 1 | |
50 | +#define false 0 | |
51 | + | |
52 | +/***** CONSTANTS DEFINITION START *****/ | |
53 | + | |
54 | +#define CCS_PROC_POLICY_DIR "/proc/caitsith/" | |
55 | +#define CCS_PROC_POLICY_POLICY "/proc/caitsith/policy" | |
56 | +#define CCS_PROC_POLICY_AUDIT "/proc/caitsith/audit" | |
57 | +#define CCS_PROC_POLICY_PROCESS_STATUS "/proc/caitsith/.process_status" | |
58 | +#define CCS_PROC_POLICY_QUERY "/proc/caitsith/query" | |
59 | + | |
60 | +/***** CONSTANTS DEFINITION END *****/ | |
61 | + | |
62 | +/***** STRUCTURES DEFINITION START *****/ | |
63 | + | |
64 | +/***** STRUCTURES DEFINITION END *****/ | |
65 | + | |
66 | +/***** PROTOTYPES DEFINITION START *****/ | |
67 | + | |
68 | +FILE *ccs_open_read(const char *filename); | |
69 | +FILE *ccs_open_write(const char *filename); | |
70 | +_Bool ccs_check_remote_host(void); | |
71 | +_Bool ccs_decode(const char *ascii, char *bin); | |
72 | +_Bool ccs_str_starts(char *str, const char *begin); | |
73 | +char *ccs_freadline(FILE *fp); | |
74 | +char *ccs_strdup(const char *string); | |
75 | +int ccs_open_stream(const char *filename); | |
76 | +void *ccs_malloc(const size_t size); | |
77 | +void *ccs_realloc(void *ptr, const size_t size); | |
78 | +void ccs_get(void); | |
79 | +void ccs_normalize_line(char *buffer); | |
80 | +void ccs_put(void); | |
81 | + | |
82 | +extern _Bool ccs_network_mode; | |
83 | +extern u16 ccs_network_port; | |
84 | +extern u32 ccs_network_ip; | |
85 | + | |
86 | +/***** PROTOTYPES DEFINITION END *****/ |
@@ -0,0 +1,324 @@ | ||
1 | +/* | |
2 | + * caitsith-queryd.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.1 2012/04/01 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | +#include "readline.h" | |
25 | + | |
26 | +/* Prototypes */ | |
27 | + | |
28 | +static void ccs_printw(const char *fmt, ...) | |
29 | + __attribute__ ((format(printf, 1, 2))); | |
30 | +static _Bool ccs_handle_query(unsigned int serial); | |
31 | + | |
32 | +/* Utility functions */ | |
33 | + | |
34 | +static void ccs_printw(const char *fmt, ...) | |
35 | +{ | |
36 | + va_list args; | |
37 | + int i; | |
38 | + int len; | |
39 | + char *buffer; | |
40 | + va_start(args, fmt); | |
41 | + len = vsnprintf((char *) &i, sizeof(i) - 1, fmt, args) + 16; | |
42 | + va_end(args); | |
43 | + buffer = ccs_malloc(len); | |
44 | + va_start(args, fmt); | |
45 | + len = vsnprintf(buffer, len, fmt, args); | |
46 | + va_end(args); | |
47 | + for (i = 0; i < len; i++) { | |
48 | + addch(buffer[i]); | |
49 | + refresh(); | |
50 | + } | |
51 | + free(buffer); | |
52 | +} | |
53 | + | |
54 | +static void ccs_send_keepalive(void) | |
55 | +{ | |
56 | + static time_t previous = 0; | |
57 | + time_t now = time(NULL); | |
58 | + if (previous != now || !previous) { | |
59 | + int ret_ignored; | |
60 | + previous = now; | |
61 | + ret_ignored = write(ccs_query_fd, "\n", 1); | |
62 | + } | |
63 | +} | |
64 | + | |
65 | +/* Variables */ | |
66 | + | |
67 | +static unsigned short int ccs_retries = 0; | |
68 | + | |
69 | +static FILE *ccs_policy_fp = NULL; | |
70 | +static int ccs_policy_fd = EOF; | |
71 | +#define CCS_MAX_READLINE_HISTORY 20 | |
72 | +static const char **ccs_readline_history = NULL; | |
73 | +static int ccs_readline_history_count = 0; | |
74 | +static char ccs_buffer[32768]; | |
75 | + | |
76 | +/* Main functions */ | |
77 | + | |
78 | +static _Bool ccs_handle_query(unsigned int serial) | |
79 | +{ | |
80 | + int c = 0; | |
81 | + int y; | |
82 | + int x; | |
83 | + int ret_ignored; | |
84 | + char *line = NULL; | |
85 | + static unsigned int prev_pid = 0; | |
86 | + unsigned int pid; | |
87 | + char qidbuf[128]; | |
88 | + char *cp = strstr(ccs_buffer, " global-pid="); | |
89 | + if (!cp || sscanf(cp + 13, "%u", &pid) != 1) { | |
90 | + ccs_printw("ERROR: Unsupported query.\n"); | |
91 | + return false; | |
92 | + } | |
93 | + cp = ccs_buffer + strlen(ccs_buffer); | |
94 | + if (*(cp - 1) != '\n') { | |
95 | + ccs_printw("ERROR: Unsupported query.\n"); | |
96 | + return false; | |
97 | + } | |
98 | + *(cp - 1) = '\0'; | |
99 | + if (pid != prev_pid) { | |
100 | + if (prev_pid) | |
101 | + ccs_printw("----------------------------------------" | |
102 | + "\n"); | |
103 | + prev_pid = pid; | |
104 | + } | |
105 | + ccs_printw("%s\n", ccs_buffer); | |
106 | + memset(qidbuf, 0, sizeof(qidbuf)); | |
107 | + snprintf(qidbuf, sizeof(qidbuf) - 1, "Q=%u\n", serial); | |
108 | + ccs_printw("Allow? ('Y'es/'N'o/'R'etry/'S'how policy/'A'dd to policy " | |
109 | + "and retry):"); | |
110 | + while (true) { | |
111 | + c = ccs_getch2(); | |
112 | + if (c == 'Y' || c == 'y' || c == 'N' || c == 'n' || c == 'R' || | |
113 | + c == 'r' || c == 'A' || c == 'a' || c == 'S' || c == 's') | |
114 | + break; | |
115 | + ccs_send_keepalive(); | |
116 | + } | |
117 | + ccs_printw("%c\n", c); | |
118 | + | |
119 | + if (c == 'S' || c == 's') { | |
120 | + if (ccs_network_mode) { | |
121 | + fprintf(ccs_policy_fp, "%s", qidbuf); | |
122 | + fputc(0, ccs_policy_fp); | |
123 | + fflush(ccs_policy_fp); | |
124 | + rewind(ccs_policy_fp); | |
125 | + while (1) { | |
126 | + char c; | |
127 | + if (fread(&c, 1, 1, ccs_policy_fp) != 1 || !c) | |
128 | + break; | |
129 | + addch(c); | |
130 | + refresh(); | |
131 | + ccs_send_keepalive(); | |
132 | + } | |
133 | + } else { | |
134 | + ret_ignored = write(ccs_policy_fd, qidbuf, | |
135 | + strlen(qidbuf)); | |
136 | + while (1) { | |
137 | + int i; | |
138 | + int len = read(ccs_policy_fd, ccs_buffer, | |
139 | + sizeof(ccs_buffer) - 1); | |
140 | + if (len <= 0) | |
141 | + break; | |
142 | + for (i = 0; i < len; i++) { | |
143 | + addch(ccs_buffer[i]); | |
144 | + refresh(); | |
145 | + } | |
146 | + ccs_send_keepalive(); | |
147 | + } | |
148 | + } | |
149 | + c = 'r'; | |
150 | + } | |
151 | + | |
152 | + /* Append to policy. */ | |
153 | + if (c != 'A' && c != 'a') | |
154 | + goto not_append; | |
155 | + c = 'r'; | |
156 | + getyx(stdscr, y, x); | |
157 | + cp = strstr(ccs_buffer, " / "); | |
158 | + if (!cp++) | |
159 | + return false; | |
160 | + cp = strchr(cp + 2, ' '); | |
161 | + if (!cp++) | |
162 | + return false; | |
163 | + cp = strchr(cp, ' '); | |
164 | + if (!cp++) | |
165 | + return false; | |
166 | + ccs_initial_readline_data = NULL; | |
167 | + ccs_readline_history_count = | |
168 | + ccs_add_history(cp, ccs_readline_history, | |
169 | + ccs_readline_history_count, | |
170 | + CCS_MAX_READLINE_HISTORY); | |
171 | + line = ccs_readline(y, 0, "Enter new entry> ", ccs_readline_history, | |
172 | + ccs_readline_history_count, 128000, 8); | |
173 | + scrollok(stdscr, TRUE); | |
174 | + ccs_printw("\n"); | |
175 | + if (!line || !*line) { | |
176 | + ccs_printw("None added.\n"); | |
177 | + goto not_append; | |
178 | + } | |
179 | + ccs_readline_history_count = | |
180 | + ccs_add_history(line, ccs_readline_history, | |
181 | + ccs_readline_history_count, | |
182 | + CCS_MAX_READLINE_HISTORY); | |
183 | + if (ccs_network_mode) { | |
184 | + fprintf(ccs_policy_fp, "%s%s\n", qidbuf, line); | |
185 | + fflush(ccs_policy_fp); | |
186 | + } else { | |
187 | + ret_ignored = write(ccs_policy_fd, qidbuf, strlen(qidbuf)); | |
188 | + ret_ignored = write(ccs_policy_fd, line, strlen(line)); | |
189 | + ret_ignored = write(ccs_policy_fd, "\n", 1); | |
190 | + } | |
191 | + ccs_printw("Added '%s'.\n", line); | |
192 | +not_append: | |
193 | + free(line); | |
194 | + /* Write answer. */ | |
195 | + if (c == 'Y' || c == 'y' || c == 'A' || c == 'a') | |
196 | + c = 1; | |
197 | + else if (c == 'R' || c == 'r') | |
198 | + c = 3; | |
199 | + else | |
200 | + c = 2; | |
201 | + snprintf(ccs_buffer, sizeof(ccs_buffer) - 1, "A%u=%u\n", serial, c); | |
202 | + ret_ignored = write(ccs_query_fd, ccs_buffer, strlen(ccs_buffer)); | |
203 | + ccs_printw("\n"); | |
204 | + return true; | |
205 | +} | |
206 | + | |
207 | +int main(int argc, char *argv[]) | |
208 | +{ | |
209 | + if (argc == 1) | |
210 | + goto ok; | |
211 | + { | |
212 | + char *cp = strchr(argv[1], ':'); | |
213 | + if (cp) { | |
214 | + *cp++ = '\0'; | |
215 | + ccs_network_ip = inet_addr(argv[1]); | |
216 | + ccs_network_port = htons(atoi(cp)); | |
217 | + ccs_network_mode = true; | |
218 | + if (!ccs_check_remote_host()) | |
219 | + return 1; | |
220 | + goto ok; | |
221 | + } | |
222 | + } | |
223 | + printf("Usage: %s [remote_ip:remote_port]\n\n", argv[0]); | |
224 | + printf("This program is used for granting access requests manually." | |
225 | + "\n"); | |
226 | + printf("This program shows access requests that are about to rejected " | |
227 | + "by the kernel's decision.\n"); | |
228 | + printf("If you answer before the kernel's decision taken effect, your " | |
229 | + "decision will take effect.\n"); | |
230 | + printf("You can use this program to respond to accidental access " | |
231 | + "requests triggered by non-routine tasks (such as restarting " | |
232 | + "daemons after updating).\n"); | |
233 | + printf("To terminate this program, use 'Ctrl-C'.\n"); | |
234 | + return 0; | |
235 | +ok: | |
236 | + if (ccs_network_mode) { | |
237 | + ccs_query_fd = ccs_open_stream("proc:query"); | |
238 | + ccs_policy_fp = ccs_open_write(CCS_PROC_POLICY_POLICY); | |
239 | + } else { | |
240 | + ccs_query_fd = open(CCS_PROC_POLICY_QUERY, O_RDWR); | |
241 | + ccs_policy_fd = open(CCS_PROC_POLICY_POLICY, O_RDWR); | |
242 | + } | |
243 | + if (ccs_query_fd == EOF) { | |
244 | + fprintf(stderr, | |
245 | + "You can't run this utility for this kernel.\n"); | |
246 | + return 1; | |
247 | + } else if (!ccs_network_mode && write(ccs_query_fd, "", 0) != 0) { | |
248 | + fprintf(stderr, "You need to register this program to %s to " | |
249 | + "run this program.\n", CCS_PROC_POLICY_POLICY); | |
250 | + return 1; | |
251 | + } | |
252 | + ccs_readline_history = ccs_malloc(CCS_MAX_READLINE_HISTORY * | |
253 | + sizeof(const char *)); | |
254 | + ccs_send_keepalive(); | |
255 | + initscr(); | |
256 | + cbreak(); | |
257 | + noecho(); | |
258 | + nonl(); | |
259 | + intrflush(stdscr, FALSE); | |
260 | + keypad(stdscr, TRUE); | |
261 | + clear(); | |
262 | + refresh(); | |
263 | + scrollok(stdscr, TRUE); | |
264 | + if (ccs_network_mode) { | |
265 | + const u32 ip = ntohl(ccs_network_ip); | |
266 | + ccs_printw("Monitoring /proc/caitsith/query via %u.%u.%u.%u:%u.", | |
267 | + (u8) (ip >> 24), (u8) (ip >> 16), (u8) (ip >> 8), | |
268 | + (u8) ip, ntohs(ccs_network_port)); | |
269 | + } else | |
270 | + ccs_printw("Monitoring /proc/caitsith/query ."); | |
271 | + ccs_printw(" Press Ctrl-C to terminate.\n\n"); | |
272 | + while (true) { | |
273 | + unsigned int serial; | |
274 | + char *cp; | |
275 | + /* Wait for query and read query. */ | |
276 | + memset(ccs_buffer, 0, sizeof(ccs_buffer)); | |
277 | + if (ccs_network_mode) { | |
278 | + int i; | |
279 | + int ret_ignored; | |
280 | + ret_ignored = write(ccs_query_fd, "", 1); | |
281 | + for (i = 0; i < sizeof(ccs_buffer) - 1; i++) { | |
282 | + if (read(ccs_query_fd, ccs_buffer + i, 1) != 1) | |
283 | + break; | |
284 | + if (!ccs_buffer[i]) | |
285 | + goto read_ok; | |
286 | + } | |
287 | + break; | |
288 | + } else { | |
289 | + fd_set rfds; | |
290 | + FD_ZERO(&rfds); | |
291 | + FD_SET(ccs_query_fd, &rfds); | |
292 | + select(ccs_query_fd + 1, &rfds, NULL, NULL, NULL); | |
293 | + if (!FD_ISSET(ccs_query_fd, &rfds)) | |
294 | + continue; | |
295 | + if (read(ccs_query_fd, ccs_buffer, | |
296 | + sizeof(ccs_buffer) - 1) <= 0) | |
297 | + continue; | |
298 | + } | |
299 | +read_ok: | |
300 | + cp = strchr(ccs_buffer, '\n'); | |
301 | + if (!cp) | |
302 | + continue; | |
303 | + *cp = '\0'; | |
304 | + | |
305 | + /* Get query number. */ | |
306 | + if (sscanf(ccs_buffer, "Q%u-%hu", &serial, &ccs_retries) != 2) | |
307 | + continue; | |
308 | + memmove(ccs_buffer, cp + 1, strlen(cp + 1) + 1); | |
309 | + | |
310 | + /* Clear pending input. */; | |
311 | + timeout(0); | |
312 | + while (true) { | |
313 | + int c = ccs_getch2(); | |
314 | + if (c == EOF || c == ERR) | |
315 | + break; | |
316 | + } | |
317 | + timeout(1000); | |
318 | + if (ccs_handle_query(serial)) | |
319 | + continue; | |
320 | + break; | |
321 | + } | |
322 | + endwin(); | |
323 | + return 0; | |
324 | +} |
@@ -0,0 +1,464 @@ | ||
1 | +/* | |
2 | + * caitsith-auditd.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.1 2012/04/01 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | +#include <signal.h> | |
25 | +#include <syslog.h> | |
26 | +#include <poll.h> | |
27 | + | |
28 | +#define CCS_AUDITD_CONF "/etc/caitsith/tools/auditd.conf" | |
29 | + | |
30 | +struct ccs_destination { | |
31 | + const char *pathname; | |
32 | + int fd; | |
33 | +}; | |
34 | + | |
35 | +static struct ccs_destination *destination_list = NULL; | |
36 | +static unsigned int destination_list_len = 0; | |
37 | + | |
38 | +enum ccs_rule_types { | |
39 | + CCS_SORT_RULE_HEADER, | |
40 | + CCS_SORT_RULE_ACL, | |
41 | + CCS_SORT_RULE_DESTINATION, | |
42 | +}; | |
43 | + | |
44 | +enum ccs_operator_types { | |
45 | + CCS_SORT_OPERATOR_CONTAINS, | |
46 | + CCS_SORT_OPERATOR_EQUALS, | |
47 | + CCS_SORT_OPERATOR_STARTS, | |
48 | +}; | |
49 | + | |
50 | +struct ccs_sort_rules { | |
51 | + enum ccs_rule_types type; | |
52 | + enum ccs_operator_types operation; | |
53 | + unsigned int index; | |
54 | + const char *string; | |
55 | + unsigned int string_len; /* strlen(string). */ | |
56 | +}; | |
57 | + | |
58 | +static struct ccs_sort_rules *rules = NULL; | |
59 | +static unsigned int rules_len = 0; | |
60 | + | |
61 | +static void ccs_auditd_init_rules(const char *filename) | |
62 | +{ | |
63 | + static _Bool first = 1; | |
64 | + FILE *fp = fopen(filename, "r"); | |
65 | + unsigned int line_no = 0; | |
66 | + unsigned int i; | |
67 | + if (!first) { | |
68 | + for (i = 0; i < rules_len; i++) | |
69 | + free((void *) rules[i].string); | |
70 | + rules_len = 0; | |
71 | + for (i = 0; i < destination_list_len; i++) { | |
72 | + free((void *) destination_list[i].pathname); | |
73 | + close(destination_list[i].fd); | |
74 | + } | |
75 | + destination_list_len = 0; | |
76 | + } | |
77 | + if (!fp) { | |
78 | + if (first) | |
79 | + fprintf(stderr, "Can't open %s for reading.\n", | |
80 | + filename); | |
81 | + else | |
82 | + syslog(LOG_WARNING, "Can't open %s for reading.\n", | |
83 | + filename); | |
84 | + exit(1); | |
85 | + } | |
86 | + ccs_get(); | |
87 | + while (true) { | |
88 | + char *line = ccs_freadline(fp); | |
89 | + struct ccs_sort_rules *ptr; | |
90 | + unsigned char c; | |
91 | + if (!line) | |
92 | + break; | |
93 | + line_no++; | |
94 | + ccs_normalize_line(line); | |
95 | + if (*line == '#' || !*line) | |
96 | + continue; | |
97 | + rules = ccs_realloc(rules, sizeof(struct ccs_sort_rules) * | |
98 | + (rules_len + 1)); | |
99 | + ptr = &rules[rules_len++]; | |
100 | + memset(ptr, 0, sizeof(*ptr)); | |
101 | + if (ccs_str_starts(line, "destination ")) { | |
102 | + if (*line != '/') | |
103 | + goto invalid_rule; | |
104 | + for (i = 0; i < destination_list_len; i++) | |
105 | + if (!strcmp(destination_list[i].pathname, | |
106 | + line)) | |
107 | + break; | |
108 | + if (i < destination_list_len) | |
109 | + goto store_destination; | |
110 | + destination_list = | |
111 | + ccs_realloc(destination_list, | |
112 | + ++destination_list_len * | |
113 | + sizeof(struct ccs_destination)); | |
114 | + if (!ccs_decode(line, line)) | |
115 | + goto invalid_rule; | |
116 | + destination_list[i].pathname = ccs_strdup(line); | |
117 | + destination_list[i].fd = EOF; | |
118 | +store_destination: | |
119 | + ptr->type = CCS_SORT_RULE_DESTINATION; | |
120 | + ptr->index = i; | |
121 | + continue; | |
122 | + } | |
123 | + if (ccs_str_starts(line, "header")) | |
124 | + ptr->type = CCS_SORT_RULE_HEADER; | |
125 | + else if (ccs_str_starts(line, "acl")) | |
126 | + ptr->type = CCS_SORT_RULE_ACL; | |
127 | + else | |
128 | + goto invalid_rule; | |
129 | + switch (sscanf(line, "[%u%c", &ptr->index, &c)) { | |
130 | + case 0: | |
131 | + break; | |
132 | + case 2: | |
133 | + if (c == ']') { | |
134 | + char *cp = strchr(line, ']') + 1; | |
135 | + memmove(line, cp, strlen(cp) + 1); | |
136 | + break; | |
137 | + } | |
138 | + default: | |
139 | + goto invalid_rule; | |
140 | + } | |
141 | + if (ccs_str_starts(line, ".contains ")) | |
142 | + ptr->operation = CCS_SORT_OPERATOR_CONTAINS; | |
143 | + else if (ccs_str_starts(line, ".equals ")) | |
144 | + ptr->operation = CCS_SORT_OPERATOR_EQUALS; | |
145 | + else if (ccs_str_starts(line, ".starts ")) | |
146 | + ptr->operation = CCS_SORT_OPERATOR_STARTS; | |
147 | + else | |
148 | + goto invalid_rule; | |
149 | + if (!*line) | |
150 | + goto invalid_rule; | |
151 | + line = ccs_strdup(line); | |
152 | + ptr->string = line; | |
153 | + ptr->string_len = strlen(line); | |
154 | + } | |
155 | + ccs_put(); | |
156 | + fclose(fp); | |
157 | + if (!rules_len) { | |
158 | + if (first) | |
159 | + fprintf(stderr, "No rules defined in %s .\n", | |
160 | + filename); | |
161 | + else | |
162 | + syslog(LOG_WARNING, "No rules defined in %s .\n", | |
163 | + filename); | |
164 | + exit(1); | |
165 | + } | |
166 | + for (i = 0; i < destination_list_len; i++) { | |
167 | + struct ccs_destination *ptr = &destination_list[i]; | |
168 | + const char *path = ptr->pathname; | |
169 | + /* This is OK because path is a strdup()ed string. */ | |
170 | + char *pos = (char *) path; | |
171 | + while (*pos) { | |
172 | + int ret_ignored; | |
173 | + if (*pos++ != '/') | |
174 | + continue; | |
175 | + *(pos - 1) = '\0'; | |
176 | + ret_ignored = mkdir(path, 0700); | |
177 | + *(pos - 1) = '/'; | |
178 | + } | |
179 | + do { | |
180 | + ptr->fd = open(path, O_WRONLY | O_APPEND | O_CREAT, | |
181 | + 0600); | |
182 | + } while (ptr->fd == EOF && errno == EINTR); | |
183 | + if (ptr->fd == EOF) { | |
184 | + if (first) | |
185 | + fprintf(stderr, "Can't open %s for writing.\n", | |
186 | + path); | |
187 | + else | |
188 | + syslog(LOG_WARNING, | |
189 | + "Can't open %s for writing.\n", path); | |
190 | + exit(1); | |
191 | + } | |
192 | + } | |
193 | + first = 0; | |
194 | + return; | |
195 | +invalid_rule: | |
196 | + if (first) | |
197 | + fprintf(stderr, "Invalid rule at line %u in %s .\n", line_no, | |
198 | + filename); | |
199 | + else | |
200 | + syslog(LOG_WARNING, "Invalid rule at line %u in %s .\n", | |
201 | + line_no, filename); | |
202 | + exit(1); | |
203 | +} | |
204 | + | |
205 | +static int ccs_check_rules(char *header, char *acl) | |
206 | +{ | |
207 | + unsigned int i; | |
208 | + _Bool matched = true; | |
209 | + for (i = 0; i < rules_len; i++) { | |
210 | + const struct ccs_sort_rules *ptr = &rules[i]; | |
211 | + char *line; | |
212 | + unsigned int index = ptr->index; | |
213 | + const char *find = ptr->string; | |
214 | + unsigned int find_len = ptr->string_len; | |
215 | + switch (ptr->type) { | |
216 | + case CCS_SORT_RULE_HEADER: | |
217 | + line = header; | |
218 | + break; | |
219 | + case CCS_SORT_RULE_ACL: | |
220 | + line = acl; | |
221 | + break; | |
222 | + default: /* CCS_SORT_RULE_DESTINATION */ | |
223 | + if (matched) | |
224 | + return ptr->index; | |
225 | + matched = true; | |
226 | + continue; | |
227 | + } | |
228 | + if (!matched) | |
229 | + continue; | |
230 | + if (!index) { | |
231 | + switch (ptr->operation) { | |
232 | + case CCS_SORT_OPERATOR_CONTAINS: | |
233 | + while (1) { | |
234 | + char *cp = strstr(line, find); | |
235 | + if (!cp) { | |
236 | + matched = false; | |
237 | + break; | |
238 | + } | |
239 | + if ((cp == line || *(cp - 1) == ' ') && | |
240 | + (!cp[find_len] || | |
241 | + cp[find_len] == ' ')) | |
242 | + break; | |
243 | + line = cp + 1; | |
244 | + } | |
245 | + break; | |
246 | + case CCS_SORT_OPERATOR_EQUALS: | |
247 | + matched = !strcmp(line, find); | |
248 | + break; | |
249 | + default: /* CCS_SORT_OPERATOR_STARTS */ | |
250 | + matched = !strncmp(line, find, find_len) && | |
251 | + (!line[find_len] || | |
252 | + line[find_len] == ' '); | |
253 | + } | |
254 | + } else { | |
255 | + char *word = line; | |
256 | + char *word_end; | |
257 | + while (--index) { | |
258 | + char *cp = strchr(word, ' '); | |
259 | + if (!cp) { | |
260 | + matched = false; | |
261 | + break; | |
262 | + } | |
263 | + word = cp + 1; | |
264 | + } | |
265 | + if (!matched) | |
266 | + continue; | |
267 | + word_end = strchr(word, ' '); | |
268 | + if (word_end) | |
269 | + *word_end = '\0'; | |
270 | + switch (ptr->operation) { | |
271 | + case CCS_SORT_OPERATOR_CONTAINS: | |
272 | + matched = strstr(word, find) != NULL; | |
273 | + break; | |
274 | + case CCS_SORT_OPERATOR_EQUALS: | |
275 | + matched = !strcmp(word, find); | |
276 | + break; | |
277 | + default: /* CCS_SORT_OPERATOR_STARTS */ | |
278 | + matched = !strncmp(word, find, find_len); | |
279 | + break; | |
280 | + } | |
281 | + if (word_end) | |
282 | + *word_end = ' '; | |
283 | + } | |
284 | + } | |
285 | + return EOF; | |
286 | +} | |
287 | + | |
288 | +static _Bool ccs_write_log(const int i, char *buffer) | |
289 | +{ | |
290 | + int len = strlen(buffer); | |
291 | + int ret; | |
292 | + struct ccs_destination *ptr = &destination_list[i]; | |
293 | + /* Create destination file if needed. */ | |
294 | + if (access(ptr->pathname, F_OK)) { | |
295 | + close(ptr->fd); | |
296 | + do { | |
297 | + ptr->fd = open(ptr->pathname, | |
298 | + O_WRONLY | O_APPEND | O_CREAT, 0600); | |
299 | + } while (ptr->fd == EOF && errno == EINTR); | |
300 | + if (ptr->fd == EOF) { | |
301 | + syslog(LOG_WARNING, "Can't open %s for writing.\n", | |
302 | + ptr->pathname); | |
303 | + return 0; | |
304 | + } | |
305 | + } | |
306 | + do { | |
307 | + ret = write(ptr->fd, buffer, len); | |
308 | + if (ret == len) | |
309 | + return 1; | |
310 | + } while (ret == EOF && errno == EINTR); | |
311 | + syslog(LOG_WARNING, "Can't write to %s .\n", ptr->pathname); | |
312 | + return 0; | |
313 | +} | |
314 | + | |
315 | +static void block_sighup(const _Bool block) | |
316 | +{ | |
317 | + sigset_t sigset; | |
318 | + sigemptyset(&sigset); | |
319 | + sigaddset(&sigset, SIGHUP); | |
320 | + sigprocmask(block ? SIG_BLOCK : SIG_UNBLOCK, &sigset, NULL); | |
321 | +} | |
322 | + | |
323 | +static void ccs_reload_config(int sig) | |
324 | +{ | |
325 | + block_sighup(1); | |
326 | + syslog(LOG_WARNING, "Reloading configuration file.\n"); | |
327 | + ccs_auditd_init_rules(CCS_AUDITD_CONF); | |
328 | + block_sighup(0); | |
329 | +} | |
330 | + | |
331 | +int main(int argc, char *argv[]) | |
332 | +{ | |
333 | + unsigned int i; | |
334 | + int fd_in; | |
335 | + for (i = 1; i < argc; i++) { | |
336 | + char *ptr = argv[i]; | |
337 | + char *cp = strchr(ptr, ':'); | |
338 | + if (!cp) | |
339 | + goto usage; | |
340 | + *cp++ = '\0'; | |
341 | + if (ccs_network_mode) | |
342 | + goto usage; | |
343 | + ccs_network_ip = inet_addr(ptr); | |
344 | + ccs_network_port = htons(atoi(cp)); | |
345 | + ccs_network_mode = true; | |
346 | + if (!ccs_check_remote_host()) | |
347 | + return 1; | |
348 | + } | |
349 | + ccs_auditd_init_rules(CCS_AUDITD_CONF); | |
350 | + if (ccs_network_mode) | |
351 | + goto start; | |
352 | + if (access(CCS_PROC_POLICY_AUDIT, R_OK)) { | |
353 | + fprintf(stderr, "You can't run this daemon for this kernel." | |
354 | + "\n"); | |
355 | + return 1; | |
356 | + } | |
357 | + { /* Get exclusive lock. */ | |
358 | + int fd = open("/proc/self/exe", O_RDONLY); | |
359 | + if (flock(fd, LOCK_EX | LOCK_NB) == EOF) | |
360 | + return 0; | |
361 | + } | |
362 | +start: | |
363 | + if (ccs_network_mode) | |
364 | + fd_in = ccs_open_stream("proc:audit"); | |
365 | + else | |
366 | + fd_in = open(CCS_PROC_POLICY_AUDIT, O_RDONLY); | |
367 | + if (fd_in == EOF) { | |
368 | + fprintf(stderr, "Can't open %s for reading.\n", | |
369 | + CCS_PROC_POLICY_AUDIT); | |
370 | + return 1; | |
371 | + } | |
372 | + switch (fork()) { | |
373 | + case 0: | |
374 | + break; | |
375 | + case -1: | |
376 | + fprintf(stderr, "Can't fork()\n"); | |
377 | + return 1; | |
378 | + default: | |
379 | + return 0; | |
380 | + } | |
381 | + if (setsid() == EOF) { | |
382 | + fprintf(stderr, "Can't setsid()\n"); | |
383 | + return 1; | |
384 | + } | |
385 | + switch (fork()) { | |
386 | + case 0: | |
387 | + break; | |
388 | + case -1: | |
389 | + fprintf(stderr, "Can't fork()\n"); | |
390 | + return 1; | |
391 | + default: | |
392 | + return 0; | |
393 | + } | |
394 | + if (chdir("/")) { | |
395 | + fprintf(stderr, "Can't chdir()\n"); | |
396 | + return 1; | |
397 | + } | |
398 | + close(0); | |
399 | + close(1); | |
400 | + close(2); | |
401 | + openlog("caitsith-auditd", 0, LOG_USER); | |
402 | + syslog(LOG_WARNING, "Started.\n"); | |
403 | + signal(SIGHUP, ccs_reload_config); | |
404 | + while (true) { | |
405 | + static char buffer[32768]; | |
406 | + char *acl; | |
407 | + char *tail; | |
408 | + int ret; | |
409 | + memset(buffer, 0, sizeof(buffer)); | |
410 | + if (ccs_network_mode) { | |
411 | + int j; | |
412 | + for (j = 0; j < sizeof(buffer) - 1; j++) { | |
413 | + do { | |
414 | + ret = read(fd_in, buffer + j, 1); | |
415 | + } while (ret == EOF && errno == EINTR); | |
416 | + if (ret != 1) | |
417 | + goto out; | |
418 | + if (!buffer[j]) | |
419 | + break; | |
420 | + } | |
421 | + if (j == sizeof(buffer) - 1) | |
422 | + goto out; | |
423 | + } else { | |
424 | + while (read(fd_in, buffer, sizeof(buffer) - 1) <= 0) { | |
425 | + /* Wait for data. */ | |
426 | + struct pollfd pfd = { | |
427 | + .fd = fd_in, | |
428 | + .events = POLLIN, | |
429 | + }; | |
430 | + if (poll(&pfd, 1, -1) == EOF && errno != EINTR) | |
431 | + goto out; | |
432 | + } | |
433 | + } | |
434 | + /* Split into two parts. */ | |
435 | + acl = strstr(buffer, " / "); | |
436 | + if (!acl) | |
437 | + continue; | |
438 | + *acl = '\0'; | |
439 | + acl += 3; | |
440 | + tail = strchr(acl, '\n'); | |
441 | + if (!tail) | |
442 | + continue; | |
443 | + *tail = '\0'; | |
444 | + block_sighup(1); | |
445 | + /* Check for filtering rules. */ | |
446 | + i = ccs_check_rules(buffer, acl); | |
447 | + if (i != EOF) { | |
448 | + *tail = '\n'; | |
449 | + *(acl - 3) = ' '; | |
450 | + /* Write the audit log. */ | |
451 | + if (!ccs_write_log(i, buffer)) | |
452 | + break; | |
453 | + } | |
454 | + block_sighup(0); | |
455 | + } | |
456 | +out: | |
457 | + syslog(LOG_WARNING, "Terminated.\n"); | |
458 | + closelog(); | |
459 | + return 1; | |
460 | +usage: | |
461 | + fprintf(stderr, "%s [remote_ip:remote_port]\n" | |
462 | + " See %s for configuration.\n", argv[0], CCS_AUDITD_CONF); | |
463 | + return 1; | |
464 | +} |
@@ -0,0 +1,256 @@ | ||
1 | +/* | |
2 | + * caitsith-notifyd.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.1 2012/04/01 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | +#include <sys/wait.h> | |
25 | +#include <signal.h> | |
26 | +#include <syslog.h> | |
27 | +#include <poll.h> | |
28 | + | |
29 | +#define CCS_NOTIFYD_CONF "/etc/caitsith/tools/notifyd.conf" | |
30 | + | |
31 | +static const char *proc_policy_query = "/proc/caitsith/query"; | |
32 | +static int query_fd = EOF; | |
33 | +static int time_to_wait = 0; | |
34 | +static char **action_to_take = NULL; | |
35 | +static int minimal_interval = 0; | |
36 | + | |
37 | +static void ccs_notifyd_init_rules(const char *filename) | |
38 | +{ | |
39 | + static _Bool first = 1; | |
40 | + FILE *fp = fopen(filename, "r"); | |
41 | + unsigned int line_no = 0; | |
42 | + char *action = NULL; | |
43 | + if (!first) { | |
44 | + free(action_to_take); | |
45 | + action_to_take = NULL; | |
46 | + time_to_wait = 0; | |
47 | + minimal_interval = 0; | |
48 | + } | |
49 | + if (!fp) { | |
50 | + if (first) | |
51 | + fprintf(stderr, "Can't open %s for reading.\n", | |
52 | + filename); | |
53 | + else | |
54 | + syslog(LOG_WARNING, "Can't open %s for reading.\n", | |
55 | + filename); | |
56 | + exit(1); | |
57 | + } | |
58 | + ccs_get(); | |
59 | + while (true) { | |
60 | + char *line = ccs_freadline(fp); | |
61 | + if (!line) | |
62 | + break; | |
63 | + line_no++; | |
64 | + ccs_normalize_line(line); | |
65 | + if (*line == '#' || !*line) | |
66 | + continue; | |
67 | + if (sscanf(line, "time_to_wait %u", &time_to_wait) == 1 || | |
68 | + sscanf(line, "minimal_interval %u", &minimal_interval) | |
69 | + == 1) | |
70 | + continue; | |
71 | + if (!ccs_str_starts(line, "action_to_take ")) | |
72 | + continue; | |
73 | + if (!*line) | |
74 | + goto invalid_rule; | |
75 | + if (action) | |
76 | + goto invalid_rule; | |
77 | + action = ccs_strdup(line); | |
78 | + } | |
79 | + ccs_put(); | |
80 | + fclose(fp); | |
81 | + if (!action) { | |
82 | + if (first) | |
83 | + fprintf(stderr, "No actions defined in %s .\n", | |
84 | + filename); | |
85 | + else | |
86 | + syslog(LOG_WARNING, "No actions defined in %s .\n", | |
87 | + filename); | |
88 | + exit(1); | |
89 | + } | |
90 | + { | |
91 | + int count = 0; | |
92 | + char *sp = action; | |
93 | + while (true) { | |
94 | + char *cp = strsep(&sp, " "); | |
95 | + action_to_take = ccs_realloc(action_to_take, | |
96 | + sizeof(char *) * ++count); | |
97 | + action_to_take[count - 1] = cp; | |
98 | + if (!cp) | |
99 | + break; | |
100 | + if (!ccs_decode(cp, cp)) | |
101 | + goto invalid_rule; | |
102 | + } | |
103 | + } | |
104 | + first = 0; | |
105 | + return; | |
106 | +invalid_rule: | |
107 | + if (first) | |
108 | + fprintf(stderr, "Invalid rule at line %u in %s .\n", line_no, | |
109 | + filename); | |
110 | + else | |
111 | + syslog(LOG_WARNING, "Invalid rule at line %u in %s .\n", | |
112 | + line_no, filename); | |
113 | + exit(1); | |
114 | +} | |
115 | + | |
116 | +static void block_sighup(const _Bool block) | |
117 | +{ | |
118 | + sigset_t sigset; | |
119 | + sigemptyset(&sigset); | |
120 | + sigaddset(&sigset, SIGHUP); | |
121 | + sigprocmask(block ? SIG_BLOCK : SIG_UNBLOCK, &sigset, NULL); | |
122 | +} | |
123 | + | |
124 | +static void main_loop(void) | |
125 | +{ | |
126 | + static char buffer[32768]; | |
127 | + while (query_fd != EOF) { | |
128 | + int pipe_fd[2]; | |
129 | + pid_t pid; | |
130 | + memset(buffer, 0, sizeof(buffer)); | |
131 | + while (read(query_fd, buffer, sizeof(buffer) - 1) <= 0) { | |
132 | + /* Wait for data. */ | |
133 | + struct pollfd pfd = { | |
134 | + .fd = query_fd, | |
135 | + .events = POLLIN, | |
136 | + }; | |
137 | + if (poll(&pfd, 1, -1) == EOF && errno != EINTR) | |
138 | + return; | |
139 | + } | |
140 | + if (pipe(pipe_fd) == EOF) { | |
141 | + syslog(LOG_WARNING, "Can't create pipe.\n"); | |
142 | + return; | |
143 | + } | |
144 | + block_sighup(1); | |
145 | + pid = fork(); | |
146 | + if (pid == -1) { | |
147 | + syslog(LOG_WARNING, "Can't fork().\n"); | |
148 | + return; | |
149 | + } | |
150 | + if (!pid) { | |
151 | + int ret_ignored; | |
152 | + ret_ignored = close(query_fd); | |
153 | + ret_ignored = close(pipe_fd[1]); | |
154 | + ret_ignored = close(0); | |
155 | + ret_ignored = dup2(pipe_fd[0], 0); | |
156 | + ret_ignored = close(pipe_fd[0]); | |
157 | + execvp(action_to_take[0], action_to_take); | |
158 | + syslog(LOG_WARNING, "Can't execute %s\n", | |
159 | + action_to_take[0]); | |
160 | + closelog(); | |
161 | + _exit(1); | |
162 | + } else { | |
163 | + int ret_ignored; | |
164 | + int len = strlen(buffer); | |
165 | + close(pipe_fd[0]); | |
166 | + /* This is OK because read() < sizeof(buffer). */ | |
167 | + buffer[len++] = '\n'; | |
168 | + ret_ignored = write(pipe_fd[1], buffer, len); | |
169 | + close(pipe_fd[1]); | |
170 | + } | |
171 | + block_sighup(0); | |
172 | + while (time_to_wait-- > 0) { | |
173 | + int ret_ignored; | |
174 | + sleep(1); | |
175 | + ret_ignored = write(query_fd, "\n", 1); | |
176 | + } | |
177 | + close(query_fd); | |
178 | + while (waitpid(pid, NULL, __WALL) == EOF && errno == EINTR); | |
179 | + sleep(minimal_interval); | |
180 | + do { | |
181 | + query_fd = open(proc_policy_query, O_RDWR); | |
182 | + } while (query_fd == EOF && errno == EINTR); | |
183 | + } | |
184 | +} | |
185 | + | |
186 | +static void ccs_reload_config(int sig) | |
187 | +{ | |
188 | + block_sighup(1); | |
189 | + syslog(LOG_WARNING, "Reloading configuration file.\n"); | |
190 | + ccs_notifyd_init_rules(CCS_NOTIFYD_CONF); | |
191 | + block_sighup(0); | |
192 | +} | |
193 | + | |
194 | +int main(int argc, char *argv[]) | |
195 | +{ | |
196 | + unsetenv("SHELLOPTS"); /* Make sure popen() executes commands. */ | |
197 | + if (argc != 1) | |
198 | + goto usage; | |
199 | + ccs_notifyd_init_rules(CCS_NOTIFYD_CONF); | |
200 | + query_fd = open(proc_policy_query, O_RDWR); | |
201 | + if (query_fd == EOF) { | |
202 | + fprintf(stderr, "You can't run this daemon for this kernel." | |
203 | + "\n"); | |
204 | + return 1; | |
205 | + } else if (time_to_wait && write(query_fd, "", 0) != 0) { | |
206 | + fprintf(stderr, "You need to give this program permission to " | |
207 | + "modify policy.\n"); | |
208 | + return 1; | |
209 | + } | |
210 | + umask(0); | |
211 | + switch (fork()) { | |
212 | + case 0: | |
213 | + break; | |
214 | + case -1: | |
215 | + fprintf(stderr, "Can't fork()\n"); | |
216 | + return 1; | |
217 | + default: | |
218 | + return 0; | |
219 | + } | |
220 | + if (setsid() == EOF) { | |
221 | + fprintf(stderr, "Can't setsid()\n"); | |
222 | + return 1; | |
223 | + } | |
224 | + switch (fork()) { | |
225 | + case 0: | |
226 | + break; | |
227 | + case -1: | |
228 | + fprintf(stderr, "Can't fork()\n"); | |
229 | + return 1; | |
230 | + default: | |
231 | + return 0; | |
232 | + } | |
233 | + if (chdir("/")) { | |
234 | + fprintf(stderr, "Can't chdir()\n"); | |
235 | + return 1; | |
236 | + } | |
237 | + { /* Get exclusive lock. */ | |
238 | + int fd = open("/proc/self/exe", O_RDONLY); | |
239 | + if (flock(fd, LOCK_EX | LOCK_NB) == EOF) | |
240 | + return 0; | |
241 | + } | |
242 | + close(0); | |
243 | + close(1); | |
244 | + close(2); | |
245 | + openlog("caitsith-notifyd", 0, LOG_USER); | |
246 | + syslog(LOG_WARNING, "Started.\n"); | |
247 | + signal(SIGHUP, ccs_reload_config); | |
248 | + main_loop(); | |
249 | + syslog(LOG_WARNING, "Terminated.\n"); | |
250 | + closelog(); | |
251 | + return 1; | |
252 | +usage: | |
253 | + fprintf(stderr, "%s\n See %s for configuration.\n", argv[0], | |
254 | + CCS_NOTIFYD_CONF); | |
255 | + return 1; | |
256 | +} |
@@ -0,0 +1,177 @@ | ||
1 | +/* | |
2 | + * caitsith-savepolicy.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.1 2012/04/01 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | + | |
25 | +/** | |
26 | + * ccs_move_proc_to_file - Save /proc/caitsith/ to /etc/caitsith/ . | |
27 | + * | |
28 | + * @src: Filename to save from. | |
29 | + * @dest: Filename to save to. | |
30 | + * | |
31 | + * Returns true on success, false otherwise. | |
32 | + */ | |
33 | +static _Bool ccs_move_proc_to_file(const char *src, const char *dest) | |
34 | +{ | |
35 | + FILE *proc_fp = ccs_open_read(src); | |
36 | + FILE *file_fp; | |
37 | + _Bool result = true; | |
38 | + if (!proc_fp) { | |
39 | + fprintf(stderr, "Can't open %s for reading.\n", src); | |
40 | + return false; | |
41 | + } | |
42 | + file_fp = dest ? fopen(dest, "w") : stdout; | |
43 | + if (!file_fp) { | |
44 | + fprintf(stderr, "Can't open %s for writing.\n", dest); | |
45 | + fclose(proc_fp); | |
46 | + return false; | |
47 | + } | |
48 | + while (true) { | |
49 | + const int c = fgetc(proc_fp); | |
50 | + if (ccs_network_mode && !c) | |
51 | + break; | |
52 | + if (c == EOF) | |
53 | + break; | |
54 | + if (fputc(c, file_fp) == EOF) | |
55 | + result = false; | |
56 | + } | |
57 | + fclose(proc_fp); | |
58 | + if (file_fp != stdout) | |
59 | + if (fclose(file_fp) == EOF) | |
60 | + result = false; | |
61 | + return result; | |
62 | +} | |
63 | + | |
64 | +static const char *ccs_policy_dir = NULL; | |
65 | + | |
66 | +static _Bool ccs_cat_file(const char *path) | |
67 | +{ | |
68 | + FILE *fp = ccs_open_read(path); | |
69 | + _Bool result = true; | |
70 | + if (!fp) { | |
71 | + fprintf(stderr, "Can't open %s\n", path); | |
72 | + return false; | |
73 | + } | |
74 | + while (true) { | |
75 | + int c = fgetc(fp); | |
76 | + if (ccs_network_mode && !c) | |
77 | + break; | |
78 | + if (c == EOF) | |
79 | + break; | |
80 | + if (putchar(c) == EOF) | |
81 | + result = false; | |
82 | + } | |
83 | + fclose(fp); | |
84 | + return result; | |
85 | +} | |
86 | + | |
87 | +static _Bool ccs_save_policy(void) | |
88 | +{ | |
89 | + time_t now = time(NULL); | |
90 | + char stamp[32] = { }; | |
91 | + while (1) { | |
92 | + struct tm *tm = localtime(&now); | |
93 | + snprintf(stamp, sizeof(stamp) - 1, | |
94 | + "%02d-%02d-%02d.%02d:%02d:%02d", | |
95 | + tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday, | |
96 | + tm->tm_hour, tm->tm_min, tm->tm_sec); | |
97 | + if (access(stamp, F_OK)) | |
98 | + break; | |
99 | + else if (errno == EEXIST) | |
100 | + now++; | |
101 | + else { | |
102 | + fprintf(stderr, "Can't create %s/policy/%s .\n", | |
103 | + ccs_policy_dir, stamp); | |
104 | + return false; | |
105 | + } | |
106 | + } | |
107 | + if (!ccs_move_proc_to_file(CCS_PROC_POLICY_POLICY, stamp) || | |
108 | + (rename("current", "previous") && errno != ENOENT) || | |
109 | + symlink(stamp, "current")) { | |
110 | + fprintf(stderr, "Failed to save policy.\n"); | |
111 | + return false; | |
112 | + } | |
113 | + return true; | |
114 | +} | |
115 | + | |
116 | +int main(int argc, char *argv[]) | |
117 | +{ | |
118 | + _Bool use_stdout = false; | |
119 | + int i; | |
120 | + for (i = 1; i < argc; i++) { | |
121 | + char *ptr = argv[i]; | |
122 | + char *cp = strchr(ptr, ':'); | |
123 | + if (*ptr == '/') { | |
124 | + if (ccs_policy_dir || use_stdout) | |
125 | + goto usage; | |
126 | + ccs_policy_dir = ptr; | |
127 | + } else if (cp) { | |
128 | + *cp++ = '\0'; | |
129 | + ccs_network_ip = inet_addr(ptr); | |
130 | + ccs_network_port = htons(atoi(cp)); | |
131 | + if (ccs_network_mode) { | |
132 | + fprintf(stderr, "You cannot specify multiple " | |
133 | + "%s at the same time.\n\n", | |
134 | + "remote agents"); | |
135 | + goto usage; | |
136 | + } | |
137 | + ccs_network_mode = true; | |
138 | + } else if (*ptr++ == '-' && !*ptr) { | |
139 | + if (ccs_policy_dir || use_stdout) | |
140 | + goto usage; | |
141 | + use_stdout = true; | |
142 | + } else | |
143 | + goto usage; | |
144 | + } | |
145 | + if (ccs_network_mode) { | |
146 | + if (!ccs_check_remote_host()) | |
147 | + return 1; | |
148 | + } else if (access(CCS_PROC_POLICY_DIR, F_OK)) { | |
149 | + fprintf(stderr, | |
150 | + "You can't run this program for this kernel.\n"); | |
151 | + return 1; | |
152 | + } | |
153 | + if (use_stdout) | |
154 | + return !ccs_cat_file(CCS_PROC_POLICY_POLICY); | |
155 | + if (!ccs_policy_dir) { | |
156 | + if (ccs_network_mode && !use_stdout) { | |
157 | + fprintf(stderr, "You need to specify %s.\n\n", | |
158 | + "policy directory"); | |
159 | + goto usage; | |
160 | + } | |
161 | + ccs_policy_dir = "/etc/caitsith/"; | |
162 | + } | |
163 | + if (chdir(ccs_policy_dir) || chdir("policy/")) { | |
164 | + fprintf(stderr, "Directory %s/policy/ doesn't exist.\n", | |
165 | + ccs_policy_dir); | |
166 | + return 1; | |
167 | + } | |
168 | + return !ccs_save_policy(); | |
169 | +usage: | |
170 | + printf("%s [policy_dir|-] [remote_ip:remote_port]]\n\n" | |
171 | + "policy_dir : Use policy_dir rather than /etc/caitsith/ directory.\n" | |
172 | + "- : Print /proc/caitsith/policy to stdout.\n" | |
173 | + "remote_ip:remote_port : Read from caitsith-agent " | |
174 | + "listening at remote_ip:remote_port rather than /proc/caitsith/ " | |
175 | + "directory.\n", argv[0]); | |
176 | + return 1; | |
177 | +} |
@@ -0,0 +1,105 @@ | ||
1 | +/* | |
2 | + * caitsith-loadpolicy.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.1 2012/04/01 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | + | |
25 | +/** | |
26 | + * ccs_close_write - Close stream opened by ccs_open_write(). | |
27 | + * | |
28 | + * @fp: Pointer to "FILE". | |
29 | + * | |
30 | + * Returns true on success, false otherwise. | |
31 | + */ | |
32 | +static _Bool ccs_close_write(FILE *fp) | |
33 | +{ | |
34 | + _Bool result = true; | |
35 | + if (ccs_network_mode) { | |
36 | + if (fputc(0, fp) == EOF) | |
37 | + result = false; | |
38 | + if (fflush(fp) == EOF) | |
39 | + result = false; | |
40 | + if (fgetc(fp) == EOF) | |
41 | + result = false; | |
42 | + } | |
43 | + if (fclose(fp) == EOF) | |
44 | + result = false; | |
45 | + return result; | |
46 | +} | |
47 | + | |
48 | +static _Bool ccs_move_file_to_proc(const char *dest) | |
49 | +{ | |
50 | + FILE *proc_fp = ccs_open_write(dest); | |
51 | + _Bool result = true; | |
52 | + if (!proc_fp) { | |
53 | + fprintf(stderr, "Can't open %s for writing.\n", dest); | |
54 | + return false; | |
55 | + } | |
56 | + ccs_get(); | |
57 | + while (true) { | |
58 | + char *line = ccs_freadline(stdin); | |
59 | + if (!line) | |
60 | + break; | |
61 | + if (line[0]) | |
62 | + if (fprintf(proc_fp, "%s\n", line) < 0) | |
63 | + result = false; | |
64 | + } | |
65 | + ccs_put(); | |
66 | + if (!ccs_close_write(proc_fp)) | |
67 | + result = false; | |
68 | + return result; | |
69 | +} | |
70 | + | |
71 | +int main(int argc, char *argv[]) | |
72 | +{ | |
73 | + int i; | |
74 | + for (i = 1; i < argc; i++) { | |
75 | + char *ptr = argv[i]; | |
76 | + char *cp = strchr(ptr, ':'); | |
77 | + if (!cp) | |
78 | + goto usage; | |
79 | + *cp++ = '\0'; | |
80 | + ccs_network_ip = inet_addr(ptr); | |
81 | + ccs_network_port = htons(atoi(cp)); | |
82 | + if (ccs_network_mode) { | |
83 | + fprintf(stderr, "You cannot specify multiple " | |
84 | + "%s at the same time.\n\n", | |
85 | + "remote agents"); | |
86 | + goto usage; | |
87 | + } | |
88 | + ccs_network_mode = true; | |
89 | + } | |
90 | + if (ccs_network_mode) { | |
91 | + if (!ccs_check_remote_host()) | |
92 | + return 1; | |
93 | + } else if (access(CCS_PROC_POLICY_DIR, F_OK)) { | |
94 | + fprintf(stderr, | |
95 | + "You can't run this program for this kernel.\n"); | |
96 | + return 1; | |
97 | + } | |
98 | + return !ccs_move_file_to_proc(CCS_PROC_POLICY_POLICY); | |
99 | +usage: | |
100 | + printf("%s [remote_ip:remote_port]\n\n" | |
101 | + "remote_ip:remote_port : Write to caitsith-agent listening at " | |
102 | + "remote_ip:remote_port rather than /proc/caitsith/ " | |
103 | + "directory.\n", argv[0]); | |
104 | + return 1; | |
105 | +} |
@@ -0,0 +1,383 @@ | ||
1 | +/* | |
2 | + * caitsith-pstree.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.1 2012/04/01 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | + | |
25 | +struct ccs_task_entry { | |
26 | + pid_t pid; | |
27 | + pid_t ppid; | |
28 | + char *name; | |
29 | + char *domain; | |
30 | + _Bool selected; | |
31 | + int index; | |
32 | + int depth; | |
33 | +}; | |
34 | + | |
35 | +/* The list of processes currently running. */ | |
36 | +static struct ccs_task_entry *ccs_task_list = NULL; | |
37 | +/* The length of ccs_task_list . */ | |
38 | +static int ccs_task_list_len = 0; | |
39 | + | |
40 | +/* Serial number for sorting ccs_task_list . */ | |
41 | +static int ccs_dump_index = 0; | |
42 | + | |
43 | +/** | |
44 | + * ccs_sort_process_entry - Sort ccs_tasklist list. | |
45 | + * | |
46 | + * @pid: Pid to search. | |
47 | + * @depth: Depth of the process for printing like pstree command. | |
48 | + * | |
49 | + * Returns nothing. | |
50 | + */ | |
51 | +static void ccs_sort_process_entry(const pid_t pid, const int depth) | |
52 | +{ | |
53 | + int i; | |
54 | + for (i = 0; i < ccs_task_list_len; i++) { | |
55 | + if (pid != ccs_task_list[i].pid) | |
56 | + continue; | |
57 | + ccs_task_list[i].index = ccs_dump_index++; | |
58 | + ccs_task_list[i].depth = depth; | |
59 | + ccs_task_list[i].selected = true; | |
60 | + } | |
61 | + for (i = 0; i < ccs_task_list_len; i++) { | |
62 | + if (pid != ccs_task_list[i].ppid) | |
63 | + continue; | |
64 | + ccs_sort_process_entry(ccs_task_list[i].pid, depth + 1); | |
65 | + } | |
66 | +} | |
67 | + | |
68 | +/** | |
69 | + * ccs_task_entry_compare - Compare routine for qsort() callback. | |
70 | + * | |
71 | + * @a: Pointer to "void". | |
72 | + * @b: Pointer to "void". | |
73 | + * | |
74 | + * Returns index diff value. | |
75 | + */ | |
76 | +static int ccs_task_entry_compare(const void *a, const void *b) | |
77 | +{ | |
78 | + const struct ccs_task_entry *a0 = (struct ccs_task_entry *) a; | |
79 | + const struct ccs_task_entry *b0 = (struct ccs_task_entry *) b; | |
80 | + return a0->index - b0->index; | |
81 | +} | |
82 | + | |
83 | +/** | |
84 | + * ccs_add_process_entry - Add entry for running processes. | |
85 | + * | |
86 | + * @line: A line containing PID and domainname. | |
87 | + * @ppid: Parent PID. | |
88 | + * @name: Comm name (allocated by strdup()). | |
89 | + * | |
90 | + * Returns nothing. | |
91 | + * | |
92 | + * @name is free()d on failure. | |
93 | + */ | |
94 | +static void ccs_add_process_entry(const char *line, const pid_t ppid, | |
95 | + char *name) | |
96 | +{ | |
97 | + int index; | |
98 | + unsigned int pid = 0; | |
99 | + char *domain; | |
100 | + if (!line || sscanf(line, "%u", &pid) != 1) { | |
101 | + free(name); | |
102 | + return; | |
103 | + } | |
104 | + domain = strchr(line, ' '); | |
105 | + if (domain++) | |
106 | + domain = ccs_strdup(domain); | |
107 | + else | |
108 | + domain = ccs_strdup("<UNKNOWN>"); | |
109 | + index = ccs_task_list_len++; | |
110 | + ccs_task_list = ccs_realloc(ccs_task_list, ccs_task_list_len * | |
111 | + sizeof(struct ccs_task_entry)); | |
112 | + memset(&ccs_task_list[index], 0, sizeof(ccs_task_list[0])); | |
113 | + ccs_task_list[index].pid = pid; | |
114 | + ccs_task_list[index].ppid = ppid; | |
115 | + ccs_task_list[index].name = name; | |
116 | + ccs_task_list[index].domain = domain; | |
117 | +} | |
118 | + | |
119 | +/** | |
120 | + * ccs_get_ppid - Get PPID of the given PID. | |
121 | + * | |
122 | + * @pid: A pid_t value. | |
123 | + * | |
124 | + * Returns PPID value. | |
125 | + */ | |
126 | +static pid_t ccs_get_ppid(const pid_t pid) | |
127 | +{ | |
128 | + char buffer[1024]; | |
129 | + FILE *fp; | |
130 | + pid_t ppid = 1; | |
131 | + memset(buffer, 0, sizeof(buffer)); | |
132 | + snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/status", pid); | |
133 | + fp = fopen(buffer, "r"); | |
134 | + if (fp) { | |
135 | + while (memset(buffer, 0, sizeof(buffer)) && | |
136 | + fgets(buffer, sizeof(buffer) - 1, fp)) { | |
137 | + if (sscanf(buffer, "PPid: %u", &ppid) == 1) | |
138 | + break; | |
139 | + } | |
140 | + fclose(fp); | |
141 | + } | |
142 | + return ppid; | |
143 | +} | |
144 | + | |
145 | +/** | |
146 | + * ccs_get_name - Get comm name of the given PID. | |
147 | + * | |
148 | + * @pid: A pid_t value. | |
149 | + * | |
150 | + * Returns comm name using on success, NULL otherwise. | |
151 | + * | |
152 | + * The caller must free() the returned pointer. | |
153 | + */ | |
154 | +static char *ccs_get_name(const pid_t pid) | |
155 | +{ | |
156 | + char buffer[1024]; | |
157 | + FILE *fp; | |
158 | + memset(buffer, 0, sizeof(buffer)); | |
159 | + snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/status", pid); | |
160 | + fp = fopen(buffer, "r"); | |
161 | + if (fp) { | |
162 | + static const int offset = sizeof(buffer) / 6; | |
163 | + while (memset(buffer, 0, sizeof(buffer)) && | |
164 | + fgets(buffer, sizeof(buffer) - 1, fp)) { | |
165 | + if (!strncmp(buffer, "Name:\t", 6)) { | |
166 | + char *cp = buffer + 6; | |
167 | + memmove(buffer, cp, strlen(cp) + 1); | |
168 | + cp = strchr(buffer, '\n'); | |
169 | + if (cp) | |
170 | + *cp = '\0'; | |
171 | + break; | |
172 | + } | |
173 | + } | |
174 | + fclose(fp); | |
175 | + if (buffer[0] && strlen(buffer) < offset - 1) { | |
176 | + const char *src = buffer; | |
177 | + char *dest = buffer + offset; | |
178 | + while (1) { | |
179 | + unsigned char c = *src++; | |
180 | + if (!c) { | |
181 | + *dest = '\0'; | |
182 | + break; | |
183 | + } | |
184 | + if (c == '\\') { | |
185 | + c = *src++; | |
186 | + if (c == '\\') { | |
187 | + memmove(dest, "\\134", 4); | |
188 | + dest += 4; | |
189 | + } else if (c == 'n') { | |
190 | + memmove(dest, "\\012", 4); | |
191 | + dest += 4; | |
192 | + } else { | |
193 | + break; | |
194 | + } | |
195 | + } else if (c > ' ' && c <= 126) { | |
196 | + *dest++ = c; | |
197 | + } else { | |
198 | + *dest++ = '\\'; | |
199 | + *dest++ = (c >> 6) + '0'; | |
200 | + *dest++ = ((c >> 3) & 7) + '0'; | |
201 | + *dest++ = (c & 7) + '0'; | |
202 | + } | |
203 | + } | |
204 | + return strdup(buffer + offset); | |
205 | + } | |
206 | + } | |
207 | + return NULL; | |
208 | +} | |
209 | + | |
210 | +/** | |
211 | + * ccs_read_process_list - Read all process's information. | |
212 | + * | |
213 | + * @show_all: Ture if kernel threads should be included, false otherwise. | |
214 | + * | |
215 | + * Returns nothing. | |
216 | + */ | |
217 | +static void ccs_read_process_list(_Bool show_all) | |
218 | +{ | |
219 | + int i; | |
220 | + while (ccs_task_list_len) { | |
221 | + ccs_task_list_len--; | |
222 | + free((void *) ccs_task_list[ccs_task_list_len].name); | |
223 | + free((void *) ccs_task_list[ccs_task_list_len].domain); | |
224 | + } | |
225 | + ccs_dump_index = 0; | |
226 | + if (ccs_network_mode) { | |
227 | + FILE *fp = ccs_open_write(show_all ? | |
228 | + "proc:all_process_status" : | |
229 | + "proc:process_status"); | |
230 | + if (!fp) | |
231 | + return; | |
232 | + ccs_get(); | |
233 | + while (true) { | |
234 | + char *line = ccs_freadline(fp); | |
235 | + unsigned int pid = 0; | |
236 | + unsigned int ppid = 0; | |
237 | + char *name; | |
238 | + if (!line) | |
239 | + break; | |
240 | + sscanf(line, "PID=%u PPID=%u", &pid, &ppid); | |
241 | + name = strstr(line, "NAME="); | |
242 | + if (name) | |
243 | + name = ccs_strdup(name + 5); | |
244 | + else | |
245 | + name = ccs_strdup("<UNKNOWN>"); | |
246 | + line = ccs_freadline(fp); | |
247 | + ccs_add_process_entry(line, ppid, name); | |
248 | + } | |
249 | + ccs_put(); | |
250 | + fclose(fp); | |
251 | + } else { | |
252 | + static const int line_len = 8192; | |
253 | + char *line; | |
254 | + int status_fd = open(CCS_PROC_POLICY_PROCESS_STATUS, O_RDWR); | |
255 | + DIR *dir = opendir("/proc/"); | |
256 | + if (status_fd == EOF || !dir) { | |
257 | + if (status_fd != EOF) | |
258 | + close(status_fd); | |
259 | + if (dir) | |
260 | + closedir(dir); | |
261 | + return; | |
262 | + } | |
263 | + line = ccs_malloc(line_len); | |
264 | + while (1) { | |
265 | + char *name; | |
266 | + int ret_ignored; | |
267 | + unsigned int pid = 0; | |
268 | + char buffer[128]; | |
269 | + char test[16]; | |
270 | + struct dirent *dent = readdir(dir); | |
271 | + if (!dent) | |
272 | + break; | |
273 | + if (dent->d_type != DT_DIR || | |
274 | + sscanf(dent->d_name, "%u", &pid) != 1 || !pid) | |
275 | + continue; | |
276 | + memset(buffer, 0, sizeof(buffer)); | |
277 | + if (!show_all) { | |
278 | + snprintf(buffer, sizeof(buffer) - 1, | |
279 | + "/proc/%u/exe", pid); | |
280 | + if (readlink(buffer, test, sizeof(test)) <= 0) | |
281 | + continue; | |
282 | + } | |
283 | + name = ccs_get_name(pid); | |
284 | + if (!name) | |
285 | + name = ccs_strdup("<UNKNOWN>"); | |
286 | + snprintf(buffer, sizeof(buffer) - 1, "%u\n", pid); | |
287 | + ret_ignored = write(status_fd, buffer, strlen(buffer)); | |
288 | + memset(line, 0, line_len); | |
289 | + ret_ignored = read(status_fd, line, line_len - 1); | |
290 | + ccs_add_process_entry(line, ccs_get_ppid(pid), name); | |
291 | + } | |
292 | + free(line); | |
293 | + closedir(dir); | |
294 | + close(status_fd); | |
295 | + } | |
296 | + ccs_sort_process_entry(1, 0); | |
297 | + for (i = 0; i < ccs_task_list_len; i++) { | |
298 | + if (ccs_task_list[i].selected) { | |
299 | + ccs_task_list[i].selected = false; | |
300 | + continue; | |
301 | + } | |
302 | + ccs_task_list[i].index = ccs_dump_index++; | |
303 | + ccs_task_list[i].depth = 0; | |
304 | + } | |
305 | + qsort(ccs_task_list, ccs_task_list_len, sizeof(struct ccs_task_entry), | |
306 | + ccs_task_entry_compare); | |
307 | +} | |
308 | + | |
309 | +static void ccs_dump(const pid_t pid, const int depth) | |
310 | +{ | |
311 | + int i; | |
312 | + for (i = 0; i < ccs_task_list_len; i++) { | |
313 | + int j; | |
314 | + if (pid != ccs_task_list[i].pid) | |
315 | + continue; | |
316 | + for (j = 0; j < depth - 1; j++) | |
317 | + printf(" "); | |
318 | + for (; j < depth; j++) | |
319 | + printf(" +-"); | |
320 | + printf(" %s (%u) %s\n", ccs_task_list[i].name, | |
321 | + ccs_task_list[i].pid, ccs_task_list[i].domain); | |
322 | + ccs_task_list[i].selected = true; | |
323 | + } | |
324 | + for (i = 0; i < ccs_task_list_len; i++) { | |
325 | + if (pid != ccs_task_list[i].ppid) | |
326 | + continue; | |
327 | + ccs_dump(ccs_task_list[i].pid, depth + 1); | |
328 | + } | |
329 | +} | |
330 | + | |
331 | +int main(int argc, char *argv[]) | |
332 | +{ | |
333 | + static _Bool show_all = false; | |
334 | + int i; | |
335 | + for (i = 1; i < argc; i++) { | |
336 | + char *ptr = argv[i]; | |
337 | + char *cp = strchr(ptr, ':'); | |
338 | + if (cp) { | |
339 | + *cp++ = '\0'; | |
340 | + if (ccs_network_mode) | |
341 | + goto usage; | |
342 | + ccs_network_ip = inet_addr(ptr); | |
343 | + ccs_network_port = htons(atoi(cp)); | |
344 | + ccs_network_mode = true; | |
345 | + if (!ccs_check_remote_host()) | |
346 | + return 1; | |
347 | + } else if (!strcmp(ptr, "-a")) { | |
348 | + show_all = true; | |
349 | + } else { | |
350 | +usage: | |
351 | + fprintf(stderr, "Usage: %s " | |
352 | + "[-a] [remote_ip:remote_port]\n", argv[0]); | |
353 | + return 0; | |
354 | + } | |
355 | + } | |
356 | + ccs_read_process_list(show_all); | |
357 | + if (!ccs_task_list_len) { | |
358 | + if (ccs_network_mode) { | |
359 | + fprintf(stderr, "Can't connect.\n"); | |
360 | + return 1; | |
361 | + } else { | |
362 | + fprintf(stderr, "You can't use this command " | |
363 | + "for this kernel.\n"); | |
364 | + return 1; | |
365 | + } | |
366 | + } | |
367 | + ccs_dump(1, 0); | |
368 | + for (i = 0; i < ccs_task_list_len; i++) { | |
369 | + if (ccs_task_list[i].selected) | |
370 | + continue; | |
371 | + printf(" %s (%u) %s\n", ccs_task_list[i].name, | |
372 | + ccs_task_list[i].pid, ccs_task_list[i].domain); | |
373 | + ccs_task_list[i].selected = true; | |
374 | + } | |
375 | + while (ccs_task_list_len) { | |
376 | + ccs_task_list_len--; | |
377 | + free((void *) ccs_task_list[ccs_task_list_len].name); | |
378 | + free((void *) ccs_task_list[ccs_task_list_len].domain); | |
379 | + } | |
380 | + free(ccs_task_list); | |
381 | + ccs_task_list = NULL; | |
382 | + return 0; | |
383 | +} |
@@ -0,0 +1,303 @@ | ||
1 | +/* | |
2 | + * readline.h | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.1 2012/04/01 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include <ncurses.h> | |
24 | + | |
25 | +static int ccs_getch0(void) | |
26 | +{ | |
27 | + static int enter_key = EOF; | |
28 | + int c; | |
29 | +again: | |
30 | + c = getch(); | |
31 | + if (c == 127 || c == 8) | |
32 | + c = KEY_BACKSPACE; | |
33 | + /* syslog(LOG_INFO, "ccs_getch0='%c' (%d)\n", c, c); */ | |
34 | + if (c == '\r' || c == '\n') { | |
35 | + if (enter_key == EOF) | |
36 | + enter_key = c; | |
37 | + else if (c != enter_key) | |
38 | + goto again; | |
39 | + } | |
40 | + return c; | |
41 | +} | |
42 | + | |
43 | +static int ccs_getch2(void) | |
44 | +{ | |
45 | + static int c0 = 0; | |
46 | + static int c1 = 0; | |
47 | + static int c2 = 0; | |
48 | + static int c3 = 0; | |
49 | + static int len = 0; | |
50 | + if (len > 0) { | |
51 | + c0 = c1; | |
52 | + c1 = c2; | |
53 | + c2 = c3; | |
54 | + len--; | |
55 | + return c0; | |
56 | + } | |
57 | + c0 = ccs_getch0(); | |
58 | + if (c0 != 0x1B) | |
59 | + return c0; | |
60 | + c1 = ccs_getch0(); | |
61 | + if (c1 != '[') { | |
62 | + len = 1; | |
63 | + return c0; | |
64 | + } | |
65 | + c2 = ccs_getch0(); | |
66 | + if (c2 < '1' || c2 > '6') { | |
67 | + len = 2; | |
68 | + return c0; | |
69 | + } | |
70 | + c3 = ccs_getch0(); | |
71 | + if (c3 != '~') { | |
72 | + len = 3; | |
73 | + return c0; | |
74 | + } | |
75 | + /* syslog(LOG_INFO, "ccs_getch2='%c'\n", c2); */ | |
76 | + switch (c2) { | |
77 | + case '1': | |
78 | + return KEY_HOME; | |
79 | + case '2': | |
80 | + return KEY_IC; | |
81 | + case '3': | |
82 | + return KEY_DC; | |
83 | + case '4': | |
84 | + return KEY_END; | |
85 | + case '5': | |
86 | + return KEY_PPAGE; | |
87 | + case '6': | |
88 | + return KEY_NPAGE; | |
89 | + } | |
90 | + return 0; | |
91 | +} | |
92 | + | |
93 | +static int ccs_add_history(const char *buffer, const char **history, | |
94 | + const int history_count, const int max_history) | |
95 | +{ | |
96 | + char *cp = buffer ? strdup(buffer) : NULL; | |
97 | + if (!cp) | |
98 | + return history_count; | |
99 | + if (history_count && !strcmp(history[history_count - 1], cp)) { | |
100 | + free(cp); | |
101 | + return history_count; | |
102 | + } | |
103 | + if (history_count < max_history) { | |
104 | + history[history_count] = cp; | |
105 | + return history_count + 1; | |
106 | + } else if (max_history) { | |
107 | + int i; | |
108 | + free((char *) history[0]); | |
109 | + for (i = 0; i < history_count - 1; i++) | |
110 | + history[i] = history[i + 1]; | |
111 | + history[history_count - 1] = cp; | |
112 | + return history_count; | |
113 | + } | |
114 | + return 0; | |
115 | +} | |
116 | + | |
117 | +static int ccs_query_fd = EOF; | |
118 | +static char *ccs_initial_readline_data = NULL; | |
119 | + | |
120 | +static char *ccs_readline(const int start_y, const int start_x, | |
121 | + const char *prompt, const char *history[], | |
122 | + const int history_count, const int max_length, | |
123 | + const int scroll_width) | |
124 | +{ | |
125 | + const int prompt_len = prompt ? strlen(prompt) : 0; | |
126 | + int buffer_len = 0; | |
127 | + int line_pos = 0; | |
128 | + int cur_pos = 0; | |
129 | + int history_pos = 0; | |
130 | + _Bool tmp_saved = false; | |
131 | + static char *buffer = NULL; | |
132 | + static char *tmp_buffer = NULL; | |
133 | + { | |
134 | + int i; | |
135 | + for (i = 0; i < history_count; i++) | |
136 | + if (!history[i]) | |
137 | + return NULL; | |
138 | + } | |
139 | + { | |
140 | + char *tmp; | |
141 | + tmp = realloc(buffer, max_length + 1); | |
142 | + if (!tmp) | |
143 | + return NULL; | |
144 | + buffer = tmp; | |
145 | + tmp = realloc(tmp_buffer, max_length + 1); | |
146 | + if (!tmp) | |
147 | + return NULL; | |
148 | + tmp_buffer = tmp; | |
149 | + memset(buffer, 0, max_length + 1); | |
150 | + memset(tmp_buffer, 0, max_length + 1); | |
151 | + } | |
152 | + move(start_y, start_x); | |
153 | + history_pos = history_count; | |
154 | + if (ccs_initial_readline_data) { | |
155 | + strncpy(buffer, ccs_initial_readline_data, max_length); | |
156 | + buffer_len = strlen(buffer); | |
157 | + ungetch(KEY_END); | |
158 | + } | |
159 | + while (true) { | |
160 | + int window_width; | |
161 | + int window_height; | |
162 | + int c; | |
163 | + int x; | |
164 | + int y; | |
165 | + int i; | |
166 | + int ret_ignored; | |
167 | + getmaxyx(stdscr, window_height, window_width); | |
168 | + window_width -= prompt_len; | |
169 | + getyx(stdscr, y, x); | |
170 | + move(y, 0); | |
171 | + while (cur_pos > window_width - 1) { | |
172 | + cur_pos--; | |
173 | + line_pos++; | |
174 | + } | |
175 | + if (prompt_len) | |
176 | + printw("%s", prompt); | |
177 | + for (i = line_pos; i < line_pos + window_width; i++) { | |
178 | + if (i < buffer_len) | |
179 | + addch(buffer[i]); | |
180 | + else | |
181 | + break; | |
182 | + } | |
183 | + clrtoeol(); | |
184 | + move(y, cur_pos + prompt_len); | |
185 | + refresh(); | |
186 | + c = ccs_getch2(); | |
187 | + if (ccs_query_fd != EOF) | |
188 | + ret_ignored = write(ccs_query_fd, "\n", 1); | |
189 | + if (c == 4) { /* Ctrl-D */ | |
190 | + if (!buffer_len) | |
191 | + buffer_len = -1; | |
192 | + break; | |
193 | + } else if (c == KEY_IC) { | |
194 | + scrollok(stdscr, TRUE); | |
195 | + printw("\n"); | |
196 | + for (i = 0; i < history_count; i++) | |
197 | + printw("%d: '%s'\n", i, history[i]); | |
198 | + scrollok(stdscr, FALSE); | |
199 | + } else if (c >= 0x20 && c <= 0x7E && | |
200 | + buffer_len < max_length - 1) { | |
201 | + for (i = buffer_len - 1; i >= line_pos + cur_pos; i--) | |
202 | + buffer[i + 1] = buffer[i]; | |
203 | + buffer[line_pos + cur_pos] = c; | |
204 | + buffer[++buffer_len] = '\0'; | |
205 | + if (cur_pos < window_width - 1) | |
206 | + cur_pos++; | |
207 | + else | |
208 | + line_pos++; | |
209 | + } else if (c == '\r' || c == '\n') { | |
210 | + break; | |
211 | + } else if (c == KEY_BACKSPACE) { | |
212 | + if (line_pos + cur_pos) { | |
213 | + buffer_len--; | |
214 | + for (i = line_pos + cur_pos - 1; | |
215 | + i < buffer_len; i++) | |
216 | + buffer[i] = buffer[i + 1]; | |
217 | + buffer[buffer_len] = '\0'; | |
218 | + if (line_pos >= scroll_width && cur_pos == 0) { | |
219 | + line_pos -= scroll_width; | |
220 | + cur_pos += scroll_width - 1; | |
221 | + } else if (cur_pos) { | |
222 | + cur_pos--; | |
223 | + } else if (line_pos) { | |
224 | + line_pos--; | |
225 | + } | |
226 | + } | |
227 | + } else if (c == KEY_DC) { | |
228 | + if (line_pos + cur_pos < buffer_len) { | |
229 | + buffer_len--; | |
230 | + for (i = line_pos + cur_pos; i < buffer_len; | |
231 | + i++) | |
232 | + buffer[i] = buffer[i + 1]; | |
233 | + buffer[buffer_len] = '\0'; | |
234 | + } | |
235 | + } else if (c == KEY_UP) { | |
236 | + if (history_pos) { | |
237 | + if (!tmp_saved) { | |
238 | + tmp_saved = true; | |
239 | + strncpy(tmp_buffer, buffer, | |
240 | + max_length); | |
241 | + } | |
242 | + history_pos--; | |
243 | + strncpy(buffer, history[history_pos], | |
244 | + max_length); | |
245 | + buffer_len = strlen(buffer); | |
246 | + goto end_key; | |
247 | + } | |
248 | + } else if (c == KEY_DOWN) { | |
249 | + if (history_pos < history_count - 1) { | |
250 | + history_pos++; | |
251 | + strncpy(buffer, history[history_pos], | |
252 | + max_length); | |
253 | + buffer_len = strlen(buffer); | |
254 | + goto end_key; | |
255 | + } else if (tmp_saved) { | |
256 | + tmp_saved = false; | |
257 | + history_pos = history_count; | |
258 | + strncpy(buffer, tmp_buffer, max_length); | |
259 | + buffer_len = strlen(buffer); | |
260 | + goto end_key; | |
261 | + } | |
262 | + } else if (c == KEY_HOME) { | |
263 | + cur_pos = 0; | |
264 | + line_pos = 0; | |
265 | + } else if (c == KEY_END) { | |
266 | + goto end_key; | |
267 | + } else if (c == KEY_LEFT) { | |
268 | + if (line_pos >= scroll_width && cur_pos == 0) { | |
269 | + line_pos -= scroll_width; | |
270 | + cur_pos += scroll_width - 1; | |
271 | + } else if (cur_pos) { | |
272 | + cur_pos--; | |
273 | + } else if (line_pos) { | |
274 | + line_pos--; | |
275 | + } | |
276 | + } else if (c == KEY_RIGHT) { | |
277 | + if (line_pos + cur_pos < buffer_len) { | |
278 | + if (cur_pos < window_width - 1) | |
279 | + cur_pos++; | |
280 | + else if (line_pos + cur_pos < | |
281 | + buffer_len - scroll_width && | |
282 | + cur_pos >= scroll_width - 1) { | |
283 | + cur_pos -= scroll_width - 1; | |
284 | + line_pos += scroll_width; | |
285 | + } else { | |
286 | + line_pos++; | |
287 | + } | |
288 | + } | |
289 | + } | |
290 | + continue; | |
291 | +end_key: | |
292 | + cur_pos = buffer_len; | |
293 | + line_pos = 0; | |
294 | + if (cur_pos > window_width - 1) { | |
295 | + line_pos = buffer_len - (window_width - 1); | |
296 | + cur_pos = window_width - 1; | |
297 | + } | |
298 | + } | |
299 | + if (buffer_len == -1) | |
300 | + return NULL; | |
301 | + ccs_normalize_line(buffer); | |
302 | + return strdup(buffer); | |
303 | +} |
@@ -0,0 +1,372 @@ | ||
1 | +/* | |
2 | + * caitsithtools.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.1 2012/04/01 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | + | |
25 | +/* Use caitsith-agent process? */ | |
26 | +_Bool ccs_network_mode = false; | |
27 | +/* The IPv4 address of the remote host running the caitsith-agent . */ | |
28 | +u32 ccs_network_ip = INADDR_NONE; | |
29 | +/* The port number of the remote host running the caitsith-agent . */ | |
30 | +u16 ccs_network_port = 0; | |
31 | + | |
32 | +/* Prototypes */ | |
33 | + | |
34 | +/* Utility functions */ | |
35 | + | |
36 | +/** | |
37 | + * ccs_out_of_memory - Print error message and abort. | |
38 | + * | |
39 | + * This function does not return. | |
40 | + */ | |
41 | +static void ccs_out_of_memory(void) | |
42 | +{ | |
43 | + fprintf(stderr, "Out of memory. Aborted.\n"); | |
44 | + exit(1); | |
45 | +} | |
46 | + | |
47 | +/** | |
48 | + * ccs_strdup - strdup() with abort on error. | |
49 | + * | |
50 | + * @string: String to duplicate. | |
51 | + * | |
52 | + * Returns copy of @string on success, abort otherwise. | |
53 | + */ | |
54 | +char *ccs_strdup(const char *string) | |
55 | +{ | |
56 | + char *cp = strdup(string); | |
57 | + if (!cp) | |
58 | + ccs_out_of_memory(); | |
59 | + return cp; | |
60 | +} | |
61 | + | |
62 | +/** | |
63 | + * ccs_realloc - realloc() with abort on error. | |
64 | + * | |
65 | + * @ptr: Pointer to void. | |
66 | + * @size: New size. | |
67 | + * | |
68 | + * Returns return value of realloc() on success, abort otherwise. | |
69 | + */ | |
70 | +void *ccs_realloc(void *ptr, const size_t size) | |
71 | +{ | |
72 | + void *vp = realloc(ptr, size); | |
73 | + if (!vp) | |
74 | + ccs_out_of_memory(); | |
75 | + return vp; | |
76 | +} | |
77 | + | |
78 | +/** | |
79 | + * ccs_malloc - malloc() with abort on error. | |
80 | + * | |
81 | + * @size: Size to allocate. | |
82 | + * | |
83 | + * Returns return value of malloc() on success, abort otherwise. | |
84 | + * | |
85 | + * Allocated memory is cleared with 0. | |
86 | + */ | |
87 | +void *ccs_malloc(const size_t size) | |
88 | +{ | |
89 | + void *vp = malloc(size); | |
90 | + if (!vp) | |
91 | + ccs_out_of_memory(); | |
92 | + memset(vp, 0, size); | |
93 | + return vp; | |
94 | +} | |
95 | + | |
96 | +/** | |
97 | + * ccs_str_starts - Check whether the given string starts with the given keyword. | |
98 | + * | |
99 | + * @str: Pointer to "char *". | |
100 | + * @begin: Pointer to "const char *". | |
101 | + * | |
102 | + * Returns true if @str starts with @begin, false otherwise. | |
103 | + * | |
104 | + * Note that @begin will be removed from @str before returning true. Therefore, | |
105 | + * @str must not be "const char *". | |
106 | + * | |
107 | + * Note that this function in kernel source has different arguments and behaves | |
108 | + * differently. | |
109 | + */ | |
110 | +_Bool ccs_str_starts(char *str, const char *begin) | |
111 | +{ | |
112 | + const int len = strlen(begin); | |
113 | + if (strncmp(str, begin, len)) | |
114 | + return false; | |
115 | + memmove(str, str + len, strlen(str + len) + 1); | |
116 | + return true; | |
117 | +} | |
118 | + | |
119 | +/** | |
120 | + * ccs_normalize_line - Format string. | |
121 | + * | |
122 | + * @buffer: The line to normalize. | |
123 | + * | |
124 | + * Returns nothing. | |
125 | + * | |
126 | + * Leading and trailing whitespaces are removed. | |
127 | + * Multiple whitespaces are packed into single space. | |
128 | + */ | |
129 | +void ccs_normalize_line(char *buffer) | |
130 | +{ | |
131 | + unsigned char *sp = (unsigned char *) buffer; | |
132 | + unsigned char *dp = (unsigned char *) buffer; | |
133 | + _Bool first = true; | |
134 | + while (*sp && (*sp <= ' ' || 127 <= *sp)) | |
135 | + sp++; | |
136 | + while (*sp) { | |
137 | + if (!first) | |
138 | + *dp++ = ' '; | |
139 | + first = false; | |
140 | + while (' ' < *sp && *sp < 127) | |
141 | + *dp++ = *sp++; | |
142 | + while (*sp && (*sp <= ' ' || 127 <= *sp)) | |
143 | + sp++; | |
144 | + } | |
145 | + *dp = '\0'; | |
146 | +} | |
147 | + | |
148 | +/** | |
149 | + * ccs_decode - Decode a string in CaitSith's rule to a string in C. | |
150 | + * | |
151 | + * @ascii: Pointer to "const char". | |
152 | + * @bin: Pointer to "char". Must not contain wildcards nor '\000'. | |
153 | + * | |
154 | + * Returns true if @ascii was successfully decoded, false otherwise. | |
155 | + * | |
156 | + * Note that it is legal to pass @ascii == @bin if the caller want to decode | |
157 | + * a string in a temporary buffer. | |
158 | + */ | |
159 | +_Bool ccs_decode(const char *ascii, char *bin) | |
160 | +{ | |
161 | + while (true) { | |
162 | + char c = *ascii++; | |
163 | + *bin++ = c; | |
164 | + if (!c) | |
165 | + break; | |
166 | + if (c == '\\') { | |
167 | + char d; | |
168 | + char e; | |
169 | + u8 f; | |
170 | + c = *ascii++; | |
171 | + switch (c) { | |
172 | + case '0': /* "\ooo" */ | |
173 | + case '1': | |
174 | + case '2': | |
175 | + case '3': | |
176 | + d = *ascii++; | |
177 | + if (d < '0' || d > '7') | |
178 | + break; | |
179 | + e = *ascii++; | |
180 | + if (e < '0' || e > '7') | |
181 | + break; | |
182 | + f = (u8) ((c - '0') << 6) + | |
183 | + (((u8) (d - '0')) << 3) + | |
184 | + (((u8) (e - '0'))); | |
185 | + if (f <= ' ' || f >= 127 || f == '\\') { | |
186 | + *(bin - 1) = f; | |
187 | + continue; | |
188 | + } | |
189 | + } | |
190 | + return false; | |
191 | + } else if (c <= ' ' || c >= 127) { | |
192 | + return false; | |
193 | + } | |
194 | + } | |
195 | + return true; | |
196 | +} | |
197 | + | |
198 | +/** | |
199 | + * ccs_open_read - Open a file for reading. | |
200 | + * | |
201 | + * @filename: String to send to remote caitsith-agent program if using | |
202 | + * network mode, file to open for reading otherwise. | |
203 | + * | |
204 | + * Returns pointer to "FILE" on success, NULL otherwise. | |
205 | + */ | |
206 | +FILE *ccs_open_read(const char *filename) | |
207 | +{ | |
208 | + if (ccs_network_mode) { | |
209 | + FILE *fp = ccs_open_write(filename); | |
210 | + if (fp) { | |
211 | + fputc(0, fp); | |
212 | + fflush(fp); | |
213 | + } | |
214 | + return fp; | |
215 | + } else { | |
216 | + return fopen(filename, "r"); | |
217 | + } | |
218 | +} | |
219 | + | |
220 | +/** | |
221 | + * ccs_open_stream - Establish IP connection. | |
222 | + * | |
223 | + * @filename: String to send to remote caitsith-agent program. | |
224 | + * | |
225 | + * Retruns file descriptor on success, EOF otherwise. | |
226 | + */ | |
227 | +int ccs_open_stream(const char *filename) | |
228 | +{ | |
229 | + const int fd = socket(AF_INET, SOCK_STREAM, 0); | |
230 | + struct sockaddr_in addr; | |
231 | + char c; | |
232 | + int len = strlen(filename) + 1; | |
233 | + memset(&addr, 0, sizeof(addr)); | |
234 | + addr.sin_family = AF_INET; | |
235 | + addr.sin_addr.s_addr = ccs_network_ip; | |
236 | + addr.sin_port = ccs_network_port; | |
237 | + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) || | |
238 | + write(fd, filename, len) != len || read(fd, &c, 1) != 1 || c) { | |
239 | + close(fd); | |
240 | + return EOF; | |
241 | + } | |
242 | + return fd; | |
243 | +} | |
244 | + | |
245 | +/** | |
246 | + * ccs_open_write - Open a file for writing. | |
247 | + * | |
248 | + * @filename: String to send to remote caitsith-agent program if using | |
249 | + * network mode, file to open for writing otherwise. | |
250 | + * | |
251 | + * Returns pointer to "FILE" on success, NULL otherwise. | |
252 | + */ | |
253 | +FILE *ccs_open_write(const char *filename) | |
254 | +{ | |
255 | + if (ccs_network_mode) { | |
256 | + const int fd = socket(AF_INET, SOCK_STREAM, 0); | |
257 | + struct sockaddr_in addr; | |
258 | + FILE *fp; | |
259 | + memset(&addr, 0, sizeof(addr)); | |
260 | + addr.sin_family = AF_INET; | |
261 | + addr.sin_addr.s_addr = ccs_network_ip; | |
262 | + addr.sin_port = ccs_network_port; | |
263 | + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr))) { | |
264 | + close(fd); | |
265 | + return NULL; | |
266 | + } | |
267 | + fp = fdopen(fd, "r+"); | |
268 | + /* setbuf(fp, NULL); */ | |
269 | + fprintf(fp, "%s", filename); | |
270 | + fputc(0, fp); | |
271 | + fflush(fp); | |
272 | + if (fgetc(fp) != 0) { | |
273 | + fclose(fp); | |
274 | + return NULL; | |
275 | + } | |
276 | + return fp; | |
277 | + } else { | |
278 | + return fdopen(open(filename, O_WRONLY), "w"); | |
279 | + } | |
280 | +} | |
281 | + | |
282 | +/* Is the shared buffer for ccs_freadline() owned? */ | |
283 | +static _Bool ccs_buffer_locked = false; | |
284 | + | |
285 | +/** | |
286 | + * ccs_get - Mark the shared buffer for ccs_freadline() owned. | |
287 | + * | |
288 | + * Returns nothing. | |
289 | + * | |
290 | + * This is for avoiding accidental overwriting. | |
291 | + * ccs_freadline() have their own memory buffer. | |
292 | + */ | |
293 | +void ccs_get(void) | |
294 | +{ | |
295 | + if (ccs_buffer_locked) | |
296 | + ccs_out_of_memory(); | |
297 | + ccs_buffer_locked = true; | |
298 | +} | |
299 | + | |
300 | +/** | |
301 | + * ccs_put - Mark the shared buffer for ccs_freadline() no longer owned. | |
302 | + * | |
303 | + * Returns nothing. | |
304 | + * | |
305 | + * This is for avoiding accidental overwriting. | |
306 | + * ccs_freadline() have their own memory buffer. | |
307 | + */ | |
308 | +void ccs_put(void) | |
309 | +{ | |
310 | + if (!ccs_buffer_locked) | |
311 | + ccs_out_of_memory(); | |
312 | + ccs_buffer_locked = false; | |
313 | +} | |
314 | + | |
315 | +/** | |
316 | + * ccs_freadline - Read a line from file to dynamically allocated buffer. | |
317 | + * | |
318 | + * @fp: Pointer to "FILE". | |
319 | + * | |
320 | + * Returns pointer to dynamically allocated buffer on success, NULL otherwise. | |
321 | + * | |
322 | + * The caller must not free() the returned pointer. | |
323 | + */ | |
324 | +char *ccs_freadline(FILE *fp) | |
325 | +{ | |
326 | + static char *policy = NULL; | |
327 | + int pos = 0; | |
328 | + while (true) { | |
329 | + static int max_policy_len = 0; | |
330 | + const int c = fgetc(fp); | |
331 | + if (c == EOF) | |
332 | + return NULL; | |
333 | + if (ccs_network_mode && !c) | |
334 | + return NULL; | |
335 | + if (pos == max_policy_len) { | |
336 | + max_policy_len += 4096; | |
337 | + policy = ccs_realloc(policy, max_policy_len); | |
338 | + } | |
339 | + policy[pos++] = (char) c; | |
340 | + if (c == '\n') { | |
341 | + policy[--pos] = '\0'; | |
342 | + break; | |
343 | + } | |
344 | + } | |
345 | + ccs_normalize_line(policy); | |
346 | + return policy; | |
347 | +} | |
348 | + | |
349 | +/** | |
350 | + * ccs_check_remote_host - Check whether the remote host is running with the CaitSith 0.1 kernel or not. | |
351 | + * | |
352 | + * Returns true if running with CaitSith 0.1 kernel, false otherwise. | |
353 | + */ | |
354 | +_Bool ccs_check_remote_host(void) | |
355 | +{ | |
356 | + int major = 0; | |
357 | + int minor = 0; | |
358 | + FILE *fp = ccs_open_read("version"); | |
359 | + if (!fp || | |
360 | + fscanf(fp, "%u.%u", &major, &minor) < 2 || | |
361 | + major != 0 || minor != 1) { | |
362 | + const u32 ip = ntohl(ccs_network_ip); | |
363 | + fprintf(stderr, "Can't connect to %u.%u.%u.%u:%u\n", | |
364 | + (u8) (ip >> 24), (u8) (ip >> 16), | |
365 | + (u8) (ip >> 8), (u8) ip, ntohs(ccs_network_port)); | |
366 | + if (fp) | |
367 | + fclose(fp); | |
368 | + return false; | |
369 | + } | |
370 | + fclose(fp); | |
371 | + return true; | |
372 | +} |
@@ -0,0 +1,38 @@ | ||
1 | +include ../Include.make | |
2 | + | |
3 | +BUILD_FILES := caitsith-auditd caitsith-loadpolicy caitsith-notifyd caitsith-pstree caitsith-queryd \ | |
4 | + caitsith-savepolicy | |
5 | + | |
6 | +all: libcaitsithtools.so $(BUILD_FILES) | |
7 | + | |
8 | +$(BUILD_FILES): libcaitsithtools.so | |
9 | + | |
10 | +/usr/include/curses.h: | |
11 | + @echo "/usr/include/curses.h is missing." | |
12 | + @echo "Run 'yum install ncurses-devel' or 'apt-get install libncurses-dev'" | |
13 | + sleep 10 | |
14 | + | |
15 | +libcaitsithtools.so: caitsithtools.c caitsithtools.h | |
16 | + $(CC) $(CFLAGS) -fPIC caitsithtools.c -shared -Wl,-soname,libcaitsithtools.so.1 -o libcaitsithtools.so.1.0.0 | |
17 | + ln -sf libcaitsithtools.so.1.0.0 libcaitsithtools.so | |
18 | + | |
19 | +.c: | |
20 | + $(CC) $(CFLAGS) -o $@ $< -lcaitsithtools -L. | |
21 | + | |
22 | +caitsith-queryd: caitsithtools.h caitsith-queryd.c readline.h /usr/include/curses.h libcaitsithtools.so | |
23 | + $(CC) $(CFLAGS) -o caitsith-queryd caitsith-queryd.c -lncurses -lcaitsithtools -L. | |
24 | + | |
25 | +install: all | |
26 | + mkdir -p -m 0755 $(INSTALLDIR)$(USRLIBDIR) | |
27 | + $(INSTALL) -m 0755 libcaitsithtools.so.1.0.0 $(INSTALLDIR)$(USRLIBDIR) | |
28 | + ln -sf libcaitsithtools.so.1.0.0 $(INSTALLDIR)$(USRLIBDIR)/libcaitsithtools.so.4 | |
29 | +ifeq ($(INSTALLDIR),) | |
30 | + ldconfig || true | |
31 | +endif | |
32 | + mkdir -p -m 0755 $(INSTALLDIR)$(USRSBINDIR) | |
33 | + $(INSTALL) -m 0755 $(BUILD_FILES) $(INSTALLDIR)$(USRSBINDIR) | |
34 | + | |
35 | +clean: | |
36 | + rm -f -- $(BUILD_FILES) libcaitsithtools.so* | |
37 | + | |
38 | +.PHONY: clean install |
@@ -0,0 +1,713 @@ | ||
1 | +/* | |
2 | + * init_policy.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2011 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.1 2012/04/01 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#define _FILE_OFFSET_BITS 64 | |
24 | +#define _LARGEFILE_SOURCE | |
25 | +#define _LARGEFILE64_SOURCE | |
26 | +#include <stdio.h> | |
27 | +#include <string.h> | |
28 | +#include <stdlib.h> | |
29 | +#include <sys/types.h> | |
30 | +#include <sys/stat.h> | |
31 | +#include <unistd.h> | |
32 | +#include <dirent.h> | |
33 | +#include <limits.h> | |
34 | +#include <sys/vfs.h> | |
35 | +#include <time.h> | |
36 | +#include <errno.h> | |
37 | + | |
38 | +#if defined(__GLIBC__) | |
39 | +/** | |
40 | + * get_realpath - Wrapper for realpath(3). | |
41 | + * | |
42 | + * @path: Pathname to resolve. | |
43 | + * | |
44 | + * Returns realpath of @path on success, NULL otherwise. | |
45 | + * | |
46 | + * Caller must free() the returned pointer if this function did not return | |
47 | + * NULL. | |
48 | + */ | |
49 | +static inline char *get_realpath(const char *path) | |
50 | +{ | |
51 | + return realpath(path, NULL); | |
52 | +} | |
53 | +#else | |
54 | +/** | |
55 | + * get_realpath - Fallback routine for realpath(3). | |
56 | + * | |
57 | + * @path: Pathname to resolve. | |
58 | + * | |
59 | + * Returns realpath of @path on success, NULL otherwise. | |
60 | + * | |
61 | + * realpath(@path, NULL) works on GLIBC, but will SIGSEGV on others. | |
62 | + * | |
63 | + * Caller must free() the returned pointer if this function did not return | |
64 | + * NULL. | |
65 | + */ | |
66 | +static char *get_realpath(const char *path) | |
67 | +{ | |
68 | + struct stat buf; | |
69 | + static const int pwd_len = PATH_MAX * 2; | |
70 | + char *dir = strdup(path); | |
71 | + char *pwd = malloc(pwd_len); | |
72 | + char *basename = NULL; | |
73 | + int len; | |
74 | + if (!dir || !pwd) | |
75 | + goto out; | |
76 | + if (stat(dir, &buf)) | |
77 | + goto out; | |
78 | + len = strlen(dir); | |
79 | + while (len > 1 && dir[len - 1] == '/') | |
80 | + dir[--len] = '\0'; | |
81 | + while (!lstat(dir, &buf) && S_ISLNK(buf.st_mode)) { | |
82 | + char *new_dir; | |
83 | + char *old_dir = dir; | |
84 | + memset(pwd, 0, pwd_len); | |
85 | + if (readlink(dir, pwd, pwd_len - 1) < 1) | |
86 | + goto out; | |
87 | + if (pwd[0] == '/') { | |
88 | + dir[0] = '\0'; | |
89 | + } else { | |
90 | + char *cp = strrchr(dir, '/'); | |
91 | + if (cp) | |
92 | + *cp = '\0'; | |
93 | + } | |
94 | + len = strlen(dir) + strlen(pwd) + 4; | |
95 | + new_dir = malloc(len); | |
96 | + if (new_dir) | |
97 | + snprintf(new_dir, len - 1, "%s/%s", dir, pwd); | |
98 | + dir = new_dir; | |
99 | + free(old_dir); | |
100 | + if (!dir) | |
101 | + goto out; | |
102 | + } | |
103 | + if (!dir) | |
104 | + goto out; | |
105 | + basename = strrchr(dir, '/'); | |
106 | + if (basename) | |
107 | + *basename++ = '\0'; | |
108 | + else | |
109 | + basename = ""; | |
110 | + if (chdir(dir)) | |
111 | + goto out; | |
112 | + memset(pwd, 0, pwd_len); | |
113 | + if (!getcwd(pwd, pwd_len - 1)) | |
114 | + goto out; | |
115 | + if (strcmp(pwd, "/")) | |
116 | + len = strlen(pwd); | |
117 | + else | |
118 | + len = 0; | |
119 | + snprintf(pwd + len, pwd_len - len - 1, "/%s", basename); | |
120 | + free(dir); | |
121 | + return pwd; | |
122 | +out: | |
123 | + free(dir); | |
124 | + free(pwd); | |
125 | + return NULL; | |
126 | +} | |
127 | +#endif | |
128 | + | |
129 | +#define elementof(x) (sizeof(x) / sizeof(x[0])) | |
130 | + | |
131 | +/** | |
132 | + * scandir_file_filter - Callback for scandir(). | |
133 | + * | |
134 | + * @buf: Pointer to "const struct dirent". | |
135 | + * | |
136 | + * Returns non 0 if @buf seems to be a file, 0 otherwise. | |
137 | + * | |
138 | + * Since several kernels have a bug that leaves @buf->d_type == DT_UNKNOWN, | |
139 | + * we allow it for now and recheck it later. | |
140 | + */ | |
141 | +static int scandir_file_filter(const struct dirent *buf) | |
142 | +{ | |
143 | + return (buf->d_type == DT_REG || buf->d_type == DT_UNKNOWN) && | |
144 | + strcmp(buf->d_name, ".") && strcmp(buf->d_name, ".."); | |
145 | +} | |
146 | + | |
147 | +/** | |
148 | + * revalidate_path - Recheck file's attribute. | |
149 | + * | |
150 | + * @path: Pathname to check. | |
151 | + * | |
152 | + * Returns type of @path. | |
153 | + * | |
154 | + * This is needed by buggy kernels that report DT_UNKNOWN upon scandir(). | |
155 | + */ | |
156 | +static unsigned char revalidate_path(const char *path) | |
157 | +{ | |
158 | + struct stat buf; | |
159 | + unsigned char type = DT_UNKNOWN; | |
160 | + if (!lstat(path, &buf)) { | |
161 | + if (S_ISREG(buf.st_mode)) | |
162 | + type = DT_REG; | |
163 | + else if (S_ISDIR(buf.st_mode)) | |
164 | + type = DT_DIR; | |
165 | + else if (S_ISLNK(buf.st_mode)) | |
166 | + type = DT_LNK; | |
167 | + } | |
168 | + return type; | |
169 | +} | |
170 | + | |
171 | +/* File handle to /etc/caitsith/policy/current . */ | |
172 | +static FILE *filp = NULL; | |
173 | + | |
174 | +/** | |
175 | + * printf_encoded - Print a word to the policy file, with escaping as needed. | |
176 | + * | |
177 | + * @str: Word to print. Needn't to follow CaitSith's escape rules. | |
178 | + * | |
179 | + * Returns nothing. | |
180 | + * | |
181 | + * If @str starts with "/proc/", it is converted with "proc:/". | |
182 | + */ | |
183 | +static void printf_encoded(const char *str) | |
184 | +{ | |
185 | + if (!strncmp(str, "/proc/", 6)) { | |
186 | + fprintf(filp, "proc:"); | |
187 | + str += 5; | |
188 | + } | |
189 | + while (1) { | |
190 | + const char c = *str++; | |
191 | + if (!c) | |
192 | + break; | |
193 | + if (c > ' ' && c < 127 && c != '\\') | |
194 | + fputc(c, filp); | |
195 | + else | |
196 | + fprintf(filp, "\\%c%c%c", (c >> 6) + '0', | |
197 | + ((c >> 3) & 7) + '0', (c & 7) + '0'); | |
198 | + } | |
199 | +} | |
200 | + | |
201 | +static void make_default_domain_transition(const char *path) | |
202 | +{ | |
203 | + fprintf(filp, " 10 allow path=\""); | |
204 | + printf_encoded(path); | |
205 | + fprintf(filp, "\" transition=\""); | |
206 | + printf_encoded(path); | |
207 | + fprintf(filp, "\"\n"); | |
208 | +} | |
209 | + | |
210 | + | |
211 | +/* Shared buffer for scandir(). */ | |
212 | +static char path[8192]; | |
213 | + | |
214 | +/** | |
215 | + * scan_executable_files - Find executable files in the specific directory. | |
216 | + * | |
217 | + * @dir: Directory name to scan. | |
218 | + * | |
219 | + * Returns nothing. | |
220 | + */ | |
221 | +static void scan_executable_files(const char *dir) | |
222 | +{ | |
223 | + struct dirent **namelist; | |
224 | + int n = scandir(dir, &namelist, scandir_file_filter, 0); | |
225 | + int i; | |
226 | + if (n < 0) | |
227 | + return; | |
228 | + for (i = 0; i < n; i++) { | |
229 | + unsigned char type = namelist[i]->d_type; | |
230 | + snprintf(path, sizeof(path) - 1, "%s/%s", dir, | |
231 | + namelist[i]->d_name); | |
232 | + if (type == DT_UNKNOWN) | |
233 | + type = revalidate_path(path); | |
234 | + if (type == DT_REG && !access(path, X_OK)) | |
235 | + make_default_domain_transition(path); | |
236 | + free(namelist[i]); | |
237 | + } | |
238 | + free(namelist); | |
239 | +} | |
240 | + | |
241 | +/** | |
242 | + * scan_modprobe_and_hotplug - Mark modprobe and hotplug as domain_transition entries. | |
243 | + * | |
244 | + * Returns nothing. | |
245 | + */ | |
246 | +static void scan_modprobe_and_hotplug(void) | |
247 | +{ | |
248 | + static const char * const files[2] = { | |
249 | + "/proc/sys/kernel/modprobe", "/proc/sys/kernel/hotplug" | |
250 | + }; | |
251 | + int i; | |
252 | + for (i = 0; i < elementof(files); i++) { | |
253 | + char *ret_ignored; | |
254 | + char buffer[PATH_MAX + 1]; | |
255 | + char *cp; | |
256 | + FILE *fp = fopen(files[i], "r"); | |
257 | + if (!fp) | |
258 | + continue; | |
259 | + memset(buffer, 0, sizeof(buffer)); | |
260 | + ret_ignored = fgets(buffer, sizeof(buffer) - 1, fp); | |
261 | + fclose(fp); | |
262 | + cp = strrchr(buffer, '\n'); | |
263 | + if (cp) | |
264 | + *cp = '\0'; | |
265 | + if (!buffer[0]) | |
266 | + continue; | |
267 | + cp = get_realpath(buffer); | |
268 | + if (!cp) | |
269 | + continue; | |
270 | + /* We ignore /bin/true if /proc/sys/kernel/modprobe said so. */ | |
271 | + if (strcmp(cp, "/bin/true") && !access(cp, X_OK)) | |
272 | + make_default_domain_transition(cp); | |
273 | + free(cp); | |
274 | + } | |
275 | +} | |
276 | + | |
277 | +/** | |
278 | + * scan_init_dir - Mark programs under /etc/init.d/ directory as default domain transition entries. | |
279 | + * | |
280 | + * Returns nothing. | |
281 | + */ | |
282 | +static void scan_init_dir(void) | |
283 | +{ | |
284 | + char *dir = get_realpath("/etc/init.d/"); | |
285 | + if (!dir) | |
286 | + return; | |
287 | + scan_executable_files(dir); | |
288 | + free(dir); | |
289 | +} | |
290 | + | |
291 | +/** | |
292 | + * scan_daemons - Mark daemon programs as default domain transition entries. | |
293 | + * | |
294 | + * Returns nothing. | |
295 | + */ | |
296 | +static void scan_daemons(void) | |
297 | +{ | |
298 | + static const char * const files[] = { | |
299 | + "/sbin/cardmgr", | |
300 | + "/sbin/getty", | |
301 | + "/sbin/init", | |
302 | + "/sbin/klogd", | |
303 | + "/sbin/mingetty", | |
304 | + "/sbin/portmap", | |
305 | + "/sbin/rpc.statd", | |
306 | + "/sbin/syslogd", | |
307 | + "/sbin/udevd", | |
308 | + "/usr/X11R6/bin/xfs", | |
309 | + "/usr/bin/dbus-daemon", | |
310 | + "/usr/bin/dbus-daemon-1", | |
311 | + "/usr/bin/jserver", | |
312 | + "/usr/bin/mDNSResponder", | |
313 | + "/usr/bin/nifd", | |
314 | + "/usr/bin/spamd", | |
315 | + "/usr/sbin/acpid", | |
316 | + "/usr/sbin/afpd", | |
317 | + "/usr/sbin/anacron", | |
318 | + "/usr/sbin/apache2", | |
319 | + "/usr/sbin/apmd", | |
320 | + "/usr/sbin/atalkd", | |
321 | + "/usr/sbin/atd", | |
322 | + "/usr/sbin/cannaserver", | |
323 | + "/usr/sbin/cpuspeed", | |
324 | + "/usr/sbin/cron", | |
325 | + "/usr/sbin/crond", | |
326 | + "/usr/sbin/cupsd", | |
327 | + "/usr/sbin/dhcpd", | |
328 | + "/usr/sbin/exim4", | |
329 | + "/usr/sbin/gpm", | |
330 | + "/usr/sbin/hald", | |
331 | + "/usr/sbin/htt", | |
332 | + "/usr/sbin/httpd", | |
333 | + "/usr/sbin/inetd", | |
334 | + "/usr/sbin/logrotate", | |
335 | + "/usr/sbin/lpd", | |
336 | + "/usr/sbin/nmbd", | |
337 | + "/usr/sbin/papd", | |
338 | + "/usr/sbin/rpc.idmapd", | |
339 | + "/usr/sbin/rpc.mountd", | |
340 | + "/usr/sbin/rpc.rquotad", | |
341 | + "/usr/sbin/sendmail.sendmail", | |
342 | + "/usr/sbin/smartd", | |
343 | + "/usr/sbin/smbd", | |
344 | + "/usr/sbin/squid", | |
345 | + "/usr/sbin/sshd", | |
346 | + "/usr/sbin/vmware-guestd", | |
347 | + "/usr/sbin/vsftpd", | |
348 | + "/usr/sbin/xinetd" | |
349 | + }; | |
350 | + int i; | |
351 | + for (i = 0; i < elementof(files); i++) { | |
352 | + char *cp = get_realpath(files[i]); | |
353 | + if (!cp) | |
354 | + continue; | |
355 | + if (!access(cp, X_OK)) | |
356 | + make_default_domain_transition(cp); | |
357 | + free(cp); | |
358 | + } | |
359 | +} | |
360 | + | |
361 | +/** | |
362 | + * mkdir2 - mkdir() with ignoring EEXIST error. | |
363 | + * | |
364 | + * @dir: Directory to create. | |
365 | + * @mode: Create mode. | |
366 | + * | |
367 | + * Returns 0 on success, EOF otehrwise. | |
368 | + */ | |
369 | +static int mkdir2(const char *dir, int mode) | |
370 | +{ | |
371 | + return mkdir(dir, mode) == 0 || errno == EEXIST ? 0 : EOF; | |
372 | +} | |
373 | + | |
374 | +/* Policy directory. Default is "/etc/caitsith/". */ | |
375 | +static char *policy_dir = NULL; | |
376 | + | |
377 | +/** | |
378 | + * make_policy_dir - Create policy directories and tools directories. | |
379 | + * | |
380 | + * Returns nothing. | |
381 | + */ | |
382 | +static void make_policy_dir(void) | |
383 | +{ | |
384 | + char *dir = policy_dir; | |
385 | + const time_t now = time(NULL); | |
386 | + struct tm *tm = localtime(&now); | |
387 | + char stamp[20] = { }; | |
388 | + snprintf(stamp, sizeof(stamp) - 1, "%02d-%02d-%02d.%02d:%02d:%02d", | |
389 | + tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, | |
390 | + tm->tm_min, tm->tm_sec); | |
391 | + if (!chdir(policy_dir) && !chdir("policy")) | |
392 | + goto tools_dir; | |
393 | + fprintf(stderr, "Creating policy directory... "); | |
394 | + while (1) { | |
395 | + const char c = *dir++; | |
396 | + if (!c) | |
397 | + break; | |
398 | + if (c != '/') | |
399 | + continue; | |
400 | + *(dir - 1) = '\0'; | |
401 | + mkdir(policy_dir, 0700); | |
402 | + *(dir - 1) = '/'; | |
403 | + } | |
404 | + if (mkdir2(policy_dir, 0700) || chdir(policy_dir) || | |
405 | + mkdir2("policy", 0700) || chdir("policy")) { | |
406 | + fprintf(stderr, "failed.\n"); | |
407 | + exit(1); | |
408 | + } else { | |
409 | + fprintf(stderr, "OK\n"); | |
410 | + } | |
411 | +tools_dir: | |
412 | + if (!chdir(policy_dir) && !chdir("tools")) | |
413 | + return; | |
414 | + fprintf(stderr, "Creating configuration directory... "); | |
415 | + mkdir("tools", 0700); | |
416 | + if (!chdir("tools")) | |
417 | + fprintf(stderr, "OK\n"); | |
418 | + else { | |
419 | + fprintf(stderr, "failed.\n"); | |
420 | + exit(1); | |
421 | + } | |
422 | +} | |
423 | + | |
424 | +/** | |
425 | + * chdir_policy - Change to policy directory. | |
426 | + * | |
427 | + * Returns 1 on success, 0 otherwise. | |
428 | + */ | |
429 | +static _Bool chdir_policy(void) | |
430 | +{ | |
431 | + if (chdir(policy_dir) || chdir("policy")) { | |
432 | + fprintf(stderr, "ERROR: Can't chdir to %s/policy/ " | |
433 | + "directory.\n", policy_dir); | |
434 | + return 0; | |
435 | + } | |
436 | + return 1; | |
437 | +} | |
438 | + | |
439 | +/** | |
440 | + * close_file - Close file and rename. | |
441 | + * | |
442 | + * @fp: Pointer to "FILE". | |
443 | + * @condition: Preconditions before rename(). | |
444 | + * @old: Temporary file's pathname. | |
445 | + * @new: Final file's pathname. | |
446 | + * | |
447 | + * Returns nothing. | |
448 | + */ | |
449 | +static void close_file(FILE *fp, _Bool condition, const char *old, | |
450 | + const char *new) | |
451 | +{ | |
452 | + if (fsync(fileno(fp)) || fclose(fp) || !condition || rename(old, new)) | |
453 | + fprintf(stderr, "failed.\n"); | |
454 | + else | |
455 | + fprintf(stderr, "OK.\n"); | |
456 | +} | |
457 | + | |
458 | +/** | |
459 | + * make_policy - Make /etc/caitsith/policy/current . | |
460 | + * | |
461 | + * Returns nothing. | |
462 | + */ | |
463 | +static void make_policy(void) | |
464 | +{ | |
465 | + if (!chdir_policy()) | |
466 | + return; | |
467 | + if (!access("current", R_OK)) | |
468 | + return; | |
469 | + filp = fopen("current.tmp", "w"); | |
470 | + if (!filp) { | |
471 | + fprintf(stderr, "ERROR: Can't create policy.\n"); | |
472 | + return; | |
473 | + } | |
474 | + fprintf(stderr, "Creating default policy... "); | |
475 | + fprintf(filp, "POLICY_VERSION=20120401\n"); | |
476 | + fprintf(filp, "\n"); | |
477 | + fprintf(filp, "quota memory audit 16777216\n"); | |
478 | + fprintf(filp, "quota memory query 1048576\n"); | |
479 | + fprintf(filp, "quota audit[1] allowed=0 denied=1024 unmatched=1024\n"); | |
480 | + fprintf(filp, "\n"); | |
481 | + fprintf(filp, "10000 acl execute\n" | |
482 | + " audit 0\n"); | |
483 | + scan_modprobe_and_hotplug(); | |
484 | + scan_daemons(); | |
485 | + scan_init_dir(); | |
486 | + fprintf(filp, "\n"); | |
487 | + { | |
488 | + char *tools_dir = get_realpath("/usr/sbin"); | |
489 | + fprintf(filp, "0 acl modify_policy\n" | |
490 | + " audit 1\n" | |
491 | + " 1 deny task.uid!=0\n" | |
492 | + " 1 deny task.euid!=0\n" | |
493 | + " 100 allow task.exe=\"%s/caitsith-loadpolicy\"\n" | |
494 | + " 100 allow task.exe=\"%s/caitsith-queryd\"\n" | |
495 | + " 10000 deny\n", tools_dir, tools_dir); | |
496 | + } | |
497 | + close_file(filp, chdir_policy(), "current.tmp", "current"); | |
498 | + filp = NULL; | |
499 | +} | |
500 | + | |
501 | +/* The name of loadable kernel module to load. */ | |
502 | +static const char *module_name = "caitsith"; | |
503 | + | |
504 | +/** | |
505 | + * make_module_loader - Make /etc/caitsith/caitsith-load-module . | |
506 | + * | |
507 | + * Returns nothing. | |
508 | + */ | |
509 | +static void make_module_loader(void) | |
510 | +{ | |
511 | + FILE *fp; | |
512 | + if (chdir(policy_dir) || !access("caitsith-load-module", X_OK) | |
513 | + || !module_name[0]) | |
514 | + return; | |
515 | + fp = fopen("caitsith-load-module.tmp", "w"); | |
516 | + if (!fp) { | |
517 | + fprintf(stderr, "ERROR: Can't create module loader.\n"); | |
518 | + return; | |
519 | + } | |
520 | + fprintf(stderr, "Creating module loader... "); | |
521 | + fprintf(fp, "#! /bin/sh\n"); | |
522 | + fprintf(fp, "export PATH=$PATH:/sbin:/bin\n"); | |
523 | + fprintf(fp, "exec modprobe %s\n", module_name); | |
524 | + close_file(fp, !chmod("caitsith-load-module.tmp", 0700), | |
525 | + "caitsith-load-module.tmp", "caitsith-load-module"); | |
526 | +} | |
527 | + | |
528 | +/* Content of /etc/caitsith/tools/auditd.conf . */ | |
529 | +static const char auditd_data[] = | |
530 | +"# This file contains sorting rules used by caitsith-auditd command.\n" | |
531 | +"\n" | |
532 | +"# An audit log consists with two parts delimited by \" / \" sequence.\n" | |
533 | +"# You can refer the former part using 'header' keyword, the latter part\n" | |
534 | +"# using 'acl' keyword.\n" | |
535 | +"#\n" | |
536 | +"# Words in each part are separated by a space character. Therefore, you can\n" | |
537 | +"# use 'header[index]', 'acl[index]' for referring index'th word of the\n" | |
538 | +"# part.\n" | |
539 | +"# The index starts from 1, and 0 refers the whole line\n" | |
540 | +"# (i.e. 'header[0]' = 'header', 'acl[0]' = 'acl').\n" | |
541 | +"#\n" | |
542 | +"# Three operators are provided for conditional sorting.\n" | |
543 | +"# '.contains' is for 'fgrep keyword' match.\n" | |
544 | +"# '.equals' is for 'grep ^keyword$' match.\n" | |
545 | +"# '.starts' is for 'grep ^keyword' match.\n" | |
546 | +"#\n" | |
547 | +"# Sorting rules are defined using multi-lined chunks. A chunk is terminated\n" | |
548 | +"# by a 'destination' line which specifies the pathname to write the audit\n" | |
549 | +"# log. A 'destination' line is processed only when all preceding 'header'\n" | |
550 | +"# and 'acl' lines in that chunk have matched.\n" | |
551 | +"# Evaluation stops at the first processed 'destination' line.\n" | |
552 | +"# Therefore, no audit logs are written more than once.\n" | |
553 | +"#\n" | |
554 | +"# More specific matches should be placed before less specific matches.\n" | |
555 | +"# For example:\n" | |
556 | +"#\n" | |
557 | +"# header.contains result=denied\n" | |
558 | +"# acl.contains task.domain=\"/usr/sbin/httpd\"\n" | |
559 | +"# destination /var/log/caitsith/httpd_denied.log\n" | |
560 | +"#\n" | |
561 | +"# This chunk should be placed before the chunk that matches logs with\n" | |
562 | +"# result=denied. If placed after, the audit logs for /usr/sbin/httpd will\n" | |
563 | +"# be sent to /var/log/caitsith/denied.log .\n" | |
564 | +"\n" | |
565 | +"# Please use CaitSith's escape rule (e.g. '\\040' rather than '\\ ' for\n" | |
566 | +"# representing a ' ' in a word).\n" | |
567 | +"\n" | |
568 | +"# Send all allowed logs to /dev/null.\n" | |
569 | +"header.contains result=allowed\n" | |
570 | +"destination /dev/null\n" | |
571 | +"\n" | |
572 | +"# Send all unmatched logs to /var/log/caitsith/unmatched.log\n" | |
573 | +"header.contains result=unmatched\n" | |
574 | +"destination /var/log/caitsith/unmatched.log\n" | |
575 | +"\n" | |
576 | +"# Send all denied logs to /var/log/caitsith/denied.log\n" | |
577 | +"header.contains result=denied\n" | |
578 | +"destination /var/log/caitsith/denied.log\n" | |
579 | +"\n"; | |
580 | + | |
581 | +/** | |
582 | + * make_auditd_conf - Make /etc/caitsith/tools/auditd.conf . | |
583 | + * | |
584 | + * Returns nothing. | |
585 | + */ | |
586 | +static void make_auditd_conf(void) | |
587 | +{ | |
588 | + FILE *fp; | |
589 | + if (chdir(policy_dir) || chdir("tools") || | |
590 | + !access("auditd.conf", R_OK)) | |
591 | + return; | |
592 | + fp = fopen("auditd.tmp", "w"); | |
593 | + if (!fp) { | |
594 | + fprintf(stderr, "ERROR: Can't create configuration file.\n"); | |
595 | + return; | |
596 | + } | |
597 | + fprintf(stderr, "Creating configuration file for caitsith-auditd ... "); | |
598 | + fprintf(fp, "%s", auditd_data); | |
599 | + close_file(fp, !chmod("auditd.tmp", 0644), "auditd.tmp", | |
600 | + "auditd.conf"); | |
601 | +} | |
602 | + | |
603 | +/* Content of /etc/caitsith/tools/notifyd.conf . */ | |
604 | +static const char notifyd_data[] = | |
605 | +"# This file contains configuration used by caitsith-notifyd command.\n" | |
606 | +"\n" | |
607 | +"# caitsith-notifyd is a daemon that notifies the occurrence of policy violation\n" | |
608 | +"# in enforcing mode.\n" | |
609 | +"#\n" | |
610 | +"# time_to_wait is grace time in second before rejecting the request that\n" | |
611 | +"# caused policy violation in enforcing mode. For example, if you specify\n" | |
612 | +"# 30, you will be given 30 seconds for starting caitsith-queryd command and\n" | |
613 | +"# responding to the policy violation event.\n" | |
614 | +"# If you specify non 0 value, you need to register caitsith-notifyd command to\n" | |
615 | +"# /proc/caitsith/policy as well as caitsith-queryd command, for caitsith-notifyd needs to\n" | |
616 | +"# behave as if caitsith-queryd command is running.\n" | |
617 | +"# Also, you should avoid specifying too large value (e.g. 3600) because\n" | |
618 | +"# the request will remain pending for that period if you can't respond.\n" | |
619 | +"#\n" | |
620 | +"# action_to_take is a command line you want to use for notification.\n" | |
621 | +"# The command specified by this parameter must read the policy violation\n" | |
622 | +"# notification from standard input. For example, mail, curl and xmessage\n" | |
623 | +"# commands can read from standard input.\n" | |
624 | +"# This parameter is passed to execve(). Thus, please use a wrapper program\n" | |
625 | +"# if you need shell processing (e.g. wildcard expansion, environment\n" | |
626 | +"# variables).\n" | |
627 | +"#\n" | |
628 | +"# minimal_interval is grace time in second before re-notifying the next\n" | |
629 | +"# occurrence of policy violation. You can specify 60 to limit notification\n" | |
630 | +"# to once per a minute, 3600 to limit notification to once per an hour.\n" | |
631 | +"# You can specify 0 to unlimit, but notifying of every policy violation\n" | |
632 | +"# events (e.g. sending a mail) might annoy you because policy violation\n" | |
633 | +"# can occur in clusters if once occurred.\n" | |
634 | +"\n" | |
635 | +"# Please use CaitSith's escape rule (e.g. '\\040' rather than '\\ ' for\n" | |
636 | +"# representing a ' ' in a word).\n" | |
637 | +"\n" | |
638 | +"# Examples:\n" | |
639 | +"#\n" | |
640 | +"# time_to_wait 180\n" | |
641 | +"# action_to_take mail admin@example.com\n" | |
642 | +"#\n" | |
643 | +"# Wait for 180 seconds before rejecting the request.\n" | |
644 | +"# The occurrence is notified by sending mail to admin@example.com\n" | |
645 | +"# (if SMTP service is available).\n" | |
646 | +"#\n" | |
647 | +"# time_to_wait 0\n" | |
648 | +"# action_to_take curl --data-binary @- https://your.server/path_to_cgi\n" | |
649 | +"#\n" | |
650 | +"# Reject the request immediately.\n" | |
651 | +"# The occurrence is notified by executing curl command.\n" | |
652 | +"#\n" | |
653 | +"time_to_wait 0\n" | |
654 | +"action_to_take mail -s Notification\\040from\\040caitsith-notifyd root@localhost\n" | |
655 | +"minimal_interval 60\n" | |
656 | +"\n"; | |
657 | + | |
658 | +/** | |
659 | + * make_notifyd_conf - Make /etc/caitsith/tools/notifyd.conf . | |
660 | + * | |
661 | + * Returns nothing. | |
662 | + */ | |
663 | +static void make_notifyd_conf(void) | |
664 | +{ | |
665 | + FILE *fp; | |
666 | + if (chdir(policy_dir) || chdir("tools") || | |
667 | + !access("notifyd.conf", R_OK)) | |
668 | + return; | |
669 | + fp = fopen("notifyd.tmp", "w"); | |
670 | + if (!fp) { | |
671 | + fprintf(stderr, "ERROR: Can't create configuration file.\n"); | |
672 | + return; | |
673 | + } | |
674 | + fprintf(stderr, "Creating configuration file for caitsith-notifyd ... "); | |
675 | + fprintf(fp, "%s", notifyd_data); | |
676 | + close_file(fp, !chmod("notifyd.tmp", 0644), "notifyd.tmp", | |
677 | + "notifyd.conf"); | |
678 | +} | |
679 | + | |
680 | +int main(int argc, char *argv[]) | |
681 | +{ | |
682 | + int i; | |
683 | + const char *dir = NULL; | |
684 | + for (i = 1; i < argc; i++) { | |
685 | + char *arg = argv[i]; | |
686 | + if (*arg == '-' && *(arg + 1) == '-') | |
687 | + arg += 2; | |
688 | + if (!strncmp(arg, "root=", 5)) { | |
689 | + if (chroot(arg + 5) || chdir("/")) { | |
690 | + fprintf(stderr, "Can't chroot to '%s'\n", | |
691 | + arg + 5); | |
692 | + return 1; | |
693 | + } | |
694 | + } else if (!strncmp(arg, "policy_dir=", 11)) { | |
695 | + dir = arg + 11; | |
696 | + } else if (!strncmp(arg, "module_name=", 12)) { | |
697 | + module_name = arg + 12; | |
698 | + } else { | |
699 | + fprintf(stderr, "Unknown option: '%s'\n", argv[i]); | |
700 | + return 1; | |
701 | + } | |
702 | + } | |
703 | + if (!dir) | |
704 | + dir = "/etc/caitsith"; | |
705 | + policy_dir = strdup(dir); | |
706 | + memset(path, 0, sizeof(path)); | |
707 | + make_policy_dir(); | |
708 | + make_policy(); | |
709 | + make_module_loader(); | |
710 | + make_auditd_conf(); | |
711 | + make_notifyd_conf(); | |
712 | + return 0; | |
713 | +} |
@@ -0,0 +1,378 @@ | ||
1 | +/* | |
2 | + * caitsith-agent.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.1 2012/04/01 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include <stdio.h> | |
24 | +#include <string.h> | |
25 | +#include <stdlib.h> | |
26 | +#include <sys/types.h> | |
27 | +#include <sys/socket.h> | |
28 | +#include <netinet/in.h> | |
29 | +#include <arpa/inet.h> | |
30 | +#include <sys/stat.h> | |
31 | +#include <fcntl.h> | |
32 | +#include <unistd.h> | |
33 | +#include <poll.h> | |
34 | +#include <signal.h> | |
35 | +#include <dirent.h> | |
36 | + | |
37 | +static _Bool wait_data(const int fd) | |
38 | +{ | |
39 | + struct pollfd pfd = { .fd = fd, .events = POLLIN}; | |
40 | + poll(&pfd, 1, -1); | |
41 | + return 1; | |
42 | +} | |
43 | + | |
44 | +static void show_tasklist(FILE *fp, const _Bool show_all) | |
45 | +{ | |
46 | + int status_fd = open(".process_status", O_RDWR); | |
47 | + DIR *dir = opendir("/proc/"); | |
48 | + if (status_fd == EOF || !dir) { | |
49 | + if (status_fd != EOF) | |
50 | + close(status_fd); | |
51 | + if (dir) | |
52 | + closedir(dir); | |
53 | + return; | |
54 | + } | |
55 | + fputc(0, fp); | |
56 | + while (1) { | |
57 | + int ret_ignored; | |
58 | + FILE *status_fp; | |
59 | + pid_t ppid = 1; | |
60 | + char *name = NULL; | |
61 | + char buffer[1024]; | |
62 | + char test[16]; | |
63 | + unsigned int pid; | |
64 | + struct dirent *dent = readdir(dir); | |
65 | + if (!dent) | |
66 | + break; | |
67 | + if (dent->d_type != DT_DIR || | |
68 | + sscanf(dent->d_name, "%u", &pid) != 1 || !pid) | |
69 | + continue; | |
70 | + memset(buffer, 0, sizeof(buffer)); | |
71 | + if (!show_all) { | |
72 | + snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/exe", | |
73 | + pid); | |
74 | + if (readlink(buffer, test, sizeof(test)) <= 0) | |
75 | + continue; | |
76 | + } | |
77 | + snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/status", pid); | |
78 | + status_fp = fopen(buffer, "r"); | |
79 | + if (status_fp) { | |
80 | + while (memset(buffer, 0, sizeof(buffer)) && | |
81 | + fgets(buffer, sizeof(buffer) - 1, status_fp)) { | |
82 | + if (!strncmp(buffer, "Name:\t", 6)) { | |
83 | + char *cp = buffer + 6; | |
84 | + memmove(buffer, cp, strlen(cp) + 1); | |
85 | + cp = strchr(buffer, '\n'); | |
86 | + if (cp) | |
87 | + *cp = '\0'; | |
88 | + name = strdup(buffer); | |
89 | + } | |
90 | + if (sscanf(buffer, "PPid: %u", &ppid) == 1) | |
91 | + break; | |
92 | + } | |
93 | + fclose(status_fp); | |
94 | + } | |
95 | + snprintf(buffer, sizeof(buffer) - 1, "%u\n", pid); | |
96 | + ret_ignored = write(status_fd, buffer, strlen(buffer)); | |
97 | + memset(buffer, 0, sizeof(buffer)); | |
98 | + ret_ignored = read(status_fd, buffer, sizeof(buffer)); | |
99 | + if (!buffer[0]) | |
100 | + continue; | |
101 | + fprintf(fp, "PID=%u PPID=%u NAME=", pid, ppid); | |
102 | + if (name) { | |
103 | + const char *cp = name; | |
104 | + while (1) { | |
105 | + unsigned char c = *cp++; | |
106 | + if (!c) | |
107 | + break; | |
108 | + if (c == '\\') { | |
109 | + c = *cp++; | |
110 | + if (c == '\\') | |
111 | + fprintf(fp, "\\\\"); | |
112 | + else if (c == 'n') | |
113 | + fprintf(fp, "\\012"); | |
114 | + else | |
115 | + break; | |
116 | + } else if (c > ' ' && c <= 126) { | |
117 | + fputc(c, fp); | |
118 | + } else { | |
119 | + fprintf(fp, "\\%c%c%c", | |
120 | + (c >> 6) + '0', | |
121 | + ((c >> 3) & 7) + '0', | |
122 | + (c & 7) + '0'); | |
123 | + } | |
124 | + } | |
125 | + free(name); | |
126 | + } else { | |
127 | + fprintf(fp, "<UNKNOWN>"); | |
128 | + } | |
129 | + fputc('\n', fp); | |
130 | + ret_ignored = fwrite(buffer, strlen(buffer), 1, fp); | |
131 | + while (1) { | |
132 | + int len = read(status_fd, buffer, sizeof(buffer)); | |
133 | + if (len <= 0) | |
134 | + break; | |
135 | + ret_ignored = fwrite(buffer, len, 1, fp); | |
136 | + } | |
137 | + fputc('\n', fp); | |
138 | + } | |
139 | + fputc(0, fp); | |
140 | + closedir(dir); | |
141 | + close(status_fd); | |
142 | + fflush(fp); | |
143 | +} | |
144 | + | |
145 | +static void handle_audit(const int client) | |
146 | +{ | |
147 | + int ret_ignored; | |
148 | + const int fd = open("audit", O_RDONLY); | |
149 | + if (fd == EOF) | |
150 | + return; | |
151 | + /* Return \0 to indicate success. */ | |
152 | + ret_ignored = write(client, "", 1); | |
153 | + while (wait_data(fd)) { | |
154 | + char buffer[4096]; | |
155 | + const int len = read(fd, buffer, sizeof(buffer)); | |
156 | + if (!len) | |
157 | + continue; | |
158 | + if (len == EOF || write(client, buffer, len) != len) | |
159 | + break; | |
160 | + } | |
161 | + close(fd); | |
162 | +} | |
163 | + | |
164 | +static void handle_query(const int client) | |
165 | +{ | |
166 | + int ret_ignored; | |
167 | + const int fd = open("query", O_RDWR); | |
168 | + if (fd == EOF) | |
169 | + return; | |
170 | + /* Return \0 to indicate success. */ | |
171 | + ret_ignored = write(client, "", 1); | |
172 | + while (wait_data(client)) { | |
173 | + char buffer[4096]; | |
174 | + int len = recv(client, buffer, sizeof(buffer), MSG_DONTWAIT); | |
175 | + int nonzero_len; | |
176 | + if (len <= 0) | |
177 | + break; | |
178 | +restart: | |
179 | + for (nonzero_len = 0 ; nonzero_len < len; nonzero_len++) | |
180 | + if (!buffer[nonzero_len]) | |
181 | + break; | |
182 | + if (nonzero_len) { | |
183 | + if (write(fd, buffer, nonzero_len) != nonzero_len) | |
184 | + break; | |
185 | + } else { | |
186 | + while (wait_data(fd)) { | |
187 | + char buffer2[4096]; | |
188 | + const int len = read(fd, buffer2, | |
189 | + sizeof(buffer2)); | |
190 | + if (!len) | |
191 | + continue; | |
192 | + if (len == EOF || | |
193 | + write(client, buffer2, len) != len) { | |
194 | + shutdown(client, SHUT_RDWR); | |
195 | + break; | |
196 | + } | |
197 | + if (!buffer2[len - 1]) | |
198 | + break; | |
199 | + } | |
200 | + nonzero_len = 1; | |
201 | + } | |
202 | + len -= nonzero_len; | |
203 | + memmove(buffer, buffer + nonzero_len, len); | |
204 | + if (len) | |
205 | + goto restart; | |
206 | + } | |
207 | + close(fd); | |
208 | +} | |
209 | + | |
210 | +static _Bool verbose = 0; | |
211 | + | |
212 | +static void handle_policy(const int client, const char *filename) | |
213 | +{ | |
214 | + int ret_ignored; | |
215 | + char *cp = strrchr(filename, '/'); | |
216 | + int fd = open(cp ? cp + 1 : filename, O_RDWR); | |
217 | + if (fd == EOF) | |
218 | + goto out; | |
219 | + /* Return \0 to indicate success. */ | |
220 | + if (write(client, "", 1) != 1) | |
221 | + goto out; | |
222 | + if (verbose) { | |
223 | + ret_ignored = write(2, "opened ", 7); | |
224 | + ret_ignored = write(2, filename, strlen(filename)); | |
225 | + ret_ignored = write(2, "\n", 1); | |
226 | + } | |
227 | + while (wait_data(client)) { | |
228 | + char buffer[4096]; | |
229 | + int len = recv(client, buffer, sizeof(buffer), MSG_DONTWAIT); | |
230 | + int nonzero_len; | |
231 | + if (len <= 0) | |
232 | + break; | |
233 | +restart: | |
234 | + for (nonzero_len = 0 ; nonzero_len < len; nonzero_len++) | |
235 | + if (!buffer[nonzero_len]) | |
236 | + break; | |
237 | + if (nonzero_len) { | |
238 | + if (write(fd, buffer, nonzero_len) != nonzero_len) | |
239 | + break; | |
240 | + if (verbose) | |
241 | + ret_ignored = write(1, buffer, nonzero_len); | |
242 | + } else { | |
243 | + while (1) { | |
244 | + char buffer2[4096]; | |
245 | + const int len = read(fd, buffer2, | |
246 | + sizeof(buffer2)); | |
247 | + if (len == 0) | |
248 | + break; | |
249 | + /* Don't send \0 because it is EOF marker. */ | |
250 | + if (len < 0 || memchr(buffer2, '\0', len) || | |
251 | + write(client, buffer2, len) != len) | |
252 | + goto out; | |
253 | + } | |
254 | + /* Return \0 to indicate EOF. */ | |
255 | + if (write(client, "", 1) != 1) | |
256 | + goto out; | |
257 | + nonzero_len = 1; | |
258 | + } | |
259 | + len -= nonzero_len; | |
260 | + memmove(buffer, buffer + nonzero_len, len); | |
261 | + if (len) | |
262 | + goto restart; | |
263 | + } | |
264 | +out: | |
265 | + if (verbose) | |
266 | + ret_ignored = write(2, "disconnected\n", 13); | |
267 | +} | |
268 | + | |
269 | +static void do_child(const int client) | |
270 | +{ | |
271 | + int i; | |
272 | + char buffer[1024]; | |
273 | + /* Read filename. */ | |
274 | + for (i = 0; i < sizeof(buffer); i++) { | |
275 | + if (read(client, buffer + i, 1) != 1) | |
276 | + goto out; | |
277 | + if (!buffer[i]) | |
278 | + break; | |
279 | + } | |
280 | + if (!memchr(buffer, '\0', sizeof(buffer))) | |
281 | + goto out; | |
282 | + if (!strcmp(buffer, "proc:query")) | |
283 | + handle_query(client); | |
284 | + else if (!strcmp(buffer, "proc:audit")) | |
285 | + handle_audit(client); | |
286 | + else if (!strncmp(buffer, "proc:", 5)) { | |
287 | + /* Open /proc/\$/ for reading. */ | |
288 | + FILE *fp = fdopen(client, "w"); | |
289 | + if (fp) { | |
290 | + show_tasklist(fp, !strcmp(buffer + 5, | |
291 | + "all_process_status")); | |
292 | + fclose(fp); | |
293 | + } | |
294 | + } else | |
295 | + handle_policy(client, buffer); | |
296 | +out: | |
297 | + close(client); | |
298 | +} | |
299 | + | |
300 | +int main(int argc, char *argv[]) | |
301 | +{ | |
302 | + const int listener = socket(AF_INET, SOCK_STREAM, 0); | |
303 | + struct sockaddr_in addr; | |
304 | + socklen_t size = sizeof(addr); | |
305 | + char *port; | |
306 | + if (chdir("/proc/caitsith/")) | |
307 | + return 1; | |
308 | + { | |
309 | + int i; | |
310 | + for (i = 1; i < argc; i++) { | |
311 | + if (strcmp(argv[i], "--verbose")) | |
312 | + continue; | |
313 | + verbose = 1; | |
314 | + argc--; | |
315 | + for (; i < argc; i++) | |
316 | + argv[i] = argv[i + 1]; | |
317 | + break; | |
318 | + } | |
319 | + } | |
320 | + if (argc != 2) { | |
321 | +usage: | |
322 | + fprintf(stderr, "%s listen_address:listen_port\n", argv[0]); | |
323 | + return 1; | |
324 | + } | |
325 | + port = strchr(argv[1], ':'); | |
326 | + if (!port) | |
327 | + goto usage; | |
328 | + *port++ = '\0'; | |
329 | + memset(&addr, 0, sizeof(addr)); | |
330 | + addr.sin_family = AF_INET; | |
331 | + addr.sin_addr.s_addr = inet_addr(argv[1]); | |
332 | + addr.sin_port = htons(atoi(port)); | |
333 | + if (bind(listener, (struct sockaddr *) &addr, sizeof(addr)) || | |
334 | + listen(listener, 5) || | |
335 | + getsockname(listener, (struct sockaddr *) &addr, &size)) { | |
336 | + close(listener); | |
337 | + return 1; | |
338 | + } | |
339 | + { | |
340 | + const unsigned int ip = ntohl(addr.sin_addr.s_addr); | |
341 | + printf("Listening at %u.%u.%u.%u:%u\n", | |
342 | + (unsigned char) (ip >> 24), (unsigned char) (ip >> 16), | |
343 | + (unsigned char) (ip >> 8), (unsigned char) ip, | |
344 | + ntohs(addr.sin_port)); | |
345 | + fflush(stdout); | |
346 | + } | |
347 | + close(0); | |
348 | + if (!verbose) { | |
349 | + close(1); | |
350 | + close(2); | |
351 | + } | |
352 | + signal(SIGCHLD, SIG_IGN); | |
353 | + while (1) { | |
354 | + socklen_t size = sizeof(addr); | |
355 | + const int client = accept(listener, (struct sockaddr *) &addr, | |
356 | + &size); | |
357 | + if (client == EOF) { | |
358 | + if (verbose) | |
359 | + fprintf(stderr, "accept() failed\n"); | |
360 | + continue; | |
361 | + } | |
362 | + switch (fork()) { | |
363 | + case 0: | |
364 | + close(listener); | |
365 | + do_child(client); | |
366 | + _exit(0); | |
367 | + case -1: | |
368 | + if (verbose) | |
369 | + fprintf(stderr, "fork() failed\n"); | |
370 | + close(client); | |
371 | + break; | |
372 | + default: | |
373 | + close(client); | |
374 | + } | |
375 | + } | |
376 | + close(listener); | |
377 | + return 1; | |
378 | +} |
@@ -0,0 +1,87 @@ | ||
1 | +/* | |
2 | + * audit-exec-param.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.1 2012/04/01 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include <stdio.h> | |
24 | +#include <stdlib.h> | |
25 | +#include <unistd.h> | |
26 | +#include <syslog.h> | |
27 | +#include <string.h> | |
28 | +#include <sys/types.h> | |
29 | +#include <sys/stat.h> | |
30 | +#include <fcntl.h> | |
31 | +#include <errno.h> | |
32 | + | |
33 | +int main(int raw_argc, char *raw_argv[]) | |
34 | +{ | |
35 | + int i; | |
36 | + int argc; | |
37 | + int envc; | |
38 | + char *filename; | |
39 | + char **argv; | |
40 | + char **envp; | |
41 | + if (1) { | |
42 | + int fd = open("/proc/caitsith/.execute_handler", 0); | |
43 | + close(fd); | |
44 | + if (fd == EOF && errno != ENOENT) { | |
45 | + fprintf(stderr, "FATAL: I'm not execute_handler.\n"); | |
46 | + return 1; | |
47 | + } | |
48 | + } | |
49 | + if (raw_argc < 7) | |
50 | + return 1; | |
51 | + filename = raw_argv[4]; | |
52 | + argc = atoi(raw_argv[5]); | |
53 | + envc = atoi(raw_argv[6]); | |
54 | + if (raw_argc != argc + envc + 7) | |
55 | + return 1; | |
56 | + for (i = 5; i < argc + 5; i++) | |
57 | + raw_argv[i] = raw_argv[i + 2]; | |
58 | + raw_argv[argc + 5] = NULL; | |
59 | + for (i = argc + 6; i < argc + envc + 6; i++) | |
60 | + raw_argv[i] = raw_argv[i + 1]; | |
61 | + raw_argv[argc + envc + 6] = NULL; | |
62 | + argv = raw_argv + 5; | |
63 | + envp = raw_argv + argc + 6; | |
64 | + /* | |
65 | + * Check parameters passed to execve() request. | |
66 | + */ | |
67 | + if (1) { | |
68 | + openlog(raw_argv[0], LOG_NDELAY, LOG_USER); | |
69 | + syslog(LOG_INFO, "Domain = %s\n", raw_argv[1]); | |
70 | + syslog(LOG_INFO, "Caller Program = %s\n", raw_argv[2]); | |
71 | + syslog(LOG_INFO, "Process Status = %s\n", raw_argv[3]); | |
72 | + syslog(LOG_INFO, "Requested Program = %s\n", filename); | |
73 | + syslog(LOG_INFO, "argc=%d\n", argc); | |
74 | + syslog(LOG_INFO, "envc=%d\n", envc); | |
75 | + for (i = 0; i < argc; i++) | |
76 | + syslog(LOG_INFO, "argv[%d] = %s\n", i, argv[i]); | |
77 | + for (i = 0; i < envc; i++) | |
78 | + syslog(LOG_INFO, "envp[%d] = %s\n", i, envp[i]); | |
79 | + closelog(); | |
80 | + } | |
81 | + /* | |
82 | + * Continue if filename and argv[] and envp[] are appropriate. | |
83 | + */ | |
84 | + if (1) | |
85 | + execve(filename, argv, envp); | |
86 | + return 1; | |
87 | +} |
@@ -0,0 +1,18 @@ | ||
1 | +include ../Include.make | |
2 | + | |
3 | +BUILD_FILES = audit-exec-param caitsith-agent init_policy | |
4 | + | |
5 | +all: $(BUILD_FILES) | |
6 | + | |
7 | +install: all | |
8 | + mkdir -p -m 0755 $(INSTALLDIR)/$(USRLIBDIR)/caitsith | |
9 | + $(INSTALL) -m 0755 $(BUILD_FILES) $(INSTALLDIR)/$(USRLIBDIR)/caitsith/ | |
10 | + $(INSTALL) -m 0644 ../README.caitsith ../COPYING.caitsith $(INSTALLDIR)/$(USRLIBDIR)/caitsith/ | |
11 | + | |
12 | +.c: | |
13 | + $(CC) $(CFLAGS) -o $@ $< | |
14 | + | |
15 | +clean: | |
16 | + rm -f -- $(BUILD_FILES) | |
17 | + | |
18 | +.PHONY: clean install |
@@ -0,0 +1,192 @@ | ||
1 | +/* | |
2 | + * caitsith-init.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.1 2012/04/01 | |
9 | + * | |
10 | + * This program is executed automatically by kernel | |
11 | + * when execution of /sbin/init is requested. | |
12 | + * | |
13 | + * This program is free software; you can redistribute it and/or modify it | |
14 | + * under the terms of the GNU General Public License v2 as published by the | |
15 | + * Free Software Foundation. | |
16 | + * | |
17 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
18 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
20 | + * more details. | |
21 | + * | |
22 | + * You should have received a copy of the GNU General Public License along with | |
23 | + * this program; if not, write to the Free Software Foundation, Inc., | |
24 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
25 | + */ | |
26 | +#define _FILE_OFFSET_BITS 64 | |
27 | +#define _LARGEFILE_SOURCE | |
28 | +#define _LARGEFILE64_SOURCE | |
29 | +#include <stdio.h> | |
30 | +#include <string.h> | |
31 | +#include <stdlib.h> | |
32 | +#include <sys/types.h> | |
33 | +#include <sys/stat.h> | |
34 | +#include <sys/wait.h> | |
35 | +#include <sys/mount.h> | |
36 | +#include <fcntl.h> | |
37 | +#include <unistd.h> | |
38 | +#include <dirent.h> | |
39 | +#include <limits.h> | |
40 | +#include <sys/vfs.h> | |
41 | +#include <errno.h> | |
42 | + | |
43 | +static void panic(void) | |
44 | +{ | |
45 | + printf("Fatal error while loading policy.\n"); | |
46 | + fflush(stdout); | |
47 | + while (1) | |
48 | + sleep(100); | |
49 | +} | |
50 | + | |
51 | +#define policy_dir "/etc/caitsith/policy" | |
52 | +#define proc_policy "/proc/caitsith/policy" | |
53 | +static _Bool proc_unmount = 0; | |
54 | + | |
55 | +static char buffer[8192]; | |
56 | + | |
57 | +static void copy_files(const char *src, const char *dest) | |
58 | +{ | |
59 | + int sfd; | |
60 | + int dfd = open(dest, O_WRONLY); | |
61 | + if (dfd == EOF) { | |
62 | + if (errno != ENOENT) | |
63 | + panic(); | |
64 | + return; | |
65 | + } | |
66 | + sfd = open(src, O_RDONLY); | |
67 | + if (sfd != EOF) { | |
68 | + while (1) { | |
69 | + int ret_ignored; | |
70 | + int len = read(sfd, buffer, sizeof(buffer)); | |
71 | + if (len <= 0) | |
72 | + break; | |
73 | + ret_ignored = write(dfd, buffer, len); | |
74 | + } | |
75 | + close(sfd); | |
76 | + } | |
77 | + close(dfd); | |
78 | +} | |
79 | + | |
80 | +static void show_stat(void) | |
81 | +{ | |
82 | + unsigned int acl = 0; | |
83 | + unsigned int size = -1; | |
84 | + FILE *fp = fopen(proc_policy, "r"); | |
85 | + if (!fp) | |
86 | + return; | |
87 | + while (memset(buffer, 0, sizeof(buffer)) && | |
88 | + fgets(buffer, sizeof(buffer) - 1, fp)) { | |
89 | + if (!strncmp(buffer, "acl ", 4)) | |
90 | + acl++; | |
91 | + else if (size == -1) | |
92 | + sscanf(buffer, "stat Memory used by policy: %u", | |
93 | + &size); | |
94 | + } | |
95 | + fclose(fp); | |
96 | + printf("%u ACL entr%s.\n", acl, acl > 1 ? "ies" : "y"); | |
97 | + if (size != -1) | |
98 | + printf("%u KB used by policy.\n", (size + 1023) / 1024); | |
99 | +} | |
100 | + | |
101 | +int main(int argc, char *argv[]) | |
102 | +{ | |
103 | + struct stat buf; | |
104 | + | |
105 | + /* Mount /proc if not mounted. */ | |
106 | + if (lstat("/proc/self/", &buf) || !S_ISDIR(buf.st_mode)) | |
107 | + proc_unmount = !mount("/proc", "/proc/", "proc", 0, NULL); | |
108 | + | |
109 | + /* | |
110 | + * Open /dev/console if stdio are not connected. | |
111 | + * | |
112 | + * WARNING: Don't let this program be invoked implicitly | |
113 | + * if you are not operating from console. | |
114 | + * Otherwise, you will get unable to respond to prompt | |
115 | + * if something went wrong. | |
116 | + */ | |
117 | + if (access("/proc/self/fd/0", R_OK)) { | |
118 | + close(0); | |
119 | + close(1); | |
120 | + close(2); | |
121 | + open("/dev/console", O_RDONLY); | |
122 | + open("/dev/console", O_WRONLY); | |
123 | + open("/dev/console", O_WRONLY); | |
124 | + } | |
125 | + | |
126 | + /* Load kernel module if needed. */ | |
127 | + if (lstat("/proc/caitsith/", &buf) || !S_ISDIR(buf.st_mode)) { | |
128 | + if (!access("/etc/caitsith/caitsith-load-module", X_OK)) { | |
129 | + const pid_t pid = fork(); | |
130 | + switch (pid) { | |
131 | + case 0: | |
132 | + execl("/etc/caitsith/caitsith-load-module", | |
133 | + "/etc/caitsith/caitsith-load-module", NULL); | |
134 | + _exit(0); | |
135 | + case -1: | |
136 | + panic(); | |
137 | + } | |
138 | + while (waitpid(pid, NULL, __WALL) == EOF && | |
139 | + errno == EINTR); | |
140 | + } | |
141 | + } | |
142 | + | |
143 | + /* Stop if policy interface doesn't exist. */ | |
144 | + if (lstat("/proc/caitsith/", &buf) || !S_ISDIR(buf.st_mode)) { | |
145 | + printf("FATAL: Policy interface does not exist.\n"); | |
146 | + fflush(stdout); | |
147 | + while (1) | |
148 | + sleep(100); | |
149 | + } | |
150 | + | |
151 | + /* | |
152 | + * Unmount /proc and execute /sbin/init if this program was executed by | |
153 | + * passing init=/sbin/caitsith-init . The kernel will try to execute this | |
154 | + * program again with getpid() != 1 when /sbin/init starts. | |
155 | + */ | |
156 | + if (getpid() == 1) { | |
157 | + if (proc_unmount) | |
158 | + umount("/proc/"); | |
159 | + argv[0] = "/sbin/init"; | |
160 | + execv(argv[0], argv); | |
161 | + printf("FATAL: Failed to execute %s\n", argv[0]); | |
162 | + fflush(stdout); | |
163 | + while (1) | |
164 | + sleep(100); | |
165 | + } | |
166 | + | |
167 | + /* Load policy. */ | |
168 | + if (!chdir(policy_dir)) | |
169 | + copy_files("current", proc_policy); | |
170 | + | |
171 | + /* Do additional initialization. */ | |
172 | + if (!access("/etc/caitsith/caitsith-post-init", X_OK)) { | |
173 | + const pid_t pid = fork(); | |
174 | + switch (pid) { | |
175 | + case 0: | |
176 | + execl("/etc/caitsith/caitsith-post-init", | |
177 | + "/etc/caitsith/caitsith-post-init", NULL); | |
178 | + _exit(0); | |
179 | + case -1: | |
180 | + panic(); | |
181 | + } | |
182 | + while (waitpid(pid, NULL, __WALL) == EOF && | |
183 | + errno == EINTR); | |
184 | + } | |
185 | + | |
186 | + show_stat(); | |
187 | + | |
188 | + if (proc_unmount) | |
189 | + umount("/proc"); | |
190 | + | |
191 | + return 0; | |
192 | +} |
@@ -0,0 +1,17 @@ | ||
1 | +include ../Include.make | |
2 | + | |
3 | +BUILD_FILES = caitsith-init | |
4 | + | |
5 | +all: $(BUILD_FILES) | |
6 | + | |
7 | +install: all | |
8 | + mkdir -p -m 0755 $(INSTALLDIR)$(SBINDIR) | |
9 | + $(INSTALL) -m 0700 $(BUILD_FILES) $(INSTALLDIR)$(SBINDIR) | |
10 | + | |
11 | +.c: | |
12 | + $(CC) $(CFLAGS) -o $@ $< | |
13 | + | |
14 | +clean: | |
15 | + rm -f -- $(BUILD_FILES) | |
16 | + | |
17 | +.PHONY: clean install |
@@ -0,0 +1,21 @@ | ||
1 | +all: | |
2 | + $(MAKE) -C sbin all | |
3 | + $(MAKE) -C usr_sbin all | |
4 | + $(MAKE) -C usr_lib_caitsith all | |
5 | + | |
6 | +install: all | |
7 | + $(MAKE) -C sbin install | |
8 | + $(MAKE) -C usr_sbin install | |
9 | + $(MAKE) -C usr_lib_caitsith install | |
10 | + | |
11 | +clean: | |
12 | +## | |
13 | +## I don't enable "find" line because older versions does not support -delete | |
14 | +## action. | |
15 | +## | |
16 | +# find -name '*~' -delete | |
17 | + $(MAKE) -C sbin clean | |
18 | + $(MAKE) -C usr_sbin clean | |
19 | + $(MAKE) -C usr_lib_caitsith clean | |
20 | + | |
21 | +.PHONY: clean install |