From 588e85230bd45a35ad5e13a5520b59ece0049ef7 Mon Sep 17 00:00:00 2001 From: Mikko Ylinen Date: Tue, 29 Aug 2017 15:45:18 +0300 Subject: linux-yocto: drop all 4.1 content linux-yocto_4.1.bb recipe has been removed from oe-core master and that triggers a bitbake error due to orphan bbappends maintained in meta-security. To fix the error, drop linux-yocto_4.1.bbappend plus the patches and the config fragments for it. Signed-off-by: Mikko Ylinen Signed-off-by: Armin Kuster --- .../linux/linux-yocto-4.1/ccs-patch-4.1.diff | 968 - .../linux/linux-yocto-4.1/ccs-patch-4.2.diff | 681 - .../linux-yocto-4.1/ccs-tools-yocto.4.1.patch | 1029 -- .../linux-yocto-4.1/ccs-tools-yocto_security.patch | 17920 ------------------- recipes-kernel/linux/linux-yocto-4.1/tomoyo.cfg | 16 - recipes-kernel/linux/linux-yocto-4.1/tomoyo.scc | 4 - recipes-kernel/linux/linux-yocto_4.1.bbappend | 9 - 7 files changed, 20627 deletions(-) delete mode 100644 recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.1.diff delete mode 100644 recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.2.diff delete mode 100644 recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto.4.1.patch delete mode 100644 recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto_security.patch delete mode 100644 recipes-kernel/linux/linux-yocto-4.1/tomoyo.cfg delete mode 100644 recipes-kernel/linux/linux-yocto-4.1/tomoyo.scc delete mode 100644 recipes-kernel/linux/linux-yocto_4.1.bbappend diff --git a/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.1.diff b/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.1.diff deleted file mode 100644 index 9ad49fd..0000000 --- a/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.1.diff +++ /dev/null @@ -1,968 +0,0 @@ -This is TOMOYO Linux patch for kernel 4.1.8. - -Source code for this patch is https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.1.8.tar.xz ---- - fs/exec.c | 2 - fs/open.c | 2 - fs/proc/version.c | 7 ++ - include/linux/init_task.h | 9 +++ - include/linux/sched.h | 6 ++ - include/linux/security.h | 62 +++++++++++++++---------- - include/net/ip.h | 4 + - kernel/fork.c | 5 ++ - kernel/kexec.c | 3 + - kernel/module.c | 5 ++ - kernel/ptrace.c | 10 ++++ - kernel/reboot.c | 3 + - kernel/sched/core.c | 2 - kernel/signal.c | 10 ++++ - kernel/sys.c | 8 +++ - kernel/time/ntp.c | 8 +++ - net/ipv4/raw.c | 4 + - net/ipv4/udp.c | 4 + - net/ipv6/raw.c | 4 + - net/ipv6/udp.c | 4 + - net/socket.c | 4 + - net/unix/af_unix.c | 4 + - security/Kconfig | 2 - security/Makefile | 3 + - security/security.c | 110 ++++++++++++++++++++++++++++++++++++++++------ - 25 files changed, 248 insertions(+), 37 deletions(-) - ---- linux-4.1.8.orig/fs/exec.c -+++ linux-4.1.8/fs/exec.c -@@ -1461,7 +1461,7 @@ static int exec_binprm(struct linux_binp - old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent)); - rcu_read_unlock(); - -- ret = search_binary_handler(bprm); -+ ret = ccs_search_binary_handler(bprm); - if (ret >= 0) { - audit_bprm(bprm); - trace_sched_process_exec(current, old_pid, bprm); ---- linux-4.1.8.orig/fs/open.c -+++ linux-4.1.8/fs/open.c -@@ -1106,6 +1106,8 @@ EXPORT_SYMBOL(sys_close); - */ - SYSCALL_DEFINE0(vhangup) - { -+ if (!ccs_capable(CCS_SYS_VHANGUP)) -+ return -EPERM; - if (capable(CAP_SYS_TTY_CONFIG)) { - tty_vhangup_self(); - return 0; ---- linux-4.1.8.orig/fs/proc/version.c -+++ linux-4.1.8/fs/proc/version.c -@@ -32,3 +32,10 @@ static int __init proc_version_init(void - return 0; - } - fs_initcall(proc_version_init); -+ -+static int __init ccs_show_version(void) -+{ -+ printk(KERN_INFO "Hook version: 4.1.8 2015/09/26\n"); -+ return 0; -+} -+fs_initcall(ccs_show_version); ---- linux-4.1.8.orig/include/linux/init_task.h -+++ linux-4.1.8/include/linux/init_task.h -@@ -182,6 +182,14 @@ extern struct task_group root_task_group - # define INIT_KASAN(tsk) - #endif - -+#if defined(CONFIG_CCSECURITY) && !defined(CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY) -+#define INIT_CCSECURITY \ -+ .ccs_domain_info = NULL, \ -+ .ccs_flags = 0, -+#else -+#define INIT_CCSECURITY -+#endif -+ - /* - * INIT_TASK is used to set up the first task table, touch at - * your own risk!. Base=0, limit=0x1fffff (=2MB) -@@ -258,6 +266,7 @@ extern struct task_group root_task_group - INIT_VTIME(tsk) \ - INIT_NUMA_BALANCING(tsk) \ - INIT_KASAN(tsk) \ -+ INIT_CCSECURITY \ - } - - ---- linux-4.1.8.orig/include/linux/sched.h -+++ linux-4.1.8/include/linux/sched.h -@@ -6,6 +6,8 @@ - #include - - -+struct ccs_domain_info; -+ - struct sched_param { - int sched_priority; - }; -@@ -1724,6 +1726,10 @@ struct task_struct { - #ifdef CONFIG_DEBUG_ATOMIC_SLEEP - unsigned long task_state_change; - #endif -+#if defined(CONFIG_CCSECURITY) && !defined(CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY) -+ struct ccs_domain_info *ccs_domain_info; -+ u32 ccs_flags; -+#endif - }; - - /* Future-safe accessor for struct task_struct's cpus_allowed. */ ---- linux-4.1.8.orig/include/linux/security.h -+++ linux-4.1.8/include/linux/security.h -@@ -53,6 +53,7 @@ struct msg_queue; - struct xattr; - struct xfrm_sec_ctx; - struct mm_struct; -+#include - - /* Maximum number of letters for an LSM name string */ - #define SECURITY_NAME_MAX 10 -@@ -2042,7 +2043,10 @@ static inline int security_syslog(int ty - static inline int security_settime(const struct timespec *ts, - const struct timezone *tz) - { -- return cap_settime(ts, tz); -+ int error = cap_settime(ts, tz); -+ if (!error) -+ error = ccs_settime(ts, tz); -+ return error; - } - - static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) -@@ -2111,18 +2115,18 @@ static inline int security_sb_mount(cons - const char *type, unsigned long flags, - void *data) - { -- return 0; -+ return ccs_sb_mount(dev_name, path, type, flags, data); - } - - static inline int security_sb_umount(struct vfsmount *mnt, int flags) - { -- return 0; -+ return ccs_sb_umount(mnt, flags); - } - - static inline int security_sb_pivotroot(struct path *old_path, - struct path *new_path) - { -- return 0; -+ return ccs_sb_pivotroot(old_path, new_path); - } - - static inline int security_sb_set_mnt_opts(struct super_block *sb, -@@ -2260,7 +2264,7 @@ static inline int security_inode_setattr - - static inline int security_inode_getattr(const struct path *path) - { -- return 0; -+ return ccs_inode_getattr(path); - } - - static inline int security_inode_setxattr(struct dentry *dentry, -@@ -2336,7 +2340,7 @@ static inline void security_file_free(st - static inline int security_file_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) - { -- return 0; -+ return ccs_file_ioctl(file, cmd, arg); - } - - static inline int security_mmap_file(struct file *file, unsigned long prot, -@@ -2365,7 +2369,7 @@ static inline int security_file_lock(str - static inline int security_file_fcntl(struct file *file, unsigned int cmd, - unsigned long arg) - { -- return 0; -+ return ccs_file_fcntl(file, cmd, arg); - } - - static inline void security_file_set_fowner(struct file *file) -@@ -2388,7 +2392,7 @@ static inline int security_file_receive( - static inline int security_file_open(struct file *file, - const struct cred *cred) - { -- return 0; -+ return ccs_file_open(file, cred); - } - - static inline int security_task_create(unsigned long clone_flags) -@@ -2750,7 +2754,7 @@ static inline int security_unix_may_send - static inline int security_socket_create(int family, int type, - int protocol, int kern) - { -- return 0; -+ return ccs_socket_create(family, type, protocol, kern); - } - - static inline int security_socket_post_create(struct socket *sock, -@@ -2765,19 +2769,19 @@ static inline int security_socket_bind(s - struct sockaddr *address, - int addrlen) - { -- return 0; -+ return ccs_socket_bind(sock, address, addrlen); - } - - static inline int security_socket_connect(struct socket *sock, - struct sockaddr *address, - int addrlen) - { -- return 0; -+ return ccs_socket_connect(sock, address, addrlen); - } - - static inline int security_socket_listen(struct socket *sock, int backlog) - { -- return 0; -+ return ccs_socket_listen(sock, backlog); - } - - static inline int security_socket_accept(struct socket *sock, -@@ -2789,7 +2793,7 @@ static inline int security_socket_accept - static inline int security_socket_sendmsg(struct socket *sock, - struct msghdr *msg, int size) - { -- return 0; -+ return ccs_socket_sendmsg(sock, msg, size); - } - - static inline int security_socket_recvmsg(struct socket *sock, -@@ -3031,42 +3035,42 @@ int security_path_chroot(struct path *pa - #else /* CONFIG_SECURITY_PATH */ - static inline int security_path_unlink(struct path *dir, struct dentry *dentry) - { -- return 0; -+ return ccs_path_unlink(dir, dentry); - } - - static inline int security_path_mkdir(struct path *dir, struct dentry *dentry, - umode_t mode) - { -- return 0; -+ return ccs_path_mkdir(dir, dentry, mode); - } - - static inline int security_path_rmdir(struct path *dir, struct dentry *dentry) - { -- return 0; -+ return ccs_path_rmdir(dir, dentry); - } - - static inline int security_path_mknod(struct path *dir, struct dentry *dentry, - umode_t mode, unsigned int dev) - { -- return 0; -+ return ccs_path_mknod(dir, dentry, mode, dev); - } - - static inline int security_path_truncate(struct path *path) - { -- return 0; -+ return ccs_path_truncate(path); - } - - static inline int security_path_symlink(struct path *dir, struct dentry *dentry, - const char *old_name) - { -- return 0; -+ return ccs_path_symlink(dir, dentry, old_name); - } - - static inline int security_path_link(struct dentry *old_dentry, - struct path *new_dir, - struct dentry *new_dentry) - { -- return 0; -+ return ccs_path_link(old_dentry, new_dir, new_dentry); - } - - static inline int security_path_rename(struct path *old_dir, -@@ -3075,22 +3079,32 @@ static inline int security_path_rename(s - struct dentry *new_dentry, - unsigned int flags) - { -- return 0; -+ /* -+ * Not using RENAME_EXCHANGE here in order to avoid KABI breakage -+ * by doing "#include " . -+ */ -+ if (flags & (1 << 1)) { -+ int err = ccs_path_rename(new_dir, new_dentry, old_dir, -+ old_dentry); -+ if (err) -+ return err; -+ } -+ return ccs_path_rename(old_dir, old_dentry, new_dir, new_dentry); - } - - static inline int security_path_chmod(struct path *path, umode_t mode) - { -- return 0; -+ return ccs_path_chmod(path, mode); - } - - static inline int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) - { -- return 0; -+ return ccs_path_chown(path, uid, gid); - } - - static inline int security_path_chroot(struct path *path) - { -- return 0; -+ return ccs_path_chroot(path); - } - #endif /* CONFIG_SECURITY_PATH */ - ---- linux-4.1.8.orig/include/net/ip.h -+++ linux-4.1.8/include/net/ip.h -@@ -216,6 +216,8 @@ void inet_get_local_port_range(struct ne - #ifdef CONFIG_SYSCTL - static inline int inet_is_local_reserved_port(struct net *net, int port) - { -+ if (ccs_lport_reserved(port)) -+ return 1; - if (!net->ipv4.sysctl_local_reserved_ports) - return 0; - return test_bit(port, net->ipv4.sysctl_local_reserved_ports); -@@ -229,6 +231,8 @@ static inline bool sysctl_dev_name_is_al - #else - static inline int inet_is_local_reserved_port(struct net *net, int port) - { -+ if (ccs_lport_reserved(port)) -+ return 1; - return 0; - } - #endif ---- linux-4.1.8.orig/kernel/fork.c -+++ linux-4.1.8/kernel/fork.c -@@ -257,6 +257,7 @@ void __put_task_struct(struct task_struc - delayacct_tsk_free(tsk); - put_signal_struct(tsk->signal); - -+ ccs_free_task_security(tsk); - if (!profile_handoff_task(tsk)) - free_task(tsk); - } -@@ -1423,6 +1424,9 @@ static struct task_struct *copy_process( - goto bad_fork_cleanup_perf; - /* copy all the process information */ - shm_init_task(p); -+ retval = ccs_alloc_task_security(p); -+ if (retval) -+ goto bad_fork_cleanup_audit; - retval = copy_semundo(clone_flags, p); - if (retval) - goto bad_fork_cleanup_audit; -@@ -1627,6 +1631,7 @@ bad_fork_cleanup_semundo: - exit_sem(p); - bad_fork_cleanup_audit: - audit_free(p); -+ ccs_free_task_security(p); - bad_fork_cleanup_perf: - perf_event_free_task(p); - bad_fork_cleanup_policy: ---- linux-4.1.8.orig/kernel/kexec.c -+++ linux-4.1.8/kernel/kexec.c -@@ -41,6 +41,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -1245,6 +1246,8 @@ SYSCALL_DEFINE4(kexec_load, unsigned lon - /* We only trust the superuser with rebooting the system. */ - if (!capable(CAP_SYS_BOOT) || kexec_load_disabled) - return -EPERM; -+ if (!ccs_capable(CCS_SYS_KEXEC_LOAD)) -+ return -EPERM; - - /* - * Verify we have a legal set of flags ---- linux-4.1.8.orig/kernel/module.c -+++ linux-4.1.8/kernel/module.c -@@ -61,6 +61,7 @@ - #include - #include - #include "module-internal.h" -+#include - - #define CREATE_TRACE_POINTS - #include -@@ -799,6 +800,8 @@ SYSCALL_DEFINE2(delete_module, const cha - - if (!capable(CAP_SYS_MODULE) || modules_disabled) - return -EPERM; -+ if (!ccs_capable(CCS_USE_KERNEL_MODULE)) -+ return -EPERM; - - if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0) - return -EFAULT; -@@ -3155,6 +3158,8 @@ static int may_init_module(void) - { - if (!capable(CAP_SYS_MODULE) || modules_disabled) - return -EPERM; -+ if (!ccs_capable(CCS_USE_KERNEL_MODULE)) -+ return -EPERM; - - return 0; - } ---- linux-4.1.8.orig/kernel/ptrace.c -+++ linux-4.1.8/kernel/ptrace.c -@@ -1034,6 +1034,11 @@ SYSCALL_DEFINE4(ptrace, long, request, l - { - struct task_struct *child; - long ret; -+ { -+ const int rc = ccs_ptrace_permission(request, pid); -+ if (rc) -+ return rc; -+ } - - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); -@@ -1180,6 +1185,11 @@ COMPAT_SYSCALL_DEFINE4(ptrace, compat_lo - { - struct task_struct *child; - long ret; -+ { -+ const int rc = ccs_ptrace_permission(request, pid); -+ if (rc) -+ return rc; -+ } - - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); ---- linux-4.1.8.orig/kernel/reboot.c -+++ linux-4.1.8/kernel/reboot.c -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include - - /* - * this indicates whether you can reboot with ctrl-alt-del: the default is yes -@@ -295,6 +296,8 @@ SYSCALL_DEFINE4(reboot, int, magic1, int - magic2 != LINUX_REBOOT_MAGIC2B && - magic2 != LINUX_REBOOT_MAGIC2C)) - return -EINVAL; -+ if (!ccs_capable(CCS_SYS_REBOOT)) -+ return -EPERM; - - /* - * If pid namespaces are enabled and the current task is in a child ---- linux-4.1.8.orig/kernel/sched/core.c -+++ linux-4.1.8/kernel/sched/core.c -@@ -3145,6 +3145,8 @@ int can_nice(const struct task_struct *p - SYSCALL_DEFINE1(nice, int, increment) - { - long nice, retval; -+ if (!ccs_capable(CCS_SYS_NICE)) -+ return -EPERM; - - /* - * Setpriority might change our priority at the same moment. ---- linux-4.1.8.orig/kernel/signal.c -+++ linux-4.1.8/kernel/signal.c -@@ -2901,6 +2901,8 @@ SYSCALL_DEFINE4(rt_sigtimedwait, const s - SYSCALL_DEFINE2(kill, pid_t, pid, int, sig) - { - struct siginfo info; -+ if (ccs_kill_permission(pid, sig)) -+ return -EPERM; - - info.si_signo = sig; - info.si_errno = 0; -@@ -2969,6 +2971,8 @@ SYSCALL_DEFINE3(tgkill, pid_t, tgid, pid - /* This is only valid for single tasks */ - if (pid <= 0 || tgid <= 0) - return -EINVAL; -+ if (ccs_tgkill_permission(tgid, pid, sig)) -+ return -EPERM; - - return do_tkill(tgid, pid, sig); - } -@@ -2985,6 +2989,8 @@ SYSCALL_DEFINE2(tkill, pid_t, pid, int, - /* This is only valid for single tasks */ - if (pid <= 0) - return -EINVAL; -+ if (ccs_tkill_permission(pid, sig)) -+ return -EPERM; - - return do_tkill(0, pid, sig); - } -@@ -2999,6 +3005,8 @@ static int do_rt_sigqueueinfo(pid_t pid, - return -EPERM; - - info->si_signo = sig; -+ if (ccs_sigqueue_permission(pid, sig)) -+ return -EPERM; - - /* POSIX.1b doesn't mention process groups. */ - return kill_proc_info(sig, info, pid); -@@ -3047,6 +3055,8 @@ static int do_rt_tgsigqueueinfo(pid_t tg - return -EPERM; - - info->si_signo = sig; -+ if (ccs_tgsigqueue_permission(tgid, pid, sig)) -+ return -EPERM; - - return do_send_specific(tgid, pid, sig, info); - } ---- linux-4.1.8.orig/kernel/sys.c -+++ linux-4.1.8/kernel/sys.c -@@ -183,6 +183,10 @@ SYSCALL_DEFINE3(setpriority, int, which, - - if (which > PRIO_USER || which < PRIO_PROCESS) - goto out; -+ if (!ccs_capable(CCS_SYS_NICE)) { -+ error = -EPERM; -+ goto out; -+ } - - /* normalize: avoid signed division (rounding problems) */ - error = -ESRCH; -@@ -1222,6 +1226,8 @@ SYSCALL_DEFINE2(sethostname, char __user - - if (len < 0 || len > __NEW_UTS_LEN) - return -EINVAL; -+ if (!ccs_capable(CCS_SYS_SETHOSTNAME)) -+ return -EPERM; - down_write(&uts_sem); - errno = -EFAULT; - if (!copy_from_user(tmp, name, len)) { -@@ -1272,6 +1278,8 @@ SYSCALL_DEFINE2(setdomainname, char __us - return -EPERM; - if (len < 0 || len > __NEW_UTS_LEN) - return -EINVAL; -+ if (!ccs_capable(CCS_SYS_SETHOSTNAME)) -+ return -EPERM; - - down_write(&uts_sem); - errno = -EFAULT; ---- linux-4.1.8.orig/kernel/time/ntp.c -+++ linux-4.1.8/kernel/time/ntp.c -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include - - #include "ntp_internal.h" - -@@ -626,10 +627,15 @@ int ntp_validate_timex(struct timex *txc - if (!(txc->modes & ADJ_OFFSET_READONLY) && - !capable(CAP_SYS_TIME)) - return -EPERM; -+ if (!(txc->modes & ADJ_OFFSET_READONLY) && -+ !ccs_capable(CCS_SYS_SETTIME)) -+ return -EPERM; - } else { - /* In order to modify anything, you gotta be super-user! */ - if (txc->modes && !capable(CAP_SYS_TIME)) - return -EPERM; -+ if (txc->modes && !ccs_capable(CCS_SYS_SETTIME)) -+ return -EPERM; - /* - * if the quartz is off by more than 10% then - * something is VERY wrong! -@@ -642,6 +648,8 @@ int ntp_validate_timex(struct timex *txc - - if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME))) - return -EPERM; -+ if ((txc->modes & ADJ_SETOFFSET) && !ccs_capable(CCS_SYS_SETTIME)) -+ return -EPERM; - - /* - * Check for potential multiplication overflows that can ---- linux-4.1.8.orig/net/ipv4/raw.c -+++ linux-4.1.8/net/ipv4/raw.c -@@ -727,6 +727,10 @@ static int raw_recvmsg(struct sock *sk, - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) - goto out; -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out; -+ } - - copied = skb->len; - if (len < copied) { ---- linux-4.1.8.orig/net/ipv4/udp.c -+++ linux-4.1.8/net/ipv4/udp.c -@@ -1272,6 +1272,10 @@ try_again: - &peeked, &off, &err); - if (!skb) - goto out; -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out; -+ } - - ulen = skb->len - sizeof(struct udphdr); - copied = len; ---- linux-4.1.8.orig/net/ipv6/raw.c -+++ linux-4.1.8/net/ipv6/raw.c -@@ -477,6 +477,10 @@ static int rawv6_recvmsg(struct sock *sk - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) - goto out; -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out; -+ } - - copied = skb->len; - if (copied > len) { ---- linux-4.1.8.orig/net/ipv6/udp.c -+++ linux-4.1.8/net/ipv6/udp.c -@@ -413,6 +413,10 @@ try_again: - &peeked, &off, &err); - if (!skb) - goto out; -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out; -+ } - - ulen = skb->len - sizeof(struct udphdr); - copied = len; ---- linux-4.1.8.orig/net/socket.c -+++ linux-4.1.8/net/socket.c -@@ -1485,6 +1485,10 @@ SYSCALL_DEFINE4(accept4, int, fd, struct - if (err < 0) - goto out_fd; - -+ if (ccs_socket_post_accept_permission(sock, newsock)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out_fd; -+ } - if (upeer_sockaddr) { - if (newsock->ops->getname(newsock, (struct sockaddr *)&address, - &len, 2) < 0) { ---- linux-4.1.8.orig/net/unix/af_unix.c -+++ linux-4.1.8/net/unix/af_unix.c -@@ -1800,6 +1800,10 @@ static int unix_dgram_recvmsg(struct soc - wake_up_interruptible_sync_poll(&u->peer_wait, - POLLOUT | POLLWRNORM | POLLWRBAND); - -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out_unlock; -+ } - if (msg->msg_name) - unix_copy_addr(msg, skb->sk); - ---- linux-4.1.8.orig/security/Kconfig -+++ linux-4.1.8/security/Kconfig -@@ -168,5 +168,7 @@ config DEFAULT_SECURITY - default "yama" if DEFAULT_SECURITY_YAMA - default "" if DEFAULT_SECURITY_DAC - -+source security/ccsecurity/Kconfig -+ - endmenu - ---- linux-4.1.8.orig/security/Makefile -+++ linux-4.1.8/security/Makefile -@@ -27,3 +27,6 @@ obj-$(CONFIG_CGROUP_DEVICE) += device_c - # Object integrity file lists - subdir-$(CONFIG_INTEGRITY) += integrity - obj-$(CONFIG_INTEGRITY) += integrity/ -+ -+subdir-$(CONFIG_CCSECURITY) += ccsecurity -+obj-$(CONFIG_CCSECURITY) += ccsecurity/ ---- linux-4.1.8.orig/security/security.c -+++ linux-4.1.8/security/security.c -@@ -226,7 +226,10 @@ int security_syslog(int type) - - int security_settime(const struct timespec *ts, const struct timezone *tz) - { -- return security_ops->settime(ts, tz); -+ int error = security_ops->settime(ts, tz); -+ if (!error) -+ error = ccs_settime(ts, tz); -+ return error; - } - - int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) -@@ -303,17 +306,26 @@ int security_sb_statfs(struct dentry *de - int security_sb_mount(const char *dev_name, struct path *path, - const char *type, unsigned long flags, void *data) - { -- return security_ops->sb_mount(dev_name, path, type, flags, data); -+ int error = security_ops->sb_mount(dev_name, path, type, flags, data); -+ if (!error) -+ error = ccs_sb_mount(dev_name, path, type, flags, data); -+ return error; - } - - int security_sb_umount(struct vfsmount *mnt, int flags) - { -- return security_ops->sb_umount(mnt, flags); -+ int error = security_ops->sb_umount(mnt, flags); -+ if (!error) -+ error = ccs_sb_umount(mnt, flags); -+ return error; - } - - int security_sb_pivotroot(struct path *old_path, struct path *new_path) - { -- return security_ops->sb_pivotroot(old_path, new_path); -+ int error = security_ops->sb_pivotroot(old_path, new_path); -+ if (!error) -+ error = ccs_sb_pivotroot(old_path, new_path); -+ return error; - } - - int security_sb_set_mnt_opts(struct super_block *sb, -@@ -410,31 +422,47 @@ EXPORT_SYMBOL(security_old_inode_init_se - int security_path_mknod(struct path *dir, struct dentry *dentry, umode_t mode, - unsigned int dev) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) - return 0; -+ error = ccs_path_mknod(dir, dentry, mode, dev); -+ if (error) -+ return error; - return security_ops->path_mknod(dir, dentry, mode, dev); - } - EXPORT_SYMBOL(security_path_mknod); - - int security_path_mkdir(struct path *dir, struct dentry *dentry, umode_t mode) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) - return 0; -+ error = ccs_path_mkdir(dir, dentry, mode); -+ if (error) -+ return error; - return security_ops->path_mkdir(dir, dentry, mode); - } - EXPORT_SYMBOL(security_path_mkdir); - - int security_path_rmdir(struct path *dir, struct dentry *dentry) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) - return 0; -+ error = ccs_path_rmdir(dir, dentry); -+ if (error) -+ return error; - return security_ops->path_rmdir(dir, dentry); - } - - int security_path_unlink(struct path *dir, struct dentry *dentry) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) - return 0; -+ error = ccs_path_unlink(dir, dentry); -+ if (error) -+ return error; - return security_ops->path_unlink(dir, dentry); - } - EXPORT_SYMBOL(security_path_unlink); -@@ -442,16 +470,24 @@ EXPORT_SYMBOL(security_path_unlink); - int security_path_symlink(struct path *dir, struct dentry *dentry, - const char *old_name) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) - return 0; -+ error = ccs_path_symlink(dir, dentry, old_name); -+ if (error) -+ return error; - return security_ops->path_symlink(dir, dentry, old_name); - } - - int security_path_link(struct dentry *old_dentry, struct path *new_dir, - struct dentry *new_dentry) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)))) - return 0; -+ error = ccs_path_link(old_dentry, new_dir, new_dentry); -+ if (error) -+ return error; - return security_ops->path_link(old_dentry, new_dir, new_dentry); - } - -@@ -459,6 +495,7 @@ int security_path_rename(struct path *ol - struct path *new_dir, struct dentry *new_dentry, - unsigned int flags) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) || - (d_is_positive(new_dentry) && IS_PRIVATE(d_backing_inode(new_dentry))))) - return 0; -@@ -468,8 +505,15 @@ int security_path_rename(struct path *ol - old_dir, old_dentry); - if (err) - return err; -+ err = ccs_path_rename(new_dir, new_dentry, old_dir, -+ old_dentry); -+ if (err) -+ return err; - } - -+ error = ccs_path_rename(old_dir, old_dentry, new_dir, new_dentry); -+ if (error) -+ return error; - return security_ops->path_rename(old_dir, old_dentry, new_dir, - new_dentry); - } -@@ -477,27 +521,42 @@ EXPORT_SYMBOL(security_path_rename); - - int security_path_truncate(struct path *path) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) - return 0; -+ error = ccs_path_truncate(path); -+ if (error) -+ return error; - return security_ops->path_truncate(path); - } - - int security_path_chmod(struct path *path, umode_t mode) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) - return 0; -+ error = ccs_path_chmod(path, mode); -+ if (error) -+ return error; - return security_ops->path_chmod(path, mode); - } - - int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) - return 0; -+ error = ccs_path_chown(path, uid, gid); -+ if (error) -+ return error; - return security_ops->path_chown(path, uid, gid); - } - - int security_path_chroot(struct path *path) - { -+ int error = ccs_path_chroot(path); -+ if (error) -+ return error; - return security_ops->path_chroot(path); - } - #endif -@@ -610,9 +669,13 @@ EXPORT_SYMBOL_GPL(security_inode_setattr - - int security_inode_getattr(const struct path *path) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) - return 0; -- return security_ops->inode_getattr(path); -+ error = security_ops->inode_getattr(path); -+ if (!error) -+ error = ccs_inode_getattr(path); -+ return error; - } - - int security_inode_setxattr(struct dentry *dentry, const char *name, -@@ -729,7 +792,10 @@ void security_file_free(struct file *fil - - int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) - { -- return security_ops->file_ioctl(file, cmd, arg); -+ int error = security_ops->file_ioctl(file, cmd, arg); -+ if (!error) -+ error = ccs_file_ioctl(file, cmd, arg); -+ return error; - } - - static inline unsigned long mmap_prot(struct file *file, unsigned long prot) -@@ -794,7 +860,10 @@ int security_file_lock(struct file *file - - int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg) - { -- return security_ops->file_fcntl(file, cmd, arg); -+ int error = security_ops->file_fcntl(file, cmd, arg); -+ if (!error) -+ error = ccs_file_fcntl(file, cmd, arg); -+ return error; - } - - void security_file_set_fowner(struct file *file) -@@ -818,6 +887,8 @@ int security_file_open(struct file *file - int ret; - - ret = security_ops->file_open(file, cred); -+ if (!ret) -+ ret = ccs_file_open(file, cred); - if (ret) - return ret; - -@@ -1168,7 +1239,10 @@ EXPORT_SYMBOL(security_unix_may_send); - - int security_socket_create(int family, int type, int protocol, int kern) - { -- return security_ops->socket_create(family, type, protocol, kern); -+ int error = security_ops->socket_create(family, type, protocol, kern); -+ if (!error) -+ error = ccs_socket_create(family, type, protocol, kern); -+ return error; - } - - int security_socket_post_create(struct socket *sock, int family, -@@ -1180,17 +1254,26 @@ int security_socket_post_create(struct s - - int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) - { -- return security_ops->socket_bind(sock, address, addrlen); -+ int error = security_ops->socket_bind(sock, address, addrlen); -+ if (!error) -+ error = ccs_socket_bind(sock, address, addrlen); -+ return error; - } - - int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) - { -- return security_ops->socket_connect(sock, address, addrlen); -+ int error = security_ops->socket_connect(sock, address, addrlen); -+ if (!error) -+ error = ccs_socket_connect(sock, address, addrlen); -+ return error; - } - - int security_socket_listen(struct socket *sock, int backlog) - { -- return security_ops->socket_listen(sock, backlog); -+ int error = security_ops->socket_listen(sock, backlog); -+ if (!error) -+ error = ccs_socket_listen(sock, backlog); -+ return error; - } - - int security_socket_accept(struct socket *sock, struct socket *newsock) -@@ -1200,7 +1283,10 @@ int security_socket_accept(struct socket - - int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) - { -- return security_ops->socket_sendmsg(sock, msg, size); -+ int error = security_ops->socket_sendmsg(sock, msg, size); -+ if (!error) -+ error = ccs_socket_sendmsg(sock, msg, size); -+ return error; - } - - int security_socket_recvmsg(struct socket *sock, struct msghdr *msg, diff --git a/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.2.diff b/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.2.diff deleted file mode 100644 index d1b021a..0000000 --- a/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.2.diff +++ /dev/null @@ -1,681 +0,0 @@ -This is TOMOYO Linux patch for kernel 4.2.1. - -Source code for this patch is https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.2.1.tar.xz ---- - fs/exec.c | 2 - - fs/open.c | 2 + - fs/proc/version.c | 7 +++++ - include/linux/init_task.h | 9 ++++++ - include/linux/sched.h | 6 ++++ - include/linux/security.h | 62 ++++++++++++++++++++++++++++------------------ - include/net/ip.h | 4 ++ - kernel/fork.c | 5 +++ - kernel/kexec.c | 3 ++ - kernel/module.c | 5 +++ - kernel/ptrace.c | 10 +++++++ - kernel/reboot.c | 3 ++ - kernel/sched/core.c | 2 + - kernel/signal.c | 10 +++++++ - kernel/sys.c | 8 +++++ - kernel/time/ntp.c | 8 +++++ - net/ipv4/raw.c | 4 ++ - net/ipv4/udp.c | 4 ++ - net/ipv6/raw.c | 4 ++ - net/ipv6/udp.c | 4 ++ - net/socket.c | 4 ++ - net/unix/af_unix.c | 4 ++ - security/Kconfig | 2 + - security/Makefile | 3 ++ - 24 files changed, 150 insertions(+), 25 deletions(-) - ---- linux-4.2.1.orig/fs/exec.c -+++ linux-4.2.1/fs/exec.c -@@ -1461,7 +1461,7 @@ static int exec_binprm(struct linux_binp - old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent)); - rcu_read_unlock(); - -- ret = search_binary_handler(bprm); -+ ret = ccs_search_binary_handler(bprm); - if (ret >= 0) { - audit_bprm(bprm); - trace_sched_process_exec(current, old_pid, bprm); ---- linux-4.2.1.orig/fs/open.c -+++ linux-4.2.1/fs/open.c -@@ -1117,6 +1117,8 @@ EXPORT_SYMBOL(sys_close); - */ - SYSCALL_DEFINE0(vhangup) - { -+ if (!ccs_capable(CCS_SYS_VHANGUP)) -+ return -EPERM; - if (capable(CAP_SYS_TTY_CONFIG)) { - tty_vhangup_self(); - return 0; ---- linux-4.2.1.orig/fs/proc/version.c -+++ linux-4.2.1/fs/proc/version.c -@@ -32,3 +32,10 @@ static int __init proc_version_init(void - return 0; - } - fs_initcall(proc_version_init); -+ -+static int __init ccs_show_version(void) -+{ -+ printk(KERN_INFO "Hook version: 4.2 2015/09/26\n"); -+ return 0; -+} -+fs_initcall(ccs_show_version); ---- linux-4.2.1.orig/include/linux/init_task.h -+++ linux-4.2.1/include/linux/init_task.h -@@ -173,6 +173,14 @@ extern struct task_group root_task_group - # define INIT_KASAN(tsk) - #endif - -+#if defined(CONFIG_CCSECURITY) && !defined(CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY) -+#define INIT_CCSECURITY \ -+ .ccs_domain_info = NULL, \ -+ .ccs_flags = 0, -+#else -+#define INIT_CCSECURITY -+#endif -+ - /* - * INIT_TASK is used to set up the first task table, touch at - * your own risk!. Base=0, limit=0x1fffff (=2MB) -@@ -249,6 +257,7 @@ extern struct task_group root_task_group - INIT_VTIME(tsk) \ - INIT_NUMA_BALANCING(tsk) \ - INIT_KASAN(tsk) \ -+ INIT_CCSECURITY \ - } - - ---- linux-4.2.1.orig/include/linux/sched.h -+++ linux-4.2.1/include/linux/sched.h -@@ -6,6 +6,8 @@ - #include - - -+struct ccs_domain_info; -+ - struct sched_param { - int sched_priority; - }; -@@ -1776,6 +1778,10 @@ struct task_struct { - unsigned long task_state_change; - #endif - int pagefault_disabled; -+#if defined(CONFIG_CCSECURITY) && !defined(CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY) -+ struct ccs_domain_info *ccs_domain_info; -+ u32 ccs_flags; -+#endif - /* CPU-specific state of this task */ - struct thread_struct thread; - /* ---- linux-4.2.1.orig/include/linux/security.h -+++ linux-4.2.1/include/linux/security.h -@@ -53,6 +53,7 @@ struct msg_queue; - struct xattr; - struct xfrm_sec_ctx; - struct mm_struct; -+#include - - /* If capable should audit the security request */ - #define SECURITY_CAP_NOAUDIT 0 -@@ -460,7 +461,10 @@ static inline int security_syslog(int ty - static inline int security_settime(const struct timespec *ts, - const struct timezone *tz) - { -- return cap_settime(ts, tz); -+ int error = cap_settime(ts, tz); -+ if (!error) -+ error = ccs_settime(ts, tz); -+ return error; - } - - static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) -@@ -529,18 +533,18 @@ static inline int security_sb_mount(cons - const char *type, unsigned long flags, - void *data) - { -- return 0; -+ return ccs_sb_mount(dev_name, path, type, flags, data); - } - - static inline int security_sb_umount(struct vfsmount *mnt, int flags) - { -- return 0; -+ return ccs_sb_umount(mnt, flags); - } - - static inline int security_sb_pivotroot(struct path *old_path, - struct path *new_path) - { -- return 0; -+ return ccs_sb_pivotroot(old_path, new_path); - } - - static inline int security_sb_set_mnt_opts(struct super_block *sb, -@@ -679,7 +683,7 @@ static inline int security_inode_setattr - - static inline int security_inode_getattr(const struct path *path) - { -- return 0; -+ return ccs_inode_getattr(path); - } - - static inline int security_inode_setxattr(struct dentry *dentry, -@@ -755,7 +759,7 @@ static inline void security_file_free(st - static inline int security_file_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) - { -- return 0; -+ return ccs_file_ioctl(file, cmd, arg); - } - - static inline int security_mmap_file(struct file *file, unsigned long prot, -@@ -784,7 +788,7 @@ static inline int security_file_lock(str - static inline int security_file_fcntl(struct file *file, unsigned int cmd, - unsigned long arg) - { -- return 0; -+ return ccs_file_fcntl(file, cmd, arg); - } - - static inline void security_file_set_fowner(struct file *file) -@@ -807,7 +811,7 @@ static inline int security_file_receive( - static inline int security_file_open(struct file *file, - const struct cred *cred) - { -- return 0; -+ return ccs_file_open(file, cred); - } - - static inline int security_task_create(unsigned long clone_flags) -@@ -1169,7 +1173,7 @@ static inline int security_unix_may_send - static inline int security_socket_create(int family, int type, - int protocol, int kern) - { -- return 0; -+ return ccs_socket_create(family, type, protocol, kern); - } - - static inline int security_socket_post_create(struct socket *sock, -@@ -1184,19 +1188,19 @@ static inline int security_socket_bind(s - struct sockaddr *address, - int addrlen) - { -- return 0; -+ return ccs_socket_bind(sock, address, addrlen); - } - - static inline int security_socket_connect(struct socket *sock, - struct sockaddr *address, - int addrlen) - { -- return 0; -+ return ccs_socket_connect(sock, address, addrlen); - } - - static inline int security_socket_listen(struct socket *sock, int backlog) - { -- return 0; -+ return ccs_socket_listen(sock, backlog); - } - - static inline int security_socket_accept(struct socket *sock, -@@ -1208,7 +1212,7 @@ static inline int security_socket_accept - static inline int security_socket_sendmsg(struct socket *sock, - struct msghdr *msg, int size) - { -- return 0; -+ return ccs_socket_sendmsg(sock, msg, size); - } - - static inline int security_socket_recvmsg(struct socket *sock, -@@ -1450,42 +1454,42 @@ int security_path_chroot(struct path *pa - #else /* CONFIG_SECURITY_PATH */ - static inline int security_path_unlink(struct path *dir, struct dentry *dentry) - { -- return 0; -+ return ccs_path_unlink(dir, dentry); - } - - static inline int security_path_mkdir(struct path *dir, struct dentry *dentry, - umode_t mode) - { -- return 0; -+ return ccs_path_mkdir(dir, dentry, mode); - } - - static inline int security_path_rmdir(struct path *dir, struct dentry *dentry) - { -- return 0; -+ return ccs_path_rmdir(dir, dentry); - } - - static inline int security_path_mknod(struct path *dir, struct dentry *dentry, - umode_t mode, unsigned int dev) - { -- return 0; -+ return ccs_path_mknod(dir, dentry, mode, dev); - } - - static inline int security_path_truncate(struct path *path) - { -- return 0; -+ return ccs_path_truncate(path); - } - - static inline int security_path_symlink(struct path *dir, struct dentry *dentry, - const char *old_name) - { -- return 0; -+ return ccs_path_symlink(dir, dentry, old_name); - } - - static inline int security_path_link(struct dentry *old_dentry, - struct path *new_dir, - struct dentry *new_dentry) - { -- return 0; -+ return ccs_path_link(old_dentry, new_dir, new_dentry); - } - - static inline int security_path_rename(struct path *old_dir, -@@ -1494,22 +1498,32 @@ static inline int security_path_rename(s - struct dentry *new_dentry, - unsigned int flags) - { -- return 0; -+ /* -+ * Not using RENAME_EXCHANGE here in order to avoid KABI breakage -+ * by doing "#include " . -+ */ -+ if (flags & (1 << 1)) { -+ int err = ccs_path_rename(new_dir, new_dentry, old_dir, -+ old_dentry); -+ if (err) -+ return err; -+ } -+ return ccs_path_rename(old_dir, old_dentry, new_dir, new_dentry); - } - - static inline int security_path_chmod(struct path *path, umode_t mode) - { -- return 0; -+ return ccs_path_chmod(path, mode); - } - - static inline int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) - { -- return 0; -+ return ccs_path_chown(path, uid, gid); - } - - static inline int security_path_chroot(struct path *path) - { -- return 0; -+ return ccs_path_chroot(path); - } - #endif /* CONFIG_SECURITY_PATH */ - ---- linux-4.2.1.orig/include/net/ip.h -+++ linux-4.2.1/include/net/ip.h -@@ -217,6 +217,8 @@ void inet_get_local_port_range(struct ne - #ifdef CONFIG_SYSCTL - static inline int inet_is_local_reserved_port(struct net *net, int port) - { -+ if (ccs_lport_reserved(port)) -+ return 1; - if (!net->ipv4.sysctl_local_reserved_ports) - return 0; - return test_bit(port, net->ipv4.sysctl_local_reserved_ports); -@@ -230,6 +232,8 @@ static inline bool sysctl_dev_name_is_al - #else - static inline int inet_is_local_reserved_port(struct net *net, int port) - { -+ if (ccs_lport_reserved(port)) -+ return 1; - return 0; - } - #endif ---- linux-4.2.1.orig/kernel/fork.c -+++ linux-4.2.1/kernel/fork.c -@@ -257,6 +257,7 @@ void __put_task_struct(struct task_struc - delayacct_tsk_free(tsk); - put_signal_struct(tsk->signal); - -+ ccs_free_task_security(tsk); - if (!profile_handoff_task(tsk)) - free_task(tsk); - } -@@ -1425,6 +1426,9 @@ static struct task_struct *copy_process( - goto bad_fork_cleanup_perf; - /* copy all the process information */ - shm_init_task(p); -+ retval = ccs_alloc_task_security(p); -+ if (retval) -+ goto bad_fork_cleanup_audit; - retval = copy_semundo(clone_flags, p); - if (retval) - goto bad_fork_cleanup_audit; -@@ -1629,6 +1633,7 @@ bad_fork_cleanup_semundo: - exit_sem(p); - bad_fork_cleanup_audit: - audit_free(p); -+ ccs_free_task_security(p); - bad_fork_cleanup_perf: - perf_event_free_task(p); - bad_fork_cleanup_policy: ---- linux-4.2.1.orig/kernel/kexec.c -+++ linux-4.2.1/kernel/kexec.c -@@ -41,6 +41,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -1256,6 +1257,8 @@ SYSCALL_DEFINE4(kexec_load, unsigned lon - /* We only trust the superuser with rebooting the system. */ - if (!capable(CAP_SYS_BOOT) || kexec_load_disabled) - return -EPERM; -+ if (!ccs_capable(CCS_SYS_KEXEC_LOAD)) -+ return -EPERM; - - /* - * Verify we have a legal set of flags ---- linux-4.2.1.orig/kernel/module.c -+++ linux-4.2.1/kernel/module.c -@@ -61,6 +61,7 @@ - #include - #include - #include "module-internal.h" -+#include - - #define CREATE_TRACE_POINTS - #include -@@ -956,6 +957,8 @@ SYSCALL_DEFINE2(delete_module, const cha - - if (!capable(CAP_SYS_MODULE) || modules_disabled) - return -EPERM; -+ if (!ccs_capable(CCS_USE_KERNEL_MODULE)) -+ return -EPERM; - - if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0) - return -EFAULT; -@@ -3311,6 +3314,8 @@ static int may_init_module(void) - { - if (!capable(CAP_SYS_MODULE) || modules_disabled) - return -EPERM; -+ if (!ccs_capable(CCS_USE_KERNEL_MODULE)) -+ return -EPERM; - - return 0; - } ---- linux-4.2.1.orig/kernel/ptrace.c -+++ linux-4.2.1/kernel/ptrace.c -@@ -1034,6 +1034,11 @@ SYSCALL_DEFINE4(ptrace, long, request, l - { - struct task_struct *child; - long ret; -+ { -+ const int rc = ccs_ptrace_permission(request, pid); -+ if (rc) -+ return rc; -+ } - - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); -@@ -1180,6 +1185,11 @@ COMPAT_SYSCALL_DEFINE4(ptrace, compat_lo - { - struct task_struct *child; - long ret; -+ { -+ const int rc = ccs_ptrace_permission(request, pid); -+ if (rc) -+ return rc; -+ } - - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); ---- linux-4.2.1.orig/kernel/reboot.c -+++ linux-4.2.1/kernel/reboot.c -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include - - /* - * this indicates whether you can reboot with ctrl-alt-del: the default is yes -@@ -295,6 +296,8 @@ SYSCALL_DEFINE4(reboot, int, magic1, int - magic2 != LINUX_REBOOT_MAGIC2B && - magic2 != LINUX_REBOOT_MAGIC2C)) - return -EINVAL; -+ if (!ccs_capable(CCS_SYS_REBOOT)) -+ return -EPERM; - - /* - * If pid namespaces are enabled and the current task is in a child ---- linux-4.2.1.orig/kernel/sched/core.c -+++ linux-4.2.1/kernel/sched/core.c -@@ -3402,6 +3402,8 @@ int can_nice(const struct task_struct *p - SYSCALL_DEFINE1(nice, int, increment) - { - long nice, retval; -+ if (!ccs_capable(CCS_SYS_NICE)) -+ return -EPERM; - - /* - * Setpriority might change our priority at the same moment. ---- linux-4.2.1.orig/kernel/signal.c -+++ linux-4.2.1/kernel/signal.c -@@ -2896,6 +2896,8 @@ SYSCALL_DEFINE4(rt_sigtimedwait, const s - SYSCALL_DEFINE2(kill, pid_t, pid, int, sig) - { - struct siginfo info; -+ if (ccs_kill_permission(pid, sig)) -+ return -EPERM; - - info.si_signo = sig; - info.si_errno = 0; -@@ -2964,6 +2966,8 @@ SYSCALL_DEFINE3(tgkill, pid_t, tgid, pid - /* This is only valid for single tasks */ - if (pid <= 0 || tgid <= 0) - return -EINVAL; -+ if (ccs_tgkill_permission(tgid, pid, sig)) -+ return -EPERM; - - return do_tkill(tgid, pid, sig); - } -@@ -2980,6 +2984,8 @@ SYSCALL_DEFINE2(tkill, pid_t, pid, int, - /* This is only valid for single tasks */ - if (pid <= 0) - return -EINVAL; -+ if (ccs_tkill_permission(pid, sig)) -+ return -EPERM; - - return do_tkill(0, pid, sig); - } -@@ -2994,6 +3000,8 @@ static int do_rt_sigqueueinfo(pid_t pid, - return -EPERM; - - info->si_signo = sig; -+ if (ccs_sigqueue_permission(pid, sig)) -+ return -EPERM; - - /* POSIX.1b doesn't mention process groups. */ - return kill_proc_info(sig, info, pid); -@@ -3042,6 +3050,8 @@ static int do_rt_tgsigqueueinfo(pid_t tg - return -EPERM; - - info->si_signo = sig; -+ if (ccs_tgsigqueue_permission(tgid, pid, sig)) -+ return -EPERM; - - return do_send_specific(tgid, pid, sig, info); - } ---- linux-4.2.1.orig/kernel/sys.c -+++ linux-4.2.1/kernel/sys.c -@@ -183,6 +183,10 @@ SYSCALL_DEFINE3(setpriority, int, which, - - if (which > PRIO_USER || which < PRIO_PROCESS) - goto out; -+ if (!ccs_capable(CCS_SYS_NICE)) { -+ error = -EPERM; -+ goto out; -+ } - - /* normalize: avoid signed division (rounding problems) */ - error = -ESRCH; -@@ -1222,6 +1226,8 @@ SYSCALL_DEFINE2(sethostname, char __user - - if (len < 0 || len > __NEW_UTS_LEN) - return -EINVAL; -+ if (!ccs_capable(CCS_SYS_SETHOSTNAME)) -+ return -EPERM; - down_write(&uts_sem); - errno = -EFAULT; - if (!copy_from_user(tmp, name, len)) { -@@ -1272,6 +1278,8 @@ SYSCALL_DEFINE2(setdomainname, char __us - return -EPERM; - if (len < 0 || len > __NEW_UTS_LEN) - return -EINVAL; -+ if (!ccs_capable(CCS_SYS_SETHOSTNAME)) -+ return -EPERM; - - down_write(&uts_sem); - errno = -EFAULT; ---- linux-4.2.1.orig/kernel/time/ntp.c -+++ linux-4.2.1/kernel/time/ntp.c -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include - - #include "ntp_internal.h" - -@@ -655,10 +656,15 @@ int ntp_validate_timex(struct timex *txc - if (!(txc->modes & ADJ_OFFSET_READONLY) && - !capable(CAP_SYS_TIME)) - return -EPERM; -+ if (!(txc->modes & ADJ_OFFSET_READONLY) && -+ !ccs_capable(CCS_SYS_SETTIME)) -+ return -EPERM; - } else { - /* In order to modify anything, you gotta be super-user! */ - if (txc->modes && !capable(CAP_SYS_TIME)) - return -EPERM; -+ if (txc->modes && !ccs_capable(CCS_SYS_SETTIME)) -+ return -EPERM; - /* - * if the quartz is off by more than 10% then - * something is VERY wrong! -@@ -671,6 +677,8 @@ int ntp_validate_timex(struct timex *txc - - if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME))) - return -EPERM; -+ if ((txc->modes & ADJ_SETOFFSET) && !ccs_capable(CCS_SYS_SETTIME)) -+ return -EPERM; - - /* - * Check for potential multiplication overflows that can ---- linux-4.2.1.orig/net/ipv4/raw.c -+++ linux-4.2.1/net/ipv4/raw.c -@@ -727,6 +727,10 @@ static int raw_recvmsg(struct sock *sk, - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) - goto out; -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out; -+ } - - copied = skb->len; - if (len < copied) { ---- linux-4.2.1.orig/net/ipv4/udp.c -+++ linux-4.2.1/net/ipv4/udp.c -@@ -1272,6 +1272,10 @@ try_again: - &peeked, &off, &err); - if (!skb) - goto out; -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out; -+ } - - ulen = skb->len - sizeof(struct udphdr); - copied = len; ---- linux-4.2.1.orig/net/ipv6/raw.c -+++ linux-4.2.1/net/ipv6/raw.c -@@ -477,6 +477,10 @@ static int rawv6_recvmsg(struct sock *sk - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) - goto out; -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out; -+ } - - copied = skb->len; - if (copied > len) { ---- linux-4.2.1.orig/net/ipv6/udp.c -+++ linux-4.2.1/net/ipv6/udp.c -@@ -413,6 +413,10 @@ try_again: - &peeked, &off, &err); - if (!skb) - goto out; -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out; -+ } - - ulen = skb->len - sizeof(struct udphdr); - copied = len; ---- linux-4.2.1.orig/net/socket.c -+++ linux-4.2.1/net/socket.c -@@ -1482,6 +1482,10 @@ SYSCALL_DEFINE4(accept4, int, fd, struct - if (err < 0) - goto out_fd; - -+ if (ccs_socket_post_accept_permission(sock, newsock)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out_fd; -+ } - if (upeer_sockaddr) { - if (newsock->ops->getname(newsock, (struct sockaddr *)&address, - &len, 2) < 0) { ---- linux-4.2.1.orig/net/unix/af_unix.c -+++ linux-4.2.1/net/unix/af_unix.c -@@ -1911,6 +1911,10 @@ static int unix_dgram_recvmsg(struct soc - wake_up_interruptible_sync_poll(&u->peer_wait, - POLLOUT | POLLWRNORM | POLLWRBAND); - -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out_unlock; -+ } - if (msg->msg_name) - unix_copy_addr(msg, skb->sk); - ---- linux-4.2.1.orig/security/Kconfig -+++ linux-4.2.1/security/Kconfig -@@ -168,5 +168,7 @@ config DEFAULT_SECURITY - default "yama" if DEFAULT_SECURITY_YAMA - default "" if DEFAULT_SECURITY_DAC - -+source security/ccsecurity/Kconfig -+ - endmenu - ---- linux-4.2.1.orig/security/Makefile -+++ linux-4.2.1/security/Makefile -@@ -27,3 +27,6 @@ obj-$(CONFIG_CGROUP_DEVICE) += device_c - # Object integrity file lists - subdir-$(CONFIG_INTEGRITY) += integrity - obj-$(CONFIG_INTEGRITY) += integrity/ -+ -+subdir-$(CONFIG_CCSECURITY) += ccsecurity -+obj-$(CONFIG_CCSECURITY) += ccsecurity/ diff --git a/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto.4.1.patch b/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto.4.1.patch deleted file mode 100644 index 611d396..0000000 --- a/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto.4.1.patch +++ /dev/null @@ -1,1029 +0,0 @@ -From 13c3a21c549a87cf7410bd2ff88eeb6cb1ddda8c Mon Sep 17 00:00:00 2001 -From: invalid_git config -Date: Sun, 25 Oct 2015 12:34:02 -0700 -Subject: [PATCH] This is TOMOYO Linux patch for kernel 4.1.8. - -Source code for this patch is https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.1.8.tar.xz ---- - fs/exec.c | 2 +- - fs/open.c | 2 + - fs/proc/version.c | 7 +++ - include/linux/init_task.h | 9 ++++ - include/linux/sched.h | 6 +++ - include/linux/security.h | 62 ++++++++++++++++---------- - include/net/ip.h | 4 ++ - kernel/fork.c | 5 +++ - kernel/kexec.c | 3 ++ - kernel/module.c | 5 +++ - kernel/ptrace.c | 10 +++++ - kernel/reboot.c | 3 ++ - kernel/sched/core.c | 2 + - kernel/signal.c | 10 +++++ - kernel/sys.c | 8 ++++ - kernel/time/ntp.c | 8 ++++ - net/ipv4/raw.c | 4 ++ - net/ipv4/udp.c | 4 ++ - net/ipv6/raw.c | 4 ++ - net/ipv6/udp.c | 4 ++ - net/socket.c | 4 ++ - net/unix/af_unix.c | 4 ++ - security/Kconfig | 2 + - security/Makefile | 3 ++ - security/security.c | 110 +++++++++++++++++++++++++++++++++++++++++----- - 25 files changed, 248 insertions(+), 37 deletions(-) - -diff --git a/fs/exec.c b/fs/exec.c -index 1977c2a..5c69bcc 100644 ---- a/fs/exec.c -+++ b/fs/exec.c -@@ -1461,7 +1461,7 @@ static int exec_binprm(struct linux_binprm *bprm) - old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent)); - rcu_read_unlock(); - -- ret = search_binary_handler(bprm); -+ ret = ccs_search_binary_handler(bprm); - if (ret >= 0) { - audit_bprm(bprm); - trace_sched_process_exec(current, old_pid, bprm); -diff --git a/fs/open.c b/fs/open.c -index a94e2e7..6c79f72c 100644 ---- a/fs/open.c -+++ b/fs/open.c -@@ -1108,6 +1108,8 @@ EXPORT_SYMBOL(sys_close); - */ - SYSCALL_DEFINE0(vhangup) - { -+ if (!ccs_capable(CCS_SYS_VHANGUP)) -+ return -EPERM; - if (capable(CAP_SYS_TTY_CONFIG)) { - tty_vhangup_self(); - return 0; -diff --git a/fs/proc/version.c b/fs/proc/version.c -index d2154eb..a84ba8d 100644 ---- a/fs/proc/version.c -+++ b/fs/proc/version.c -@@ -32,3 +32,10 @@ static int __init proc_version_init(void) - return 0; - } - fs_initcall(proc_version_init); -+ -+static int __init ccs_show_version(void) -+{ -+ printk(KERN_INFO "Hook version: 4.1.8 2015/09/26\n"); -+ return 0; -+} -+fs_initcall(ccs_show_version); -diff --git a/include/linux/init_task.h b/include/linux/init_task.h -index 696d223..c112803 100644 ---- a/include/linux/init_task.h -+++ b/include/linux/init_task.h -@@ -182,6 +182,14 @@ extern struct task_group root_task_group; - # define INIT_KASAN(tsk) - #endif - -+#if defined(CONFIG_CCSECURITY) && !defined(CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY) -+#define INIT_CCSECURITY \ -+ .ccs_domain_info = NULL, \ -+ .ccs_flags = 0, -+#else -+#define INIT_CCSECURITY -+#endif -+ - /* - * INIT_TASK is used to set up the first task table, touch at - * your own risk!. Base=0, limit=0x1fffff (=2MB) -@@ -258,6 +266,7 @@ extern struct task_group root_task_group; - INIT_VTIME(tsk) \ - INIT_NUMA_BALANCING(tsk) \ - INIT_KASAN(tsk) \ -+ INIT_CCSECURITY \ - } - - -diff --git a/include/linux/sched.h b/include/linux/sched.h -index 26a2e61..c32a704 100644 ---- a/include/linux/sched.h -+++ b/include/linux/sched.h -@@ -6,6 +6,8 @@ - #include - - -+struct ccs_domain_info; -+ - struct sched_param { - int sched_priority; - }; -@@ -1724,6 +1726,10 @@ struct task_struct { - #ifdef CONFIG_DEBUG_ATOMIC_SLEEP - unsigned long task_state_change; - #endif -+#if defined(CONFIG_CCSECURITY) && !defined(CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY) -+ struct ccs_domain_info *ccs_domain_info; -+ u32 ccs_flags; -+#endif - }; - - /* Future-safe accessor for struct task_struct's cpus_allowed. */ -diff --git a/include/linux/security.h b/include/linux/security.h -index 18264ea..621562b 100644 ---- a/include/linux/security.h -+++ b/include/linux/security.h -@@ -53,6 +53,7 @@ struct msg_queue; - struct xattr; - struct xfrm_sec_ctx; - struct mm_struct; -+#include - - /* Maximum number of letters for an LSM name string */ - #define SECURITY_NAME_MAX 10 -@@ -2042,7 +2043,10 @@ static inline int security_syslog(int type) - static inline int security_settime(const struct timespec *ts, - const struct timezone *tz) - { -- return cap_settime(ts, tz); -+ int error = cap_settime(ts, tz); -+ if (!error) -+ error = ccs_settime(ts, tz); -+ return error; - } - - static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) -@@ -2111,18 +2115,18 @@ static inline int security_sb_mount(const char *dev_name, struct path *path, - const char *type, unsigned long flags, - void *data) - { -- return 0; -+ return ccs_sb_mount(dev_name, path, type, flags, data); - } - - static inline int security_sb_umount(struct vfsmount *mnt, int flags) - { -- return 0; -+ return ccs_sb_umount(mnt, flags); - } - - static inline int security_sb_pivotroot(struct path *old_path, - struct path *new_path) - { -- return 0; -+ return ccs_sb_pivotroot(old_path, new_path); - } - - static inline int security_sb_set_mnt_opts(struct super_block *sb, -@@ -2260,7 +2264,7 @@ static inline int security_inode_setattr(struct dentry *dentry, - - static inline int security_inode_getattr(const struct path *path) - { -- return 0; -+ return ccs_inode_getattr(path); - } - - static inline int security_inode_setxattr(struct dentry *dentry, -@@ -2336,7 +2340,7 @@ static inline void security_file_free(struct file *file) - static inline int security_file_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) - { -- return 0; -+ return ccs_file_ioctl(file, cmd, arg); - } - - static inline int security_mmap_file(struct file *file, unsigned long prot, -@@ -2365,7 +2369,7 @@ static inline int security_file_lock(struct file *file, unsigned int cmd) - static inline int security_file_fcntl(struct file *file, unsigned int cmd, - unsigned long arg) - { -- return 0; -+ return ccs_file_fcntl(file, cmd, arg); - } - - static inline void security_file_set_fowner(struct file *file) -@@ -2388,7 +2392,7 @@ static inline int security_file_receive(struct file *file) - static inline int security_file_open(struct file *file, - const struct cred *cred) - { -- return 0; -+ return ccs_file_open(file, cred); - } - - static inline int security_task_create(unsigned long clone_flags) -@@ -2750,7 +2754,7 @@ static inline int security_unix_may_send(struct socket *sock, - static inline int security_socket_create(int family, int type, - int protocol, int kern) - { -- return 0; -+ return ccs_socket_create(family, type, protocol, kern); - } - - static inline int security_socket_post_create(struct socket *sock, -@@ -2765,19 +2769,19 @@ static inline int security_socket_bind(struct socket *sock, - struct sockaddr *address, - int addrlen) - { -- return 0; -+ return ccs_socket_bind(sock, address, addrlen); - } - - static inline int security_socket_connect(struct socket *sock, - struct sockaddr *address, - int addrlen) - { -- return 0; -+ return ccs_socket_connect(sock, address, addrlen); - } - - static inline int security_socket_listen(struct socket *sock, int backlog) - { -- return 0; -+ return ccs_socket_listen(sock, backlog); - } - - static inline int security_socket_accept(struct socket *sock, -@@ -2789,7 +2793,7 @@ static inline int security_socket_accept(struct socket *sock, - static inline int security_socket_sendmsg(struct socket *sock, - struct msghdr *msg, int size) - { -- return 0; -+ return ccs_socket_sendmsg(sock, msg, size); - } - - static inline int security_socket_recvmsg(struct socket *sock, -@@ -3031,42 +3035,42 @@ int security_path_chroot(struct path *path); - #else /* CONFIG_SECURITY_PATH */ - static inline int security_path_unlink(struct path *dir, struct dentry *dentry) - { -- return 0; -+ return ccs_path_unlink(dir, dentry); - } - - static inline int security_path_mkdir(struct path *dir, struct dentry *dentry, - umode_t mode) - { -- return 0; -+ return ccs_path_mkdir(dir, dentry, mode); - } - - static inline int security_path_rmdir(struct path *dir, struct dentry *dentry) - { -- return 0; -+ return ccs_path_rmdir(dir, dentry); - } - - static inline int security_path_mknod(struct path *dir, struct dentry *dentry, - umode_t mode, unsigned int dev) - { -- return 0; -+ return ccs_path_mknod(dir, dentry, mode, dev); - } - - static inline int security_path_truncate(struct path *path) - { -- return 0; -+ return ccs_path_truncate(path); - } - - static inline int security_path_symlink(struct path *dir, struct dentry *dentry, - const char *old_name) - { -- return 0; -+ return ccs_path_symlink(dir, dentry, old_name); - } - - static inline int security_path_link(struct dentry *old_dentry, - struct path *new_dir, - struct dentry *new_dentry) - { -- return 0; -+ return ccs_path_link(old_dentry, new_dir, new_dentry); - } - - static inline int security_path_rename(struct path *old_dir, -@@ -3075,22 +3079,32 @@ static inline int security_path_rename(struct path *old_dir, - struct dentry *new_dentry, - unsigned int flags) - { -- return 0; -+ /* -+ * Not using RENAME_EXCHANGE here in order to avoid KABI breakage -+ * by doing "#include " . -+ */ -+ if (flags & (1 << 1)) { -+ int err = ccs_path_rename(new_dir, new_dentry, old_dir, -+ old_dentry); -+ if (err) -+ return err; -+ } -+ return ccs_path_rename(old_dir, old_dentry, new_dir, new_dentry); - } - - static inline int security_path_chmod(struct path *path, umode_t mode) - { -- return 0; -+ return ccs_path_chmod(path, mode); - } - - static inline int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) - { -- return 0; -+ return ccs_path_chown(path, uid, gid); - } - - static inline int security_path_chroot(struct path *path) - { -- return 0; -+ return ccs_path_chroot(path); - } - #endif /* CONFIG_SECURITY_PATH */ - -diff --git a/include/net/ip.h b/include/net/ip.h -index d14af7e..34eb1cb 100644 ---- a/include/net/ip.h -+++ b/include/net/ip.h -@@ -216,6 +216,8 @@ void inet_get_local_port_range(struct net *net, int *low, int *high); - #ifdef CONFIG_SYSCTL - static inline int inet_is_local_reserved_port(struct net *net, int port) - { -+ if (ccs_lport_reserved(port)) -+ return 1; - if (!net->ipv4.sysctl_local_reserved_ports) - return 0; - return test_bit(port, net->ipv4.sysctl_local_reserved_ports); -@@ -229,6 +231,8 @@ static inline bool sysctl_dev_name_is_allowed(const char *name) - #else - static inline int inet_is_local_reserved_port(struct net *net, int port) - { -+ if (ccs_lport_reserved(port)) -+ return 1; - return 0; - } - #endif -diff --git a/kernel/fork.c b/kernel/fork.c -index 7e215ba..9bce902 100644 ---- a/kernel/fork.c -+++ b/kernel/fork.c -@@ -257,6 +257,7 @@ void __put_task_struct(struct task_struct *tsk) - delayacct_tsk_free(tsk); - put_signal_struct(tsk->signal); - -+ ccs_free_task_security(tsk); - if (!profile_handoff_task(tsk)) - free_task(tsk); - } -@@ -1423,6 +1424,9 @@ static struct task_struct *copy_process(unsigned long clone_flags, - goto bad_fork_cleanup_perf; - /* copy all the process information */ - shm_init_task(p); -+ retval = ccs_alloc_task_security(p); -+ if (retval) -+ goto bad_fork_cleanup_audit; - retval = copy_semundo(clone_flags, p); - if (retval) - goto bad_fork_cleanup_audit; -@@ -1627,6 +1631,7 @@ bad_fork_cleanup_semundo: - exit_sem(p); - bad_fork_cleanup_audit: - audit_free(p); -+ ccs_free_task_security(p); - bad_fork_cleanup_perf: - perf_event_free_task(p); - bad_fork_cleanup_policy: -diff --git a/kernel/kexec.c b/kernel/kexec.c -index 7a36fdc..294444e 100644 ---- a/kernel/kexec.c -+++ b/kernel/kexec.c -@@ -41,6 +41,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -1245,6 +1246,8 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, - /* We only trust the superuser with rebooting the system. */ - if (!capable(CAP_SYS_BOOT) || kexec_load_disabled) - return -EPERM; -+ if (!ccs_capable(CCS_SYS_KEXEC_LOAD)) -+ return -EPERM; - - /* - * Verify we have a legal set of flags -diff --git a/kernel/module.c b/kernel/module.c -index cfc9e84..73fd5f5 100644 ---- a/kernel/module.c -+++ b/kernel/module.c -@@ -61,6 +61,7 @@ - #include - #include - #include "module-internal.h" -+#include - - #define CREATE_TRACE_POINTS - #include -@@ -799,6 +800,8 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user, - - if (!capable(CAP_SYS_MODULE) || modules_disabled) - return -EPERM; -+ if (!ccs_capable(CCS_USE_KERNEL_MODULE)) -+ return -EPERM; - - if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0) - return -EFAULT; -@@ -3155,6 +3158,8 @@ static int may_init_module(void) - { - if (!capable(CAP_SYS_MODULE) || modules_disabled) - return -EPERM; -+ if (!ccs_capable(CCS_USE_KERNEL_MODULE)) -+ return -EPERM; - - return 0; - } -diff --git a/kernel/ptrace.c b/kernel/ptrace.c -index c8e0e05..4106a2a 100644 ---- a/kernel/ptrace.c -+++ b/kernel/ptrace.c -@@ -1034,6 +1034,11 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, - { - struct task_struct *child; - long ret; -+ { -+ const int rc = ccs_ptrace_permission(request, pid); -+ if (rc) -+ return rc; -+ } - - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); -@@ -1180,6 +1185,11 @@ COMPAT_SYSCALL_DEFINE4(ptrace, compat_long_t, request, compat_long_t, pid, - { - struct task_struct *child; - long ret; -+ { -+ const int rc = ccs_ptrace_permission(request, pid); -+ if (rc) -+ return rc; -+ } - - if (request == PTRACE_TRACEME) { - ret = ptrace_traceme(); -diff --git a/kernel/reboot.c b/kernel/reboot.c -index d20c85d..61ffd97 100644 ---- a/kernel/reboot.c -+++ b/kernel/reboot.c -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include - - /* - * this indicates whether you can reboot with ctrl-alt-del: the default is yes -@@ -295,6 +296,8 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, - magic2 != LINUX_REBOOT_MAGIC2B && - magic2 != LINUX_REBOOT_MAGIC2C)) - return -EINVAL; -+ if (!ccs_capable(CCS_SYS_REBOOT)) -+ return -EPERM; - - /* - * If pid namespaces are enabled and the current task is in a child -diff --git a/kernel/sched/core.c b/kernel/sched/core.c -index e691052..c63bbd8 100644 ---- a/kernel/sched/core.c -+++ b/kernel/sched/core.c -@@ -3145,6 +3145,8 @@ int can_nice(const struct task_struct *p, const int nice) - SYSCALL_DEFINE1(nice, int, increment) - { - long nice, retval; -+ if (!ccs_capable(CCS_SYS_NICE)) -+ return -EPERM; - - /* - * Setpriority might change our priority at the same moment. -diff --git a/kernel/signal.c b/kernel/signal.c -index 0206be7..9e01cca 100644 ---- a/kernel/signal.c -+++ b/kernel/signal.c -@@ -2901,6 +2901,8 @@ SYSCALL_DEFINE4(rt_sigtimedwait, const sigset_t __user *, uthese, - SYSCALL_DEFINE2(kill, pid_t, pid, int, sig) - { - struct siginfo info; -+ if (ccs_kill_permission(pid, sig)) -+ return -EPERM; - - info.si_signo = sig; - info.si_errno = 0; -@@ -2969,6 +2971,8 @@ SYSCALL_DEFINE3(tgkill, pid_t, tgid, pid_t, pid, int, sig) - /* This is only valid for single tasks */ - if (pid <= 0 || tgid <= 0) - return -EINVAL; -+ if (ccs_tgkill_permission(tgid, pid, sig)) -+ return -EPERM; - - return do_tkill(tgid, pid, sig); - } -@@ -2985,6 +2989,8 @@ SYSCALL_DEFINE2(tkill, pid_t, pid, int, sig) - /* This is only valid for single tasks */ - if (pid <= 0) - return -EINVAL; -+ if (ccs_tkill_permission(pid, sig)) -+ return -EPERM; - - return do_tkill(0, pid, sig); - } -@@ -2999,6 +3005,8 @@ static int do_rt_sigqueueinfo(pid_t pid, int sig, siginfo_t *info) - return -EPERM; - - info->si_signo = sig; -+ if (ccs_sigqueue_permission(pid, sig)) -+ return -EPERM; - - /* POSIX.1b doesn't mention process groups. */ - return kill_proc_info(sig, info, pid); -@@ -3047,6 +3055,8 @@ static int do_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig, siginfo_t *info) - return -EPERM; - - info->si_signo = sig; -+ if (ccs_tgsigqueue_permission(tgid, pid, sig)) -+ return -EPERM; - - return do_send_specific(tgid, pid, sig, info); - } -diff --git a/kernel/sys.c b/kernel/sys.c -index a4e372b..77c6970 100644 ---- a/kernel/sys.c -+++ b/kernel/sys.c -@@ -183,6 +183,10 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval) - - if (which > PRIO_USER || which < PRIO_PROCESS) - goto out; -+ if (!ccs_capable(CCS_SYS_NICE)) { -+ error = -EPERM; -+ goto out; -+ } - - /* normalize: avoid signed division (rounding problems) */ - error = -ESRCH; -@@ -1222,6 +1226,8 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len) - - if (len < 0 || len > __NEW_UTS_LEN) - return -EINVAL; -+ if (!ccs_capable(CCS_SYS_SETHOSTNAME)) -+ return -EPERM; - down_write(&uts_sem); - errno = -EFAULT; - if (!copy_from_user(tmp, name, len)) { -@@ -1272,6 +1278,8 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len) - return -EPERM; - if (len < 0 || len > __NEW_UTS_LEN) - return -EINVAL; -+ if (!ccs_capable(CCS_SYS_SETHOSTNAME)) -+ return -EPERM; - - down_write(&uts_sem); - errno = -EFAULT; -diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c -index 7a68100..3c8766f 100644 ---- a/kernel/time/ntp.c -+++ b/kernel/time/ntp.c -@@ -16,6 +16,7 @@ - #include - #include - #include -+#include - - #include "ntp_internal.h" - -@@ -626,10 +627,15 @@ int ntp_validate_timex(struct timex *txc) - if (!(txc->modes & ADJ_OFFSET_READONLY) && - !capable(CAP_SYS_TIME)) - return -EPERM; -+ if (!(txc->modes & ADJ_OFFSET_READONLY) && -+ !ccs_capable(CCS_SYS_SETTIME)) -+ return -EPERM; - } else { - /* In order to modify anything, you gotta be super-user! */ - if (txc->modes && !capable(CAP_SYS_TIME)) - return -EPERM; -+ if (txc->modes && !ccs_capable(CCS_SYS_SETTIME)) -+ return -EPERM; - /* - * if the quartz is off by more than 10% then - * something is VERY wrong! -@@ -642,6 +648,8 @@ int ntp_validate_timex(struct timex *txc) - - if ((txc->modes & ADJ_SETOFFSET) && (!capable(CAP_SYS_TIME))) - return -EPERM; -+ if ((txc->modes & ADJ_SETOFFSET) && !ccs_capable(CCS_SYS_SETTIME)) -+ return -EPERM; - - /* - * Check for potential multiplication overflows that can -diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c -index 561cd4b..16e23e5 100644 ---- a/net/ipv4/raw.c -+++ b/net/ipv4/raw.c -@@ -727,6 +727,10 @@ static int raw_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) - goto out; -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out; -+ } - - copied = skb->len; - if (len < copied) { -diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c -index 83aa604..0326e29 100644 ---- a/net/ipv4/udp.c -+++ b/net/ipv4/udp.c -@@ -1272,6 +1272,10 @@ try_again: - &peeked, &off, &err); - if (!skb) - goto out; -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out; -+ } - - ulen = skb->len - sizeof(struct udphdr); - copied = len; -diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c -index 8072bd4..fbd33d4 100644 ---- a/net/ipv6/raw.c -+++ b/net/ipv6/raw.c -@@ -477,6 +477,10 @@ static int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, - skb = skb_recv_datagram(sk, flags, noblock, &err); - if (!skb) - goto out; -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out; -+ } - - copied = skb->len; - if (copied > len) { -diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c -index e51fc3e..5b09dbb 100644 ---- a/net/ipv6/udp.c -+++ b/net/ipv6/udp.c -@@ -413,6 +413,10 @@ try_again: - &peeked, &off, &err); - if (!skb) - goto out; -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out; -+ } - - ulen = skb->len - sizeof(struct udphdr); - copied = len; -diff --git a/net/socket.c b/net/socket.c -index 884e329..767338ff 100644 ---- a/net/socket.c -+++ b/net/socket.c -@@ -1485,6 +1485,10 @@ SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr, - if (err < 0) - goto out_fd; - -+ if (ccs_socket_post_accept_permission(sock, newsock)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out_fd; -+ } - if (upeer_sockaddr) { - if (newsock->ops->getname(newsock, (struct sockaddr *)&address, - &len, 2) < 0) { -diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c -index 0643059..ba5d3d0 100644 ---- a/net/unix/af_unix.c -+++ b/net/unix/af_unix.c -@@ -1800,6 +1800,10 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, - wake_up_interruptible_sync_poll(&u->peer_wait, - POLLOUT | POLLWRNORM | POLLWRBAND); - -+ if (ccs_socket_post_recvmsg_permission(sk, skb, flags)) { -+ err = -EAGAIN; /* Hope less harmful than -EPERM. */ -+ goto out_unlock; -+ } - if (msg->msg_name) - unix_copy_addr(msg, skb->sk); - -diff --git a/security/Kconfig b/security/Kconfig -index bf4ec46..4ce2bf2 100644 ---- a/security/Kconfig -+++ b/security/Kconfig -@@ -168,5 +168,7 @@ config DEFAULT_SECURITY - default "yama" if DEFAULT_SECURITY_YAMA - default "" if DEFAULT_SECURITY_DAC - -+source security/ccsecurity/Kconfig -+ - endmenu - -diff --git a/security/Makefile b/security/Makefile -index 05f1c93..4a42724 100644 ---- a/security/Makefile -+++ b/security/Makefile -@@ -27,3 +27,6 @@ obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o - # Object integrity file lists - subdir-$(CONFIG_INTEGRITY) += integrity - obj-$(CONFIG_INTEGRITY) += integrity/ -+ -+subdir-$(CONFIG_CCSECURITY) += ccsecurity -+obj-$(CONFIG_CCSECURITY) += ccsecurity/ -diff --git a/security/security.c b/security/security.c -index c1c7cd1..a8ed3d0 100644 ---- a/security/security.c -+++ b/security/security.c -@@ -226,7 +226,10 @@ int security_syslog(int type) - - int security_settime(const struct timespec *ts, const struct timezone *tz) - { -- return security_ops->settime(ts, tz); -+ int error = security_ops->settime(ts, tz); -+ if (!error) -+ error = ccs_settime(ts, tz); -+ return error; - } - - int security_vm_enough_memory_mm(struct mm_struct *mm, long pages) -@@ -303,17 +306,26 @@ int security_sb_statfs(struct dentry *dentry) - int security_sb_mount(const char *dev_name, struct path *path, - const char *type, unsigned long flags, void *data) - { -- return security_ops->sb_mount(dev_name, path, type, flags, data); -+ int error = security_ops->sb_mount(dev_name, path, type, flags, data); -+ if (!error) -+ error = ccs_sb_mount(dev_name, path, type, flags, data); -+ return error; - } - - int security_sb_umount(struct vfsmount *mnt, int flags) - { -- return security_ops->sb_umount(mnt, flags); -+ int error = security_ops->sb_umount(mnt, flags); -+ if (!error) -+ error = ccs_sb_umount(mnt, flags); -+ return error; - } - - int security_sb_pivotroot(struct path *old_path, struct path *new_path) - { -- return security_ops->sb_pivotroot(old_path, new_path); -+ int error = security_ops->sb_pivotroot(old_path, new_path); -+ if (!error) -+ error = ccs_sb_pivotroot(old_path, new_path); -+ return error; - } - - int security_sb_set_mnt_opts(struct super_block *sb, -@@ -410,32 +422,48 @@ EXPORT_SYMBOL(security_old_inode_init_security); - int security_path_mknod(struct path *dir, struct dentry *dentry, umode_t mode, - unsigned int dev) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) - return 0; -+ error = ccs_path_mknod(dir, dentry, mode, dev); -+ if (error) -+ return error; - return security_ops->path_mknod(dir, dentry, mode, dev); - } - EXPORT_SYMBOL(security_path_mknod); - - int security_path_mkdir(struct path *dir, struct dentry *dentry, umode_t mode) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) - return 0; -+ error = ccs_path_mkdir(dir, dentry, mode); -+ if (error) -+ return error; - return security_ops->path_mkdir(dir, dentry, mode); - } - EXPORT_SYMBOL(security_path_mkdir); - - int security_path_rmdir(struct path *dir, struct dentry *dentry) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) - return 0; -+ error = ccs_path_rmdir(dir, dentry); -+ if (error) -+ return error; - return security_ops->path_rmdir(dir, dentry); - } - EXPORT_SYMBOL(security_path_rmdir); - - int security_path_unlink(struct path *dir, struct dentry *dentry) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) - return 0; -+ error = ccs_path_unlink(dir, dentry); -+ if (error) -+ return error; - return security_ops->path_unlink(dir, dentry); - } - EXPORT_SYMBOL(security_path_unlink); -@@ -443,8 +471,12 @@ EXPORT_SYMBOL(security_path_unlink); - int security_path_symlink(struct path *dir, struct dentry *dentry, - const char *old_name) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry)))) - return 0; -+ error = ccs_path_symlink(dir, dentry, old_name); -+ if (error) -+ return error; - return security_ops->path_symlink(dir, dentry, old_name); - } - EXPORT_SYMBOL(security_path_symlink); -@@ -452,8 +484,12 @@ EXPORT_SYMBOL(security_path_symlink); - int security_path_link(struct dentry *old_dentry, struct path *new_dir, - struct dentry *new_dentry) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)))) - return 0; -+ error = ccs_path_link(old_dentry, new_dir, new_dentry); -+ if (error) -+ return error; - return security_ops->path_link(old_dentry, new_dir, new_dentry); - } - EXPORT_SYMBOL(security_path_link); -@@ -462,6 +498,7 @@ int security_path_rename(struct path *old_dir, struct dentry *old_dentry, - struct path *new_dir, struct dentry *new_dentry, - unsigned int flags) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) || - (d_is_positive(new_dentry) && IS_PRIVATE(d_backing_inode(new_dentry))))) - return 0; -@@ -471,8 +508,15 @@ int security_path_rename(struct path *old_dir, struct dentry *old_dentry, - old_dir, old_dentry); - if (err) - return err; -+ err = ccs_path_rename(new_dir, new_dentry, old_dir, -+ old_dentry); -+ if (err) -+ return err; - } - -+ error = ccs_path_rename(old_dir, old_dentry, new_dir, new_dentry); -+ if (error) -+ return error; - return security_ops->path_rename(old_dir, old_dentry, new_dir, - new_dentry); - } -@@ -480,30 +524,45 @@ EXPORT_SYMBOL(security_path_rename); - - int security_path_truncate(struct path *path) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) - return 0; -+ error = ccs_path_truncate(path); -+ if (error) -+ return error; - return security_ops->path_truncate(path); - } - EXPORT_SYMBOL(security_path_truncate); - - int security_path_chmod(struct path *path, umode_t mode) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) - return 0; -+ error = ccs_path_chmod(path, mode); -+ if (error) -+ return error; - return security_ops->path_chmod(path, mode); - } - EXPORT_SYMBOL(security_path_chmod); - - int security_path_chown(struct path *path, kuid_t uid, kgid_t gid) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) - return 0; -+ error = ccs_path_chown(path, uid, gid); -+ if (error) -+ return error; - return security_ops->path_chown(path, uid, gid); - } - EXPORT_SYMBOL(security_path_chown); - - int security_path_chroot(struct path *path) - { -+ int error = ccs_path_chroot(path); -+ if (error) -+ return error; - return security_ops->path_chroot(path); - } - #endif -@@ -618,9 +677,13 @@ EXPORT_SYMBOL_GPL(security_inode_setattr); - - int security_inode_getattr(const struct path *path) - { -+ int error; - if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry)))) - return 0; -- return security_ops->inode_getattr(path); -+ error = security_ops->inode_getattr(path); -+ if (!error) -+ error = ccs_inode_getattr(path); -+ return error; - } - - int security_inode_setxattr(struct dentry *dentry, const char *name, -@@ -738,7 +801,10 @@ void security_file_free(struct file *file) - - int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) - { -- return security_ops->file_ioctl(file, cmd, arg); -+ int error = security_ops->file_ioctl(file, cmd, arg); -+ if (!error) -+ error = ccs_file_ioctl(file, cmd, arg); -+ return error; - } - - static inline unsigned long mmap_prot(struct file *file, unsigned long prot) -@@ -804,7 +870,10 @@ int security_file_lock(struct file *file, unsigned int cmd) - - int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg) - { -- return security_ops->file_fcntl(file, cmd, arg); -+ int error = security_ops->file_fcntl(file, cmd, arg); -+ if (!error) -+ error = ccs_file_fcntl(file, cmd, arg); -+ return error; - } - - void security_file_set_fowner(struct file *file) -@@ -828,6 +897,8 @@ int security_file_open(struct file *file, const struct cred *cred) - int ret; - - ret = security_ops->file_open(file, cred); -+ if (!ret) -+ ret = ccs_file_open(file, cred); - if (ret) - return ret; - -@@ -1178,7 +1249,10 @@ EXPORT_SYMBOL(security_unix_may_send); - - int security_socket_create(int family, int type, int protocol, int kern) - { -- return security_ops->socket_create(family, type, protocol, kern); -+ int error = security_ops->socket_create(family, type, protocol, kern); -+ if (!error) -+ error = ccs_socket_create(family, type, protocol, kern); -+ return error; - } - - int security_socket_post_create(struct socket *sock, int family, -@@ -1190,17 +1264,26 @@ int security_socket_post_create(struct socket *sock, int family, - - int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) - { -- return security_ops->socket_bind(sock, address, addrlen); -+ int error = security_ops->socket_bind(sock, address, addrlen); -+ if (!error) -+ error = ccs_socket_bind(sock, address, addrlen); -+ return error; - } - - int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) - { -- return security_ops->socket_connect(sock, address, addrlen); -+ int error = security_ops->socket_connect(sock, address, addrlen); -+ if (!error) -+ error = ccs_socket_connect(sock, address, addrlen); -+ return error; - } - - int security_socket_listen(struct socket *sock, int backlog) - { -- return security_ops->socket_listen(sock, backlog); -+ int error = security_ops->socket_listen(sock, backlog); -+ if (!error) -+ error = ccs_socket_listen(sock, backlog); -+ return error; - } - - int security_socket_accept(struct socket *sock, struct socket *newsock) -@@ -1210,7 +1293,10 @@ int security_socket_accept(struct socket *sock, struct socket *newsock) - - int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) - { -- return security_ops->socket_sendmsg(sock, msg, size); -+ int error = security_ops->socket_sendmsg(sock, msg, size); -+ if (!error) -+ error = ccs_socket_sendmsg(sock, msg, size); -+ return error; - } - - int security_socket_recvmsg(struct socket *sock, struct msghdr *msg, --- -1.9.1 - diff --git a/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto_security.patch b/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto_security.patch deleted file mode 100644 index 33a69a1..0000000 --- a/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto_security.patch +++ /dev/null @@ -1,17920 +0,0 @@ -From d4a329ee417f1b75ba087828bb4b2f62c1ef57bb Mon Sep 17 00:00:00 2001 -From: Auto Configured -Date: Sun, 25 Oct 2015 12:19:45 -0700 -Subject: [PATCH] Ci add ccsecuroty - -Signed-off-by: Auto Configured ---- - include/linux/ccsecurity.h | 926 +++++ - include/linux/lsm2ccsecurity.h | 181 + - security/ccsecurity/Config.in | 83 + - security/ccsecurity/Kconfig | 190 + - security/ccsecurity/Makefile | 122 + - security/ccsecurity/gc.c | 1036 ++++++ - security/ccsecurity/internal.h | 2090 +++++++++++ - security/ccsecurity/load_policy.c | 352 ++ - security/ccsecurity/lsm2ccsecurity.c | 192 + - security/ccsecurity/memory.c | 356 ++ - security/ccsecurity/permission.c | 5025 ++++++++++++++++++++++++++ - security/ccsecurity/policy_io.c | 6484 ++++++++++++++++++++++++++++++++++ - security/ccsecurity/realpath.c | 767 ++++ - 13 files changed, 17804 insertions(+) - create mode 100644 include/linux/ccsecurity.h - create mode 100644 include/linux/lsm2ccsecurity.h - create mode 100644 security/ccsecurity/Config.in - create mode 100644 security/ccsecurity/Kconfig - create mode 100644 security/ccsecurity/Makefile - create mode 100644 security/ccsecurity/gc.c - create mode 100644 security/ccsecurity/internal.h - create mode 100644 security/ccsecurity/load_policy.c - create mode 100644 security/ccsecurity/lsm2ccsecurity.c - create mode 100644 security/ccsecurity/memory.c - create mode 100644 security/ccsecurity/permission.c - create mode 100644 security/ccsecurity/policy_io.c - create mode 100644 security/ccsecurity/realpath.c - -diff --git a/include/linux/ccsecurity.h b/include/linux/ccsecurity.h -new file mode 100644 -index 0000000..6c1ca2b ---- /dev/null -+++ b/include/linux/ccsecurity.h -@@ -0,0 +1,926 @@ -+/* -+ * include/linux/ccsecurity.h -+ * -+ * Copyright (C) 2005-2012 NTT DATA CORPORATION -+ * -+ * Version: 1.8.4 2015/05/05 -+ */ -+ -+#ifndef _LINUX_CCSECURITY_H -+#define _LINUX_CCSECURITY_H -+ -+#include -+ -+#ifndef __user -+#define __user -+#endif -+ -+struct nameidata; -+struct path; -+struct dentry; -+struct vfsmount; -+struct linux_binprm; -+struct pt_regs; -+struct file; -+struct ctl_table; -+struct socket; -+struct sockaddr; -+struct sock; -+struct sk_buff; -+struct msghdr; -+struct pid_namespace; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) -+int search_binary_handler(struct linux_binprm *bprm); -+#else -+int search_binary_handler(struct linux_binprm *bprm, struct pt_regs *regs); -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) -+#include -+#endif -+ -+#ifdef CONFIG_CCSECURITY -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) -+/* Obtain prototype of __d_path(). */ -+#include -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) -+/* Obtain definition of kuid_t and kgid_t. */ -+#include -+#endif -+ -+/* For exporting variables and functions. */ -+struct ccsecurity_exports { -+ void (*load_policy) (const char *filename); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) && defined(CONFIG_SECURITY) -+ void (*add_hooks) (void); -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) -+ char * (*d_absolute_path) (const struct path *, char *, int); -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) -+ typeof(__d_path) (*__d_path); -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) -+ spinlock_t *vfsmount_lock; -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) -+ struct task_struct * (*find_task_by_vpid) (pid_t nr); -+ struct task_struct * (*find_task_by_pid_ns) (pid_t nr, -+ struct pid_namespace *ns); -+#endif -+}; -+ -+/* For doing access control. */ -+struct ccsecurity_operations { -+ void (*check_profile) (void); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) -+ int (*chroot_permission) (struct path *path); -+ int (*pivot_root_permission) (struct path *old_path, -+ struct path *new_path); -+ int (*mount_permission) (const char *dev_name, struct path *path, -+ const char *type, unsigned long flags, -+ void *data_page); -+#else -+ int (*chroot_permission) (struct nameidata *nd); -+ int (*pivot_root_permission) (struct nameidata *old_nd, -+ struct nameidata *new_nd); -+ int (*mount_permission) (const char *dev_name, struct nameidata *nd, -+ const char *type, unsigned long flags, -+ void *data_page); -+#endif -+ int (*umount_permission) (struct vfsmount *mnt, int flags); -+ _Bool (*lport_reserved) (const u16 port); -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) -+ void (*save_open_mode) (int mode); -+ void (*clear_open_mode) (void); -+ int (*open_permission) (struct dentry *dentry, struct vfsmount *mnt, -+ const int flag); -+#else -+ int (*open_permission) (struct file *file); -+#endif -+ int (*ptrace_permission) (long request, long pid); -+ int (*ioctl_permission) (struct file *filp, unsigned int cmd, -+ unsigned long arg); -+ int (*parse_table) (int __user *name, int nlen, void __user *oldval, -+ void __user *newval, struct ctl_table *table); -+ _Bool (*capable) (const u8 operation); -+ int (*mknod_permission) (struct dentry *dentry, struct vfsmount *mnt, -+ unsigned int mode, unsigned int dev); -+ int (*mkdir_permission) (struct dentry *dentry, struct vfsmount *mnt, -+ unsigned int mode); -+ int (*rmdir_permission) (struct dentry *dentry, struct vfsmount *mnt); -+ int (*unlink_permission) (struct dentry *dentry, struct vfsmount *mnt); -+ int (*symlink_permission) (struct dentry *dentry, struct vfsmount *mnt, -+ const char *from); -+ int (*truncate_permission) (struct dentry *dentry, -+ struct vfsmount *mnt); -+ int (*rename_permission) (struct dentry *old_dentry, -+ struct dentry *new_dentry, -+ struct vfsmount *mnt); -+ int (*link_permission) (struct dentry *old_dentry, -+ struct dentry *new_dentry, -+ struct vfsmount *mnt); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) -+ int (*open_exec_permission) (struct dentry *dentry, -+ struct vfsmount *mnt); -+ int (*uselib_permission) (struct dentry *dentry, struct vfsmount *mnt); -+#endif -+ int (*fcntl_permission) (struct file *file, unsigned int cmd, -+ unsigned long arg); -+ int (*kill_permission) (pid_t pid, int sig); -+ int (*tgkill_permission) (pid_t tgid, pid_t pid, int sig); -+ int (*tkill_permission) (pid_t pid, int sig); -+ int (*socket_create_permission) (int family, int type, int protocol); -+ int (*socket_listen_permission) (struct socket *sock); -+ int (*socket_connect_permission) (struct socket *sock, -+ struct sockaddr *addr, int addr_len); -+ int (*socket_bind_permission) (struct socket *sock, -+ struct sockaddr *addr, int addr_len); -+ int (*socket_post_accept_permission) (struct socket *sock, -+ struct socket *newsock); -+ int (*socket_sendmsg_permission) (struct socket *sock, -+ struct msghdr *msg, int size); -+ int (*socket_post_recvmsg_permission) (struct sock *sk, -+ struct sk_buff *skb, int flags); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) -+ int (*chown_permission) (struct dentry *dentry, struct vfsmount *mnt, -+ kuid_t user, kgid_t group); -+#else -+ int (*chown_permission) (struct dentry *dentry, struct vfsmount *mnt, -+ uid_t user, gid_t group); -+#endif -+ int (*chmod_permission) (struct dentry *dentry, struct vfsmount *mnt, -+ mode_t mode); -+ int (*getattr_permission) (struct vfsmount *mnt, -+ struct dentry *dentry); -+ int (*sigqueue_permission) (pid_t pid, int sig); -+ int (*tgsigqueue_permission) (pid_t tgid, pid_t pid, int sig); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) -+ int (*search_binary_handler) (struct linux_binprm *bprm); -+#else -+ int (*search_binary_handler) (struct linux_binprm *bprm, -+ struct pt_regs *regs); -+#endif -+#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -+ int (*alloc_task_security) (const struct task_struct *task); -+ void (*free_task_security) (const struct task_struct *task); -+#endif -+ _Bool disabled; -+}; -+ -+extern struct ccsecurity_operations ccsecurity_ops; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) -+ -+static inline int ccs_chroot_permission(struct path *path) -+{ -+ int (*func) (struct path *) = ccsecurity_ops.chroot_permission; -+ return func ? func(path) : 0; -+} -+ -+static inline int ccs_pivot_root_permission(struct path *old_path, -+ struct path *new_path) -+{ -+ int (*func) (struct path *, struct path *) -+ = ccsecurity_ops.pivot_root_permission; -+ return func ? func(old_path, new_path) : 0; -+} -+ -+static inline int ccs_mount_permission(const char *dev_name, struct path *path, -+ const char *type, unsigned long flags, -+ void *data_page) -+{ -+ int (*func) (const char *, struct path *, const char *, unsigned long, -+ void *) = ccsecurity_ops.mount_permission; -+ return func ? func(dev_name, path, type, flags, data_page) : 0; -+} -+ -+#else -+ -+static inline int ccs_chroot_permission(struct nameidata *nd) -+{ -+ int (*func) (struct nameidata *) = ccsecurity_ops.chroot_permission; -+ return func ? func(nd) : 0; -+} -+ -+static inline int ccs_pivot_root_permission(struct nameidata *old_nd, -+ struct nameidata *new_nd) -+{ -+ int (*func) (struct nameidata *, struct nameidata *) -+ = ccsecurity_ops.pivot_root_permission; -+ return func ? func(old_nd, new_nd) : 0; -+} -+ -+static inline int ccs_mount_permission(const char *dev_name, -+ struct nameidata *nd, const char *type, -+ unsigned long flags, void *data_page) -+{ -+ int (*func) (const char *, struct nameidata *, const char *, -+ unsigned long, void *) = ccsecurity_ops.mount_permission; -+ return func ? func(dev_name, nd, type, flags, data_page) : 0; -+} -+ -+#endif -+ -+static inline int ccs_umount_permission(struct vfsmount *mnt, int flags) -+{ -+ int (*func) (struct vfsmount *, int) -+ = ccsecurity_ops.umount_permission; -+ return func ? func(mnt, flags) : 0; -+} -+ -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) -+ -+static inline void ccs_save_open_mode(int mode) -+{ -+ void (*func) (int) = ccsecurity_ops.save_open_mode; -+ if (func) -+ func(mode); -+} -+ -+static inline void ccs_clear_open_mode(void) -+{ -+ void (*func) (void) = ccsecurity_ops.clear_open_mode; -+ if (func) -+ func(); -+} -+ -+static inline int ccs_open_permission(struct dentry *dentry, -+ struct vfsmount *mnt, const int flag) -+{ -+ int (*func) (struct dentry *, struct vfsmount *, const int) -+ = ccsecurity_ops.open_permission; -+ return func ? func(dentry, mnt, flag) : 0; -+} -+ -+#else -+ -+static inline int ccs_open_permission(struct file *filp) -+{ -+ int (*func) (struct file *) = ccsecurity_ops.open_permission; -+ return func ? func(filp) : 0; -+} -+ -+#endif -+ -+static inline int ccs_fcntl_permission(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ int (*func) (struct file *, unsigned int, unsigned long) -+ = ccsecurity_ops.fcntl_permission; -+ return func ? func(file, cmd, arg) : 0; -+} -+ -+static inline int ccs_ioctl_permission(struct file *filp, unsigned int cmd, -+ unsigned long arg) -+{ -+ int (*func) (struct file *, unsigned int, unsigned long) -+ = ccsecurity_ops.ioctl_permission; -+ return func ? func(filp, cmd, arg) : 0; -+} -+ -+static inline int ccs_parse_table(int __user *name, int nlen, -+ void __user *oldval, void __user *newval, -+ struct ctl_table *table) -+{ -+ int (*func) (int __user *, int, void __user *, void __user *, -+ struct ctl_table *) = ccsecurity_ops.parse_table; -+ return func ? func(name, nlen, oldval, newval, table) : 0; -+} -+ -+static inline int ccs_mknod_permission(struct dentry *dentry, -+ struct vfsmount *mnt, unsigned int mode, -+ unsigned int dev) -+{ -+ int (*func) (struct dentry *, struct vfsmount *, unsigned int, -+ unsigned int) = ccsecurity_ops.mknod_permission; -+ return func ? func(dentry, mnt, mode, dev) : 0; -+} -+ -+static inline int ccs_mkdir_permission(struct dentry *dentry, -+ struct vfsmount *mnt, unsigned int mode) -+{ -+ int (*func) (struct dentry *, struct vfsmount *, unsigned int) -+ = ccsecurity_ops.mkdir_permission; -+ return func ? func(dentry, mnt, mode) : 0; -+} -+ -+static inline int ccs_rmdir_permission(struct dentry *dentry, -+ struct vfsmount *mnt) -+{ -+ int (*func) (struct dentry *, struct vfsmount *) -+ = ccsecurity_ops.rmdir_permission; -+ return func ? func(dentry, mnt) : 0; -+} -+ -+static inline int ccs_unlink_permission(struct dentry *dentry, -+ struct vfsmount *mnt) -+{ -+ int (*func) (struct dentry *, struct vfsmount *) -+ = ccsecurity_ops.unlink_permission; -+ return func ? func(dentry, mnt) : 0; -+} -+ -+static inline int ccs_symlink_permission(struct dentry *dentry, -+ struct vfsmount *mnt, -+ const char *from) -+{ -+ int (*func) (struct dentry *, struct vfsmount *, const char *) -+ = ccsecurity_ops.symlink_permission; -+ return func ? func(dentry, mnt, from) : 0; -+} -+ -+static inline int ccs_truncate_permission(struct dentry *dentry, -+ struct vfsmount *mnt) -+{ -+ int (*func) (struct dentry *, struct vfsmount *) -+ = ccsecurity_ops.truncate_permission; -+ return func ? func(dentry, mnt) : 0; -+} -+ -+static inline int ccs_rename_permission(struct dentry *old_dentry, -+ struct dentry *new_dentry, -+ struct vfsmount *mnt) -+{ -+ int (*func) (struct dentry *, struct dentry *, struct vfsmount *) -+ = ccsecurity_ops.rename_permission; -+ return func ? func(old_dentry, new_dentry, mnt) : 0; -+} -+ -+static inline int ccs_link_permission(struct dentry *old_dentry, -+ struct dentry *new_dentry, -+ struct vfsmount *mnt) -+{ -+ int (*func) (struct dentry *, struct dentry *, struct vfsmount *) -+ = ccsecurity_ops.link_permission; -+ return func ? func(old_dentry, new_dentry, mnt) : 0; -+} -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) -+ -+static inline int ccs_open_exec_permission(struct dentry *dentry, -+ struct vfsmount *mnt) -+{ -+ int (*func) (struct dentry *, struct vfsmount *) -+ = ccsecurity_ops.open_exec_permission; -+ return func ? func(dentry, mnt) : 0; -+} -+ -+static inline int ccs_uselib_permission(struct dentry *dentry, -+ struct vfsmount *mnt) -+{ -+ int (*func) (struct dentry *, struct vfsmount *) -+ = ccsecurity_ops.uselib_permission; -+ return func ? func(dentry, mnt) : 0; -+} -+ -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) -+ -+static inline int ccs_chown_permission(struct dentry *dentry, -+ struct vfsmount *mnt, kuid_t user, -+ kgid_t group) -+{ -+ int (*func) (struct dentry *, struct vfsmount *, kuid_t, kgid_t) -+ = ccsecurity_ops.chown_permission; -+ return func ? func(dentry, mnt, user, group) : 0; -+} -+ -+#else -+ -+static inline int ccs_chown_permission(struct dentry *dentry, -+ struct vfsmount *mnt, uid_t user, -+ gid_t group) -+{ -+ int (*func) (struct dentry *, struct vfsmount *, uid_t, gid_t) -+ = ccsecurity_ops.chown_permission; -+ return func ? func(dentry, mnt, user, group) : 0; -+} -+ -+#endif -+ -+static inline int ccs_chmod_permission(struct dentry *dentry, -+ struct vfsmount *mnt, mode_t mode) -+{ -+ int (*func) (struct dentry *, struct vfsmount *, mode_t) -+ = ccsecurity_ops.chmod_permission; -+ return func ? func(dentry, mnt, mode) : 0; -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) -+ -+static inline int ccs_search_binary_handler(struct linux_binprm *bprm) -+{ -+ return ccsecurity_ops.search_binary_handler(bprm); -+} -+ -+#else -+ -+static inline int ccs_search_binary_handler(struct linux_binprm *bprm, -+ struct pt_regs *regs) -+{ -+ return ccsecurity_ops.search_binary_handler(bprm, regs); -+} -+ -+#endif -+ -+#else -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) -+ -+static inline int ccs_chroot_permission(struct path *path) -+{ -+ return 0; -+} -+ -+static inline int ccs_pivot_root_permission(struct path *old_path, -+ struct path *new_path) -+{ -+ return 0; -+} -+ -+static inline int ccs_mount_permission(const char *dev_name, struct path *path, -+ const char *type, unsigned long flags, -+ void *data_page) -+{ -+ return 0; -+} -+ -+#else -+ -+static inline int ccs_chroot_permission(struct nameidata *nd) -+{ -+ return 0; -+} -+ -+static inline int ccs_pivot_root_permission(struct nameidata *old_nd, -+ struct nameidata *new_nd) -+{ -+ return 0; -+} -+ -+static inline int ccs_mount_permission(const char *dev_name, -+ struct nameidata *nd, const char *type, -+ unsigned long flags, void *data_page) -+{ -+ return 0; -+} -+ -+#endif -+ -+static inline int ccs_umount_permission(struct vfsmount *mnt, int flags) -+{ -+ return 0; -+} -+ -+static inline void ccs_save_open_mode(int mode) -+{ -+} -+ -+static inline void ccs_clear_open_mode(void) -+{ -+} -+ -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) -+ -+static inline int ccs_open_permission(struct dentry *dentry, -+ struct vfsmount *mnt, const int flag) -+{ -+ return 0; -+} -+ -+#else -+ -+static inline int ccs_open_permission(struct file *filp) -+{ -+ return 0; -+} -+ -+#endif -+ -+static inline int ccs_ioctl_permission(struct file *filp, unsigned int cmd, -+ unsigned long arg) -+{ -+ return 0; -+} -+ -+static inline int ccs_parse_table(int __user *name, int nlen, -+ void __user *oldval, void __user *newval, -+ struct ctl_table *table) -+{ -+ return 0; -+} -+ -+static inline int ccs_mknod_permission(struct dentry *dentry, -+ struct vfsmount *mnt, unsigned int mode, -+ unsigned int dev) -+{ -+ return 0; -+} -+ -+static inline int ccs_mkdir_permission(struct dentry *dentry, -+ struct vfsmount *mnt, unsigned int mode) -+{ -+ return 0; -+} -+ -+static inline int ccs_rmdir_permission(struct dentry *dentry, -+ struct vfsmount *mnt) -+{ -+ return 0; -+} -+ -+static inline int ccs_unlink_permission(struct dentry *dentry, -+ struct vfsmount *mnt) -+{ -+ return 0; -+} -+ -+static inline int ccs_symlink_permission(struct dentry *dentry, -+ struct vfsmount *mnt, -+ const char *from) -+{ -+ return 0; -+} -+ -+static inline int ccs_truncate_permission(struct dentry *dentry, -+ struct vfsmount *mnt) -+{ -+ return 0; -+} -+ -+static inline int ccs_rename_permission(struct dentry *old_dentry, -+ struct dentry *new_dentry, -+ struct vfsmount *mnt) -+{ -+ return 0; -+} -+ -+static inline int ccs_link_permission(struct dentry *old_dentry, -+ struct dentry *new_dentry, -+ struct vfsmount *mnt) -+{ -+ return 0; -+} -+ -+static inline int ccs_open_exec_permission(struct dentry *dentry, -+ struct vfsmount *mnt) -+{ -+ return 0; -+} -+ -+static inline int ccs_uselib_permission(struct dentry *dentry, -+ struct vfsmount *mnt) -+{ -+ return 0; -+} -+ -+static inline int ccs_fcntl_permission(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ return 0; -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) -+ -+static inline int ccs_chown_permission(struct dentry *dentry, -+ struct vfsmount *mnt, kuid_t user, -+ kgid_t group) -+{ -+ return 0; -+} -+ -+#else -+ -+static inline int ccs_chown_permission(struct dentry *dentry, -+ struct vfsmount *mnt, uid_t user, -+ gid_t group) -+{ -+ return 0; -+} -+ -+#endif -+ -+static inline int ccs_chmod_permission(struct dentry *dentry, -+ struct vfsmount *mnt, mode_t mode) -+{ -+ return 0; -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) -+ -+static inline int ccs_search_binary_handler(struct linux_binprm *bprm) -+{ -+ return search_binary_handler(bprm); -+} -+ -+#else -+ -+static inline int ccs_search_binary_handler(struct linux_binprm *bprm, -+ struct pt_regs *regs) -+{ -+ return search_binary_handler(bprm, regs); -+} -+ -+#endif -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -+ -+static inline int ccs_alloc_task_security(const struct task_struct *task) -+{ -+ int (*func) (const struct task_struct *) -+ = ccsecurity_ops.alloc_task_security; -+ return func ? func(task) : 0; -+} -+ -+static inline void ccs_free_task_security(const struct task_struct *task) -+{ -+ void (*func) (const struct task_struct *) -+ = ccsecurity_ops.free_task_security; -+ if (func) -+ func(task); -+} -+ -+#else -+ -+static inline int ccs_alloc_task_security(const struct task_struct *task) -+{ -+ return 0; -+} -+ -+static inline void ccs_free_task_security(const struct task_struct *task) -+{ -+} -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_FILE_GETATTR -+ -+static inline int ccs_getattr_permission(struct vfsmount *mnt, -+ struct dentry *dentry) -+{ -+ int (*func) (struct vfsmount *, struct dentry *) -+ = ccsecurity_ops.getattr_permission; -+ return func ? func(mnt, dentry) : 0; -+} -+ -+#else -+ -+static inline int ccs_getattr_permission(struct vfsmount *mnt, -+ struct dentry *dentry) -+{ -+ return 0; -+} -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_NETWORK -+ -+static inline int ccs_socket_listen_permission(struct socket *sock) -+{ -+ int (*func) (struct socket *) -+ = ccsecurity_ops.socket_listen_permission; -+ return func ? func(sock) : 0; -+} -+ -+static inline int ccs_socket_connect_permission(struct socket *sock, -+ struct sockaddr *addr, -+ int addr_len) -+{ -+ int (*func) (struct socket *, struct sockaddr *, int) -+ = ccsecurity_ops.socket_connect_permission; -+ return func ? func(sock, addr, addr_len) : 0; -+} -+ -+static inline int ccs_socket_bind_permission(struct socket *sock, -+ struct sockaddr *addr, -+ int addr_len) -+{ -+ int (*func) (struct socket *, struct sockaddr *, int) -+ = ccsecurity_ops.socket_bind_permission; -+ return func ? func(sock, addr, addr_len) : 0; -+} -+ -+static inline int ccs_socket_post_accept_permission(struct socket *sock, -+ struct socket *newsock) -+{ -+ int (*func) (struct socket *, struct socket *) -+ = ccsecurity_ops.socket_post_accept_permission; -+ return func ? func(sock, newsock) : 0; -+} -+ -+static inline int ccs_socket_sendmsg_permission(struct socket *sock, -+ struct msghdr *msg, -+ int size) -+{ -+ int (*func) (struct socket *, struct msghdr *, int) -+ = ccsecurity_ops.socket_sendmsg_permission; -+ return func ? func(sock, msg, size) : 0; -+} -+ -+#else -+ -+static inline int ccs_socket_listen_permission(struct socket *sock) -+{ -+ return 0; -+} -+ -+static inline int ccs_socket_connect_permission(struct socket *sock, -+ struct sockaddr *addr, -+ int addr_len) -+{ -+ return 0; -+} -+ -+static inline int ccs_socket_bind_permission(struct socket *sock, -+ struct sockaddr *addr, -+ int addr_len) -+{ -+ return 0; -+} -+ -+static inline int ccs_socket_post_accept_permission(struct socket *sock, -+ struct socket *newsock) -+{ -+ return 0; -+} -+ -+static inline int ccs_socket_sendmsg_permission(struct socket *sock, -+ struct msghdr *msg, -+ int size) -+{ -+ return 0; -+} -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ -+static inline int ccs_socket_post_recvmsg_permission(struct sock *sk, -+ struct sk_buff *skb, -+ int flags) -+{ -+ int (*func) (struct sock *, struct sk_buff *, int) -+ = ccsecurity_ops.socket_post_recvmsg_permission; -+ return func ? func(sk, skb, flags) : 0; -+} -+ -+#else -+ -+static inline int ccs_socket_post_recvmsg_permission(struct sock *sk, -+ struct sk_buff *skb, -+ int flags) -+{ -+ return 0; -+} -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_PORTRESERVE -+ -+static inline _Bool ccs_lport_reserved(const u16 port) -+{ -+ _Bool (*func) (const u16) = ccsecurity_ops.lport_reserved; -+ return func ? func(port) : 0; -+} -+ -+#else -+ -+static inline _Bool ccs_lport_reserved(const u16 port) -+{ -+ return 0; -+} -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ -+static inline _Bool ccs_capable(const u8 operation) -+{ -+ _Bool (*func) (const u8) = ccsecurity_ops.capable; -+ return func ? func(operation) : 1; -+} -+ -+static inline int ccs_socket_create_permission(int family, int type, -+ int protocol) -+{ -+ int (*func) (int, int, int) = ccsecurity_ops.socket_create_permission; -+ return func ? func(family, type, protocol) : 0; -+} -+ -+static inline int ccs_ptrace_permission(long request, long pid) -+{ -+ int (*func) (long, long) = ccsecurity_ops.ptrace_permission; -+ return func ? func(request, pid) : 0; -+} -+ -+#else -+ -+static inline _Bool ccs_capable(const u8 operation) -+{ -+ return 1; -+} -+ -+static inline int ccs_socket_create_permission(int family, int type, -+ int protocol) -+{ -+ return 0; -+} -+ -+static inline int ccs_ptrace_permission(long request, long pid) -+{ -+ return 0; -+} -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_IPC -+ -+static inline int ccs_kill_permission(pid_t pid, int sig) -+{ -+ int (*func) (pid_t, int) = ccsecurity_ops.kill_permission; -+ return func ? func(pid, sig) : 0; -+} -+ -+static inline int ccs_tgkill_permission(pid_t tgid, pid_t pid, int sig) -+{ -+ int (*func) (pid_t, pid_t, int) = ccsecurity_ops.tgkill_permission; -+ return func ? func(tgid, pid, sig) : 0; -+} -+ -+static inline int ccs_tkill_permission(pid_t pid, int sig) -+{ -+ int (*func) (pid_t, int) = ccsecurity_ops.tkill_permission; -+ return func ? func(pid, sig) : 0; -+} -+ -+static inline int ccs_sigqueue_permission(pid_t pid, int sig) -+{ -+ int (*func) (pid_t, int) = ccsecurity_ops.sigqueue_permission; -+ return func ? func(pid, sig) : 0; -+} -+ -+static inline int ccs_tgsigqueue_permission(pid_t tgid, pid_t pid, int sig) -+{ -+ int (*func) (pid_t, pid_t, int) = ccsecurity_ops.tgsigqueue_permission; -+ return func ? func(tgid, pid, sig) : 0; -+} -+ -+#else -+ -+static inline int ccs_kill_permission(pid_t pid, int sig) -+{ -+ return 0; -+} -+ -+static inline int ccs_tgkill_permission(pid_t tgid, pid_t pid, int sig) -+{ -+ return 0; -+} -+ -+static inline int ccs_tkill_permission(pid_t pid, int sig) -+{ -+ return 0; -+} -+ -+static inline int ccs_sigqueue_permission(pid_t pid, int sig) -+{ -+ return 0; -+} -+ -+static inline int ccs_tgsigqueue_permission(pid_t tgid, pid_t pid, int sig) -+{ -+ return 0; -+} -+ -+#endif -+ -+/* Index numbers for Capability Controls. */ -+enum ccs_capability_acl_index { -+ /* socket(PF_ROUTE, *, *) */ -+ CCS_USE_ROUTE_SOCKET, -+ /* socket(PF_PACKET, *, *) */ -+ CCS_USE_PACKET_SOCKET, -+ /* sys_reboot() */ -+ CCS_SYS_REBOOT, -+ /* sys_vhangup() */ -+ CCS_SYS_VHANGUP, -+ /* do_settimeofday(), sys_adjtimex() */ -+ CCS_SYS_SETTIME, -+ /* sys_nice(), sys_setpriority() */ -+ CCS_SYS_NICE, -+ /* sys_sethostname(), sys_setdomainname() */ -+ CCS_SYS_SETHOSTNAME, -+ /* sys_create_module(), sys_init_module(), sys_delete_module() */ -+ CCS_USE_KERNEL_MODULE, -+ /* sys_kexec_load() */ -+ CCS_SYS_KEXEC_LOAD, -+ /* sys_ptrace() */ -+ CCS_SYS_PTRACE, -+ CCS_MAX_CAPABILITY_INDEX -+}; -+ -+#endif -diff --git a/include/linux/lsm2ccsecurity.h b/include/linux/lsm2ccsecurity.h -new file mode 100644 -index 0000000..ab4ea5c ---- /dev/null -+++ b/include/linux/lsm2ccsecurity.h -@@ -0,0 +1,181 @@ -+/* -+ * include/linux/lsm2ccsecurity.h -+ * -+ * Copyright (C) 2005-2012 NTT DATA CORPORATION -+ * -+ * Version: 1.8.4 2015/07/11 -+ */ -+ -+#ifndef _LINUX_LSM2CCSECURITY_H -+#define _LINUX_LSM2CCSECURITY_H -+ -+#include -+#include -+ -+#ifdef CONFIG_CCSECURITY -+ -+int ccs_settime(const struct timespec *ts, const struct timezone *tz); -+int ccs_sb_mount(const char *dev_name, struct path *path, const char *type, -+ unsigned long flags, void *data); -+int ccs_sb_umount(struct vfsmount *mnt, int flags); -+int ccs_sb_pivotroot(struct path *old_path, struct path *new_path); -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) -+int ccs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry); -+#else -+int ccs_inode_getattr(const struct path *path); -+#endif -+int ccs_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg); -+int ccs_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg); -+int ccs_file_open(struct file *file, const struct cred *cred); -+int ccs_socket_create(int family, int type, int protocol, int kern); -+int ccs_socket_bind(struct socket *sock, struct sockaddr *address, -+ int addrlen); -+int ccs_socket_connect(struct socket *sock, struct sockaddr *address, -+ int addrlen); -+int ccs_socket_listen(struct socket *sock, int backlog); -+int ccs_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size); -+int ccs_path_unlink(struct path *dir, struct dentry *dentry); -+int ccs_path_mkdir(struct path *dir, struct dentry *dentry, umode_t mode); -+int ccs_path_rmdir(struct path *dir, struct dentry *dentry); -+int ccs_path_mknod(struct path *dir, struct dentry *dentry, umode_t mode, -+ unsigned int dev); -+int ccs_path_truncate(struct path *path); -+int ccs_path_symlink(struct path *dir, struct dentry *dentry, -+ const char *old_name); -+int ccs_path_link(struct dentry *old_dentry, struct path *new_dir, -+ struct dentry *new_dentry); -+int ccs_path_rename(struct path *old_dir, struct dentry *old_dentry, -+ struct path *new_dir, struct dentry *new_dentry); -+int ccs_path_chmod(struct path *path, umode_t mode); -+int ccs_path_chown(struct path *path, kuid_t uid, kgid_t gid); -+int ccs_path_chroot(struct path *path); -+ -+#else -+ -+static inline int ccs_settime(const struct timespec *ts, -+ const struct timezone *tz) -+{ -+ return 0; -+} -+static inline int ccs_sb_mount(const char *dev_name, struct path *path, -+ const char *type, unsigned long flags, -+ void *data) -+{ -+ return 0; -+} -+static inline int ccs_sb_umount(struct vfsmount *mnt, int flags) -+{ -+ return 0; -+} -+static inline int ccs_sb_pivotroot(struct path *old_path, -+ struct path *new_path) -+{ -+ return 0; -+} -+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) -+static inline int ccs_inode_getattr(struct vfsmount *mnt, -+ struct dentry *dentry) -+{ -+ return 0; -+} -+#else -+static inline int ccs_inode_getattr(const struct path *path) -+{ -+ return 0; -+} -+#endif -+static inline int ccs_file_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ return 0; -+} -+static inline int ccs_file_fcntl(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ return 0; -+} -+static inline int ccs_file_open(struct file *file, const struct cred *cred) -+{ -+ return 0; -+} -+static inline int ccs_socket_create(int family, int type, int protocol, -+ int kern) -+{ -+ return 0; -+} -+static inline int ccs_socket_bind(struct socket *sock, -+ struct sockaddr *address, int addrlen) -+{ -+ return 0; -+} -+static inline int ccs_socket_connect(struct socket *sock, -+ struct sockaddr *address, int addrlen) -+{ -+ return 0; -+} -+static inline int ccs_socket_listen(struct socket *sock, int backlog) -+{ -+ return 0; -+} -+static inline int ccs_socket_sendmsg(struct socket *sock, struct msghdr *msg, -+ int size) -+{ -+ return 0; -+} -+static inline int ccs_path_unlink(struct path *dir, struct dentry *dentry) -+{ -+ return 0; -+} -+static inline int ccs_path_mkdir(struct path *dir, struct dentry *dentry, -+ umode_t mode) -+{ -+ return 0; -+} -+static inline int ccs_path_rmdir(struct path *dir, struct dentry *dentry) -+{ -+ return 0; -+} -+static inline int ccs_path_mknod(struct path *dir, struct dentry *dentry, -+ umode_t mode, unsigned int dev) -+{ -+ return 0; -+} -+static inline int ccs_path_truncate(struct path *path) -+{ -+ return 0; -+} -+static inline int ccs_path_symlink(struct path *dir, struct dentry *dentry, -+ const char *old_name) -+{ -+ return 0; -+} -+static inline int ccs_path_link(struct dentry *old_dentry, -+ struct path *new_dir, -+ struct dentry *new_dentry) -+{ -+ return 0; -+} -+static inline int ccs_path_rename(struct path *old_dir, -+ struct dentry *old_dentry, -+ struct path *new_dir, -+ struct dentry *new_dentry) -+{ -+ return 0; -+} -+static inline int ccs_path_chmod(struct path *path, umode_t mode) -+{ -+ return 0; -+} -+static inline int ccs_path_chown(struct path *path, kuid_t uid, kgid_t gid) -+{ -+ return 0; -+} -+static inline int ccs_path_chroot(struct path *path) -+{ -+ return 0; -+} -+ -+#endif /* defined(CONFIG_CCSECURITY) */ -+ -+#endif /* !defined(_LINUX_LSM2CCSECURITY_H) */ -diff --git a/security/ccsecurity/Config.in b/security/ccsecurity/Config.in -new file mode 100644 -index 0000000..da39bad6 ---- /dev/null -+++ b/security/ccsecurity/Config.in -@@ -0,0 +1,83 @@ -+# -+# Mandatory Access Control configuration -+# -+mainmenu_option next_comment -+comment 'Security options' -+ -+[ -z "$CONFIG_CCSECURITY" ] && define_bool CONFIG_CCSECURITY y -+bool 'CCSecurity support' CONFIG_CCSECURITY -+ -+if [ "$CONFIG_CCSECURITY" = "y" ]; then -+ -+ [ -z "$CONFIG_CCSECURITY_LKM" ] && define_bool CONFIG_CCSECURITY_LKM n -+ bool 'Compile as loadable kernel module' CONFIG_CCSECURITY_LKM -+ -+ [ -z "$CONFIG_CCSECURITY_DISABLE_BY_DEFAULT" ] && define_bool CONFIG_CCSECURITY_DISABLE_BY_DEFAULT n -+ bool 'Disable by default' CONFIG_CCSECURITY_DISABLE_BY_DEFAULT -+ -+ [ -z "$CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY" ] && define_int CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY 2048 -+ [ $CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY -lt 0 ] && define_int CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY 0 -+ int 'Default maximal count for learning mode' CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY -+ -+ [ -z "$CONFIG_CCSECURITY_MAX_AUDIT_LOG" ] && define_int CONFIG_CCSECURITY_MAX_AUDIT_LOG 1024 -+ [ $CONFIG_CCSECURITY_MAX_AUDIT_LOG -lt 0 ] && define_int CONFIG_CCSECURITY_MAX_AUDIT_LOG 0 -+ int 'Default maximal count for audit log' CONFIG_CCSECURITY_MAX_AUDIT_LOG -+ -+ [ -z "$CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER" ] && define_bool CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER n -+ bool 'Activate without calling userspace policy loader.' CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER -+ -+ if [ "$CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER" = "n" ]; then -+ -+ define_string CONFIG_CCSECURITY_POLICY_LOADER "/sbin/ccs-init" -+ string 'Location of userspace policy loader' CONFIG_CCSECURITY_POLICY_LOADER "/sbin/ccs-init" -+ -+ define_string CONFIG_CCSECURITY_ACTIVATION_TRIGGER "/sbin/init" -+ string 'Trigger for calling userspace policy loader' CONFIG_CCSECURITY_ACTIVATION_TRIGGER "/sbin/init" -+ -+ fi -+ -+ [ -z "$CONFIG_CCSECURITY_FILE_READDIR" ] && define_bool CONFIG_CCSECURITY_FILE_READDIR y -+ bool "Enable readdir operation restriction." CONFIG_CCSECURITY_FILE_READDIR -+ -+ [ -z "$CONFIG_CCSECURITY_FILE_GETATTR" ] && define_bool CONFIG_CCSECURITY_FILE_GETATTR y -+ bool "Enable getattr operation restriction." CONFIG_CCSECURITY_FILE_GETATTR -+ -+ if [ "$CONFIG_NET" = "y" ]; then -+ -+ [ -z "$CONFIG_CCSECURITY_NETWORK" ] && define_bool CONFIG_CCSECURITY_NETWORK y -+ bool "Enable socket operation restriction." CONFIG_CCSECURITY_NETWORK -+ -+ if [ "$CONFIG_CCSECURITY_NETWORK" = "y" ]; then -+ -+ #[ -z "$CONFIG_CCSECURITY_NETWORK_RECVMSG" ] && -+ define_bool CONFIG_CCSECURITY_NETWORK_RECVMSG y -+ -+ fi -+ -+ fi -+ -+ [ -z "$CONFIG_CCSECURITY_CAPABILITY" ] && define_bool CONFIG_CCSECURITY_CAPABILITY y -+ bool "Enable non-POSIX capability operation restriction." CONFIG_CCSECURITY_CAPABILITY -+ -+ [ -z "$CONFIG_CCSECURITY_IPC" ] && define_bool CONFIG_CCSECURITY_IPC y -+ bool "Enable IPC operation restriction." CONFIG_CCSECURITY_IPC -+ -+ [ -z "$CONFIG_CCSECURITY_MISC" ] && define_bool CONFIG_CCSECURITY_MISC y -+ bool "Enable environment variable names restriction." CONFIG_CCSECURITY_MISC -+ -+ [ -z "$CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER" ] && define_bool CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER y -+ bool "Enable execute handler functionality." CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ -+ [ -z "$CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION" ] && define_bool CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION y -+ bool "Enable domain transition without program execution request." CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+ -+ if [ "$CONFIG_NET" = "y" ]; then -+ -+ [ -z "$CONFIG_CCSECURITY_PORTRESERVE" ] && define_bool CONFIG_CCSECURITY_PORTRESERVE y -+ bool "Enable local port reserver." CONFIG_CCSECURITY_PORTRESERVE -+ -+ fi -+ -+fi -+ -+endmenu -diff --git a/security/ccsecurity/Kconfig b/security/ccsecurity/Kconfig -new file mode 100644 -index 0000000..8d7b200 ---- /dev/null -+++ b/security/ccsecurity/Kconfig -@@ -0,0 +1,190 @@ -+config CCSECURITY -+ bool "CCSecurity support" -+ default y -+ help -+ Say Y here to support non-LSM version of TOMOYO Linux. -+ http://tomoyo.osdn.jp/ -+ -+config CCSECURITY_LKM -+ bool "Compile as loadable kernel module" -+ default n -+ depends on CCSECURITY && MODULES -+ help -+ This version of TOMOYO depends on patching the kernel source in order -+ to insert some hooks which LSM does not provide. Therefore, -+ recompiling the kernel is inevitable. But if you want to keep -+ vmlinux's size as small as possible, you can compile most part of -+ TOMOYO as a loadable kernel module by saying Y here. -+ -+config CCSECURITY_DISABLE_BY_DEFAULT -+ bool "Disable by default" -+ default n -+ depends on CCSECURITY -+ help -+ Say Y here if you want TOMOYO disabled by default. -+ To enable TOMOYO, pass ccsecurity=on to kernel command line. -+ To disable TOMOYO, pass ccsecurity=off to kernel command line. -+ -+config CCSECURITY_USE_EXTERNAL_TASK_SECURITY -+ bool "Do not modify 'struct task_struct' in order to keep KABI" -+ default n -+ depends on CCSECURITY -+ help -+ Say Y here if you want to keep KABI for prebuilt kernel modules -+ unchanged. TOMOYO needs "struct ccs_domain_info *" and "u32" for each -+ "struct task_struct". But embedding these variables into -+ "struct task_struct" breaks KABI for prebuilt kernel modules (which -+ means that you will need to rebuild prebuilt kernel modules). -+ If you say Y here, these variables are managed outside -+ "struct task_struct" rather than embedding into "struct task_struct", -+ but accessing these variables becomes slower because lookup operation -+ is performed every time the current thread needs to access them. -+ -+config CCSECURITY_MAX_ACCEPT_ENTRY -+ int "Default maximal count for learning mode" -+ default 2048 -+ range 0 2147483647 -+ depends on CCSECURITY -+ help -+ This is the default value for maximal ACL entries -+ that are automatically appended into policy at "learning mode". -+ Some programs access thousands of objects, so running -+ such programs in "learning mode" dulls the system response -+ and consumes much memory. -+ This is the safeguard for such programs. -+ -+config CCSECURITY_MAX_AUDIT_LOG -+ int "Default maximal count for audit log" -+ default 1024 -+ range 0 2147483647 -+ depends on CCSECURITY -+ help -+ This is the default value for maximal entries for -+ audit logs that the kernel can hold on memory. -+ You can read the log via /proc/ccs/audit. -+ If you don't need audit logs, you may set this value to 0. -+ -+config CCSECURITY_OMIT_USERSPACE_LOADER -+ bool "Activate without calling userspace policy loader." -+ default n -+ depends on CCSECURITY -+ ---help--- -+ Say Y here if you want to activate access control as soon as built-in -+ policy was loaded. This option will be useful for systems where -+ operations which can lead to the hijacking of the boot sequence are -+ needed before loading the policy. For example, you can activate -+ immediately after loading the fixed part of policy which will allow -+ only operations needed for mounting a partition which contains the -+ variant part of policy and verifying (e.g. running GPG check) and -+ loading the variant part of policy. Since you can start using -+ enforcing mode from the beginning, you can reduce the possibility of -+ hijacking the boot sequence. -+ -+ If you say Y to both "Compile as loadable kernel module" option and -+ "Activate without calling userspace policy loader." option, be sure -+ to excplicitly load the kernel module from the userspace, for -+ the kernel will not call /sbin/ccs-init when /sbin/init starts. -+ -+config CCSECURITY_POLICY_LOADER -+ string "Location of userspace policy loader" -+ default "/sbin/ccs-init" -+ depends on CCSECURITY -+ depends on !CCSECURITY_OMIT_USERSPACE_LOADER -+ ---help--- -+ This is the default pathname of policy loader which is called before -+ activation. You can override this setting via CCS_loader= kernel -+ command line option. -+ -+config CCSECURITY_ACTIVATION_TRIGGER -+ string "Trigger for calling userspace policy loader" -+ default "/sbin/init" -+ depends on CCSECURITY -+ depends on !CCSECURITY_OMIT_USERSPACE_LOADER -+ ---help--- -+ This is the default pathname of activation trigger. -+ You can override this setting via CCS_trigger= kernel command line -+ option. For example, if you pass init=/bin/systemd option, you may -+ want to also pass CCS_trigger=/bin/systemd option. -+ -+ Say Y here if you want to enable only specific functionality in order -+ to reduce object file size. -+ -+config CCSECURITY_FILE_READDIR -+ bool "Enable readdir operation restriction." -+ default y -+ depends on CCSECURITY -+ ---help--- -+ Say Y here if you want to enable analysis/restriction of opening -+ directories for reading. Reading directory entries is a commonly -+ requested operation and damage caused by not restricting it as MAC -+ might be acceptable for you. -+ -+config CCSECURITY_FILE_GETATTR -+ bool "Enable getattr operation restriction." -+ default y -+ depends on CCSECURITY -+ ---help--- -+ Say Y here if you want to enable analysis/restriction of getting -+ information of files. Getting file's information is a commonly -+ requested operation and damage caused by not restricting it as MAC -+ might be acceptable for you. -+ -+config CCSECURITY_NETWORK -+ bool "Enable socket operation restriction." -+ default y -+ depends on NET -+ depends on CCSECURITY -+ ---help--- -+ Say Y here if you want to enable analysis/restriction of INET and -+ UNIX domain socket's operations. -+ -+config CCSECURITY_CAPABILITY -+ bool "Enable non-POSIX capability operation restriction." -+ default y -+ depends on CCSECURITY -+ ---help--- -+ Say Y here if you want to enable analysis/restriction of non-POSIX -+ capabilities. -+ -+config CCSECURITY_IPC -+ bool "Enable IPC operation restriction." -+ default y -+ depends on CCSECURITY -+ ---help--- -+ Say Y here if you want to enable analysis/restriction of sending -+ signals. -+ -+config CCSECURITY_MISC -+ bool "Enable environment variable names restriction." -+ default y -+ depends on CCSECURITY -+ ---help--- -+ Say Y here if you want to enable analysis/restriction of environment -+ variable names passed upon program execution request. -+ -+config CCSECURITY_TASK_EXECUTE_HANDLER -+ bool "Enable execute handler functionality." -+ default y -+ depends on CCSECURITY -+ ---help--- -+ Say Y here if you want to enable execute handler functionality. -+ -+config CCSECURITY_TASK_DOMAIN_TRANSITION -+ bool "Enable domain transition without program execution request." -+ default y -+ depends on CCSECURITY -+ ---help--- -+ Say Y here if you want to enable domain transition without involving -+ program execution request. -+ -+config CCSECURITY_PORTRESERVE -+ bool "Enable local port reserver." -+ default y -+ depends on NET -+ depends on CCSECURITY -+ ---help--- -+ Say Y here if you want to implement -+ /proc/sys/net/ipv4/ip_local_reserved_ports as a MAC policy. -+ -+config CCSECURITY_NETWORK_RECVMSG -+ def_bool CCSECURITY_NETWORK -diff --git a/security/ccsecurity/Makefile b/security/ccsecurity/Makefile -new file mode 100644 -index 0000000..79c3632 ---- /dev/null -+++ b/security/ccsecurity/Makefile -@@ -0,0 +1,122 @@ -+ccsecurity-objs := permission.o gc.o memory.o policy_io.o realpath.o -+ -+ifeq ($(VERSION)$(PATCHLEVEL),24) -+ -+ifdef CONFIG_CCSECURITY -+O_TARGET := ccsecurity.o -+ifdef CONFIG_CCSECURITY_LKM -+all_targets: load_policy.o -+obj-m := ccsecurity.o -+obj-y := $(ccsecurity-objs) -+else -+all_targets: ccsecurity.o -+obj-y := load_policy.o $(ccsecurity-objs) -+endif -+export-objs := load_policy.o -+endif -+include $(TOPDIR)/Rules.make -+ -+policy/profile.conf: -+ @mkdir -p policy/ -+ @echo Creating an empty policy/profile.conf -+ @touch $@ -+ -+policy/exception_policy.conf: -+ @mkdir -p policy/ -+ @echo Creating a default policy/exception_policy.conf -+ @echo initialize_domain /sbin/modprobe from any >> $@ -+ @echo initialize_domain /sbin/hotplug from any >> $@ -+ -+policy/domain_policy.conf: -+ @mkdir -p policy/ -+ @echo Creating an empty policy/domain_policy.conf -+ @touch $@ -+ -+policy/manager.conf: -+ @mkdir -p policy/ -+ @echo Creating an empty policy/manager.conf -+ @touch $@ -+ -+policy/stat.conf: -+ @mkdir -p policy/ -+ @echo Creating an empty policy/stat.conf -+ @touch $@ -+ -+builtin-policy.h: policy/profile.conf policy/exception_policy.conf policy/domain_policy.conf policy/manager.conf policy/stat.conf -+ @echo Generating built-in policy for TOMOYO 1.8.x. -+ @echo "static char ccs_builtin_profile[] __initdata =" > $@.tmp -+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < policy/profile.conf >> $@.tmp -+ @echo "\"\";" >> $@.tmp -+ @echo "static char ccs_builtin_exception_policy[] __initdata =" >> $@.tmp -+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < policy/exception_policy.conf >> $@.tmp -+ @echo "\"\";" >> $@.tmp -+ @echo "static char ccs_builtin_domain_policy[] __initdata =" >> $@.tmp -+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < policy/domain_policy.conf >> $@.tmp -+ @echo "\"\";" >> $@.tmp -+ @echo "static char ccs_builtin_manager[] __initdata =" >> $@.tmp -+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < policy/manager.conf >> $@.tmp -+ @echo "\"\";" >> $@.tmp -+ @echo "static char ccs_builtin_stat[] __initdata =" >> $@.tmp -+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < policy/stat.conf >> $@.tmp -+ @echo "\"\";" >> $@.tmp -+ @mv $@.tmp $@ -+ -+policy_io.o: builtin-policy.h -+ -+else -+ -+obj-y += load_policy.o -+ifdef CONFIG_CCSECURITY_LKM -+obj-m += ccsecurity.o -+else -+obj-y += ccsecurity.o -+endif -+ -+$(obj)/policy/profile.conf: -+ @mkdir -p $(obj)/policy/ -+ @echo Creating an empty policy/profile.conf -+ @touch $@ -+ -+$(obj)/policy/exception_policy.conf: -+ @mkdir -p $(obj)/policy/ -+ @echo Creating a default policy/exception_policy.conf -+ @echo initialize_domain /sbin/modprobe from any >> $@ -+ @echo initialize_domain /sbin/hotplug from any >> $@ -+ -+$(obj)/policy/domain_policy.conf: -+ @mkdir -p $(obj)/policy/ -+ @echo Creating an empty policy/domain_policy.conf -+ @touch $@ -+ -+$(obj)/policy/manager.conf: -+ @mkdir -p $(obj)/policy/ -+ @echo Creating an empty policy/manager.conf -+ @touch $@ -+ -+$(obj)/policy/stat.conf: -+ @mkdir -p $(obj)/policy/ -+ @echo Creating an empty policy/stat.conf -+ @touch $@ -+ -+$(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf -+ @echo Generating built-in policy for TOMOYO 1.8.x. -+ @echo "static char ccs_builtin_profile[] __initdata =" > $@.tmp -+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp -+ @echo "\"\";" >> $@.tmp -+ @echo "static char ccs_builtin_exception_policy[] __initdata =" >> $@.tmp -+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/exception_policy.conf >> $@.tmp -+ @echo "\"\";" >> $@.tmp -+ @echo "static char ccs_builtin_domain_policy[] __initdata =" >> $@.tmp -+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/domain_policy.conf >> $@.tmp -+ @echo "\"\";" >> $@.tmp -+ @echo "static char ccs_builtin_manager[] __initdata =" >> $@.tmp -+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/manager.conf >> $@.tmp -+ @echo "\"\";" >> $@.tmp -+ @echo "static char ccs_builtin_stat[] __initdata =" >> $@.tmp -+ @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/stat.conf >> $@.tmp -+ @echo "\"\";" >> $@.tmp -+ @mv $@.tmp $@ -+ -+$(obj)/policy_io.o: $(obj)/builtin-policy.h -+ -+endif -diff --git a/security/ccsecurity/gc.c b/security/ccsecurity/gc.c -new file mode 100644 -index 0000000..0a578ab ---- /dev/null -+++ b/security/ccsecurity/gc.c -@@ -0,0 +1,1036 @@ -+/* -+ * security/ccsecurity/gc.c -+ * -+ * Copyright (C) 2005-2012 NTT DATA CORPORATION -+ * -+ * Version: 1.8.4 2015/05/05 -+ */ -+ -+#include "internal.h" -+ -+/***** SECTION1: Constants definition *****/ -+ -+/* For compatibility with older kernels. */ -+#ifndef for_each_process -+#define for_each_process for_each_task -+#endif -+ -+/* The list for "struct ccs_io_buffer". */ -+static LIST_HEAD(ccs_io_buffer_list); -+/* Lock for protecting ccs_io_buffer_list. */ -+static DEFINE_SPINLOCK(ccs_io_buffer_list_lock); -+ -+/***** SECTION2: Structure definition *****/ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) -+ -+/* -+ * Lock for syscall users. -+ * -+ * This lock is used for protecting single SRCU section for 2.6.18 and -+ * earlier kernels because they don't have SRCU support. -+ */ -+struct ccs_lock_struct { -+ int counter_idx; /* Currently active index (0 or 1). */ -+ int counter[2]; /* Current users. Protected by ccs_counter_lock. */ -+}; -+ -+#endif -+ -+/***** SECTION3: Prototype definition section *****/ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) -+int ccs_lock(void); -+#endif -+void ccs_del_acl(struct list_head *element); -+void ccs_del_condition(struct list_head *element); -+void ccs_notify_gc(struct ccs_io_buffer *head, const bool is_register); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) -+void ccs_unlock(const int idx); -+#endif -+ -+static bool ccs_domain_used_by_task(struct ccs_domain_info *domain); -+static bool ccs_name_used_by_io_buffer(const char *string, const size_t size); -+static bool ccs_struct_used_by_io_buffer(const struct list_head *element); -+static int ccs_gc_thread(void *unused); -+static void ccs_collect_acl(struct list_head *list); -+static void ccs_collect_entry(void); -+static void ccs_collect_member(const enum ccs_policy_id id, -+ struct list_head *member_list); -+static void ccs_memory_free(const void *ptr, const enum ccs_policy_id type); -+static void ccs_put_name_union(struct ccs_name_union *ptr); -+static void ccs_put_number_union(struct ccs_number_union *ptr); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) -+static void ccs_synchronize_counter(void); -+#endif -+static void ccs_try_to_gc(const enum ccs_policy_id type, -+ struct list_head *element); -+ -+/***** SECTION4: Standalone functions section *****/ -+ -+/***** SECTION5: Variables definition section *****/ -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) -+ -+/* -+ * Lock for syscall users. -+ * -+ * This lock is held for only protecting single SRCU section. -+ */ -+struct srcu_struct ccs_ss; -+ -+#else -+ -+static struct ccs_lock_struct ccs_counter; -+/* Lock for protecting ccs_counter. */ -+static DEFINE_SPINLOCK(ccs_counter_lock); -+ -+#endif -+ -+/***** SECTION6: Dependent functions section *****/ -+ -+/** -+ * ccs_memory_free - Free memory for elements. -+ * -+ * @ptr: Pointer to allocated memory. -+ * @type: One of values in "enum ccs_policy_id". -+ * -+ * Returns nothing. -+ * -+ * Caller holds ccs_policy_lock mutex. -+ */ -+static void ccs_memory_free(const void *ptr, const enum ccs_policy_id type) -+{ -+ /* Size of an element. */ -+ static const u8 e[CCS_MAX_POLICY] = { -+#ifdef CONFIG_CCSECURITY_PORTRESERVE -+ [CCS_ID_RESERVEDPORT] = sizeof(struct ccs_reserved), -+#endif -+ [CCS_ID_GROUP] = sizeof(struct ccs_group), -+#ifdef CONFIG_CCSECURITY_NETWORK -+ [CCS_ID_ADDRESS_GROUP] = sizeof(struct ccs_address_group), -+#endif -+ [CCS_ID_PATH_GROUP] = sizeof(struct ccs_path_group), -+ [CCS_ID_NUMBER_GROUP] = sizeof(struct ccs_number_group), -+ [CCS_ID_AGGREGATOR] = sizeof(struct ccs_aggregator), -+ [CCS_ID_TRANSITION_CONTROL] -+ = sizeof(struct ccs_transition_control), -+ [CCS_ID_MANAGER] = sizeof(struct ccs_manager), -+ /* [CCS_ID_CONDITION] = "struct ccs_condition"->size, */ -+ /* [CCS_ID_NAME] = "struct ccs_name"->size, */ -+ /* [CCS_ID_ACL] = a["struct ccs_acl_info"->type], */ -+ [CCS_ID_DOMAIN] = sizeof(struct ccs_domain_info), -+ }; -+ /* Size of a domain ACL element. */ -+ static const u8 a[] = { -+ [CCS_TYPE_PATH_ACL] = sizeof(struct ccs_path_acl), -+ [CCS_TYPE_PATH2_ACL] = sizeof(struct ccs_path2_acl), -+ [CCS_TYPE_PATH_NUMBER_ACL] -+ = sizeof(struct ccs_path_number_acl), -+ [CCS_TYPE_MKDEV_ACL] = sizeof(struct ccs_mkdev_acl), -+ [CCS_TYPE_MOUNT_ACL] = sizeof(struct ccs_mount_acl), -+#ifdef CONFIG_CCSECURITY_NETWORK -+ [CCS_TYPE_INET_ACL] = sizeof(struct ccs_inet_acl), -+ [CCS_TYPE_UNIX_ACL] = sizeof(struct ccs_unix_acl), -+#endif -+#ifdef CONFIG_CCSECURITY_MISC -+ [CCS_TYPE_ENV_ACL] = sizeof(struct ccs_env_acl), -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ [CCS_TYPE_CAPABILITY_ACL] = sizeof(struct ccs_capability_acl), -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ [CCS_TYPE_SIGNAL_ACL] = sizeof(struct ccs_signal_acl), -+#endif -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ [CCS_TYPE_AUTO_EXECUTE_HANDLER] -+ = sizeof(struct ccs_handler_acl), -+ [CCS_TYPE_DENIED_EXECUTE_HANDLER] -+ = sizeof(struct ccs_handler_acl), -+#endif -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+ [CCS_TYPE_AUTO_TASK_ACL] = sizeof(struct ccs_task_acl), -+ [CCS_TYPE_MANUAL_TASK_ACL] = sizeof(struct ccs_task_acl), -+#endif -+ }; -+ size_t size; -+ if (type == CCS_ID_ACL) -+ size = a[container_of(ptr, typeof(struct ccs_acl_info), -+ list)->type]; -+ else if (type == CCS_ID_NAME) -+ size = container_of(ptr, typeof(struct ccs_name), -+ head.list)->size; -+ else if (type == CCS_ID_CONDITION) -+ size = container_of(ptr, typeof(struct ccs_condition), -+ head.list)->size; -+ else -+ size = e[type]; -+ ccs_memory_used[CCS_MEMORY_POLICY] -= ccs_round2(size); -+ kfree(ptr); -+} -+ -+/** -+ * ccs_put_name_union - Drop reference on "struct ccs_name_union". -+ * -+ * @ptr: Pointer to "struct ccs_name_union". -+ * -+ * Returns nothing. -+ */ -+static void ccs_put_name_union(struct ccs_name_union *ptr) -+{ -+ ccs_put_group(ptr->group); -+ ccs_put_name(ptr->filename); -+} -+ -+/** -+ * ccs_put_number_union - Drop reference on "struct ccs_number_union". -+ * -+ * @ptr: Pointer to "struct ccs_number_union". -+ * -+ * Returns nothing. -+ */ -+static void ccs_put_number_union(struct ccs_number_union *ptr) -+{ -+ ccs_put_group(ptr->group); -+} -+ -+/** -+ * ccs_struct_used_by_io_buffer - Check whether the list element is used by /proc/ccs/ users or not. -+ * -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns true if @element is used by /proc/ccs/ users, false otherwise. -+ */ -+static bool ccs_struct_used_by_io_buffer(const struct list_head *element) -+{ -+ struct ccs_io_buffer *head; -+ bool in_use = false; -+ spin_lock(&ccs_io_buffer_list_lock); -+ list_for_each_entry(head, &ccs_io_buffer_list, list) { -+ head->users++; -+ spin_unlock(&ccs_io_buffer_list_lock); -+ mutex_lock(&head->io_sem); -+ if (head->r.domain == element || head->r.group == element || -+ head->r.acl == element || &head->w.domain->list == element) -+ in_use = true; -+ mutex_unlock(&head->io_sem); -+ spin_lock(&ccs_io_buffer_list_lock); -+ head->users--; -+ if (in_use) -+ break; -+ } -+ spin_unlock(&ccs_io_buffer_list_lock); -+ return in_use; -+} -+ -+/** -+ * ccs_name_used_by_io_buffer - Check whether the string is used by /proc/ccs/ users or not. -+ * -+ * @string: String to check. -+ * @size: Memory allocated for @string . -+ * -+ * Returns true if @string is used by /proc/ccs/ users, false otherwise. -+ */ -+static bool ccs_name_used_by_io_buffer(const char *string, const size_t size) -+{ -+ struct ccs_io_buffer *head; -+ bool in_use = false; -+ spin_lock(&ccs_io_buffer_list_lock); -+ list_for_each_entry(head, &ccs_io_buffer_list, list) { -+ int i; -+ head->users++; -+ spin_unlock(&ccs_io_buffer_list_lock); -+ mutex_lock(&head->io_sem); -+ for (i = 0; i < CCS_MAX_IO_READ_QUEUE; i++) { -+ const char *w = head->r.w[i]; -+ if (w < string || w > string + size) -+ continue; -+ in_use = true; -+ break; -+ } -+ mutex_unlock(&head->io_sem); -+ spin_lock(&ccs_io_buffer_list_lock); -+ head->users--; -+ if (in_use) -+ break; -+ } -+ spin_unlock(&ccs_io_buffer_list_lock); -+ return in_use; -+} -+ -+/** -+ * ccs_del_transition_control - Delete members in "struct ccs_transition_control". -+ * -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_del_transition_control(struct list_head *element) -+{ -+ struct ccs_transition_control *ptr = -+ container_of(element, typeof(*ptr), head.list); -+ ccs_put_name(ptr->domainname); -+ ccs_put_name(ptr->program); -+} -+ -+/** -+ * ccs_del_aggregator - Delete members in "struct ccs_aggregator". -+ * -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_del_aggregator(struct list_head *element) -+{ -+ struct ccs_aggregator *ptr = -+ container_of(element, typeof(*ptr), head.list); -+ ccs_put_name(ptr->original_name); -+ ccs_put_name(ptr->aggregated_name); -+} -+ -+/** -+ * ccs_del_manager - Delete members in "struct ccs_manager". -+ * -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_del_manager(struct list_head *element) -+{ -+ struct ccs_manager *ptr = -+ container_of(element, typeof(*ptr), head.list); -+ ccs_put_name(ptr->manager); -+} -+ -+/** -+ * ccs_domain_used_by_task - Check whether the given pointer is referenced by a task. -+ * -+ * @domain: Pointer to "struct ccs_domain_info". -+ * -+ * Returns true if @domain is in use, false otherwise. -+ */ -+static bool ccs_domain_used_by_task(struct ccs_domain_info *domain) -+{ -+ bool in_use = false; -+ /* -+ * Don't delete this domain if somebody is doing execve(). -+ * -+ * Since ccs_finish_execve() first reverts ccs_domain_info and then -+ * updates ccs_flags, we need smp_rmb() to make sure that GC first -+ * checks ccs_flags and then checks ccs_domain_info. -+ */ -+#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -+ int idx; -+ rcu_read_lock(); -+ for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) { -+ struct ccs_security *ptr; -+ struct list_head *list = &ccs_task_security_list[idx]; -+ list_for_each_entry_rcu(ptr, list, list) { -+ if (!(ptr->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { -+ smp_rmb(); /* Avoid out of order execution. */ -+ if (ptr->ccs_domain_info != domain) -+ continue; -+ } -+ in_use = true; -+ goto out; -+ } -+ } -+out: -+ rcu_read_unlock(); -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) -+ struct task_struct *g; -+ struct task_struct *t; -+ ccs_tasklist_lock(); -+ do_each_thread(g, t) { -+ if (!(t->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { -+ smp_rmb(); /* Avoid out of order execution. */ -+ if (t->ccs_domain_info != domain) -+ continue; -+ } -+ in_use = true; -+ goto out; -+ } while_each_thread(g, t); -+out: -+ ccs_tasklist_unlock(); -+#else -+ struct task_struct *p; -+ ccs_tasklist_lock(); -+ for_each_process(p) { -+ if (!(p->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { -+ smp_rmb(); /* Avoid out of order execution. */ -+ if (p->ccs_domain_info != domain) -+ continue; -+ } -+ in_use = true; -+ break; -+ } -+ ccs_tasklist_unlock(); -+#endif -+ return in_use; -+} -+ -+/** -+ * ccs_del_acl - Delete members in "struct ccs_acl_info". -+ * -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ */ -+void ccs_del_acl(struct list_head *element) -+{ -+ struct ccs_acl_info *acl = container_of(element, typeof(*acl), list); -+ ccs_put_condition(acl->cond); -+ switch (acl->type) { -+ case CCS_TYPE_PATH_ACL: -+ { -+ struct ccs_path_acl *entry = -+ container_of(acl, typeof(*entry), head); -+ ccs_put_name_union(&entry->name); -+ } -+ break; -+ case CCS_TYPE_PATH2_ACL: -+ { -+ struct ccs_path2_acl *entry = -+ container_of(acl, typeof(*entry), head); -+ ccs_put_name_union(&entry->name1); -+ ccs_put_name_union(&entry->name2); -+ } -+ break; -+ case CCS_TYPE_PATH_NUMBER_ACL: -+ { -+ struct ccs_path_number_acl *entry = -+ container_of(acl, typeof(*entry), head); -+ ccs_put_name_union(&entry->name); -+ ccs_put_number_union(&entry->number); -+ } -+ break; -+ case CCS_TYPE_MKDEV_ACL: -+ { -+ struct ccs_mkdev_acl *entry = -+ container_of(acl, typeof(*entry), head); -+ ccs_put_name_union(&entry->name); -+ ccs_put_number_union(&entry->mode); -+ ccs_put_number_union(&entry->major); -+ ccs_put_number_union(&entry->minor); -+ } -+ break; -+ case CCS_TYPE_MOUNT_ACL: -+ { -+ struct ccs_mount_acl *entry = -+ container_of(acl, typeof(*entry), head); -+ ccs_put_name_union(&entry->dev_name); -+ ccs_put_name_union(&entry->dir_name); -+ ccs_put_name_union(&entry->fs_type); -+ ccs_put_number_union(&entry->flags); -+ } -+ break; -+#ifdef CONFIG_CCSECURITY_NETWORK -+ case CCS_TYPE_INET_ACL: -+ { -+ struct ccs_inet_acl *entry = -+ container_of(acl, typeof(*entry), head); -+ ccs_put_group(entry->address.group); -+ ccs_put_number_union(&entry->port); -+ } -+ break; -+ case CCS_TYPE_UNIX_ACL: -+ { -+ struct ccs_unix_acl *entry = -+ container_of(acl, typeof(*entry), head); -+ ccs_put_name_union(&entry->name); -+ } -+ break; -+#endif -+#ifdef CONFIG_CCSECURITY_MISC -+ case CCS_TYPE_ENV_ACL: -+ { -+ struct ccs_env_acl *entry = -+ container_of(acl, typeof(*entry), head); -+ ccs_put_name(entry->env); -+ } -+ break; -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ case CCS_TYPE_CAPABILITY_ACL: -+ { -+ /* Nothing to do. */ -+ } -+ break; -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ case CCS_TYPE_SIGNAL_ACL: -+ { -+ struct ccs_signal_acl *entry = -+ container_of(acl, typeof(*entry), head); -+ ccs_put_number_union(&entry->sig); -+ ccs_put_name(entry->domainname); -+ } -+ break; -+#endif -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ case CCS_TYPE_AUTO_EXECUTE_HANDLER: -+ case CCS_TYPE_DENIED_EXECUTE_HANDLER: -+ { -+ struct ccs_handler_acl *entry = -+ container_of(acl, typeof(*entry), head); -+ ccs_put_name(entry->handler); -+ } -+ break; -+#endif -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+ case CCS_TYPE_AUTO_TASK_ACL: -+ case CCS_TYPE_MANUAL_TASK_ACL: -+ { -+ struct ccs_task_acl *entry = -+ container_of(acl, typeof(*entry), head); -+ ccs_put_name(entry->domainname); -+ } -+ break; -+#endif -+ } -+} -+ -+/** -+ * ccs_del_domain - Delete members in "struct ccs_domain_info". -+ * -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ * -+ * Caller holds ccs_policy_lock mutex. -+ */ -+static inline void ccs_del_domain(struct list_head *element) -+{ -+ struct ccs_domain_info *domain = -+ container_of(element, typeof(*domain), list); -+ struct ccs_acl_info *acl; -+ struct ccs_acl_info *tmp; -+ /* -+ * Since this domain is referenced from neither "struct ccs_io_buffer" -+ * nor "struct task_struct", we can delete elements without checking -+ * for is_deleted flag. -+ */ -+ list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { -+ ccs_del_acl(&acl->list); -+ ccs_memory_free(acl, CCS_ID_ACL); -+ } -+ ccs_put_name(domain->domainname); -+} -+ -+/** -+ * ccs_del_path_group - Delete members in "struct ccs_path_group". -+ * -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_del_path_group(struct list_head *element) -+{ -+ struct ccs_path_group *member = -+ container_of(element, typeof(*member), head.list); -+ ccs_put_name(member->member_name); -+} -+ -+/** -+ * ccs_del_group - Delete "struct ccs_group". -+ * -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_del_group(struct list_head *element) -+{ -+ struct ccs_group *group = -+ container_of(element, typeof(*group), head.list); -+ ccs_put_name(group->group_name); -+} -+ -+/** -+ * ccs_del_address_group - Delete members in "struct ccs_address_group". -+ * -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_del_address_group(struct list_head *element) -+{ -+ /* Nothing to do. */ -+} -+ -+/** -+ * ccs_del_number_group - Delete members in "struct ccs_number_group". -+ * -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_del_number_group(struct list_head *element) -+{ -+ /* Nothing to do. */ -+} -+ -+/** -+ * ccs_del_reservedport - Delete members in "struct ccs_reserved". -+ * -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_del_reservedport(struct list_head *element) -+{ -+ /* Nothing to do. */ -+} -+ -+/** -+ * ccs_del_condition - Delete members in "struct ccs_condition". -+ * -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ */ -+void ccs_del_condition(struct list_head *element) -+{ -+ struct ccs_condition *cond = container_of(element, typeof(*cond), -+ head.list); -+ const u16 condc = cond->condc; -+ const u16 numbers_count = cond->numbers_count; -+ const u16 names_count = cond->names_count; -+ const u16 argc = cond->argc; -+ const u16 envc = cond->envc; -+ unsigned int i; -+ const struct ccs_condition_element *condp -+ = (const struct ccs_condition_element *) (cond + 1); -+ struct ccs_number_union *numbers_p -+ = (struct ccs_number_union *) (condp + condc); -+ struct ccs_name_union *names_p -+ = (struct ccs_name_union *) (numbers_p + numbers_count); -+ const struct ccs_argv *argv -+ = (const struct ccs_argv *) (names_p + names_count); -+ const struct ccs_envp *envp -+ = (const struct ccs_envp *) (argv + argc); -+ for (i = 0; i < numbers_count; i++) -+ ccs_put_number_union(numbers_p++); -+ for (i = 0; i < names_count; i++) -+ ccs_put_name_union(names_p++); -+ for (i = 0; i < argc; argv++, i++) -+ ccs_put_name(argv->value); -+ for (i = 0; i < envc; envp++, i++) { -+ ccs_put_name(envp->name); -+ ccs_put_name(envp->value); -+ } -+ ccs_put_name(cond->transit); -+} -+ -+/** -+ * ccs_del_name - Delete members in "struct ccs_name". -+ * -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_del_name(struct list_head *element) -+{ -+ /* Nothing to do. */ -+} -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) -+ -+/** -+ * ccs_lock - Alternative for srcu_read_lock(). -+ * -+ * Returns index number which has to be passed to ccs_unlock(). -+ */ -+int ccs_lock(void) -+{ -+ int idx; -+ spin_lock(&ccs_counter_lock); -+ idx = ccs_counter.counter_idx; -+ ccs_counter.counter[idx]++; -+ spin_unlock(&ccs_counter_lock); -+ return idx; -+} -+ -+/** -+ * ccs_unlock - Alternative for srcu_read_unlock(). -+ * -+ * @idx: Index number returned by ccs_lock(). -+ * -+ * Returns nothing. -+ */ -+void ccs_unlock(const int idx) -+{ -+ spin_lock(&ccs_counter_lock); -+ ccs_counter.counter[idx]--; -+ spin_unlock(&ccs_counter_lock); -+} -+ -+/** -+ * ccs_synchronize_counter - Alternative for synchronize_srcu(). -+ * -+ * Returns nothing. -+ */ -+static void ccs_synchronize_counter(void) -+{ -+ int idx; -+ int v; -+ /* -+ * Change currently active counter's index. Make it visible to other -+ * threads by doing it with ccs_counter_lock held. -+ * This function is called by garbage collector thread, and the garbage -+ * collector thread is exclusive. Therefore, it is guaranteed that -+ * SRCU grace period has expired when returning from this function. -+ */ -+ spin_lock(&ccs_counter_lock); -+ idx = ccs_counter.counter_idx; -+ ccs_counter.counter_idx ^= 1; -+ v = ccs_counter.counter[idx]; -+ spin_unlock(&ccs_counter_lock); -+ /* Wait for previously active counter to become 0. */ -+ while (v) { -+ ssleep(1); -+ spin_lock(&ccs_counter_lock); -+ v = ccs_counter.counter[idx]; -+ spin_unlock(&ccs_counter_lock); -+ } -+} -+ -+#endif -+ -+/** -+ * ccs_try_to_gc - Try to kfree() an entry. -+ * -+ * @type: One of values in "enum ccs_policy_id". -+ * @element: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ * -+ * Caller holds ccs_policy_lock mutex. -+ */ -+static void ccs_try_to_gc(const enum ccs_policy_id type, -+ struct list_head *element) -+{ -+ /* -+ * __list_del_entry() guarantees that the list element became no longer -+ * reachable from the list which the element was originally on (e.g. -+ * ccs_domain_list). Also, synchronize_srcu() guarantees that the list -+ * element became no longer referenced by syscall users. -+ */ -+ __list_del_entry(element); -+ mutex_unlock(&ccs_policy_lock); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) -+ synchronize_srcu(&ccs_ss); -+#else -+ ccs_synchronize_counter(); -+#endif -+ /* -+ * However, there are two users which may still be using the list -+ * element. We need to defer until both users forget this element. -+ * -+ * Don't kfree() until "struct ccs_io_buffer"->r.{domain,group,acl} and -+ * "struct ccs_io_buffer"->w.domain forget this element. -+ */ -+ if (ccs_struct_used_by_io_buffer(element)) -+ goto reinject; -+ switch (type) { -+ case CCS_ID_TRANSITION_CONTROL: -+ ccs_del_transition_control(element); -+ break; -+ case CCS_ID_MANAGER: -+ ccs_del_manager(element); -+ break; -+ case CCS_ID_AGGREGATOR: -+ ccs_del_aggregator(element); -+ break; -+ case CCS_ID_GROUP: -+ ccs_del_group(element); -+ break; -+ case CCS_ID_PATH_GROUP: -+ ccs_del_path_group(element); -+ break; -+#ifdef CONFIG_CCSECURITY_NETWORK -+ case CCS_ID_ADDRESS_GROUP: -+ ccs_del_address_group(element); -+ break; -+#endif -+ case CCS_ID_NUMBER_GROUP: -+ ccs_del_number_group(element); -+ break; -+#ifdef CONFIG_CCSECURITY_PORTRESERVE -+ case CCS_ID_RESERVEDPORT: -+ ccs_del_reservedport(element); -+ break; -+#endif -+ case CCS_ID_CONDITION: -+ ccs_del_condition(element); -+ break; -+ case CCS_ID_NAME: -+ /* -+ * Don't kfree() until all "struct ccs_io_buffer"->r.w[] forget -+ * this element. -+ */ -+ if (ccs_name_used_by_io_buffer -+ (container_of(element, typeof(struct ccs_name), -+ head.list)->entry.name, -+ container_of(element, typeof(struct ccs_name), -+ head.list)->size)) -+ goto reinject; -+ ccs_del_name(element); -+ break; -+ case CCS_ID_ACL: -+ ccs_del_acl(element); -+ break; -+ case CCS_ID_DOMAIN: -+ /* -+ * Don't kfree() until all "struct task_struct" forget this -+ * element. -+ */ -+ if (ccs_domain_used_by_task -+ (container_of(element, typeof(struct ccs_domain_info), -+ list))) -+ goto reinject; -+ break; -+ case CCS_MAX_POLICY: -+ break; -+ } -+ mutex_lock(&ccs_policy_lock); -+ if (type == CCS_ID_DOMAIN) -+ ccs_del_domain(element); -+ ccs_memory_free(element, type); -+ return; -+reinject: -+ /* -+ * We can safely reinject this element here bacause -+ * (1) Appending list elements and removing list elements are protected -+ * by ccs_policy_lock mutex. -+ * (2) Only this function removes list elements and this function is -+ * exclusively executed by ccs_gc_mutex mutex. -+ * are true. -+ */ -+ mutex_lock(&ccs_policy_lock); -+ list_add_rcu(element, element->prev); -+} -+ -+/** -+ * ccs_collect_member - Delete elements with "struct ccs_acl_head". -+ * -+ * @id: One of values in "enum ccs_policy_id". -+ * @member_list: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ * -+ * Caller holds ccs_policy_lock mutex. -+ */ -+static void ccs_collect_member(const enum ccs_policy_id id, -+ struct list_head *member_list) -+{ -+ struct ccs_acl_head *member; -+ struct ccs_acl_head *tmp; -+ list_for_each_entry_safe(member, tmp, member_list, list) { -+ if (!member->is_deleted) -+ continue; -+ member->is_deleted = CCS_GC_IN_PROGRESS; -+ ccs_try_to_gc(id, &member->list); -+ } -+} -+ -+/** -+ * ccs_collect_acl - Delete elements in "struct ccs_domain_info". -+ * -+ * @list: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ * -+ * Caller holds ccs_policy_lock mutex. -+ */ -+static void ccs_collect_acl(struct list_head *list) -+{ -+ struct ccs_acl_info *acl; -+ struct ccs_acl_info *tmp; -+ list_for_each_entry_safe(acl, tmp, list, list) { -+ if (!acl->is_deleted) -+ continue; -+ acl->is_deleted = CCS_GC_IN_PROGRESS; -+ ccs_try_to_gc(CCS_ID_ACL, &acl->list); -+ } -+} -+ -+/** -+ * ccs_collect_entry - Try to kfree() deleted elements. -+ * -+ * Returns nothing. -+ */ -+static void ccs_collect_entry(void) -+{ -+ int i; -+ enum ccs_policy_id id; -+ struct ccs_policy_namespace *ns; -+ mutex_lock(&ccs_policy_lock); -+ { -+ struct ccs_domain_info *domain; -+ struct ccs_domain_info *tmp; -+ list_for_each_entry_safe(domain, tmp, &ccs_domain_list, list) { -+ ccs_collect_acl(&domain->acl_info_list); -+ if (!domain->is_deleted || -+ ccs_domain_used_by_task(domain)) -+ continue; -+ ccs_try_to_gc(CCS_ID_DOMAIN, &domain->list); -+ } -+ } -+ list_for_each_entry(ns, &ccs_namespace_list, namespace_list) { -+ for (id = 0; id < CCS_MAX_POLICY; id++) -+ ccs_collect_member(id, &ns->policy_list[id]); -+ for (i = 0; i < CCS_MAX_ACL_GROUPS; i++) -+ ccs_collect_acl(&ns->acl_group[i]); -+ } -+ { -+ struct ccs_shared_acl_head *ptr; -+ struct ccs_shared_acl_head *tmp; -+ list_for_each_entry_safe(ptr, tmp, &ccs_condition_list, list) { -+ if (atomic_read(&ptr->users) > 0) -+ continue; -+ atomic_set(&ptr->users, CCS_GC_IN_PROGRESS); -+ ccs_try_to_gc(CCS_ID_CONDITION, &ptr->list); -+ } -+ } -+ list_for_each_entry(ns, &ccs_namespace_list, namespace_list) { -+ for (i = 0; i < CCS_MAX_GROUP; i++) { -+ struct list_head *list = &ns->group_list[i]; -+ struct ccs_group *group; -+ struct ccs_group *tmp; -+ switch (i) { -+ case 0: -+ id = CCS_ID_PATH_GROUP; -+ break; -+ case 1: -+ id = CCS_ID_NUMBER_GROUP; -+ break; -+ default: -+#ifdef CONFIG_CCSECURITY_NETWORK -+ id = CCS_ID_ADDRESS_GROUP; -+#else -+ continue; -+#endif -+ break; -+ } -+ list_for_each_entry_safe(group, tmp, list, head.list) { -+ ccs_collect_member(id, &group->member_list); -+ if (!list_empty(&group->member_list) || -+ atomic_read(&group->head.users) > 0) -+ continue; -+ atomic_set(&group->head.users, -+ CCS_GC_IN_PROGRESS); -+ ccs_try_to_gc(CCS_ID_GROUP, &group->head.list); -+ } -+ } -+ } -+ for (i = 0; i < CCS_MAX_HASH; i++) { -+ struct list_head *list = &ccs_name_list[i]; -+ struct ccs_shared_acl_head *ptr; -+ struct ccs_shared_acl_head *tmp; -+ list_for_each_entry_safe(ptr, tmp, list, list) { -+ if (atomic_read(&ptr->users) > 0) -+ continue; -+ atomic_set(&ptr->users, CCS_GC_IN_PROGRESS); -+ ccs_try_to_gc(CCS_ID_NAME, &ptr->list); -+ } -+ } -+ mutex_unlock(&ccs_policy_lock); -+} -+ -+/** -+ * ccs_gc_thread - Garbage collector thread function. -+ * -+ * @unused: Unused. -+ * -+ * Returns 0. -+ */ -+static int ccs_gc_thread(void *unused) -+{ -+ /* Garbage collector thread is exclusive. */ -+ static DEFINE_MUTEX(ccs_gc_mutex); -+ if (!mutex_trylock(&ccs_gc_mutex)) -+ goto out; -+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 6) -+ /* daemonize() not needed. */ -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) -+ daemonize("GC for CCS"); -+#else -+ daemonize(); -+ reparent_to_init(); -+#if defined(TASK_DEAD) -+ { -+ struct task_struct *task = current; -+ spin_lock_irq(&task->sighand->siglock); -+ siginitsetinv(&task->blocked, 0); -+ recalc_sigpending(); -+ spin_unlock_irq(&task->sighand->siglock); -+ } -+#else -+ { -+ struct task_struct *task = current; -+ spin_lock_irq(&task->sigmask_lock); -+ siginitsetinv(&task->blocked, 0); -+ recalc_sigpending(task); -+ spin_unlock_irq(&task->sigmask_lock); -+ } -+#endif -+ snprintf(current->comm, sizeof(current->comm) - 1, "GC for CCS"); -+#endif -+ ccs_collect_entry(); -+ { -+ struct ccs_io_buffer *head; -+ struct ccs_io_buffer *tmp; -+ spin_lock(&ccs_io_buffer_list_lock); -+ list_for_each_entry_safe(head, tmp, &ccs_io_buffer_list, -+ list) { -+ if (head->users) -+ continue; -+ list_del(&head->list); -+ kfree(head->read_buf); -+ kfree(head->write_buf); -+ kfree(head); -+ } -+ spin_unlock(&ccs_io_buffer_list_lock); -+ } -+ mutex_unlock(&ccs_gc_mutex); -+out: -+ /* This acts as do_exit(0). */ -+ return 0; -+} -+ -+/** -+ * ccs_notify_gc - Register/unregister /proc/ccs/ users. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @is_register: True if register, false if unregister. -+ * -+ * Returns nothing. -+ */ -+void ccs_notify_gc(struct ccs_io_buffer *head, const bool is_register) -+{ -+ bool is_write = false; -+ spin_lock(&ccs_io_buffer_list_lock); -+ if (is_register) { -+ head->users = 1; -+ list_add(&head->list, &ccs_io_buffer_list); -+ } else { -+ is_write = head->write_buf != NULL; -+ if (!--head->users) { -+ list_del(&head->list); -+ kfree(head->read_buf); -+ kfree(head->write_buf); -+ kfree(head); -+ } -+ } -+ spin_unlock(&ccs_io_buffer_list_lock); -+ if (is_write) { -+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 6) -+ struct task_struct *task = kthread_create(ccs_gc_thread, NULL, -+ "GC for CCS"); -+ if (!IS_ERR(task)) -+ wake_up_process(task); -+#else -+ kernel_thread(ccs_gc_thread, NULL, 0); -+#endif -+ } -+} -diff --git a/security/ccsecurity/internal.h b/security/ccsecurity/internal.h -new file mode 100644 -index 0000000..3f703f2 ---- /dev/null -+++ b/security/ccsecurity/internal.h -@@ -0,0 +1,2090 @@ -+/* -+ * security/ccsecurity/internal.h -+ * -+ * Copyright (C) 2005-2012 NTT DATA CORPORATION -+ * -+ * Version: 1.8.4 2015/05/05 -+ */ -+ -+#ifndef _SECURITY_CCSECURITY_INTERNAL_H -+#define _SECURITY_CCSECURITY_INTERNAL_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 38) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+#include -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) -+#include -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) -+#include -+#endif -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) -+#include -+#endif -+#include -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) || defined(RHEL_MAJOR) -+#include -+#endif -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) || (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL)) -+#include -+#endif -+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 6) -+#include -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+#define sk_family family -+#define sk_protocol protocol -+#define sk_type type -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) -+ -+/* Structure for holding "struct vfsmount *" and "struct dentry *". */ -+struct path { -+ struct vfsmount *mnt; -+ struct dentry *dentry; -+}; -+ -+#endif -+ -+#ifndef __printf -+#define __printf(a,b) __attribute__((format(printf,a,b))) -+#endif -+#ifndef __packed -+#define __packed __attribute__((__packed__)) -+#endif -+#ifndef bool -+#define bool _Bool -+#endif -+#ifndef false -+#define false 0 -+#endif -+#ifndef true -+#define true 1 -+#endif -+ -+#ifndef __user -+#define __user -+#endif -+ -+#ifndef current_uid -+#define current_uid() (current->uid) -+#endif -+#ifndef current_gid -+#define current_gid() (current->gid) -+#endif -+#ifndef current_euid -+#define current_euid() (current->euid) -+#endif -+#ifndef current_egid -+#define current_egid() (current->egid) -+#endif -+#ifndef current_suid -+#define current_suid() (current->suid) -+#endif -+#ifndef current_sgid -+#define current_sgid() (current->sgid) -+#endif -+#ifndef current_fsuid -+#define current_fsuid() (current->fsuid) -+#endif -+#ifndef current_fsgid -+#define current_fsgid() (current->fsgid) -+#endif -+ -+#ifndef DEFINE_SPINLOCK -+#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) -+#define mutex semaphore -+#define mutex_init(mutex) init_MUTEX(mutex) -+#define mutex_unlock(mutex) up(mutex) -+#define mutex_lock(mutex) down(mutex) -+#define mutex_lock_interruptible(mutex) down_interruptible(mutex) -+#define mutex_trylock(mutex) (!down_trylock(mutex)) -+#define DEFINE_MUTEX(mutexname) DECLARE_MUTEX(mutexname) -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) -+#define MS_UNBINDABLE (1<<17) /* change to unbindable */ -+#define MS_PRIVATE (1<<18) /* change to private */ -+#define MS_SLAVE (1<<19) /* change to slave */ -+#define MS_SHARED (1<<20) /* change to shared */ -+#endif -+ -+#ifndef container_of -+#define container_of(ptr, type, member) ({ \ -+ const typeof(((type *)0)->member) *__mptr = (ptr); \ -+ (type *)((char *)__mptr - offsetof(type, member)); }) -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) -+#define smp_read_barrier_depends smp_rmb -+#endif -+ -+#ifndef ACCESS_ONCE -+#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) -+#endif -+ -+#ifndef rcu_dereference -+#define rcu_dereference(p) ({ \ -+ typeof(p) _________p1 = ACCESS_ONCE(p); \ -+ smp_read_barrier_depends(); /* see RCU */ \ -+ (_________p1); \ -+ }) -+#endif -+ -+#ifndef rcu_assign_pointer -+#define rcu_assign_pointer(p, v) \ -+ ({ \ -+ if (!__builtin_constant_p(v) || \ -+ ((v) != NULL)) \ -+ smp_wmb(); /* see RCU */ \ -+ (p) = (v); \ -+ }) -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) -+#define f_vfsmnt f_path.mnt -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) -+ -+/** -+ * kzalloc() - Allocate memory. The memory is set to zero. -+ * -+ * @size: Size to allocate. -+ * @flags: GFP flags. -+ * -+ * Returns pointer to allocated memory on success, NULL otherwise. -+ * -+ * This is for compatibility with older kernels. -+ * -+ * Since several distributions backported kzalloc(), I define it as a macro -+ * rather than an inlined function in order to avoid multiple definition error. -+ */ -+#define kzalloc(size, flags) ({ \ -+ void *ret = kmalloc((size), (flags)); \ -+ if (ret) \ -+ memset(ret, 0, (size)); \ -+ ret; }) -+ -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) -+ -+/** -+ * path_put - Drop reference on "struct path". -+ * -+ * @path: Pointer to "struct path". -+ * -+ * Returns nothing. -+ * -+ * This is for compatibility with older kernels. -+ */ -+static inline void path_put(struct path *path) -+{ -+ dput(path->dentry); -+ mntput(path->mnt); -+} -+ -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+ -+/** -+ * __list_add_rcu - Insert a new entry between two known consecutive entries. -+ * -+ * @new: Pointer to "struct list_head". -+ * @prev: Pointer to "struct list_head". -+ * @next: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ * -+ * This is for compatibility with older kernels. -+ */ -+static inline void __list_add_rcu(struct list_head *new, -+ struct list_head *prev, -+ struct list_head *next) -+{ -+ new->next = next; -+ new->prev = prev; -+ rcu_assign_pointer(prev->next, new); -+ next->prev = new; -+} -+ -+/** -+ * list_add_tail_rcu - Add a new entry to rcu-protected list. -+ * -+ * @new: Pointer to "struct list_head". -+ * @head: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ * -+ * This is for compatibility with older kernels. -+ */ -+static inline void list_add_tail_rcu(struct list_head *new, -+ struct list_head *head) -+{ -+ __list_add_rcu(new, head->prev, head); -+} -+ -+/** -+ * list_add_rcu - Add a new entry to rcu-protected list. -+ * -+ * @new: Pointer to "struct list_head". -+ * @head: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ * -+ * This is for compatibility with older kernels. -+ */ -+static inline void list_add_rcu(struct list_head *new, struct list_head *head) -+{ -+ __list_add_rcu(new, head, head->next); -+} -+ -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) -+ -+/** -+ * __list_del_entry - Deletes entry from list without re-initialization. -+ * -+ * @entry: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ * -+ * This is for compatibility with older kernels. -+ */ -+static inline void __list_del_entry(struct list_head *entry) -+{ -+ __list_del(entry->prev, entry->next); -+} -+ -+#endif -+ -+#ifndef list_for_each_entry_safe -+ -+/** -+ * list_for_each_entry_safe - Iterate over list of given type safe against removal of list entry. -+ * -+ * @pos: The "type *" to use as a loop cursor. -+ * @n: Another "type *" to use as temporary storage. -+ * @head: Pointer to "struct list_head". -+ * @member: The name of the list_struct within the struct. -+ * -+ * This is for compatibility with older kernels. -+ */ -+#define list_for_each_entry_safe(pos, n, head, member) \ -+ for (pos = list_entry((head)->next, typeof(*pos), member), \ -+ n = list_entry(pos->member.next, typeof(*pos), member); \ -+ &pos->member != (head); \ -+ pos = n, n = list_entry(n->member.next, typeof(*n), member)) -+ -+#endif -+ -+#ifndef srcu_dereference -+ -+/** -+ * srcu_dereference - Fetch SRCU-protected pointer with checking. -+ * -+ * @p: The pointer to read, prior to dereferencing. -+ * @ss: Pointer to "struct srcu_struct". -+ * -+ * Returns @p. -+ * -+ * This is for compatibility with older kernels. -+ */ -+#define srcu_dereference(p, ss) rcu_dereference(p) -+ -+#endif -+ -+#ifndef list_for_each_entry_srcu -+ -+/** -+ * list_for_each_entry_srcu - Iterate over rcu list of given type. -+ * -+ * @pos: The type * to use as a loop cursor. -+ * @head: The head for your list. -+ * @member: The name of the list_struct within the struct. -+ * @ss: Pointer to "struct srcu_struct". -+ * -+ * As of 2.6.36, this macro is not provided because only TOMOYO wants it. -+ */ -+#define list_for_each_entry_srcu(pos, head, member, ss) \ -+ for (pos = list_entry(srcu_dereference((head)->next, ss), \ -+ typeof(*pos), member); \ -+ prefetch(pos->member.next), &pos->member != (head); \ -+ pos = list_entry(srcu_dereference(pos->member.next, ss), \ -+ typeof(*pos), member)) -+ -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 30) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 9)) -+ -+#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 4, 21) -+#undef ssleep -+#endif -+ -+#ifndef ssleep -+ -+/** -+ * ssleep - Sleep for specified seconds. -+ * -+ * @secs: Seconds to sleep. -+ * -+ * Returns nothing. -+ * -+ * This is for compatibility with older kernels. -+ * -+ * Since several distributions backported ssleep(), I define it as a macro -+ * rather than an inlined function in order to avoid multiple definition error. -+ */ -+#define ssleep(secs) { \ -+ set_current_state(TASK_UNINTERRUPTIBLE); \ -+ schedule_timeout((HZ * secs) + 1); \ -+ } -+ -+#endif -+ -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) -+ -+/** -+ * from_kuid - Convert kuid_t to uid_t. -+ * -+ * @ns: Unused. -+ * @uid: kuid_t value. -+ * -+ * Returns uid seen from init's user namespace. -+ */ -+#define from_kuid(ns, uid) (uid) -+ -+/** -+ * from_kgid - Convert kgid_t to gid_t. -+ * -+ * @ns: Unused. -+ * @gid: kgid_t value. -+ * -+ * Returns gid seen from init's user namespace. -+ */ -+#define from_kgid(ns, gid) (gid) -+ -+/** -+ * uid_eq - Check whether the uids are equals or not. -+ * -+ * @left: Uid seen from current user namespace. -+ * @right: Uid seen from current user namespace. -+ * -+ * Returns true if uid is root in init's user namespace, false otherwise. -+ */ -+#define uid_eq(left, right) ((left) == (right)) -+#define GLOBAL_ROOT_UID 0 -+ -+#endif -+ -+/* -+ * TOMOYO specific part start. -+ */ -+ -+#include -+ -+/* Enumeration definition for internal use. */ -+ -+/* Index numbers for Access Controls. */ -+enum ccs_acl_entry_type_index { -+ CCS_TYPE_PATH_ACL, -+ CCS_TYPE_PATH2_ACL, -+ CCS_TYPE_PATH_NUMBER_ACL, -+ CCS_TYPE_MKDEV_ACL, -+ CCS_TYPE_MOUNT_ACL, -+#ifdef CONFIG_CCSECURITY_MISC -+ CCS_TYPE_ENV_ACL, -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ CCS_TYPE_CAPABILITY_ACL, -+#endif -+#ifdef CONFIG_CCSECURITY_NETWORK -+ CCS_TYPE_INET_ACL, -+ CCS_TYPE_UNIX_ACL, -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ CCS_TYPE_SIGNAL_ACL, -+#endif -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ CCS_TYPE_AUTO_EXECUTE_HANDLER, -+ CCS_TYPE_DENIED_EXECUTE_HANDLER, -+#endif -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+ CCS_TYPE_AUTO_TASK_ACL, -+ CCS_TYPE_MANUAL_TASK_ACL, -+#endif -+}; -+ -+/* Index numbers for "struct ccs_condition". */ -+enum ccs_conditions_index { -+ CCS_TASK_UID, /* current_uid() */ -+ CCS_TASK_EUID, /* current_euid() */ -+ CCS_TASK_SUID, /* current_suid() */ -+ CCS_TASK_FSUID, /* current_fsuid() */ -+ CCS_TASK_GID, /* current_gid() */ -+ CCS_TASK_EGID, /* current_egid() */ -+ CCS_TASK_SGID, /* current_sgid() */ -+ CCS_TASK_FSGID, /* current_fsgid() */ -+ CCS_TASK_PID, /* sys_getpid() */ -+ CCS_TASK_PPID, /* sys_getppid() */ -+ CCS_EXEC_ARGC, /* "struct linux_binprm *"->argc */ -+ CCS_EXEC_ENVC, /* "struct linux_binprm *"->envc */ -+ CCS_TYPE_IS_SOCKET, /* S_IFSOCK */ -+ CCS_TYPE_IS_SYMLINK, /* S_IFLNK */ -+ CCS_TYPE_IS_FILE, /* S_IFREG */ -+ CCS_TYPE_IS_BLOCK_DEV, /* S_IFBLK */ -+ CCS_TYPE_IS_DIRECTORY, /* S_IFDIR */ -+ CCS_TYPE_IS_CHAR_DEV, /* S_IFCHR */ -+ CCS_TYPE_IS_FIFO, /* S_IFIFO */ -+ CCS_MODE_SETUID, /* S_ISUID */ -+ CCS_MODE_SETGID, /* S_ISGID */ -+ CCS_MODE_STICKY, /* S_ISVTX */ -+ CCS_MODE_OWNER_READ, /* S_IRUSR */ -+ CCS_MODE_OWNER_WRITE, /* S_IWUSR */ -+ CCS_MODE_OWNER_EXECUTE, /* S_IXUSR */ -+ CCS_MODE_GROUP_READ, /* S_IRGRP */ -+ CCS_MODE_GROUP_WRITE, /* S_IWGRP */ -+ CCS_MODE_GROUP_EXECUTE, /* S_IXGRP */ -+ CCS_MODE_OTHERS_READ, /* S_IROTH */ -+ CCS_MODE_OTHERS_WRITE, /* S_IWOTH */ -+ CCS_MODE_OTHERS_EXECUTE, /* S_IXOTH */ -+ CCS_TASK_TYPE, /* ((u8) task->ccs_flags) & -+ CCS_TASK_IS_EXECUTE_HANDLER */ -+ CCS_TASK_EXECUTE_HANDLER, /* CCS_TASK_IS_EXECUTE_HANDLER */ -+ CCS_EXEC_REALPATH, -+ CCS_SYMLINK_TARGET, -+ CCS_PATH1_UID, -+ CCS_PATH1_GID, -+ CCS_PATH1_INO, -+ CCS_PATH1_MAJOR, -+ CCS_PATH1_MINOR, -+ CCS_PATH1_PERM, -+ CCS_PATH1_TYPE, -+ CCS_PATH1_DEV_MAJOR, -+ CCS_PATH1_DEV_MINOR, -+ CCS_PATH2_UID, -+ CCS_PATH2_GID, -+ CCS_PATH2_INO, -+ CCS_PATH2_MAJOR, -+ CCS_PATH2_MINOR, -+ CCS_PATH2_PERM, -+ CCS_PATH2_TYPE, -+ CCS_PATH2_DEV_MAJOR, -+ CCS_PATH2_DEV_MINOR, -+ CCS_PATH1_PARENT_UID, -+ CCS_PATH1_PARENT_GID, -+ CCS_PATH1_PARENT_INO, -+ CCS_PATH1_PARENT_PERM, -+ CCS_PATH2_PARENT_UID, -+ CCS_PATH2_PARENT_GID, -+ CCS_PATH2_PARENT_INO, -+ CCS_PATH2_PARENT_PERM, -+ CCS_MAX_CONDITION_KEYWORD, -+ CCS_NUMBER_UNION, -+ CCS_NAME_UNION, -+ CCS_ARGV_ENTRY, -+ CCS_ENVP_ENTRY, -+}; -+ -+/* Index numbers for domain's attributes. */ -+enum ccs_domain_info_flags_index { -+ /* Quota warnning flag. */ -+ CCS_DIF_QUOTA_WARNED, -+ /* -+ * This domain was unable to create a new domain at -+ * ccs_find_next_domain() because the name of the domain to be created -+ * was too long or it could not allocate memory. -+ * More than one process continued execve() without domain transition. -+ */ -+ CCS_DIF_TRANSITION_FAILED, -+ CCS_MAX_DOMAIN_INFO_FLAGS -+}; -+ -+/* Index numbers for audit type. */ -+enum ccs_grant_log { -+ /* Follow profile's configuration. */ -+ CCS_GRANTLOG_AUTO, -+ /* Do not generate grant log. */ -+ CCS_GRANTLOG_NO, -+ /* Generate grant_log. */ -+ CCS_GRANTLOG_YES, -+}; -+ -+/* Index numbers for group entries. */ -+enum ccs_group_id { -+ CCS_PATH_GROUP, -+ CCS_NUMBER_GROUP, -+#ifdef CONFIG_CCSECURITY_NETWORK -+ CCS_ADDRESS_GROUP, -+#endif -+ CCS_MAX_GROUP -+}; -+ -+/* Index numbers for category of functionality. */ -+enum ccs_mac_category_index { -+ CCS_MAC_CATEGORY_FILE, -+#ifdef CONFIG_CCSECURITY_NETWORK -+ CCS_MAC_CATEGORY_NETWORK, -+#endif -+#ifdef CONFIG_CCSECURITY_MISC -+ CCS_MAC_CATEGORY_MISC, -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ CCS_MAC_CATEGORY_IPC, -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ CCS_MAC_CATEGORY_CAPABILITY, -+#endif -+ CCS_MAX_MAC_CATEGORY_INDEX -+}; -+ -+/* Index numbers for functionality. */ -+enum ccs_mac_index { -+ CCS_MAC_FILE_EXECUTE, -+ CCS_MAC_FILE_OPEN, -+ CCS_MAC_FILE_CREATE, -+ CCS_MAC_FILE_UNLINK, -+#ifdef CONFIG_CCSECURITY_FILE_GETATTR -+ CCS_MAC_FILE_GETATTR, -+#endif -+ CCS_MAC_FILE_MKDIR, -+ CCS_MAC_FILE_RMDIR, -+ CCS_MAC_FILE_MKFIFO, -+ CCS_MAC_FILE_MKSOCK, -+ CCS_MAC_FILE_TRUNCATE, -+ CCS_MAC_FILE_SYMLINK, -+ CCS_MAC_FILE_MKBLOCK, -+ CCS_MAC_FILE_MKCHAR, -+ CCS_MAC_FILE_LINK, -+ CCS_MAC_FILE_RENAME, -+ CCS_MAC_FILE_CHMOD, -+ CCS_MAC_FILE_CHOWN, -+ CCS_MAC_FILE_CHGRP, -+ CCS_MAC_FILE_IOCTL, -+ CCS_MAC_FILE_CHROOT, -+ CCS_MAC_FILE_MOUNT, -+ CCS_MAC_FILE_UMOUNT, -+ CCS_MAC_FILE_PIVOT_ROOT, -+#ifdef CONFIG_CCSECURITY_NETWORK -+ CCS_MAC_NETWORK_INET_STREAM_BIND, -+ CCS_MAC_NETWORK_INET_STREAM_LISTEN, -+ CCS_MAC_NETWORK_INET_STREAM_CONNECT, -+ CCS_MAC_NETWORK_INET_STREAM_ACCEPT, -+ CCS_MAC_NETWORK_INET_DGRAM_BIND, -+ CCS_MAC_NETWORK_INET_DGRAM_SEND, -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ CCS_MAC_NETWORK_INET_DGRAM_RECV, -+#endif -+ CCS_MAC_NETWORK_INET_RAW_BIND, -+ CCS_MAC_NETWORK_INET_RAW_SEND, -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ CCS_MAC_NETWORK_INET_RAW_RECV, -+#endif -+ CCS_MAC_NETWORK_UNIX_STREAM_BIND, -+ CCS_MAC_NETWORK_UNIX_STREAM_LISTEN, -+ CCS_MAC_NETWORK_UNIX_STREAM_CONNECT, -+ CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT, -+ CCS_MAC_NETWORK_UNIX_DGRAM_BIND, -+ CCS_MAC_NETWORK_UNIX_DGRAM_SEND, -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ CCS_MAC_NETWORK_UNIX_DGRAM_RECV, -+#endif -+ CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND, -+ CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN, -+ CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT, -+ CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT, -+#endif -+#ifdef CONFIG_CCSECURITY_MISC -+ CCS_MAC_ENVIRON, -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ CCS_MAC_SIGNAL, -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET, -+ CCS_MAC_CAPABILITY_USE_PACKET_SOCKET, -+ CCS_MAC_CAPABILITY_SYS_REBOOT, -+ CCS_MAC_CAPABILITY_SYS_VHANGUP, -+ CCS_MAC_CAPABILITY_SYS_SETTIME, -+ CCS_MAC_CAPABILITY_SYS_NICE, -+ CCS_MAC_CAPABILITY_SYS_SETHOSTNAME, -+ CCS_MAC_CAPABILITY_USE_KERNEL_MODULE, -+ CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD, -+ CCS_MAC_CAPABILITY_SYS_PTRACE, -+#endif -+ CCS_MAX_MAC_INDEX -+}; -+ -+/* Index numbers for /proc/ccs/stat interface. */ -+enum ccs_memory_stat_type { -+ CCS_MEMORY_POLICY, -+ CCS_MEMORY_AUDIT, -+ CCS_MEMORY_QUERY, -+ CCS_MAX_MEMORY_STAT -+}; -+ -+/* Index numbers for access controls with one pathname and three numbers. */ -+enum ccs_mkdev_acl_index { -+ CCS_TYPE_MKBLOCK, -+ CCS_TYPE_MKCHAR, -+ CCS_MAX_MKDEV_OPERATION -+}; -+ -+/* Index numbers for operation mode. */ -+enum ccs_mode_value { -+ CCS_CONFIG_DISABLED, -+ CCS_CONFIG_LEARNING, -+ CCS_CONFIG_PERMISSIVE, -+ CCS_CONFIG_ENFORCING, -+ CCS_CONFIG_MAX_MODE, -+ CCS_CONFIG_WANT_REJECT_LOG = 64, -+ CCS_CONFIG_WANT_GRANT_LOG = 128, -+ CCS_CONFIG_USE_DEFAULT = 255, -+}; -+ -+/* Index numbers for socket operations. */ -+enum ccs_network_acl_index { -+ CCS_NETWORK_BIND, /* bind() operation. */ -+ CCS_NETWORK_LISTEN, /* listen() operation. */ -+ CCS_NETWORK_CONNECT, /* connect() operation. */ -+ CCS_NETWORK_ACCEPT, /* accept() operation. */ -+ CCS_NETWORK_SEND, /* send() operation. */ -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ CCS_NETWORK_RECV, /* recv() operation. */ -+#endif -+ CCS_MAX_NETWORK_OPERATION -+}; -+ -+/* Index numbers for access controls with two pathnames. */ -+enum ccs_path2_acl_index { -+ CCS_TYPE_LINK, -+ CCS_TYPE_RENAME, -+ CCS_TYPE_PIVOT_ROOT, -+ CCS_MAX_PATH2_OPERATION -+}; -+ -+/* Index numbers for access controls with one pathname. */ -+enum ccs_path_acl_index { -+ CCS_TYPE_EXECUTE, -+ CCS_TYPE_READ, -+ CCS_TYPE_WRITE, -+ CCS_TYPE_APPEND, -+ CCS_TYPE_UNLINK, -+#ifdef CONFIG_CCSECURITY_FILE_GETATTR -+ CCS_TYPE_GETATTR, -+#endif -+ CCS_TYPE_RMDIR, -+ CCS_TYPE_TRUNCATE, -+ CCS_TYPE_SYMLINK, -+ CCS_TYPE_CHROOT, -+ CCS_TYPE_UMOUNT, -+ CCS_MAX_PATH_OPERATION -+}; -+ -+/* Index numbers for access controls with one pathname and one number. */ -+enum ccs_path_number_acl_index { -+ CCS_TYPE_CREATE, -+ CCS_TYPE_MKDIR, -+ CCS_TYPE_MKFIFO, -+ CCS_TYPE_MKSOCK, -+ CCS_TYPE_IOCTL, -+ CCS_TYPE_CHMOD, -+ CCS_TYPE_CHOWN, -+ CCS_TYPE_CHGRP, -+ CCS_MAX_PATH_NUMBER_OPERATION -+}; -+ -+/* Index numbers for stat(). */ -+enum ccs_path_stat_index { -+ /* Do not change this order. */ -+ CCS_PATH1, -+ CCS_PATH1_PARENT, -+ CCS_PATH2, -+ CCS_PATH2_PARENT, -+ CCS_MAX_PATH_STAT -+}; -+ -+/* Index numbers for entry type. */ -+enum ccs_policy_id { -+#ifdef CONFIG_CCSECURITY_PORTRESERVE -+ CCS_ID_RESERVEDPORT, -+#endif -+ CCS_ID_GROUP, -+#ifdef CONFIG_CCSECURITY_NETWORK -+ CCS_ID_ADDRESS_GROUP, -+#endif -+ CCS_ID_PATH_GROUP, -+ CCS_ID_NUMBER_GROUP, -+ CCS_ID_AGGREGATOR, -+ CCS_ID_TRANSITION_CONTROL, -+ CCS_ID_MANAGER, -+ CCS_ID_CONDITION, -+ CCS_ID_NAME, -+ CCS_ID_ACL, -+ CCS_ID_DOMAIN, -+ CCS_MAX_POLICY -+}; -+ -+/* Index numbers for /proc/ccs/stat interface. */ -+enum ccs_policy_stat_type { -+ /* Do not change this order. */ -+ CCS_STAT_POLICY_UPDATES, -+ CCS_STAT_POLICY_LEARNING, /* == CCS_CONFIG_LEARNING */ -+ CCS_STAT_POLICY_PERMISSIVE, /* == CCS_CONFIG_PERMISSIVE */ -+ CCS_STAT_POLICY_ENFORCING, /* == CCS_CONFIG_ENFORCING */ -+ CCS_MAX_POLICY_STAT -+}; -+ -+/* Index numbers for profile's PREFERENCE values. */ -+enum ccs_pref_index { -+ CCS_PREF_MAX_AUDIT_LOG, -+ CCS_PREF_MAX_LEARNING_ENTRY, -+ CCS_PREF_ENFORCING_PENALTY, -+ CCS_MAX_PREF -+}; -+ -+/* Index numbers for /proc/ccs/ interfaces. */ -+enum ccs_proc_interface_index { -+ CCS_DOMAIN_POLICY, -+ CCS_EXCEPTION_POLICY, -+ CCS_PROCESS_STATUS, -+ CCS_STAT, -+ CCS_AUDIT, -+ CCS_VERSION, -+ CCS_PROFILE, -+ CCS_QUERY, -+ CCS_MANAGER, -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ CCS_EXECUTE_HANDLER, -+#endif -+}; -+ -+/* Index numbers for special mount operations. */ -+enum ccs_special_mount { -+ CCS_MOUNT_BIND, /* mount --bind /source /dest */ -+ CCS_MOUNT_MOVE, /* mount --move /old /new */ -+ CCS_MOUNT_REMOUNT, /* mount -o remount /dir */ -+ CCS_MOUNT_MAKE_UNBINDABLE, /* mount --make-unbindable /dir */ -+ CCS_MOUNT_MAKE_PRIVATE, /* mount --make-private /dir */ -+ CCS_MOUNT_MAKE_SLAVE, /* mount --make-slave /dir */ -+ CCS_MOUNT_MAKE_SHARED, /* mount --make-shared /dir */ -+ CCS_MAX_SPECIAL_MOUNT -+}; -+ -+/* Index numbers for domain transition control keywords. */ -+enum ccs_transition_type { -+ /* Do not change this order, */ -+ CCS_TRANSITION_CONTROL_NO_RESET, -+ CCS_TRANSITION_CONTROL_RESET, -+ CCS_TRANSITION_CONTROL_NO_INITIALIZE, -+ CCS_TRANSITION_CONTROL_INITIALIZE, -+ CCS_TRANSITION_CONTROL_NO_KEEP, -+ CCS_TRANSITION_CONTROL_KEEP, -+ CCS_MAX_TRANSITION_TYPE -+}; -+ -+/* Index numbers for type of numeric values. */ -+enum ccs_value_type { -+ CCS_VALUE_TYPE_INVALID, -+ CCS_VALUE_TYPE_DECIMAL, -+ CCS_VALUE_TYPE_OCTAL, -+ CCS_VALUE_TYPE_HEXADECIMAL, -+}; -+ -+/* Constants definition for internal use. */ -+ -+/* -+ * TOMOYO uses this hash only when appending a string into the string table. -+ * Frequency of appending strings is very low. So we don't need large (e.g. -+ * 64k) hash size. 256 will be sufficient. -+ */ -+#define CCS_HASH_BITS 8 -+#define CCS_MAX_HASH (1u << CCS_HASH_BITS) -+ -+/* -+ * TOMOYO checks only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET. -+ * Therefore, we don't need SOCK_MAX. -+ */ -+#define CCS_SOCK_MAX 6 -+ -+/* Size of temporary buffer for execve() operation. */ -+#define CCS_EXEC_TMPSIZE 4096 -+ -+/* Garbage collector is trying to kfree() this element. */ -+#define CCS_GC_IN_PROGRESS -1 -+ -+/* Profile number is an integer between 0 and 255. */ -+#define CCS_MAX_PROFILES 256 -+ -+/* Group number is an integer between 0 and 255. */ -+#define CCS_MAX_ACL_GROUPS 256 -+ -+/* Current thread is doing open(O_RDONLY | O_TRUNC) ? */ -+#define CCS_OPEN_FOR_READ_TRUNCATE 1 -+/* Current thread is doing open(3) ? */ -+#define CCS_OPEN_FOR_IOCTL_ONLY 2 -+/* Current thread is doing do_execve() ? */ -+#define CCS_TASK_IS_IN_EXECVE 4 -+/* Current thread is running as an execute handler program? */ -+#define CCS_TASK_IS_EXECUTE_HANDLER 8 -+/* Current thread is allowed to modify policy via /proc/ccs/ interface? */ -+#define CCS_TASK_IS_MANAGER 16 -+ -+/* -+ * Retry this request. Returned by ccs_supervisor() if policy violation has -+ * occurred in enforcing mode and the userspace daemon decided to retry. -+ * -+ * We must choose a positive value in order to distinguish "granted" (which is -+ * 0) and "rejected" (which is a negative value) and "retry". -+ */ -+#define CCS_RETRY_REQUEST 1 -+ -+/* Ignore gfp flags which are not supported. */ -+#ifndef __GFP_HIGHIO -+#define __GFP_HIGHIO 0 -+#endif -+#ifndef __GFP_NOWARN -+#define __GFP_NOWARN 0 -+#endif -+#ifndef __GFP_NORETRY -+#define __GFP_NORETRY 0 -+#endif -+#ifndef __GFP_NOMEMALLOC -+#define __GFP_NOMEMALLOC 0 -+#endif -+ -+/* The gfp flags used by TOMOYO. */ -+#define CCS_GFP_FLAGS (__GFP_WAIT | __GFP_IO | __GFP_HIGHIO | __GFP_NOWARN | \ -+ __GFP_NORETRY | __GFP_NOMEMALLOC) -+ -+/* Size of read buffer for /proc/ccs/ interface. */ -+#define CCS_MAX_IO_READ_QUEUE 64 -+ -+/* Structure definition for internal use. */ -+ -+/* Common header for holding ACL entries. */ -+struct ccs_acl_head { -+ struct list_head list; -+ s8 is_deleted; /* true or false or CCS_GC_IN_PROGRESS */ -+} __packed; -+ -+/* Common header for shared entries. */ -+struct ccs_shared_acl_head { -+ struct list_head list; -+ atomic_t users; -+} __packed; -+ -+/* Common header for individual entries. */ -+struct ccs_acl_info { -+ struct list_head list; -+ struct ccs_condition *cond; /* Maybe NULL. */ -+ s8 is_deleted; /* true or false or CCS_GC_IN_PROGRESS */ -+ u8 type; /* One of values in "enum ccs_acl_entry_type_index". */ -+ u16 perm; -+} __packed; -+ -+/* Structure for holding a word. */ -+struct ccs_name_union { -+ /* Either @filename or @group is NULL. */ -+ const struct ccs_path_info *filename; -+ struct ccs_group *group; -+}; -+ -+/* Structure for holding a number. */ -+struct ccs_number_union { -+ unsigned long values[2]; -+ struct ccs_group *group; /* Maybe NULL. */ -+ /* One of values in "enum ccs_value_type". */ -+ u8 value_type[2]; -+}; -+ -+/* Structure for holding an IP address. */ -+struct ccs_ipaddr_union { -+ struct in6_addr ip[2]; /* Big endian. */ -+ struct ccs_group *group; /* Pointer to address group. */ -+ bool is_ipv6; /* Valid only if @group == NULL. */ -+}; -+ -+/* Structure for "path_group"/"number_group"/"address_group" directive. */ -+struct ccs_group { -+ struct ccs_shared_acl_head head; -+ /* Name of group (without leading '@'). */ -+ const struct ccs_path_info *group_name; -+ /* -+ * List of "struct ccs_path_group" or "struct ccs_number_group" or -+ * "struct ccs_address_group". -+ */ -+ struct list_head member_list; -+}; -+ -+/* Structure for "path_group" directive. */ -+struct ccs_path_group { -+ struct ccs_acl_head head; -+ const struct ccs_path_info *member_name; -+}; -+ -+/* Structure for "number_group" directive. */ -+struct ccs_number_group { -+ struct ccs_acl_head head; -+ struct ccs_number_union number; -+}; -+ -+/* Structure for "address_group" directive. */ -+struct ccs_address_group { -+ struct ccs_acl_head head; -+ /* Structure for holding an IP address. */ -+ struct ccs_ipaddr_union address; -+}; -+ -+/* Subset of "struct stat". Used by conditional ACL and audit logs. */ -+struct ccs_mini_stat { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) -+ kuid_t uid; -+ kgid_t gid; -+#else -+ uid_t uid; -+ gid_t gid; -+#endif -+ ino_t ino; -+ umode_t mode; -+ dev_t dev; -+ dev_t rdev; -+}; -+ -+/* Structure for dumping argv[] and envp[] of "struct linux_binprm". */ -+struct ccs_page_dump { -+ struct page *page; /* Previously dumped page. */ -+ char *data; /* Contents of "page". Size is PAGE_SIZE. */ -+}; -+ -+/* Structure for attribute checks in addition to pathname checks. */ -+struct ccs_obj_info { -+ /* True if ccs_get_attributes() was already called, false otherwise. */ -+ bool validate_done; -+ /* True if @stat[] is valid. */ -+ bool stat_valid[CCS_MAX_PATH_STAT]; -+ /* First pathname. Initialized with { NULL, NULL } if no path. */ -+ struct path path1; -+ /* Second pathname. Initialized with { NULL, NULL } if no path. */ -+ struct path path2; -+ /* -+ * Information on @path1, @path1's parent directory, @path2, @path2's -+ * parent directory. -+ */ -+ struct ccs_mini_stat stat[CCS_MAX_PATH_STAT]; -+ /* -+ * Content of symbolic link to be created. NULL for operations other -+ * than symlink(). -+ */ -+ struct ccs_path_info *symlink_target; -+}; -+ -+/* Structure for entries which follows "struct ccs_condition". */ -+struct ccs_condition_element { -+ /* -+ * Left hand operand. A "struct ccs_argv" for CCS_ARGV_ENTRY, a -+ * "struct ccs_envp" for CCS_ENVP_ENTRY is attached to the tail -+ * of the array of this struct. -+ */ -+ u8 left; -+ /* -+ * Right hand operand. A "struct ccs_number_union" for -+ * CCS_NUMBER_UNION, a "struct ccs_name_union" for CCS_NAME_UNION is -+ * attached to the tail of the array of this struct. -+ */ -+ u8 right; -+ /* Equation operator. True if equals or overlaps, false otherwise. */ -+ bool equals; -+}; -+ -+/* Structure for optional arguments. */ -+struct ccs_condition { -+ struct ccs_shared_acl_head head; -+ u32 size; /* Memory size allocated for this entry. */ -+ u16 condc; /* Number of conditions in this struct. */ -+ u16 numbers_count; /* Number of "struct ccs_number_union values". */ -+ u16 names_count; /* Number of "struct ccs_name_union names". */ -+ u16 argc; /* Number of "struct ccs_argv". */ -+ u16 envc; /* Number of "struct ccs_envp". */ -+ u8 grant_log; /* One of values in "enum ccs_grant_log". */ -+ bool exec_transit; /* True if transit is for "file execute". */ -+ const struct ccs_path_info *transit; /* Maybe NULL. */ -+ /* -+ * struct ccs_condition_element condition[condc]; -+ * struct ccs_number_union values[numbers_count]; -+ * struct ccs_name_union names[names_count]; -+ * struct ccs_argv argv[argc]; -+ * struct ccs_envp envp[envc]; -+ */ -+}; -+ -+struct ccs_execve; -+struct ccs_policy_namespace; -+ -+/* Structure for request info. */ -+struct ccs_request_info { -+ /* -+ * For holding parameters specific to operations which deal files. -+ * NULL if not dealing files. -+ */ -+ struct ccs_obj_info *obj; -+ /* -+ * For holding parameters specific to execve() request. -+ * NULL if not dealing do_execve(). -+ */ -+ struct ccs_execve *ee; -+ /* -+ * For holding parameters. -+ * Pointers in this union are not NULL except path->matched_path. -+ */ -+ union { -+ struct { -+ const struct ccs_path_info *filename; -+ /* -+ * For using wildcards at ccs_find_next_domain(). -+ * -+ * The matched_acl cannot be used because it may refer -+ * a "struct ccs_path_acl" with ->is_group == true. -+ * We want to use exact "struct ccs_path_info" rather -+ * than "struct ccs_path_acl". -+ */ -+ const struct ccs_path_info *matched_path; -+ /* One of values in "enum ccs_path_acl_index". */ -+ u8 operation; -+ } path; -+ struct { -+ const struct ccs_path_info *filename1; -+ const struct ccs_path_info *filename2; -+ /* One of values in "enum ccs_path2_acl_index". */ -+ u8 operation; -+ } path2; -+ struct { -+ const struct ccs_path_info *filename; -+ unsigned int mode; -+ unsigned int major; -+ unsigned int minor; -+ /* One of values in "enum ccs_mkdev_acl_index". */ -+ u8 operation; -+ } mkdev; -+ struct { -+ const struct ccs_path_info *filename; -+ unsigned long number; -+ /* -+ * One of values in "enum ccs_path_number_acl_index". -+ */ -+ u8 operation; -+ } path_number; -+#ifdef CONFIG_CCSECURITY_NETWORK -+ struct { -+ const u32 *address; /* Big endian. */ -+ u16 port; /* Host endian. */ -+ /* One of values smaller than CCS_SOCK_MAX. */ -+ u8 protocol; -+ /* One of values in "enum ccs_network_acl_index". */ -+ u8 operation; -+ bool is_ipv6; -+ } inet_network; -+ struct { -+ const struct ccs_path_info *address; -+ /* One of values smaller than CCS_SOCK_MAX. */ -+ u8 protocol; -+ /* One of values in "enum ccs_network_acl_index". */ -+ u8 operation; -+ } unix_network; -+#endif -+#ifdef CONFIG_CCSECURITY_MISC -+ struct { -+ const struct ccs_path_info *name; -+ } environ; -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ struct { -+ /* One of values in "enum ccs_capability_acl_index". */ -+ u8 operation; -+ } capability; -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ struct { -+ const char *dest_pattern; -+ int sig; -+ } signal; -+#endif -+ struct { -+ const struct ccs_path_info *type; -+ const struct ccs_path_info *dir; -+ const struct ccs_path_info *dev; -+ unsigned long flags; -+ int need_dev; -+ } mount; -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+ struct { -+ const struct ccs_path_info *domainname; -+ } task; -+#endif -+ } param; -+ /* -+ * For updating current->ccs_domain_info at ccs_update_task_domain(). -+ * Initialized to NULL at ccs_init_request_info(). -+ * Matching "struct ccs_acl_info" is copied if access request was -+ * granted. Re-initialized to NULL at ccs_update_task_domain(). -+ */ -+ struct ccs_acl_info *matched_acl; -+ u8 param_type; /* One of values in "enum ccs_acl_entry_type_index". */ -+ bool granted; /* True if granted, false otherwise. */ -+ /* True if current thread should not be carried sleep penalty. */ -+ bool dont_sleep_on_enforce_error; -+ /* -+ * For counting number of retries made for this request. -+ * This counter is incremented whenever ccs_supervisor() returned -+ * CCS_RETRY_REQUEST. -+ */ -+ u8 retry; -+ /* -+ * For holding profile number used for this request. -+ * One of values between 0 and CCS_MAX_PROFILES - 1. -+ */ -+ u8 profile; -+ /* -+ * For holding operation mode used for this request. -+ * One of CCS_CONFIG_DISABLED, CCS_CONFIG_LEARNING, -+ * CCS_CONFIG_PERMISSIVE, CCS_CONFIG_ENFORCING. -+ */ -+ u8 mode; -+ /* -+ * For holding operation index used for this request. -+ * Used by ccs_init_request_info() / ccs_get_mode() / -+ * ccs_write_log(). One of values in "enum ccs_mac_index". -+ */ -+ u8 type; -+}; -+ -+/* Structure for holding a token. */ -+struct ccs_path_info { -+ const char *name; -+ u32 hash; /* = full_name_hash(name, strlen(name)) */ -+ u16 total_len; /* = strlen(name) */ -+ u16 const_len; /* = ccs_const_part_length(name) */ -+ bool is_dir; /* = ccs_strendswith(name, "/") */ -+ bool is_patterned; /* = const_len < total_len */ -+}; -+ -+/* Structure for execve() operation. */ -+struct ccs_execve { -+ struct ccs_request_info r; -+ struct ccs_obj_info obj; -+ struct linux_binprm *bprm; -+ struct ccs_domain_info *previous_domain; -+ const struct ccs_path_info *transition; -+ /* For execute_handler */ -+ const struct ccs_path_info *handler; -+ char *handler_path; /* = kstrdup(handler->name, CCS_GFP_FLAGS) */ -+ /* For dumping argv[] and envp[]. */ -+ struct ccs_page_dump dump; -+ /* For temporary use. */ -+ char *tmp; /* Size is CCS_EXEC_TMPSIZE bytes */ -+}; -+ -+/* Structure for domain information. */ -+struct ccs_domain_info { -+ struct list_head list; -+ struct list_head acl_info_list; -+ /* Name of this domain. Never NULL. */ -+ const struct ccs_path_info *domainname; -+ /* Namespace for this domain. Never NULL. */ -+ struct ccs_policy_namespace *ns; -+ /* Group numbers to use. */ -+ unsigned long group[CCS_MAX_ACL_GROUPS / BITS_PER_LONG]; -+ u8 profile; /* Profile number to use. */ -+ bool is_deleted; /* Delete flag. */ -+ bool flags[CCS_MAX_DOMAIN_INFO_FLAGS]; -+}; -+ -+/* -+ * Structure for "reset_domain"/"no_reset_domain"/"initialize_domain"/ -+ * "no_initialize_domain"/"keep_domain"/"no_keep_domain" keyword. -+ */ -+struct ccs_transition_control { -+ struct ccs_acl_head head; -+ u8 type; /* One of values in "enum ccs_transition_type" */ -+ bool is_last_name; /* True if the domainname is ccs_last_word(). */ -+ const struct ccs_path_info *domainname; /* Maybe NULL */ -+ const struct ccs_path_info *program; /* Maybe NULL */ -+}; -+ -+/* Structure for "aggregator" keyword. */ -+struct ccs_aggregator { -+ struct ccs_acl_head head; -+ const struct ccs_path_info *original_name; -+ const struct ccs_path_info *aggregated_name; -+}; -+ -+/* Structure for "deny_autobind" keyword. */ -+struct ccs_reserved { -+ struct ccs_acl_head head; -+ struct ccs_number_union port; -+}; -+ -+/* Structure for policy manager. */ -+struct ccs_manager { -+ struct ccs_acl_head head; -+ /* A path to program or a domainname. */ -+ const struct ccs_path_info *manager; -+}; -+ -+/* Structure for argv[]. */ -+struct ccs_argv { -+ unsigned long index; -+ const struct ccs_path_info *value; -+ bool is_not; -+}; -+ -+/* Structure for envp[]. */ -+struct ccs_envp { -+ const struct ccs_path_info *name; -+ const struct ccs_path_info *value; -+ bool is_not; -+}; -+ -+/* -+ * Structure for "task auto_execute_handler" and "task denied_execute_handler" -+ * directive. -+ * -+ * If "task auto_execute_handler" directive exists and the current process is -+ * not an execute handler, all execve() requests are replaced by execve() -+ * requests of a program specified by "task auto_execute_handler" directive. -+ * If the current process is an execute handler, "task auto_execute_handler" -+ * and "task denied_execute_handler" directives are ignored. -+ * The program specified by "task execute_handler" validates execve() -+ * parameters and executes the original execve() requests if appropriate. -+ * -+ * "task denied_execute_handler" directive is used only when execve() request -+ * was rejected in enforcing mode (i.e. CONFIG::file::execute={ mode=enforcing -+ * }). The program specified by "task denied_execute_handler" does whatever it -+ * wants to do (e.g. silently terminate, change firewall settings, redirect the -+ * user to honey pot etc.). -+ */ -+struct ccs_handler_acl { -+ struct ccs_acl_info head; /* type = CCS_TYPE_*_EXECUTE_HANDLER */ -+ const struct ccs_path_info *handler; /* Pointer to single pathname. */ -+}; -+ -+/* -+ * Structure for "task auto_domain_transition" and -+ * "task manual_domain_transition" directive. -+ */ -+struct ccs_task_acl { -+ struct ccs_acl_info head; /* type = CCS_TYPE_*_TASK_ACL */ -+ /* Pointer to domainname. */ -+ const struct ccs_path_info *domainname; -+}; -+ -+/* -+ * Structure for "file execute", "file read", "file write", "file append", -+ * "file unlink", "file getattr", "file rmdir", "file truncate", -+ * "file symlink", "file chroot" and "file unmount" directive. -+ */ -+struct ccs_path_acl { -+ struct ccs_acl_info head; /* type = CCS_TYPE_PATH_ACL */ -+ struct ccs_name_union name; -+}; -+ -+/* -+ * Structure for "file rename", "file link" and "file pivot_root" directive. -+ */ -+struct ccs_path2_acl { -+ struct ccs_acl_info head; /* type = CCS_TYPE_PATH2_ACL */ -+ struct ccs_name_union name1; -+ struct ccs_name_union name2; -+}; -+ -+/* -+ * Structure for "file create", "file mkdir", "file mkfifo", "file mksock", -+ * "file ioctl", "file chmod", "file chown" and "file chgrp" directive. -+ */ -+struct ccs_path_number_acl { -+ struct ccs_acl_info head; /* type = CCS_TYPE_PATH_NUMBER_ACL */ -+ struct ccs_name_union name; -+ struct ccs_number_union number; -+}; -+ -+/* Structure for "file mkblock" and "file mkchar" directive. */ -+struct ccs_mkdev_acl { -+ struct ccs_acl_info head; /* type = CCS_TYPE_MKDEV_ACL */ -+ struct ccs_name_union name; -+ struct ccs_number_union mode; -+ struct ccs_number_union major; -+ struct ccs_number_union minor; -+}; -+ -+/* Structure for "file mount" directive. */ -+struct ccs_mount_acl { -+ struct ccs_acl_info head; /* type = CCS_TYPE_MOUNT_ACL */ -+ struct ccs_name_union dev_name; -+ struct ccs_name_union dir_name; -+ struct ccs_name_union fs_type; -+ struct ccs_number_union flags; -+}; -+ -+/* Structure for "misc env" directive in domain policy. */ -+struct ccs_env_acl { -+ struct ccs_acl_info head; /* type = CCS_TYPE_ENV_ACL */ -+ const struct ccs_path_info *env; /* environment variable */ -+}; -+ -+/* Structure for "capability" directive. */ -+struct ccs_capability_acl { -+ struct ccs_acl_info head; /* type = CCS_TYPE_CAPABILITY_ACL */ -+ u8 operation; /* One of values in "enum ccs_capability_acl_index". */ -+}; -+ -+/* Structure for "ipc signal" directive. */ -+struct ccs_signal_acl { -+ struct ccs_acl_info head; /* type = CCS_TYPE_SIGNAL_ACL */ -+ struct ccs_number_union sig; -+ /* Pointer to destination pattern. */ -+ const struct ccs_path_info *domainname; -+}; -+ -+/* Structure for "network inet" directive. */ -+struct ccs_inet_acl { -+ struct ccs_acl_info head; /* type = CCS_TYPE_INET_ACL */ -+ u8 protocol; -+ struct ccs_ipaddr_union address; -+ struct ccs_number_union port; -+}; -+ -+/* Structure for "network unix" directive. */ -+struct ccs_unix_acl { -+ struct ccs_acl_info head; /* type = CCS_TYPE_UNIX_ACL */ -+ u8 protocol; -+ struct ccs_name_union name; -+}; -+ -+/* Structure for holding string data. */ -+struct ccs_name { -+ struct ccs_shared_acl_head head; -+ int size; /* Memory size allocated for this entry. */ -+ struct ccs_path_info entry; -+}; -+ -+/* Structure for holding a line from /proc/ccs/ interface. */ -+struct ccs_acl_param { -+ char *data; /* Unprocessed data. */ -+ struct list_head *list; /* List to add or remove. */ -+ struct ccs_policy_namespace *ns; /* Namespace to use. */ -+ bool is_delete; /* True if it is a delete request. */ -+ union ccs_acl_union { -+ struct ccs_acl_info acl_info; -+ struct ccs_handler_acl handler_acl; -+ struct ccs_task_acl task_acl; -+ struct ccs_path_acl path_acl; -+ struct ccs_path2_acl path2_acl; -+ struct ccs_path_number_acl path_number_acl; -+ struct ccs_mkdev_acl mkdev_acl; -+ struct ccs_mount_acl mount_acl; -+ struct ccs_env_acl env_acl; -+ struct ccs_capability_acl capability_acl; -+ struct ccs_signal_acl signal_acl; -+ struct ccs_inet_acl inet_acl; -+ struct ccs_unix_acl unix_acl; -+ /**/ -+ struct ccs_acl_head acl_head; -+ struct ccs_transition_control transition_control; -+ struct ccs_aggregator aggregator; -+ struct ccs_reserved reserved; -+ struct ccs_manager manager; -+ struct ccs_path_group path_group; -+ struct ccs_number_group number_group; -+ struct ccs_address_group address_group; -+ } e; -+}; -+ -+/* Structure for reading/writing policy via /proc/ccs/ interfaces. */ -+struct ccs_io_buffer { -+ /* Exclusive lock for this structure. */ -+ struct mutex io_sem; -+ char __user *read_user_buf; -+ size_t read_user_buf_avail; -+ struct { -+ struct list_head *ns; -+ struct list_head *domain; -+ struct list_head *group; -+ struct list_head *acl; -+ size_t avail; -+ unsigned int step; -+ unsigned int query_index; -+ u16 index; -+ u16 cond_index; -+ u8 acl_group_index; -+ u8 cond_step; -+ u8 bit; -+ u8 w_pos; -+ bool eof; -+ bool print_this_domain_only; -+ bool print_transition_related_only; -+ bool print_cond_part; -+ const char *w[CCS_MAX_IO_READ_QUEUE]; -+ } r; -+ struct { -+ struct ccs_policy_namespace *ns; -+ struct ccs_domain_info *domain; -+ size_t avail; -+ bool is_delete; -+ } w; -+ /* Buffer for reading. */ -+ char *read_buf; -+ /* Size of read buffer. */ -+ size_t readbuf_size; -+ /* Buffer for writing. */ -+ char *write_buf; -+ /* Size of write buffer. */ -+ size_t writebuf_size; -+ /* Type of interface. */ -+ enum ccs_proc_interface_index type; -+ /* Users counter protected by ccs_io_buffer_list_lock. */ -+ u8 users; -+ /* List for telling GC not to kfree() elements. */ -+ struct list_head list; -+}; -+ -+/* Structure for /proc/ccs/profile interface. */ -+struct ccs_profile { -+ const struct ccs_path_info *comment; -+ u8 default_config; -+ u8 config[CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX]; -+ unsigned int pref[CCS_MAX_PREF]; -+}; -+ -+/* Structure for representing YYYY/MM/DD hh/mm/ss. */ -+struct ccs_time { -+ u16 year; -+ u8 month; -+ u8 day; -+ u8 hour; -+ u8 min; -+ u8 sec; -+}; -+ -+/* Structure for policy namespace. */ -+struct ccs_policy_namespace { -+ /* Profile table. Memory is allocated as needed. */ -+ struct ccs_profile *profile_ptr[CCS_MAX_PROFILES]; -+ /* List of "struct ccs_group". */ -+ struct list_head group_list[CCS_MAX_GROUP]; -+ /* List of policy. */ -+ struct list_head policy_list[CCS_MAX_POLICY]; -+ /* The global ACL referred by "use_group" keyword. */ -+ struct list_head acl_group[CCS_MAX_ACL_GROUPS]; -+ /* List for connecting to ccs_namespace_list list. */ -+ struct list_head namespace_list; -+ /* Profile version. Currently only 20150505 is supported. */ -+ unsigned int profile_version; -+ /* Name of this namespace (e.g. "", "" ). */ -+ const char *name; -+}; -+ -+/* Prototype definition for "struct ccsecurity_operations". */ -+ -+void __init ccs_permission_init(void); -+void __init ccs_mm_init(void); -+ -+/* Prototype definition for internal use. */ -+ -+bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos, -+ struct ccs_page_dump *dump); -+bool ccs_memory_ok(const void *ptr, const unsigned int size); -+char *ccs_encode(const char *str); -+char *ccs_encode2(const char *str, int str_len); -+char *ccs_realpath(const struct path *path); -+const char *ccs_get_exe(void); -+const struct ccs_path_info *ccs_get_name(const char *name); -+int ccs_audit_log(struct ccs_request_info *r); -+int ccs_check_acl(struct ccs_request_info *r); -+int ccs_init_request_info(struct ccs_request_info *r, const u8 index); -+struct ccs_domain_info *ccs_assign_domain(const char *domainname, -+ const bool transit); -+u8 ccs_get_config(const u8 profile, const u8 index); -+void *ccs_commit_ok(void *data, const unsigned int size); -+void ccs_del_acl(struct list_head *element); -+void ccs_del_condition(struct list_head *element); -+void ccs_fill_path_info(struct ccs_path_info *ptr); -+void ccs_get_attributes(struct ccs_obj_info *obj); -+void ccs_notify_gc(struct ccs_io_buffer *head, const bool is_register); -+void ccs_transition_failed(const char *domainname); -+void ccs_warn_oom(const char *function); -+void ccs_write_log(struct ccs_request_info *r, const char *fmt, ...) -+ __printf(2, 3); -+ -+/* Variable definition for internal use. */ -+ -+extern bool ccs_policy_loaded; -+extern const char * const ccs_dif[CCS_MAX_DOMAIN_INFO_FLAGS]; -+extern const u8 ccs_c2mac[CCS_MAX_CAPABILITY_INDEX]; -+extern const u8 ccs_pn2mac[CCS_MAX_PATH_NUMBER_OPERATION]; -+extern const u8 ccs_pnnn2mac[CCS_MAX_MKDEV_OPERATION]; -+extern const u8 ccs_pp2mac[CCS_MAX_PATH2_OPERATION]; -+extern struct ccs_domain_info ccs_kernel_domain; -+extern struct list_head ccs_condition_list; -+extern struct list_head ccs_domain_list; -+extern struct list_head ccs_name_list[CCS_MAX_HASH]; -+extern struct list_head ccs_namespace_list; -+extern struct mutex ccs_policy_lock; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) -+extern struct srcu_struct ccs_ss; -+#endif -+extern unsigned int ccs_memory_quota[CCS_MAX_MEMORY_STAT]; -+extern unsigned int ccs_memory_used[CCS_MAX_MEMORY_STAT]; -+ -+/* Inlined functions for internal use. */ -+ -+/** -+ * ccs_pathcmp - strcmp() for "struct ccs_path_info" structure. -+ * -+ * @a: Pointer to "struct ccs_path_info". -+ * @b: Pointer to "struct ccs_path_info". -+ * -+ * Returns true if @a != @b, false otherwise. -+ */ -+static inline bool ccs_pathcmp(const struct ccs_path_info *a, -+ const struct ccs_path_info *b) -+{ -+ return a->hash != b->hash || strcmp(a->name, b->name); -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) -+ -+/** -+ * ccs_read_lock - Take lock for protecting policy. -+ * -+ * Returns index number for ccs_read_unlock(). -+ */ -+static inline int ccs_read_lock(void) -+{ -+ return srcu_read_lock(&ccs_ss); -+} -+ -+/** -+ * ccs_read_unlock - Release lock for protecting policy. -+ * -+ * @idx: Index number returned by ccs_read_lock(). -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_read_unlock(const int idx) -+{ -+ srcu_read_unlock(&ccs_ss, idx); -+} -+ -+#else -+ -+int ccs_lock(void); -+void ccs_unlock(const int idx); -+ -+/** -+ * ccs_read_lock - Take lock for protecting policy. -+ * -+ * Returns index number for ccs_read_unlock(). -+ */ -+static inline int ccs_read_lock(void) -+{ -+ return ccs_lock(); -+} -+ -+/** -+ * ccs_read_unlock - Release lock for protecting policy. -+ * -+ * @idx: Index number returned by ccs_read_lock(). -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_read_unlock(const int idx) -+{ -+ ccs_unlock(idx); -+} -+ -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) -+ -+/** -+ * ccs_tasklist_lock - Take lock for reading list of "struct task_struct". -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_tasklist_lock(void) -+{ -+ rcu_read_lock(); -+} -+ -+/** -+ * ccs_tasklist_unlock - Release lock for reading list of "struct task_struct". -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_tasklist_unlock(void) -+{ -+ rcu_read_unlock(); -+} -+ -+#else -+ -+/** -+ * ccs_tasklist_lock - Take lock for reading list of "struct task_struct". -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_tasklist_lock(void) -+{ -+ read_lock(&tasklist_lock); -+} -+ -+/** -+ * ccs_tasklist_unlock - Release lock for reading list of "struct task_struct". -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_tasklist_unlock(void) -+{ -+ read_unlock(&tasklist_lock); -+} -+ -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) -+ -+/** -+ * ccs_sys_getppid - Copy of getppid(). -+ * -+ * Returns parent process's PID. -+ * -+ * Alpha does not have getppid() defined. To be able to build this module on -+ * Alpha, I have to copy getppid() from kernel/timer.c. -+ */ -+static inline pid_t ccs_sys_getppid(void) -+{ -+ pid_t pid; -+ rcu_read_lock(); -+ pid = task_tgid_vnr(rcu_dereference(current->real_parent)); -+ rcu_read_unlock(); -+ return pid; -+} -+ -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) -+ -+/** -+ * ccs_sys_getppid - Copy of getppid(). -+ * -+ * Returns parent process's PID. -+ * -+ * This function was rewritten to use RCU in 2.6.16.34. However, distributors -+ * which use earlier kernels (e.g. 2.6.8/2.6.9) did not backport the bugfix. -+ * Therefore, I'm using code for 2.6.16.34 for earlier kernels. -+ */ -+static inline pid_t ccs_sys_getppid(void) -+{ -+ pid_t pid; -+ rcu_read_lock(); -+#if (defined(RHEL_MAJOR) && RHEL_MAJOR == 5) || (defined(AX_MAJOR) && AX_MAJOR == 3) -+ pid = rcu_dereference(current->parent)->tgid; -+#elif defined(CONFIG_UTRACE) -+ /* -+ * RHEL 5.0 kernel does not have RHEL_MAJOR/RHEL_MINOR defined. -+ * Assume RHEL 5.0 if CONFIG_UTRACE is defined. -+ */ -+ pid = rcu_dereference(current->parent)->tgid; -+#else -+ pid = rcu_dereference(current->real_parent)->tgid; -+#endif -+ rcu_read_unlock(); -+ return pid; -+} -+ -+#else -+ -+/** -+ * ccs_sys_getppid - Copy of getppid(). -+ * -+ * Returns parent process's PID. -+ * -+ * I can't use code for 2.6.16.34 for 2.4 kernels because 2.4 kernels does not -+ * have RCU. Therefore, I'm using pessimistic lock (i.e. tasklist_lock -+ * spinlock). -+ */ -+static inline pid_t ccs_sys_getppid(void) -+{ -+ pid_t pid; -+ read_lock(&tasklist_lock); -+#ifdef TASK_DEAD -+ pid = current->group_leader->real_parent->tgid; -+#else -+ pid = current->p_opptr->pid; -+#endif -+ read_unlock(&tasklist_lock); -+ return pid; -+} -+ -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) -+ -+/** -+ * ccs_sys_getpid - Copy of getpid(). -+ * -+ * Returns current thread's PID. -+ * -+ * Alpha does not have getpid() defined. To be able to build this module on -+ * Alpha, I have to copy getpid() from kernel/timer.c. -+ */ -+static inline pid_t ccs_sys_getpid(void) -+{ -+ return task_tgid_vnr(current); -+} -+ -+#else -+ -+/** -+ * ccs_sys_getpid - Copy of getpid(). -+ * -+ * Returns current thread's PID. -+ */ -+static inline pid_t ccs_sys_getpid(void) -+{ -+ return current->tgid; -+} -+ -+#endif -+ -+/** -+ * ccs_get_mode - Get mode for specified functionality. -+ * -+ * @profile: Profile number. -+ * @index: Functionality number. -+ * -+ * Returns mode. -+ */ -+static inline u8 ccs_get_mode(const u8 profile, const u8 index) -+{ -+ return ccs_get_config(profile, index) & (CCS_CONFIG_MAX_MODE - 1); -+} -+ -+#if defined(CONFIG_SLOB) -+ -+/** -+ * ccs_round2 - Round up to power of 2 for calculating memory usage. -+ * -+ * @size: Size to be rounded up. -+ * -+ * Returns @size. -+ * -+ * Since SLOB does not round up, this function simply returns @size. -+ */ -+static inline int ccs_round2(size_t size) -+{ -+ return size; -+} -+ -+#else -+ -+/** -+ * ccs_round2 - Round up to power of 2 for calculating memory usage. -+ * -+ * @size: Size to be rounded up. -+ * -+ * Returns rounded size. -+ * -+ * Strictly speaking, SLAB may be able to allocate (e.g.) 96 bytes instead of -+ * (e.g.) 128 bytes. -+ */ -+static inline int ccs_round2(size_t size) -+{ -+#if PAGE_SIZE == 4096 -+ size_t bsize = 32; -+#else -+ size_t bsize = 64; -+#endif -+ if (!size) -+ return 0; -+ while (size > bsize) -+ bsize <<= 1; -+ return bsize; -+} -+ -+#endif -+ -+/** -+ * ccs_put_condition - Drop reference on "struct ccs_condition". -+ * -+ * @cond: Pointer to "struct ccs_condition". Maybe NULL. -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_put_condition(struct ccs_condition *cond) -+{ -+ if (cond) -+ atomic_dec(&cond->head.users); -+} -+ -+/** -+ * ccs_put_group - Drop reference on "struct ccs_group". -+ * -+ * @group: Pointer to "struct ccs_group". Maybe NULL. -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_put_group(struct ccs_group *group) -+{ -+ if (group) -+ atomic_dec(&group->head.users); -+} -+ -+/** -+ * ccs_put_name - Drop reference on "struct ccs_name". -+ * -+ * @name: Pointer to "struct ccs_path_info". Maybe NULL. -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_put_name(const struct ccs_path_info *name) -+{ -+ if (name) -+ atomic_dec(&container_of(name, struct ccs_name, entry)-> -+ head.users); -+} -+ -+/* For importing variables and functions. */ -+extern const struct ccsecurity_exports ccsecurity_exports; -+ -+#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -+ -+/* -+ * Structure for holding "struct ccs_domain_info *" and "struct ccs_execve *" -+ * and "u32 ccs_flags" for each "struct task_struct". -+ * -+ * "struct ccs_domain_info *" and "u32 ccs_flags" for each "struct task_struct" -+ * are maintained outside that "struct task_struct". Therefore, ccs_security -+ * != task_struct . This keeps KABI for distributor's prebuilt kernels but -+ * entails slow access. -+ * -+ * Memory for this structure is allocated when current thread tries to access -+ * it. Therefore, if memory allocation failed, current thread will be killed by -+ * SIGKILL. Note that if current->pid == 1, sending SIGKILL won't work. -+ */ -+struct ccs_security { -+ struct list_head list; -+ const struct task_struct *task; -+ struct ccs_domain_info *ccs_domain_info; -+ u32 ccs_flags; -+ struct rcu_head rcu; -+}; -+ -+#define CCS_TASK_SECURITY_HASH_BITS 12 -+#define CCS_MAX_TASK_SECURITY_HASH (1u << CCS_TASK_SECURITY_HASH_BITS) -+extern struct list_head ccs_task_security_list[CCS_MAX_TASK_SECURITY_HASH]; -+ -+struct ccs_security *ccs_find_task_security(const struct task_struct *task); -+ -+/** -+ * ccs_current_security - Get "struct ccs_security" for current thread. -+ * -+ * Returns pointer to "struct ccs_security" for current thread. -+ */ -+static inline struct ccs_security *ccs_current_security(void) -+{ -+ return ccs_find_task_security(current); -+} -+ -+/** -+ * ccs_task_domain - Get "struct ccs_domain_info" for specified thread. -+ * -+ * @task: Pointer to "struct task_struct". -+ * -+ * Returns pointer to "struct ccs_security" for specified thread. -+ */ -+static inline struct ccs_domain_info *ccs_task_domain(struct task_struct *task) -+{ -+ struct ccs_domain_info *domain; -+ rcu_read_lock(); -+ domain = ccs_find_task_security(task)->ccs_domain_info; -+ rcu_read_unlock(); -+ return domain; -+} -+ -+/** -+ * ccs_current_domain - Get "struct ccs_domain_info" for current thread. -+ * -+ * Returns pointer to "struct ccs_domain_info" for current thread. -+ */ -+static inline struct ccs_domain_info *ccs_current_domain(void) -+{ -+ return ccs_find_task_security(current)->ccs_domain_info; -+} -+ -+/** -+ * ccs_task_flags - Get flags for specified thread. -+ * -+ * @task: Pointer to "struct task_struct". -+ * -+ * Returns flags for specified thread. -+ */ -+static inline u32 ccs_task_flags(struct task_struct *task) -+{ -+ u32 ccs_flags; -+ rcu_read_lock(); -+ ccs_flags = ccs_find_task_security(task)->ccs_flags; -+ rcu_read_unlock(); -+ return ccs_flags; -+} -+ -+/** -+ * ccs_current_flags - Get flags for current thread. -+ * -+ * Returns flags for current thread. -+ */ -+static inline u32 ccs_current_flags(void) -+{ -+ return ccs_find_task_security(current)->ccs_flags; -+} -+ -+#else -+ -+/* -+ * "struct ccs_domain_info *" and "u32 ccs_flags" for each "struct task_struct" -+ * are maintained inside that "struct task_struct". Therefore, ccs_security == -+ * task_struct . This allows fast access but breaks KABI checks for -+ * distributor's prebuilt kernels due to changes in "struct task_struct". -+ */ -+#define ccs_security task_struct -+ -+/** -+ * ccs_find_task_security - Find "struct ccs_security" for given task. -+ * -+ * @task: Pointer to "struct task_struct". -+ * -+ * Returns pointer to "struct ccs_security". -+ */ -+static inline struct ccs_security *ccs_find_task_security(struct task_struct * -+ task) -+{ -+ return task; -+} -+ -+/** -+ * ccs_current_security - Get "struct ccs_security" for current thread. -+ * -+ * Returns pointer to "struct ccs_security" for current thread. -+ */ -+static inline struct ccs_security *ccs_current_security(void) -+{ -+ return ccs_find_task_security(current); -+} -+ -+/** -+ * ccs_task_domain - Get "struct ccs_domain_info" for specified thread. -+ * -+ * @task: Pointer to "struct task_struct". -+ * -+ * Returns pointer to "struct ccs_security" for specified thread. -+ */ -+static inline struct ccs_domain_info *ccs_task_domain(struct task_struct *task) -+{ -+ struct ccs_domain_info *domain = task->ccs_domain_info; -+ return domain ? domain : &ccs_kernel_domain; -+} -+ -+/** -+ * ccs_current_domain - Get "struct ccs_domain_info" for current thread. -+ * -+ * Returns pointer to "struct ccs_domain_info" for current thread. -+ * -+ * If current thread does not belong to a domain (which is true for initial -+ * init_task in order to hide ccs_kernel_domain from this module), -+ * current thread enters into ccs_kernel_domain. -+ */ -+static inline struct ccs_domain_info *ccs_current_domain(void) -+{ -+ struct task_struct *task = current; -+ if (!task->ccs_domain_info) -+ task->ccs_domain_info = &ccs_kernel_domain; -+ return task->ccs_domain_info; -+} -+ -+/** -+ * ccs_task_flags - Get flags for specified thread. -+ * -+ * @task: Pointer to "struct task_struct". -+ * -+ * Returns flags for specified thread. -+ */ -+static inline u32 ccs_task_flags(struct task_struct *task) -+{ -+ return ccs_find_task_security(task)->ccs_flags; -+} -+ -+/** -+ * ccs_current_flags - Get flags for current thread. -+ * -+ * Returns flags for current thread. -+ */ -+static inline u32 ccs_current_flags(void) -+{ -+ return ccs_find_task_security(current)->ccs_flags; -+} -+ -+#endif -+ -+/** -+ * ccs_current_namespace - Get "struct ccs_policy_namespace" for current thread. -+ * -+ * Returns pointer to "struct ccs_policy_namespace" for current thread. -+ */ -+static inline struct ccs_policy_namespace *ccs_current_namespace(void) -+{ -+ return ccs_current_domain()->ns; -+} -+ -+#endif -diff --git a/security/ccsecurity/load_policy.c b/security/ccsecurity/load_policy.c -new file mode 100644 -index 0000000..fc4ae30 ---- /dev/null -+++ b/security/ccsecurity/load_policy.c -@@ -0,0 +1,352 @@ -+/* -+ * security/ccsecurity/load_policy.c -+ * -+ * Copyright (C) 2005-2012 NTT DATA CORPORATION -+ * -+ * Version: 1.8.4 2015/05/05 -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+#include -+/* -+ * Regarding 2.4 kernels, we need to define __KERNEL_SYSCALLS__ in order to use -+ * waitpid() because call_usermodehelper() does not support UMH_WAIT_PROC. -+ */ -+#define __KERNEL_SYSCALLS__ -+#include -+#else -+#include -+#include -+#endif -+#ifndef LOOKUP_POSITIVE -+#define LOOKUP_POSITIVE 0 -+#endif -+ -+/* -+ * TOMOYO specific part start. -+ */ -+ -+#include -+ -+/** -+ * ccs_setup - Set enable/disable upon boot. -+ * -+ * @str: "off" to disable, "on" to enable. -+ * -+ * Returns 0. -+ */ -+static int __init ccs_setup(char *str) -+{ -+ if (!strcmp(str, "off")) -+ ccsecurity_ops.disabled = 1; -+ else if (!strcmp(str, "on")) -+ ccsecurity_ops.disabled = 0; -+ return 0; -+} -+ -+__setup("ccsecurity=", ccs_setup); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) -+#include "lsm2ccsecurity.c" -+#endif -+ -+#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER -+ -+/* Path to the policy loader. (default = CONFIG_CCSECURITY_POLICY_LOADER) */ -+static const char *ccs_loader; -+ -+/** -+ * ccs_loader_setup - Set policy loader. -+ * -+ * @str: Program to use as a policy loader (e.g. /sbin/ccs-init ). -+ * -+ * Returns 0. -+ */ -+static int __init ccs_loader_setup(char *str) -+{ -+ ccs_loader = str; -+ return 0; -+} -+ -+__setup("CCS_loader=", ccs_loader_setup); -+ -+/** -+ * ccs_policy_loader_exists - Check whether /sbin/ccs-init exists. -+ * -+ * Returns true if /sbin/ccs-init exists, false otherwise. -+ */ -+static _Bool ccs_policy_loader_exists(void) -+{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) -+ struct path path; -+ if (!ccs_loader) -+ ccs_loader = CONFIG_CCSECURITY_POLICY_LOADER; -+ if (kern_path(ccs_loader, LOOKUP_FOLLOW | LOOKUP_POSITIVE, -+ &path) == 0) { -+ path_put(&path); -+ return 1; -+ } -+#else -+ struct nameidata nd; -+ if (!ccs_loader) -+ ccs_loader = CONFIG_CCSECURITY_POLICY_LOADER; -+ if (path_lookup(ccs_loader, LOOKUP_FOLLOW | LOOKUP_POSITIVE, -+ &nd) == 0) { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) -+ path_put(&nd.path); -+#else -+ path_release(&nd); -+#endif -+ return 1; -+ } -+#endif -+ printk(KERN_INFO "Not activating Mandatory Access Control " -+ "as %s does not exist.\n", ccs_loader); -+ return 0; -+} -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+ -+/** -+ * ccs_run_loader - Start /sbin/ccs-init. -+ * -+ * @unused: Not used. -+ * -+ * Returns PID of /sbin/ccs-init on success, negative value otherwise. -+ */ -+static int ccs_run_loader(void *unused) -+{ -+ char *argv[2]; -+ char *envp[3]; -+ printk(KERN_INFO "Calling %s to load policy. Please wait.\n", -+ ccs_loader); -+ argv[0] = (char *) ccs_loader; -+ argv[1] = NULL; -+ envp[0] = "HOME=/"; -+ envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; -+ envp[2] = NULL; -+ return exec_usermodehelper(argv[0], argv, envp); -+} -+ -+#endif -+ -+/* Path to the trigger. (default = CONFIG_CCSECURITY_ACTIVATION_TRIGGER) */ -+static const char *ccs_trigger; -+ -+/** -+ * ccs_trigger_setup - Set trigger for activation. -+ * -+ * @str: Program to use as an activation trigger (e.g. /sbin/init ). -+ * -+ * Returns 0. -+ */ -+static int __init ccs_trigger_setup(char *str) -+{ -+ ccs_trigger = str; -+ return 0; -+} -+ -+__setup("CCS_trigger=", ccs_trigger_setup); -+ -+/** -+ * ccs_load_policy - Run external policy loader to load policy. -+ * -+ * @filename: The program about to start. -+ * -+ * Returns nothing. -+ * -+ * This function checks whether @filename is /sbin/init, and if so -+ * invoke /sbin/ccs-init and wait for the termination of /sbin/ccs-init -+ * and then continues invocation of /sbin/init. -+ * /sbin/ccs-init reads policy files in /etc/ccs/ directory and -+ * writes to /proc/ccs/ interfaces. -+ */ -+static void ccs_load_policy(const char *filename) -+{ -+ static _Bool done; -+ if (ccsecurity_ops.disabled || done) -+ return; -+ if (!ccs_trigger) -+ ccs_trigger = CONFIG_CCSECURITY_ACTIVATION_TRIGGER; -+ if (strcmp(filename, ccs_trigger)) -+ return; -+ if (!ccs_policy_loader_exists()) -+ return; -+ done = 1; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) -+ { -+ char *argv[2]; -+ char *envp[3]; -+ printk(KERN_INFO "Calling %s to load policy. Please wait.\n", -+ ccs_loader); -+ argv[0] = (char *) ccs_loader; -+ argv[1] = NULL; -+ envp[0] = "HOME=/"; -+ envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; -+ envp[2] = NULL; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) || defined(UMH_WAIT_PROC) -+ call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); -+#else -+ call_usermodehelper(argv[0], argv, envp, 1); -+#endif -+ } -+#elif defined(TASK_DEAD) -+ { -+ /* Copied from kernel/kmod.c */ -+ struct task_struct *task = current; -+ pid_t pid = kernel_thread(ccs_run_loader, NULL, 0); -+ sigset_t tmpsig; -+ spin_lock_irq(&task->sighand->siglock); -+ tmpsig = task->blocked; -+ siginitsetinv(&task->blocked, -+ sigmask(SIGKILL) | sigmask(SIGSTOP)); -+ recalc_sigpending(); -+ spin_unlock_irq(&task->sighand->siglock); -+ if (pid >= 0) -+ waitpid(pid, NULL, __WCLONE); -+ spin_lock_irq(&task->sighand->siglock); -+ task->blocked = tmpsig; -+ recalc_sigpending(); -+ spin_unlock_irq(&task->sighand->siglock); -+ } -+#else -+ { -+ /* Copied from kernel/kmod.c */ -+ struct task_struct *task = current; -+ pid_t pid = kernel_thread(ccs_run_loader, NULL, 0); -+ sigset_t tmpsig; -+ spin_lock_irq(&task->sigmask_lock); -+ tmpsig = task->blocked; -+ siginitsetinv(&task->blocked, -+ sigmask(SIGKILL) | sigmask(SIGSTOP)); -+ recalc_sigpending(task); -+ spin_unlock_irq(&task->sigmask_lock); -+ if (pid >= 0) -+ waitpid(pid, NULL, __WCLONE); -+ spin_lock_irq(&task->sigmask_lock); -+ task->blocked = tmpsig; -+ recalc_sigpending(task); -+ spin_unlock_irq(&task->sigmask_lock); -+ } -+#endif -+ if (ccsecurity_ops.check_profile) -+ ccsecurity_ops.check_profile(); -+ else -+ panic("Failed to load policy."); -+} -+ -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) -+ -+/** -+ * __ccs_search_binary_handler - Load policy before calling search_binary_handler(). -+ * -+ * @bprm: Pointer to "struct linux_binprm". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_search_binary_handler(struct linux_binprm *bprm) -+{ -+#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER -+ ccs_load_policy(bprm->filename); -+#endif -+ /* -+ * ccs_load_policy() executes /sbin/ccs-init if bprm->filename is -+ * /sbin/init. /sbin/ccs-init executes /etc/ccs/ccs-load-module to -+ * load loadable kernel module. The loadable kernel module modifies -+ * "struct ccsecurity_ops". Thus, we need to transfer control to -+ * __ccs_search_binary_handler() in security/ccsecurity/permission.c -+ * if "struct ccsecurity_ops" was modified. -+ */ -+ if (ccsecurity_ops.search_binary_handler -+ != __ccs_search_binary_handler) -+ return ccsecurity_ops.search_binary_handler(bprm); -+ return search_binary_handler(bprm); -+} -+ -+#else -+ -+/** -+ * __ccs_search_binary_handler - Load policy before calling search_binary_handler(). -+ * -+ * @bprm: Pointer to "struct linux_binprm". -+ * @regs: Pointer to "struct pt_regs". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_search_binary_handler(struct linux_binprm *bprm, -+ struct pt_regs *regs) -+{ -+#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER -+ ccs_load_policy(bprm->filename); -+#endif -+ /* -+ * ccs_load_policy() executes /sbin/ccs-init if bprm->filename is -+ * /sbin/init. /sbin/ccs-init executes /etc/ccs/ccs-load-module to -+ * load loadable kernel module. The loadable kernel module modifies -+ * "struct ccsecurity_ops". Thus, we need to transfer control to -+ * __ccs_search_binary_handler() in security/ccsecurity/permission.c -+ * if "struct ccsecurity_ops" was modified. -+ */ -+ if (ccsecurity_ops.search_binary_handler -+ != __ccs_search_binary_handler) -+ return ccsecurity_ops.search_binary_handler(bprm, regs); -+ return search_binary_handler(bprm, regs); -+} -+ -+#endif -+ -+/* -+ * Some exports for loadable kernel module part. -+ * -+ * Although scripts/checkpatch.pl complains about use of "extern" in C file, -+ * we don't put these into security/ccsecurity/internal.h because we want to -+ * split built-in part and loadable kernel module part. -+ */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) && LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) -+extern spinlock_t vfsmount_lock; -+#endif -+ -+/* For exporting variables and functions. */ -+const struct ccsecurity_exports ccsecurity_exports = { -+#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER -+ .load_policy = ccs_load_policy, -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) && defined(CONFIG_SECURITY) -+ .add_hooks = ccs_add_hooks, -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) -+ .d_absolute_path = d_absolute_path, -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) -+ .__d_path = __d_path, -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) -+ .vfsmount_lock = &vfsmount_lock, -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) -+ .find_task_by_vpid = find_task_by_vpid, -+ .find_task_by_pid_ns = find_task_by_pid_ns, -+#endif -+}; -+#ifdef CONFIG_CCSECURITY_LKM -+/* Only ccsecurity module need to access this struct. */ -+EXPORT_SYMBOL_GPL(ccsecurity_exports); -+#endif -+ -+/* Members are updated by loadable kernel module. */ -+struct ccsecurity_operations ccsecurity_ops = { -+ .search_binary_handler = __ccs_search_binary_handler, -+#ifdef CONFIG_CCSECURITY_DISABLE_BY_DEFAULT -+ .disabled = 1, -+#endif -+}; -+/* -+ * Non-GPL modules might need to access this struct via inlined functions -+ * embedded into include/linux/security.h and include/net/ip.h -+ */ -+EXPORT_SYMBOL(ccsecurity_ops); -diff --git a/security/ccsecurity/lsm2ccsecurity.c b/security/ccsecurity/lsm2ccsecurity.c -new file mode 100644 -index 0000000..b81d8ed ---- /dev/null -+++ b/security/ccsecurity/lsm2ccsecurity.c -@@ -0,0 +1,192 @@ -+/* -+ * security/ccsecurity/lsm2ccsecurity.c -+ * -+ * Copyright (C) 2005-2012 NTT DATA CORPORATION -+ * -+ * Version: 1.8.4 2015/07/11 -+ */ -+ -+#include -+#include -+#include -+ -+int ccs_settime(const struct timespec *ts, const struct timezone *tz) -+{ -+ return ccs_capable(CCS_SYS_SETTIME) ? 0 : -EPERM; -+} -+ -+int ccs_sb_mount(const char *dev_name, struct path *path, const char *type, -+ unsigned long flags, void *data) -+{ -+ return ccs_mount_permission(dev_name, path, type, flags, data); -+} -+ -+int ccs_sb_umount(struct vfsmount *mnt, int flags) -+{ -+ return ccs_umount_permission(mnt, flags); -+} -+ -+int ccs_sb_pivotroot(struct path *old_path, struct path *new_path) -+{ -+ return ccs_pivot_root_permission(old_path, new_path); -+} -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) -+int ccs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) -+{ -+ return ccs_getattr_permission(mnt, dentry); -+} -+#else -+int ccs_inode_getattr(const struct path *path) -+{ -+ return ccs_getattr_permission(path->mnt, path->dentry); -+} -+#endif -+ -+int ccs_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ return ccs_ioctl_permission(file, cmd, arg); -+} -+ -+int ccs_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg) -+{ -+ return ccs_fcntl_permission(file, cmd, arg); -+} -+ -+int ccs_file_open(struct file *file, const struct cred *cred) -+{ -+ return ccs_open_permission(file); -+} -+ -+int ccs_socket_create(int family, int type, int protocol, int kern) -+{ -+ return ccs_socket_create_permission(family, type, protocol); -+} -+ -+int ccs_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) -+{ -+ return ccs_socket_bind_permission(sock, address, addrlen); -+} -+ -+int ccs_socket_connect(struct socket *sock, struct sockaddr *address, -+ int addrlen) -+{ -+ return ccs_socket_connect_permission(sock, address, addrlen); -+} -+ -+int ccs_socket_listen(struct socket *sock, int backlog) -+{ -+ return ccs_socket_listen_permission(sock); -+} -+ -+int ccs_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) -+{ -+ return ccs_socket_sendmsg_permission(sock, msg, size); -+} -+ -+int ccs_path_unlink(struct path *dir, struct dentry *dentry) -+{ -+ return ccs_unlink_permission(dentry, dir->mnt); -+} -+ -+int ccs_path_mkdir(struct path *dir, struct dentry *dentry, umode_t mode) -+{ -+ return ccs_mkdir_permission(dentry, dir->mnt, mode); -+} -+ -+int ccs_path_rmdir(struct path *dir, struct dentry *dentry) -+{ -+ return ccs_rmdir_permission(dentry, dir->mnt); -+} -+ -+int ccs_path_mknod(struct path *dir, struct dentry *dentry, umode_t mode, -+ unsigned int dev) -+{ -+ return ccs_mknod_permission(dentry, dir->mnt, mode, dev); -+} -+ -+int ccs_path_truncate(struct path *path) -+{ -+ return ccs_truncate_permission(path->dentry, path->mnt); -+} -+ -+int ccs_path_symlink(struct path *dir, struct dentry *dentry, -+ const char *old_name) -+{ -+ return ccs_symlink_permission(dentry, dir->mnt, old_name); -+} -+ -+int ccs_path_link(struct dentry *old_dentry, struct path *new_dir, -+ struct dentry *new_dentry) -+{ -+ return ccs_link_permission(old_dentry, new_dentry, new_dir->mnt); -+} -+ -+int ccs_path_rename(struct path *old_dir, struct dentry *old_dentry, -+ struct path *new_dir, struct dentry *new_dentry) -+{ -+ return ccs_rename_permission(old_dentry, new_dentry, new_dir->mnt); -+} -+ -+int ccs_path_chmod(struct path *path, umode_t mode) -+{ -+ return ccs_chmod_permission(path->dentry, path->mnt, mode); -+} -+ -+int ccs_path_chown(struct path *path, kuid_t uid, kgid_t gid) -+{ -+ return ccs_chown_permission(path->dentry, path->mnt, uid, gid); -+} -+ -+int ccs_path_chroot(struct path *path) -+{ -+ return ccs_chroot_permission(path); -+} -+ -+#if !defined(CONFIG_SECURITY_PATH) -+EXPORT_SYMBOL(ccs_path_mkdir); -+EXPORT_SYMBOL(ccs_path_mknod); -+EXPORT_SYMBOL(ccs_path_unlink); -+EXPORT_SYMBOL(ccs_path_rename); -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) && defined(CONFIG_SECURITY) -+ -+#include -+ -+static struct security_hook_list ccsecurity_hooks[] = { -+ LSM_HOOK_INIT(settime, ccs_settime), -+ LSM_HOOK_INIT(sb_mount, ccs_sb_mount), -+ LSM_HOOK_INIT(sb_umount, ccs_sb_umount), -+ LSM_HOOK_INIT(sb_pivotroot, ccs_sb_pivotroot), -+ LSM_HOOK_INIT(inode_getattr, ccs_inode_getattr), -+ LSM_HOOK_INIT(file_ioctl, ccs_file_ioctl), -+ LSM_HOOK_INIT(file_fcntl, ccs_file_fcntl), -+ LSM_HOOK_INIT(file_open, ccs_file_open), -+#if defined(CONFIG_SECURITY_NETWORK) -+ LSM_HOOK_INIT(socket_create, ccs_socket_create), -+ LSM_HOOK_INIT(socket_bind, ccs_socket_bind), -+ LSM_HOOK_INIT(socket_connect, ccs_socket_connect), -+ LSM_HOOK_INIT(socket_listen, ccs_socket_listen), -+ LSM_HOOK_INIT(socket_sendmsg, ccs_socket_sendmsg), -+#endif -+#if defined(CONFIG_SECURITY_PATH) -+ LSM_HOOK_INIT(path_unlink, ccs_path_unlink), -+ LSM_HOOK_INIT(path_mkdir, ccs_path_mkdir), -+ LSM_HOOK_INIT(path_rmdir, ccs_path_rmdir), -+ LSM_HOOK_INIT(path_mknod, ccs_path_mknod), -+ LSM_HOOK_INIT(path_truncate, ccs_path_truncate), -+ LSM_HOOK_INIT(path_symlink, ccs_path_symlink), -+ LSM_HOOK_INIT(path_link, ccs_path_link), -+ LSM_HOOK_INIT(path_rename, ccs_path_rename), -+ LSM_HOOK_INIT(path_chmod, ccs_path_chmod), -+ LSM_HOOK_INIT(path_chown, ccs_path_chown), -+ LSM_HOOK_INIT(path_chroot, ccs_path_chroot), -+#endif -+}; -+ -+static void ccs_add_hooks(void) -+{ -+ security_add_hooks(ccsecurity_hooks, ARRAY_SIZE(ccsecurity_hooks)); -+} -+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) && defined(CONFIG_SECURITY) */ -diff --git a/security/ccsecurity/memory.c b/security/ccsecurity/memory.c -new file mode 100644 -index 0000000..6514a26 ---- /dev/null -+++ b/security/ccsecurity/memory.c -@@ -0,0 +1,356 @@ -+/* -+ * security/ccsecurity/memory.c -+ * -+ * Copyright (C) 2005-2012 NTT DATA CORPORATION -+ * -+ * Version: 1.8.4 2015/05/05 -+ */ -+ -+#include "internal.h" -+ -+/***** SECTION1: Constants definition *****/ -+ -+/***** SECTION2: Structure definition *****/ -+ -+/***** SECTION3: Prototype definition section *****/ -+ -+bool ccs_memory_ok(const void *ptr, const unsigned int size); -+const struct ccs_path_info *ccs_get_name(const char *name); -+#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -+struct ccs_security *ccs_find_task_security(const struct task_struct *task); -+#endif -+void *ccs_commit_ok(void *data, const unsigned int size); -+void __init ccs_mm_init(void); -+void ccs_warn_oom(const char *function); -+ -+#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -+static int __ccs_alloc_task_security(const struct task_struct *task); -+static void __ccs_free_task_security(const struct task_struct *task); -+static void ccs_add_task_security(struct ccs_security *ptr, -+ struct list_head *list); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) -+static void ccs_rcu_free(struct rcu_head *rcu); -+#else -+static void ccs_rcu_free(void *arg); -+#endif -+#endif -+ -+/***** SECTION4: Standalone functions section *****/ -+ -+/***** SECTION5: Variables definition section *****/ -+ -+/* Memoy currently used by policy/audit log/query. */ -+unsigned int ccs_memory_used[CCS_MAX_MEMORY_STAT]; -+ -+/* Memory quota for "policy"/"audit log"/"query". */ -+unsigned int ccs_memory_quota[CCS_MAX_MEMORY_STAT]; -+ -+/* The list for "struct ccs_name". */ -+struct list_head ccs_name_list[CCS_MAX_HASH]; -+ -+#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -+ -+/* Dummy security context for avoiding NULL pointer dereference. */ -+static struct ccs_security ccs_oom_security = { -+ .ccs_domain_info = &ccs_kernel_domain -+}; -+ -+/* Dummy security context for avoiding NULL pointer dereference. */ -+static struct ccs_security ccs_default_security = { -+ .ccs_domain_info = &ccs_kernel_domain -+}; -+ -+/* List of "struct ccs_security". */ -+struct list_head ccs_task_security_list[CCS_MAX_TASK_SECURITY_HASH]; -+/* Lock for protecting ccs_task_security_list[]. */ -+static DEFINE_SPINLOCK(ccs_task_security_list_lock); -+ -+#endif -+ -+/***** SECTION6: Dependent functions section *****/ -+ -+/** -+ * ccs_warn_oom - Print out of memory warning message. -+ * -+ * @function: Function's name. -+ * -+ * Returns nothing. -+ */ -+void ccs_warn_oom(const char *function) -+{ -+ /* Reduce error messages. */ -+ static pid_t ccs_last_pid; -+ const pid_t pid = current->pid; -+ if (ccs_last_pid != pid) { -+ printk(KERN_WARNING "ERROR: Out of memory at %s.\n", -+ function); -+ ccs_last_pid = pid; -+ } -+ if (!ccs_policy_loaded) -+ panic("MAC Initialization failed.\n"); -+} -+ -+/** -+ * ccs_memory_ok - Check memory quota. -+ * -+ * @ptr: Pointer to allocated memory. Maybe NULL. -+ * @size: Size in byte. Not used if @ptr is NULL. -+ * -+ * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. -+ * -+ * Caller holds ccs_policy_lock mutex. -+ */ -+bool ccs_memory_ok(const void *ptr, const unsigned int size) -+{ -+ if (ptr) { -+ const size_t s = ccs_round2(size); -+ ccs_memory_used[CCS_MEMORY_POLICY] += s; -+ if (!ccs_memory_quota[CCS_MEMORY_POLICY] || -+ ccs_memory_used[CCS_MEMORY_POLICY] <= -+ ccs_memory_quota[CCS_MEMORY_POLICY]) -+ return true; -+ ccs_memory_used[CCS_MEMORY_POLICY] -= s; -+ } -+ ccs_warn_oom(__func__); -+ return false; -+} -+ -+/** -+ * ccs_commit_ok - Allocate memory and check memory quota. -+ * -+ * @data: Data to copy from. -+ * @size: Size in byte. -+ * -+ * Returns pointer to allocated memory on success, NULL otherwise. -+ * @data is zero-cleared on success. -+ * -+ * Caller holds ccs_policy_lock mutex. -+ */ -+void *ccs_commit_ok(void *data, const unsigned int size) -+{ -+ void *ptr = kmalloc(size, CCS_GFP_FLAGS); -+ if (ccs_memory_ok(ptr, size)) { -+ memmove(ptr, data, size); -+ memset(data, 0, size); -+ return ptr; -+ } -+ kfree(ptr); -+ return NULL; -+} -+ -+/** -+ * ccs_get_name - Allocate memory for string data. -+ * -+ * @name: The string to store into the permernent memory. -+ * -+ * Returns pointer to "struct ccs_path_info" on success, NULL otherwise. -+ */ -+const struct ccs_path_info *ccs_get_name(const char *name) -+{ -+ struct ccs_name *ptr; -+ unsigned int hash; -+ int len; -+ int allocated_len; -+ struct list_head *head; -+ -+ if (!name) -+ return NULL; -+ len = strlen(name) + 1; -+ hash = full_name_hash((const unsigned char *) name, len - 1); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) || defined(RHEL_MAJOR) -+ head = &ccs_name_list[hash_long(hash, CCS_HASH_BITS)]; -+#else -+ head = &ccs_name_list[hash % CCS_MAX_HASH]; -+#endif -+ if (mutex_lock_interruptible(&ccs_policy_lock)) -+ return NULL; -+ list_for_each_entry(ptr, head, head.list) { -+ if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) || -+ atomic_read(&ptr->head.users) == CCS_GC_IN_PROGRESS) -+ continue; -+ atomic_inc(&ptr->head.users); -+ goto out; -+ } -+ allocated_len = sizeof(*ptr) + len; -+ ptr = kzalloc(allocated_len, CCS_GFP_FLAGS); -+ if (ccs_memory_ok(ptr, allocated_len)) { -+ ptr->entry.name = ((char *) ptr) + sizeof(*ptr); -+ memmove((char *) ptr->entry.name, name, len); -+ atomic_set(&ptr->head.users, 1); -+ ccs_fill_path_info(&ptr->entry); -+ ptr->size = allocated_len; -+ list_add_tail(&ptr->head.list, head); -+ } else { -+ kfree(ptr); -+ ptr = NULL; -+ } -+out: -+ mutex_unlock(&ccs_policy_lock); -+ return ptr ? &ptr->entry : NULL; -+} -+ -+#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -+ -+/** -+ * ccs_add_task_security - Add "struct ccs_security" to list. -+ * -+ * @ptr: Pointer to "struct ccs_security". -+ * @list: Pointer to "struct list_head". -+ * -+ * Returns nothing. -+ */ -+static void ccs_add_task_security(struct ccs_security *ptr, -+ struct list_head *list) -+{ -+ unsigned long flags; -+ spin_lock_irqsave(&ccs_task_security_list_lock, flags); -+ list_add_rcu(&ptr->list, list); -+ spin_unlock_irqrestore(&ccs_task_security_list_lock, flags); -+} -+ -+/** -+ * __ccs_alloc_task_security - Allocate memory for new tasks. -+ * -+ * @task: Pointer to "struct task_struct". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_alloc_task_security(const struct task_struct *task) -+{ -+ struct ccs_security *old_security = ccs_current_security(); -+ struct ccs_security *new_security = kzalloc(sizeof(*new_security), -+ GFP_KERNEL); -+ struct list_head *list = &ccs_task_security_list -+ [hash_ptr((void *) task, CCS_TASK_SECURITY_HASH_BITS)]; -+ if (!new_security) -+ return -ENOMEM; -+ new_security->task = task; -+ new_security->ccs_domain_info = old_security->ccs_domain_info; -+ new_security->ccs_flags = old_security->ccs_flags; -+ ccs_add_task_security(new_security, list); -+ return 0; -+} -+ -+/** -+ * ccs_find_task_security - Find "struct ccs_security" for given task. -+ * -+ * @task: Pointer to "struct task_struct". -+ * -+ * Returns pointer to "struct ccs_security" on success, &ccs_oom_security on -+ * out of memory, &ccs_default_security otherwise. -+ * -+ * If @task is current thread and "struct ccs_security" for current thread was -+ * not found, I try to allocate it. But if allocation failed, current thread -+ * will be killed by SIGKILL. Note that if current->pid == 1, sending SIGKILL -+ * won't work. -+ */ -+struct ccs_security *ccs_find_task_security(const struct task_struct *task) -+{ -+ struct ccs_security *ptr; -+ struct list_head *list = &ccs_task_security_list -+ [hash_ptr((void *) task, CCS_TASK_SECURITY_HASH_BITS)]; -+ /* Make sure INIT_LIST_HEAD() in ccs_mm_init() takes effect. */ -+ while (!list->next); -+ rcu_read_lock(); -+ list_for_each_entry_rcu(ptr, list, list) { -+ if (ptr->task != task) -+ continue; -+ rcu_read_unlock(); -+ return ptr; -+ } -+ rcu_read_unlock(); -+ if (task != current) -+ return &ccs_default_security; -+ /* Use GFP_ATOMIC because caller may have called rcu_read_lock(). */ -+ ptr = kzalloc(sizeof(*ptr), GFP_ATOMIC); -+ if (!ptr) { -+ printk(KERN_WARNING "Unable to allocate memory for pid=%u\n", -+ task->pid); -+ send_sig(SIGKILL, current, 0); -+ return &ccs_oom_security; -+ } -+ *ptr = ccs_default_security; -+ ptr->task = task; -+ ccs_add_task_security(ptr, list); -+ return ptr; -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) -+ -+/** -+ * ccs_rcu_free - RCU callback for releasing "struct ccs_security". -+ * -+ * @rcu: Pointer to "struct rcu_head". -+ * -+ * Returns nothing. -+ */ -+static void ccs_rcu_free(struct rcu_head *rcu) -+{ -+ struct ccs_security *ptr = container_of(rcu, typeof(*ptr), rcu); -+ kfree(ptr); -+} -+ -+#else -+ -+/** -+ * ccs_rcu_free - RCU callback for releasing "struct ccs_security". -+ * -+ * @arg: Pointer to "void". -+ * -+ * Returns nothing. -+ */ -+static void ccs_rcu_free(void *arg) -+{ -+ struct ccs_security *ptr = arg; -+ kfree(ptr); -+} -+ -+#endif -+ -+/** -+ * __ccs_free_task_security - Release memory associated with "struct task_struct". -+ * -+ * @task: Pointer to "struct task_struct". -+ * -+ * Returns nothing. -+ */ -+static void __ccs_free_task_security(const struct task_struct *task) -+{ -+ unsigned long flags; -+ struct ccs_security *ptr = ccs_find_task_security(task); -+ if (ptr == &ccs_default_security || ptr == &ccs_oom_security) -+ return; -+ spin_lock_irqsave(&ccs_task_security_list_lock, flags); -+ list_del_rcu(&ptr->list); -+ spin_unlock_irqrestore(&ccs_task_security_list_lock, flags); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) -+ call_rcu(&ptr->rcu, ccs_rcu_free); -+#else -+ call_rcu(&ptr->rcu, ccs_rcu_free, ptr); -+#endif -+} -+ -+#endif -+ -+/** -+ * ccs_mm_init - Initialize mm related code. -+ * -+ * Returns nothing. -+ */ -+void __init ccs_mm_init(void) -+{ -+ int idx; -+ for (idx = 0; idx < CCS_MAX_HASH; idx++) -+ INIT_LIST_HEAD(&ccs_name_list[idx]); -+#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -+ for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) -+ INIT_LIST_HEAD(&ccs_task_security_list[idx]); -+#endif -+ smp_wmb(); /* Avoid out of order execution. */ -+#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -+ ccsecurity_ops.alloc_task_security = __ccs_alloc_task_security; -+ ccsecurity_ops.free_task_security = __ccs_free_task_security; -+#endif -+ ccs_kernel_domain.domainname = ccs_get_name(""); -+ list_add_tail_rcu(&ccs_kernel_domain.list, &ccs_domain_list); -+} -diff --git a/security/ccsecurity/permission.c b/security/ccsecurity/permission.c -new file mode 100644 -index 0000000..d73c237 ---- /dev/null -+++ b/security/ccsecurity/permission.c -@@ -0,0 +1,5025 @@ -+/* -+ * security/ccsecurity/permission.c -+ * -+ * Copyright (C) 2005-2012 NTT DATA CORPORATION -+ * -+ * Version: 1.8.4 2015/05/05 -+ */ -+ -+#include "internal.h" -+ -+/***** SECTION1: Constants definition *****/ -+ -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) -+ -+/* -+ * may_open() receives open flags modified by open_to_namei_flags() until -+ * 2.6.32. We stop here in case some distributions backported ACC_MODE changes, -+ * for we can't determine whether may_open() receives open flags modified by -+ * open_to_namei_flags() or not. -+ */ -+#ifdef ACC_MODE -+#error ACC_MODE already defined. -+#endif -+#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) -+ -+#if defined(RHEL_MAJOR) && RHEL_MAJOR == 6 -+/* RHEL6 passes unmodified flags since 2.6.32-71.14.1.el6 . */ -+#undef ACC_MODE -+#define ACC_MODE(x) ("\004\002\006"[(x)&O_ACCMODE]) -+#endif -+ -+#endif -+ -+/* String table for special mount operations. */ -+static const char * const ccs_mounts[CCS_MAX_SPECIAL_MOUNT] = { -+ [CCS_MOUNT_BIND] = "--bind", -+ [CCS_MOUNT_MOVE] = "--move", -+ [CCS_MOUNT_REMOUNT] = "--remount", -+ [CCS_MOUNT_MAKE_UNBINDABLE] = "--make-unbindable", -+ [CCS_MOUNT_MAKE_PRIVATE] = "--make-private", -+ [CCS_MOUNT_MAKE_SLAVE] = "--make-slave", -+ [CCS_MOUNT_MAKE_SHARED] = "--make-shared", -+}; -+ -+/* Mapping table from "enum ccs_path_acl_index" to "enum ccs_mac_index". */ -+static const u8 ccs_p2mac[CCS_MAX_PATH_OPERATION] = { -+ [CCS_TYPE_EXECUTE] = CCS_MAC_FILE_EXECUTE, -+ [CCS_TYPE_READ] = CCS_MAC_FILE_OPEN, -+ [CCS_TYPE_WRITE] = CCS_MAC_FILE_OPEN, -+ [CCS_TYPE_APPEND] = CCS_MAC_FILE_OPEN, -+ [CCS_TYPE_UNLINK] = CCS_MAC_FILE_UNLINK, -+#ifdef CONFIG_CCSECURITY_FILE_GETATTR -+ [CCS_TYPE_GETATTR] = CCS_MAC_FILE_GETATTR, -+#endif -+ [CCS_TYPE_RMDIR] = CCS_MAC_FILE_RMDIR, -+ [CCS_TYPE_TRUNCATE] = CCS_MAC_FILE_TRUNCATE, -+ [CCS_TYPE_SYMLINK] = CCS_MAC_FILE_SYMLINK, -+ [CCS_TYPE_CHROOT] = CCS_MAC_FILE_CHROOT, -+ [CCS_TYPE_UMOUNT] = CCS_MAC_FILE_UMOUNT, -+}; -+ -+/* Mapping table from "enum ccs_mkdev_acl_index" to "enum ccs_mac_index". */ -+const u8 ccs_pnnn2mac[CCS_MAX_MKDEV_OPERATION] = { -+ [CCS_TYPE_MKBLOCK] = CCS_MAC_FILE_MKBLOCK, -+ [CCS_TYPE_MKCHAR] = CCS_MAC_FILE_MKCHAR, -+}; -+ -+/* Mapping table from "enum ccs_path2_acl_index" to "enum ccs_mac_index". */ -+const u8 ccs_pp2mac[CCS_MAX_PATH2_OPERATION] = { -+ [CCS_TYPE_LINK] = CCS_MAC_FILE_LINK, -+ [CCS_TYPE_RENAME] = CCS_MAC_FILE_RENAME, -+ [CCS_TYPE_PIVOT_ROOT] = CCS_MAC_FILE_PIVOT_ROOT, -+}; -+ -+/* -+ * Mapping table from "enum ccs_path_number_acl_index" to "enum ccs_mac_index". -+ */ -+const u8 ccs_pn2mac[CCS_MAX_PATH_NUMBER_OPERATION] = { -+ [CCS_TYPE_CREATE] = CCS_MAC_FILE_CREATE, -+ [CCS_TYPE_MKDIR] = CCS_MAC_FILE_MKDIR, -+ [CCS_TYPE_MKFIFO] = CCS_MAC_FILE_MKFIFO, -+ [CCS_TYPE_MKSOCK] = CCS_MAC_FILE_MKSOCK, -+ [CCS_TYPE_IOCTL] = CCS_MAC_FILE_IOCTL, -+ [CCS_TYPE_CHMOD] = CCS_MAC_FILE_CHMOD, -+ [CCS_TYPE_CHOWN] = CCS_MAC_FILE_CHOWN, -+ [CCS_TYPE_CHGRP] = CCS_MAC_FILE_CHGRP, -+}; -+ -+#ifdef CONFIG_CCSECURITY_NETWORK -+ -+/* -+ * Mapping table from "enum ccs_network_acl_index" to "enum ccs_mac_index" for -+ * inet domain socket. -+ */ -+static const u8 ccs_inet2mac[CCS_SOCK_MAX][CCS_MAX_NETWORK_OPERATION] = { -+ [SOCK_STREAM] = { -+ [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_INET_STREAM_BIND, -+ [CCS_NETWORK_LISTEN] = CCS_MAC_NETWORK_INET_STREAM_LISTEN, -+ [CCS_NETWORK_CONNECT] = CCS_MAC_NETWORK_INET_STREAM_CONNECT, -+ [CCS_NETWORK_ACCEPT] = CCS_MAC_NETWORK_INET_STREAM_ACCEPT, -+ }, -+ [SOCK_DGRAM] = { -+ [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_INET_DGRAM_BIND, -+ [CCS_NETWORK_SEND] = CCS_MAC_NETWORK_INET_DGRAM_SEND, -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ [CCS_NETWORK_RECV] = CCS_MAC_NETWORK_INET_DGRAM_RECV, -+#endif -+ }, -+ [SOCK_RAW] = { -+ [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_INET_RAW_BIND, -+ [CCS_NETWORK_SEND] = CCS_MAC_NETWORK_INET_RAW_SEND, -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ [CCS_NETWORK_RECV] = CCS_MAC_NETWORK_INET_RAW_RECV, -+#endif -+ }, -+}; -+ -+/* -+ * Mapping table from "enum ccs_network_acl_index" to "enum ccs_mac_index" for -+ * unix domain socket. -+ */ -+static const u8 ccs_unix2mac[CCS_SOCK_MAX][CCS_MAX_NETWORK_OPERATION] = { -+ [SOCK_STREAM] = { -+ [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_UNIX_STREAM_BIND, -+ [CCS_NETWORK_LISTEN] = CCS_MAC_NETWORK_UNIX_STREAM_LISTEN, -+ [CCS_NETWORK_CONNECT] = CCS_MAC_NETWORK_UNIX_STREAM_CONNECT, -+ [CCS_NETWORK_ACCEPT] = CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT, -+ }, -+ [SOCK_DGRAM] = { -+ [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_UNIX_DGRAM_BIND, -+ [CCS_NETWORK_SEND] = CCS_MAC_NETWORK_UNIX_DGRAM_SEND, -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ [CCS_NETWORK_RECV] = CCS_MAC_NETWORK_UNIX_DGRAM_RECV, -+#endif -+ }, -+ [SOCK_SEQPACKET] = { -+ [CCS_NETWORK_BIND] = CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND, -+ [CCS_NETWORK_LISTEN] = CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN, -+ [CCS_NETWORK_CONNECT] = CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT, -+ [CCS_NETWORK_ACCEPT] = CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT, -+ }, -+}; -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ -+/* -+ * Mapping table from "enum ccs_capability_acl_index" to "enum ccs_mac_index". -+ */ -+const u8 ccs_c2mac[CCS_MAX_CAPABILITY_INDEX] = { -+ [CCS_USE_ROUTE_SOCKET] = CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET, -+ [CCS_USE_PACKET_SOCKET] = CCS_MAC_CAPABILITY_USE_PACKET_SOCKET, -+ [CCS_SYS_REBOOT] = CCS_MAC_CAPABILITY_SYS_REBOOT, -+ [CCS_SYS_VHANGUP] = CCS_MAC_CAPABILITY_SYS_VHANGUP, -+ [CCS_SYS_SETTIME] = CCS_MAC_CAPABILITY_SYS_SETTIME, -+ [CCS_SYS_NICE] = CCS_MAC_CAPABILITY_SYS_NICE, -+ [CCS_SYS_SETHOSTNAME] = CCS_MAC_CAPABILITY_SYS_SETHOSTNAME, -+ [CCS_USE_KERNEL_MODULE] = CCS_MAC_CAPABILITY_USE_KERNEL_MODULE, -+ [CCS_SYS_KEXEC_LOAD] = CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD, -+ [CCS_SYS_PTRACE] = CCS_MAC_CAPABILITY_SYS_PTRACE, -+}; -+ -+#endif -+ -+/***** SECTION2: Structure definition *****/ -+ -+/* Structure for holding inet domain socket's address. */ -+struct ccs_inet_addr_info { -+ u16 port; /* In network byte order. */ -+ const u32 *address; /* In network byte order. */ -+ bool is_ipv6; -+}; -+ -+/* Structure for holding unix domain socket's address. */ -+struct ccs_unix_addr_info { -+ u8 *addr; /* This may not be '\0' terminated string. */ -+ unsigned int addr_len; -+}; -+ -+/* Structure for holding socket address. */ -+struct ccs_addr_info { -+ u8 protocol; -+ u8 operation; -+ struct ccs_inet_addr_info inet; -+ struct ccs_unix_addr_info unix0; -+}; -+ -+/***** SECTION3: Prototype definition section *****/ -+ -+bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos, -+ struct ccs_page_dump *dump); -+void ccs_get_attributes(struct ccs_obj_info *obj); -+ -+static bool ccs_alphabet_char(const char c); -+static bool ccs_argv(const unsigned int index, const char *arg_ptr, -+ const int argc, const struct ccs_argv *argv, u8 *checked); -+static bool ccs_byte_range(const char *str); -+static bool ccs_check_entry(struct ccs_request_info *r, -+ struct ccs_acl_info *ptr); -+static bool ccs_check_mkdev_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr); -+static bool ccs_check_mount_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr); -+static bool ccs_check_path2_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr); -+static bool ccs_check_path_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr); -+static bool ccs_check_path_number_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr); -+static bool ccs_compare_number_union(const unsigned long value, -+ const struct ccs_number_union *ptr); -+static bool ccs_condition(struct ccs_request_info *r, -+ const struct ccs_condition *cond); -+static bool ccs_decimal(const char c); -+static bool ccs_envp(const char *env_name, const char *env_value, -+ const int envc, const struct ccs_envp *envp, u8 *checked); -+static bool ccs_file_matches_pattern(const char *filename, -+ const char *filename_end, -+ const char *pattern, -+ const char *pattern_end); -+static bool ccs_file_matches_pattern2(const char *filename, -+ const char *filename_end, -+ const char *pattern, -+ const char *pattern_end); -+static bool ccs_get_realpath(struct ccs_path_info *buf, struct path *path); -+static bool ccs_hexadecimal(const char c); -+static bool ccs_number_matches_group(const unsigned long min, -+ const unsigned long max, -+ const struct ccs_group *group); -+static bool ccs_path_matches_pattern(const struct ccs_path_info *filename, -+ const struct ccs_path_info *pattern); -+static bool ccs_path_matches_pattern2(const char *f, const char *p); -+static bool ccs_scan_bprm(struct ccs_execve *ee, const u16 argc, -+ const struct ccs_argv *argv, const u16 envc, -+ const struct ccs_envp *envp); -+static bool ccs_scan_exec_realpath(struct file *file, -+ const struct ccs_name_union *ptr, -+ const bool match); -+static bool ccs_scan_transition(const struct list_head *list, -+ const struct ccs_path_info *domainname, -+ const struct ccs_path_info *program, -+ const char *last_name, -+ const enum ccs_transition_type type); -+static const char *ccs_last_word(const char *name); -+static const struct ccs_path_info *ccs_compare_name_union -+(const struct ccs_path_info *name, const struct ccs_name_union *ptr); -+static const struct ccs_path_info *ccs_path_matches_group -+(const struct ccs_path_info *pathname, const struct ccs_group *group); -+static enum ccs_transition_type ccs_transition_type -+(const struct ccs_policy_namespace *ns, const struct ccs_path_info *domainname, -+ const struct ccs_path_info *program); -+static int __ccs_chmod_permission(struct dentry *dentry, -+ struct vfsmount *vfsmnt, mode_t mode); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) -+static int __ccs_chown_permission(struct dentry *dentry, -+ struct vfsmount *vfsmnt, kuid_t user, -+ kgid_t group); -+#else -+static int __ccs_chown_permission(struct dentry *dentry, -+ struct vfsmount *vfsmnt, uid_t user, -+ gid_t group); -+#endif -+static int __ccs_chroot_permission(struct path *path); -+static int __ccs_fcntl_permission(struct file *file, unsigned int cmd, -+ unsigned long arg); -+static int __ccs_ioctl_permission(struct file *filp, unsigned int cmd, -+ unsigned long arg); -+static int __ccs_link_permission(struct dentry *old_dentry, -+ struct dentry *new_dentry, -+ struct vfsmount *mnt); -+static int __ccs_mkdir_permission(struct dentry *dentry, struct vfsmount *mnt, -+ unsigned int mode); -+static int __ccs_mknod_permission(struct dentry *dentry, struct vfsmount *mnt, -+ const unsigned int mode, unsigned int dev); -+static int __ccs_mount_permission(const char *dev_name, struct path *path, -+ const char *type, unsigned long flags, -+ void *data_page); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) -+static int __ccs_open_exec_permission(struct dentry *dentry, -+ struct vfsmount *mnt); -+#endif -+static int __ccs_open_permission(struct dentry *dentry, struct vfsmount *mnt, -+ const int flag); -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) || (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL)) -+static int __ccs_parse_table(int __user *name, int nlen, void __user *oldval, -+ void __user *newval, struct ctl_table *table); -+#endif -+static int __ccs_pivot_root_permission(struct path *old_path, -+ struct path *new_path); -+static int __ccs_rename_permission(struct dentry *old_dentry, -+ struct dentry *new_dentry, -+ struct vfsmount *mnt); -+static int __ccs_rmdir_permission(struct dentry *dentry, struct vfsmount *mnt); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) -+static int __ccs_search_binary_handler(struct linux_binprm *bprm); -+#else -+static int __ccs_search_binary_handler(struct linux_binprm *bprm, -+ struct pt_regs *regs); -+#endif -+static int __ccs_symlink_permission(struct dentry *dentry, -+ struct vfsmount *mnt, const char *from); -+static int __ccs_truncate_permission(struct dentry *dentry, -+ struct vfsmount *mnt); -+static int __ccs_umount_permission(struct vfsmount *mnt, int flags); -+static int __ccs_unlink_permission(struct dentry *dentry, -+ struct vfsmount *mnt); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) -+static int __ccs_uselib_permission(struct dentry *dentry, -+ struct vfsmount *mnt); -+#endif -+static int ccs_execute_permission(struct ccs_request_info *r, -+ const struct ccs_path_info *filename); -+static int ccs_find_next_domain(struct ccs_execve *ee); -+static int ccs_get_path(const char *pathname, struct path *path); -+static int ccs_kern_path(const char *pathname, int flags, struct path *path); -+static int ccs_mkdev_perm(const u8 operation, struct dentry *dentry, -+ struct vfsmount *mnt, const unsigned int mode, -+ unsigned int dev); -+static int ccs_mount_acl(struct ccs_request_info *r, const char *dev_name, -+ struct path *dir, const char *type, -+ unsigned long flags); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) -+static int ccs_new_open_permission(struct file *filp); -+#endif -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24) -+static int ccs_old_chroot_permission(struct nameidata *nd); -+static int ccs_old_mount_permission(const char *dev_name, struct nameidata *nd, -+ const char *type, unsigned long flags, -+ void *data_page); -+static int ccs_old_pivot_root_permission(struct nameidata *old_nd, -+ struct nameidata *new_nd); -+#endif -+static int ccs_path2_perm(const u8 operation, struct dentry *dentry1, -+ struct vfsmount *mnt1, struct dentry *dentry2, -+ struct vfsmount *mnt2); -+static int ccs_path_number_perm(const u8 type, struct dentry *dentry, -+ struct vfsmount *vfsmnt, unsigned long number); -+static int ccs_path_perm(const u8 operation, struct dentry *dentry, -+ struct vfsmount *mnt, const char *target); -+static int ccs_path_permission(struct ccs_request_info *r, u8 operation, -+ const struct ccs_path_info *filename); -+static int ccs_start_execve(struct linux_binprm *bprm, -+ struct ccs_execve **eep); -+static int ccs_symlink_path(const char *pathname, struct ccs_path_info *name); -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) -+static void __ccs_clear_open_mode(void); -+static void __ccs_save_open_mode(int mode); -+#endif -+static void ccs_add_slash(struct ccs_path_info *buf); -+static void ccs_finish_execve(int retval, struct ccs_execve *ee); -+ -+#ifdef CONFIG_CCSECURITY_MISC -+static bool ccs_check_env_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr); -+static int ccs_env_perm(struct ccs_request_info *r, const char *env); -+static int ccs_environ(struct ccs_execve *ee); -+#endif -+ -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+static bool __ccs_capable(const u8 operation); -+static bool ccs_check_capability_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr); -+static bool ccs_kernel_service(void); -+static int __ccs_ptrace_permission(long request, long pid); -+static int __ccs_socket_create_permission(int family, int type, int protocol); -+#endif -+ -+#ifdef CONFIG_CCSECURITY_NETWORK -+static bool ccs_address_matches_group(const bool is_ipv6, const u32 *address, -+ const struct ccs_group *group); -+static bool ccs_check_inet_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr); -+static bool ccs_check_unix_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr); -+static bool ccs_kernel_service(void); -+static int __ccs_socket_bind_permission(struct socket *sock, -+ struct sockaddr *addr, int addr_len); -+static int __ccs_socket_connect_permission(struct socket *sock, -+ struct sockaddr *addr, -+ int addr_len); -+static int __ccs_socket_listen_permission(struct socket *sock); -+static int __ccs_socket_post_accept_permission(struct socket *sock, -+ struct socket *newsock); -+static int __ccs_socket_sendmsg_permission(struct socket *sock, -+ struct msghdr *msg, int size); -+static int ccs_check_inet_address(const struct sockaddr *addr, -+ const unsigned int addr_len, const u16 port, -+ struct ccs_addr_info *address); -+static int ccs_check_unix_address(struct sockaddr *addr, -+ const unsigned int addr_len, -+ struct ccs_addr_info *address); -+static int ccs_inet_entry(const struct ccs_addr_info *address); -+static int ccs_unix_entry(const struct ccs_addr_info *address); -+static u8 ccs_sock_family(struct sock *sk); -+#endif -+ -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+static int __ccs_socket_post_recvmsg_permission(struct sock *sk, -+ struct sk_buff *skb, -+ int flags); -+#endif -+ -+#ifdef CONFIG_CCSECURITY_IPC -+static bool ccs_check_signal_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr); -+static int ccs_signal_acl(const int pid, const int sig); -+static int ccs_signal_acl0(pid_t tgid, pid_t pid, int sig); -+static int ccs_signal_acl2(const int sig, const int pid); -+#endif -+ -+#ifdef CONFIG_CCSECURITY_FILE_GETATTR -+static int __ccs_getattr_permission(struct vfsmount *mnt, -+ struct dentry *dentry); -+#endif -+ -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+static bool ccs_find_execute_handler(struct ccs_execve *ee, const u8 type); -+static int ccs_try_alt_exec(struct ccs_execve *ee); -+static void ccs_unescape(unsigned char *dest); -+#endif -+ -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+static bool ccs_check_task_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr); -+#endif -+ -+/***** SECTION4: Standalone functions section *****/ -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) -+ -+/** -+ * ccs_copy_argv - Wrapper for copy_strings_kernel(). -+ * -+ * @arg: String to copy. -+ * @bprm: Pointer to "struct linux_binprm". -+ * -+ * Returns return value of copy_strings_kernel(). -+ */ -+static inline int ccs_copy_argv(const char *arg, struct linux_binprm *bprm) -+{ -+ const int ret = copy_strings_kernel(1, &arg, bprm); -+ if (ret >= 0) -+ bprm->argc++; -+ return ret; -+} -+ -+#else -+ -+/** -+ * ccs_copy_argv - Wrapper for copy_strings_kernel(). -+ * -+ * @arg: String to copy. -+ * @bprm: Pointer to "struct linux_binprm". -+ * -+ * Returns return value of copy_strings_kernel(). -+ */ -+static inline int ccs_copy_argv(char *arg, struct linux_binprm *bprm) -+{ -+ const int ret = copy_strings_kernel(1, &arg, bprm); -+ if (ret >= 0) -+ bprm->argc++; -+ return ret; -+} -+ -+#endif -+ -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) -+ -+/** -+ * get_fs_root - Get reference on root directory. -+ * -+ * @fs: Pointer to "struct fs_struct". -+ * @root: Pointer to "struct path". -+ * -+ * Returns nothing. -+ * -+ * This is for compatibility with older kernels. -+ */ -+static inline void get_fs_root(struct fs_struct *fs, struct path *root) -+{ -+ read_lock(&fs->lock); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) -+ *root = fs->root; -+ path_get(root); -+#else -+ root->dentry = dget(fs->root); -+ root->mnt = mntget(fs->rootmnt); -+#endif -+ read_unlock(&fs->lock); -+} -+ -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+ -+/** -+ * module_put - Put a reference on module. -+ * -+ * @module: Pointer to "struct module". Maybe NULL. -+ * -+ * Returns nothing. -+ * -+ * This is for compatibility with older kernels. -+ */ -+static inline void module_put(struct module *module) -+{ -+ if (module) -+ __MOD_DEC_USE_COUNT(module); -+} -+ -+#endif -+ -+/** -+ * ccs_put_filesystem - Wrapper for put_filesystem(). -+ * -+ * @fstype: Pointer to "struct file_system_type". -+ * -+ * Returns nothing. -+ * -+ * Since put_filesystem() is not exported, I embed put_filesystem() here. -+ */ -+static inline void ccs_put_filesystem(struct file_system_type *fstype) -+{ -+ module_put(fstype->owner); -+} -+ -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) -+#if !defined(RHEL_MAJOR) || RHEL_MAJOR != 5 -+#if !defined(AX_MAJOR) || AX_MAJOR != 3 -+ -+/** -+ * ip_hdr - Get "struct iphdr". -+ * -+ * @skb: Pointer to "struct sk_buff". -+ * -+ * Returns pointer to "struct iphdr". -+ * -+ * This is for compatibility with older kernels. -+ */ -+static inline struct iphdr *ip_hdr(const struct sk_buff *skb) -+{ -+ return skb->nh.iph; -+} -+ -+/** -+ * udp_hdr - Get "struct udphdr". -+ * -+ * @skb: Pointer to "struct sk_buff". -+ * -+ * Returns pointer to "struct udphdr". -+ * -+ * This is for compatibility with older kernels. -+ */ -+static inline struct udphdr *udp_hdr(const struct sk_buff *skb) -+{ -+ return skb->h.uh; -+} -+ -+/** -+ * ipv6_hdr - Get "struct ipv6hdr". -+ * -+ * @skb: Pointer to "struct sk_buff". -+ * -+ * Returns pointer to "struct ipv6hdr". -+ * -+ * This is for compatibility with older kernels. -+ */ -+static inline struct ipv6hdr *ipv6_hdr(const struct sk_buff *skb) -+{ -+ return skb->nh.ipv6h; -+} -+ -+#endif -+#endif -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+ -+/** -+ * skb_kill_datagram - Kill a datagram forcibly. -+ * -+ * @sk: Pointer to "struct sock". -+ * @skb: Pointer to "struct sk_buff". -+ * @flags: Flags passed to skb_recv_datagram(). -+ * -+ * Returns nothing. -+ */ -+static inline void skb_kill_datagram(struct sock *sk, struct sk_buff *skb, -+ int flags) -+{ -+ /* Clear queue. */ -+ if (flags & MSG_PEEK) { -+ int clear = 0; -+ spin_lock_irq(&sk->receive_queue.lock); -+ if (skb == skb_peek(&sk->receive_queue)) { -+ __skb_unlink(skb, &sk->receive_queue); -+ clear = 1; -+ } -+ spin_unlock_irq(&sk->receive_queue.lock); -+ if (clear) -+ kfree_skb(skb); -+ } -+ skb_free_datagram(sk, skb); -+} -+ -+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) -+ -+/** -+ * skb_kill_datagram - Kill a datagram forcibly. -+ * -+ * @sk: Pointer to "struct sock". -+ * @skb: Pointer to "struct sk_buff". -+ * @flags: Flags passed to skb_recv_datagram(). -+ * -+ * Returns nothing. -+ */ -+static inline void skb_kill_datagram(struct sock *sk, struct sk_buff *skb, -+ int flags) -+{ -+ /* Clear queue. */ -+ if (flags & MSG_PEEK) { -+ int clear = 0; -+ spin_lock_bh(&sk->sk_receive_queue.lock); -+ if (skb == skb_peek(&sk->sk_receive_queue)) { -+ __skb_unlink(skb, &sk->sk_receive_queue); -+ clear = 1; -+ } -+ spin_unlock_bh(&sk->sk_receive_queue.lock); -+ if (clear) -+ kfree_skb(skb); -+ } -+ skb_free_datagram(sk, skb); -+} -+ -+#endif -+ -+#endif -+ -+/***** SECTION5: Variables definition section *****/ -+ -+/* The initial domain. */ -+struct ccs_domain_info ccs_kernel_domain; -+ -+/* The list for "struct ccs_domain_info". */ -+LIST_HEAD(ccs_domain_list); -+ -+/***** SECTION6: Dependent functions section *****/ -+ -+/** -+ * ccs_path_matches_group - Check whether the given pathname matches members of the given pathname group. -+ * -+ * @pathname: The name of pathname. -+ * @group: Pointer to "struct ccs_path_group". -+ * -+ * Returns matched member's pathname if @pathname matches pathnames in @group, -+ * NULL otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static const struct ccs_path_info *ccs_path_matches_group -+(const struct ccs_path_info *pathname, const struct ccs_group *group) -+{ -+ struct ccs_path_group *member; -+ list_for_each_entry_srcu(member, &group->member_list, head.list, -+ &ccs_ss) { -+ if (member->head.is_deleted) -+ continue; -+ if (!ccs_path_matches_pattern(pathname, member->member_name)) -+ continue; -+ return member->member_name; -+ } -+ return NULL; -+} -+ -+/** -+ * ccs_number_matches_group - Check whether the given number matches members of the given number group. -+ * -+ * @min: Min number. -+ * @max: Max number. -+ * @group: Pointer to "struct ccs_number_group". -+ * -+ * Returns true if @min and @max partially overlaps @group, false otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static bool ccs_number_matches_group(const unsigned long min, -+ const unsigned long max, -+ const struct ccs_group *group) -+{ -+ struct ccs_number_group *member; -+ bool matched = false; -+ list_for_each_entry_srcu(member, &group->member_list, head.list, -+ &ccs_ss) { -+ if (member->head.is_deleted) -+ continue; -+ if (min > member->number.values[1] || -+ max < member->number.values[0]) -+ continue; -+ matched = true; -+ break; -+ } -+ return matched; -+} -+ -+/** -+ * ccs_check_entry - Do permission check. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @ptr: Pointer to "struct ccs_acl_info". -+ * -+ * Returns true on match, false otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static bool ccs_check_entry(struct ccs_request_info *r, -+ struct ccs_acl_info *ptr) -+{ -+ if (ptr->is_deleted || ptr->type != r->param_type) -+ return false; -+ switch (r->param_type) { -+ case CCS_TYPE_PATH_ACL: -+ return ccs_check_path_acl(r, ptr); -+ case CCS_TYPE_PATH2_ACL: -+ return ccs_check_path2_acl(r, ptr); -+ case CCS_TYPE_PATH_NUMBER_ACL: -+ return ccs_check_path_number_acl(r, ptr); -+ case CCS_TYPE_MKDEV_ACL: -+ return ccs_check_mkdev_acl(r, ptr); -+ case CCS_TYPE_MOUNT_ACL: -+ return ccs_check_mount_acl(r, ptr); -+#ifdef CONFIG_CCSECURITY_MISC -+ case CCS_TYPE_ENV_ACL: -+ return ccs_check_env_acl(r, ptr); -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ case CCS_TYPE_CAPABILITY_ACL: -+ return ccs_check_capability_acl(r, ptr); -+#endif -+#ifdef CONFIG_CCSECURITY_NETWORK -+ case CCS_TYPE_INET_ACL: -+ return ccs_check_inet_acl(r, ptr); -+ case CCS_TYPE_UNIX_ACL: -+ return ccs_check_unix_acl(r, ptr); -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ case CCS_TYPE_SIGNAL_ACL: -+ return ccs_check_signal_acl(r, ptr); -+#endif -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+ case CCS_TYPE_MANUAL_TASK_ACL: -+ return ccs_check_task_acl(r, ptr); -+#endif -+ } -+ return true; -+} -+ -+/** -+ * ccs_check_acl - Do permission check. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+int ccs_check_acl(struct ccs_request_info *r) -+{ -+ const struct ccs_domain_info *domain = ccs_current_domain(); -+ int error; -+ do { -+ struct ccs_acl_info *ptr; -+ const struct list_head *list = &domain->acl_info_list; -+ u16 i = 0; -+retry: -+ list_for_each_entry_srcu(ptr, list, list, &ccs_ss) { -+ if (!ccs_check_entry(r, ptr)) -+ continue; -+ if (!ccs_condition(r, ptr->cond)) -+ continue; -+ r->matched_acl = ptr; -+ r->granted = true; -+ ccs_audit_log(r); -+ return 0; -+ } -+ for (; i < CCS_MAX_ACL_GROUPS; i++) { -+ if (!test_bit(i, domain->group)) -+ continue; -+ list = &domain->ns->acl_group[i++]; -+ goto retry; -+ } -+ r->granted = false; -+ error = ccs_audit_log(r); -+ } while (error == CCS_RETRY_REQUEST && -+ r->type != CCS_MAC_FILE_EXECUTE); -+ return error; -+} -+ -+/** -+ * ccs_last_word - Get last component of a domainname. -+ * -+ * @name: Domainname to check. -+ * -+ * Returns the last word of @name. -+ */ -+static const char *ccs_last_word(const char *name) -+{ -+ const char *cp = strrchr(name, ' '); -+ if (cp) -+ return cp + 1; -+ return name; -+} -+ -+/** -+ * ccs_scan_transition - Try to find specific domain transition type. -+ * -+ * @list: Pointer to "struct list_head". -+ * @domainname: The name of current domain. -+ * @program: The name of requested program. -+ * @last_name: The last component of @domainname. -+ * @type: One of values in "enum ccs_transition_type". -+ * -+ * Returns true if found one, false otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static bool ccs_scan_transition(const struct list_head *list, -+ const struct ccs_path_info *domainname, -+ const struct ccs_path_info *program, -+ const char *last_name, -+ const enum ccs_transition_type type) -+{ -+ const struct ccs_transition_control *ptr; -+ list_for_each_entry_srcu(ptr, list, head.list, &ccs_ss) { -+ if (ptr->head.is_deleted || ptr->type != type) -+ continue; -+ if (ptr->domainname) { -+ if (!ptr->is_last_name) { -+ if (ptr->domainname != domainname) -+ continue; -+ } else { -+ /* -+ * Use direct strcmp() since this is -+ * unlikely used. -+ */ -+ if (strcmp(ptr->domainname->name, last_name)) -+ continue; -+ } -+ } -+ if (ptr->program && ccs_pathcmp(ptr->program, program)) -+ continue; -+ return true; -+ } -+ return false; -+} -+ -+/** -+ * ccs_transition_type - Get domain transition type. -+ * -+ * @ns: Pointer to "struct ccs_policy_namespace". -+ * @domainname: The name of current domain. -+ * @program: The name of requested program. -+ * -+ * Returns CCS_TRANSITION_CONTROL_TRANSIT if executing @program causes domain -+ * transition across namespaces, CCS_TRANSITION_CONTROL_INITIALIZE if executing -+ * @program reinitializes domain transition within that namespace, -+ * CCS_TRANSITION_CONTROL_KEEP if executing @program stays at @domainname , -+ * others otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static enum ccs_transition_type ccs_transition_type -+(const struct ccs_policy_namespace *ns, const struct ccs_path_info *domainname, -+ const struct ccs_path_info *program) -+{ -+ const char *last_name = ccs_last_word(domainname->name); -+ enum ccs_transition_type type = CCS_TRANSITION_CONTROL_NO_RESET; -+ while (type < CCS_MAX_TRANSITION_TYPE) { -+ const struct list_head * const list = -+ &ns->policy_list[CCS_ID_TRANSITION_CONTROL]; -+ if (!ccs_scan_transition(list, domainname, program, last_name, -+ type)) { -+ type++; -+ continue; -+ } -+ if (type != CCS_TRANSITION_CONTROL_NO_RESET && -+ type != CCS_TRANSITION_CONTROL_NO_INITIALIZE) -+ break; -+ /* -+ * Do not check for reset_domain if no_reset_domain matched. -+ * Do not check for initialize_domain if no_initialize_domain -+ * matched. -+ */ -+ type++; -+ type++; -+ } -+ return type; -+} -+ -+/** -+ * ccs_find_next_domain - Find a domain. -+ * -+ * @ee: Pointer to "struct ccs_execve". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_find_next_domain(struct ccs_execve *ee) -+{ -+ struct ccs_request_info *r = &ee->r; -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ const struct ccs_path_info *handler = ee->handler; -+#endif -+ struct ccs_domain_info *domain = NULL; -+ struct ccs_domain_info * const old_domain = ccs_current_domain(); -+ struct linux_binprm *bprm = ee->bprm; -+ struct ccs_security *task = ccs_current_security(); -+ const struct ccs_path_info *candidate; -+ struct ccs_path_info exename; -+ int retval; -+ bool reject_on_transition_failure = false; -+ -+ /* Get symlink's pathname of program. */ -+ retval = ccs_symlink_path(bprm->filename, &exename); -+ if (retval < 0) -+ return retval; -+ -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ if (handler) { -+ /* No permission check for execute handler. */ -+ candidate = &exename; -+ if (ccs_pathcmp(candidate, handler)) { -+ /* Failed to verify execute handler. */ -+ static u8 counter = 20; -+ if (counter) { -+ counter--; -+ printk(KERN_WARNING "Failed to verify: %s\n", -+ handler->name); -+ } -+ goto out; -+ } -+ } else -+#endif -+ { -+ struct ccs_aggregator *ptr; -+ struct list_head *list; -+retry: -+ /* Check 'aggregator' directive. */ -+ candidate = &exename; -+ list = &old_domain->ns->policy_list[CCS_ID_AGGREGATOR]; -+ list_for_each_entry_srcu(ptr, list, head.list, &ccs_ss) { -+ if (ptr->head.is_deleted || -+ !ccs_path_matches_pattern(candidate, -+ ptr->original_name)) -+ continue; -+ candidate = ptr->aggregated_name; -+ break; -+ } -+ -+ /* Check execute permission. */ -+ retval = ccs_execute_permission(r, candidate); -+ if (retval == CCS_RETRY_REQUEST) -+ goto retry; -+ if (retval < 0) -+ goto out; -+ /* -+ * To be able to specify domainnames with wildcards, use the -+ * pathname specified in the policy (which may contain -+ * wildcard) rather than the pathname passed to execve() -+ * (which never contains wildcard). -+ */ -+ if (r->param.path.matched_path) -+ candidate = r->param.path.matched_path; -+ } -+ /* -+ * Check for domain transition preference if "file execute" matched. -+ * If preference is given, make do_execve() fail if domain transition -+ * has failed, for domain transition preference should be used with -+ * destination domain defined. -+ */ -+ if (r->ee->transition) { -+ const char *domainname = r->ee->transition->name; -+ reject_on_transition_failure = true; -+ if (!strcmp(domainname, "keep")) -+ goto force_keep_domain; -+ if (!strcmp(domainname, "child")) -+ goto force_child_domain; -+ if (!strcmp(domainname, "reset")) -+ goto force_reset_domain; -+ if (!strcmp(domainname, "initialize")) -+ goto force_initialize_domain; -+ if (!strcmp(domainname, "parent")) { -+ char *cp; -+ strncpy(ee->tmp, old_domain->domainname->name, -+ CCS_EXEC_TMPSIZE - 1); -+ cp = strrchr(ee->tmp, ' '); -+ if (cp) -+ *cp = '\0'; -+ } else if (*domainname == '<') -+ strncpy(ee->tmp, domainname, CCS_EXEC_TMPSIZE - 1); -+ else -+ snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s %s", -+ old_domain->domainname->name, domainname); -+ goto force_jump_domain; -+ } -+ /* -+ * No domain transition preference specified. -+ * Calculate domain to transit to. -+ */ -+ switch (ccs_transition_type(old_domain->ns, old_domain->domainname, -+ candidate)) { -+ case CCS_TRANSITION_CONTROL_RESET: -+force_reset_domain: -+ /* Transit to the root of specified namespace. */ -+ snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "<%s>", -+ candidate->name); -+ /* -+ * Make do_execve() fail if domain transition across namespaces -+ * has failed. -+ */ -+ reject_on_transition_failure = true; -+ break; -+ case CCS_TRANSITION_CONTROL_INITIALIZE: -+force_initialize_domain: -+ /* Transit to the child of current namespace's root. */ -+ snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s %s", -+ old_domain->ns->name, candidate->name); -+ break; -+ case CCS_TRANSITION_CONTROL_KEEP: -+force_keep_domain: -+ /* Keep current domain. */ -+ domain = old_domain; -+ break; -+ default: -+ if (old_domain == &ccs_kernel_domain && !ccs_policy_loaded) { -+ /* -+ * Needn't to transit from kernel domain before -+ * starting /sbin/init. But transit from kernel domain -+ * if executing initializers because they might start -+ * before /sbin/init. -+ */ -+ domain = old_domain; -+ break; -+ } -+force_child_domain: -+ /* Normal domain transition. */ -+ snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s %s", -+ old_domain->domainname->name, candidate->name); -+ break; -+ } -+force_jump_domain: -+ /* -+ * Tell GC that I started execve(). -+ * Also, tell open_exec() to check read permission. -+ */ -+ task->ccs_flags |= CCS_TASK_IS_IN_EXECVE; -+ /* -+ * Make task->ccs_flags visible to GC before changing -+ * task->ccs_domain_info. -+ */ -+ smp_wmb(); -+ /* -+ * Proceed to the next domain in order to allow reaching via PID. -+ * It will be reverted if execve() failed. Reverting is not good. -+ * But it is better than being unable to reach via PID in interactive -+ * enforcing mode. -+ */ -+ if (!domain) -+ domain = ccs_assign_domain(ee->tmp, true); -+ if (domain) -+ retval = 0; -+ else if (reject_on_transition_failure) { -+ printk(KERN_WARNING -+ "ERROR: Domain '%s' not ready.\n", ee->tmp); -+ retval = -ENOMEM; -+ } else if (r->mode == CCS_CONFIG_ENFORCING) -+ retval = -ENOMEM; -+ else { -+ retval = 0; -+ if (!old_domain->flags[CCS_DIF_TRANSITION_FAILED]) { -+ old_domain->flags[CCS_DIF_TRANSITION_FAILED] = true; -+ r->granted = false; -+ ccs_write_log(r, "%s", -+ ccs_dif[CCS_DIF_TRANSITION_FAILED]); -+ printk(KERN_WARNING -+ "ERROR: Domain '%s' not defined.\n", ee->tmp); -+ } -+ } -+out: -+ kfree(exename.name); -+ return retval; -+} -+ -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ -+/** -+ * ccs_unescape - Unescape escaped string. -+ * -+ * @dest: String to unescape. -+ * -+ * Returns nothing. -+ */ -+static void ccs_unescape(unsigned char *dest) -+{ -+ unsigned char *src = dest; -+ unsigned char c; -+ unsigned char d; -+ unsigned char e; -+ while (1) { -+ c = *src++; -+ if (!c) -+ break; -+ if (c != '\\') { -+ *dest++ = c; -+ continue; -+ } -+ c = *src++; -+ if (c == '\\') { -+ *dest++ = c; -+ continue; -+ } -+ if (c < '0' || c > '3') -+ break; -+ d = *src++; -+ if (d < '0' || d > '7') -+ break; -+ e = *src++; -+ if (e < '0' || e > '7') -+ break; -+ *dest++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0'); -+ } -+ *dest = '\0'; -+} -+ -+/** -+ * ccs_try_alt_exec - Try to start execute handler. -+ * -+ * @ee: Pointer to "struct ccs_execve". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_try_alt_exec(struct ccs_execve *ee) -+{ -+ /* -+ * Contents of modified bprm. -+ * The envp[] in original bprm is moved to argv[] so that -+ * the alternatively executed program won't be affected by -+ * some dangerous environment variables like LD_PRELOAD. -+ * -+ * modified bprm->argc -+ * = original bprm->argc + original bprm->envc + 7 -+ * modified bprm->envc -+ * = 0 -+ * -+ * modified bprm->argv[0] -+ * = the program's name specified by *_execute_handler -+ * modified bprm->argv[1] -+ * = ccs_current_domain()->domainname->name -+ * modified bprm->argv[2] -+ * = the current process's name -+ * modified bprm->argv[3] -+ * = the current process's information (e.g. uid/gid). -+ * modified bprm->argv[4] -+ * = original bprm->filename -+ * modified bprm->argv[5] -+ * = original bprm->argc in string expression -+ * modified bprm->argv[6] -+ * = original bprm->envc in string expression -+ * modified bprm->argv[7] -+ * = original bprm->argv[0] -+ * ... -+ * modified bprm->argv[bprm->argc + 6] -+ * = original bprm->argv[bprm->argc - 1] -+ * modified bprm->argv[bprm->argc + 7] -+ * = original bprm->envp[0] -+ * ... -+ * modified bprm->argv[bprm->envc + bprm->argc + 6] -+ * = original bprm->envp[bprm->envc - 1] -+ */ -+ struct linux_binprm *bprm = ee->bprm; -+ struct file *filp; -+ int retval; -+ const int original_argc = bprm->argc; -+ const int original_envc = bprm->envc; -+ -+ /* Close the requested program's dentry. */ -+ ee->obj.path1.dentry = NULL; -+ ee->obj.path1.mnt = NULL; -+ ee->obj.stat_valid[CCS_PATH1] = false; -+ ee->obj.stat_valid[CCS_PATH1_PARENT] = false; -+ ee->obj.validate_done = false; -+ allow_write_access(bprm->file); -+ fput(bprm->file); -+ bprm->file = NULL; -+ -+ /* Invalidate page dump cache. */ -+ ee->dump.page = NULL; -+ -+ /* Move envp[] to argv[] */ -+ bprm->argc += bprm->envc; -+ bprm->envc = 0; -+ -+ /* Set argv[6] */ -+ { -+ snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_envc); -+ retval = ccs_copy_argv(ee->tmp, bprm); -+ if (retval < 0) -+ goto out; -+ } -+ -+ /* Set argv[5] */ -+ { -+ snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_argc); -+ retval = ccs_copy_argv(ee->tmp, bprm); -+ if (retval < 0) -+ goto out; -+ } -+ -+ /* Set argv[4] */ -+ { -+ retval = ccs_copy_argv(bprm->filename, bprm); -+ if (retval < 0) -+ goto out; -+ } -+ -+ /* Set argv[3] */ -+ { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) -+ /* -+ * Pass uid/gid seen from current user namespace, for these -+ * values are used by programs in current user namespace in -+ * order to decide whether to execve() or not (rather than by -+ * auditing daemon in init's user namespace). -+ */ -+ snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, -+ "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d " -+ "sgid=%d fsuid=%d fsgid=%d", ccs_sys_getpid(), -+ __kuid_val(current_uid()), __kgid_val(current_gid()), -+ __kuid_val(current_euid()), -+ __kgid_val(current_egid()), -+ __kuid_val(current_suid()), -+ __kgid_val(current_sgid()), -+ __kuid_val(current_fsuid()), -+ __kgid_val(current_fsgid())); -+#else -+ snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, -+ "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d " -+ "sgid=%d fsuid=%d fsgid=%d", ccs_sys_getpid(), -+ current_uid(), current_gid(), current_euid(), -+ current_egid(), current_suid(), current_sgid(), -+ current_fsuid(), current_fsgid()); -+#endif -+ retval = ccs_copy_argv(ee->tmp, bprm); -+ if (retval < 0) -+ goto out; -+ } -+ -+ /* Set argv[2] */ -+ { -+ char *exe = (char *) ccs_get_exe(); -+ if (exe) { -+ retval = ccs_copy_argv(exe, bprm); -+ kfree(exe); -+ } else { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) -+ retval = ccs_copy_argv("", bprm); -+#else -+ snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, ""); -+ retval = ccs_copy_argv(ee->tmp, bprm); -+#endif -+ } -+ if (retval < 0) -+ goto out; -+ } -+ -+ /* Set argv[1] */ -+ { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) -+ retval = ccs_copy_argv(ccs_current_domain()->domainname->name, -+ bprm); -+#else -+ snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s", -+ ccs_current_domain()->domainname->name); -+ retval = ccs_copy_argv(ee->tmp, bprm); -+#endif -+ if (retval < 0) -+ goto out; -+ } -+ -+ /* Set argv[0] */ -+ { -+ struct path root; -+ char *cp; -+ int root_len; -+ int handler_len; -+ get_fs_root(current->fs, &root); -+ cp = ccs_realpath(&root); -+ path_put(&root); -+ if (!cp) { -+ retval = -ENOMEM; -+ goto out; -+ } -+ root_len = strlen(cp); -+ retval = strncmp(ee->handler->name, cp, root_len); -+ root_len--; -+ kfree(cp); -+ if (retval) { -+ retval = -ENOENT; -+ goto out; -+ } -+ handler_len = ee->handler->total_len + 1; -+ cp = kmalloc(handler_len, CCS_GFP_FLAGS); -+ if (!cp) { -+ retval = -ENOMEM; -+ goto out; -+ } -+ /* ee->handler_path is released by ccs_finish_execve(). */ -+ ee->handler_path = cp; -+ /* Adjust root directory for open_exec(). */ -+ memmove(cp, ee->handler->name + root_len, -+ handler_len - root_len); -+ ccs_unescape(cp); -+ retval = -ENOENT; -+ if (*cp != '/') -+ goto out; -+ retval = ccs_copy_argv(cp, bprm); -+ if (retval < 0) -+ goto out; -+ } -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24) -+ bprm->argv_len = bprm->exec - bprm->p; -+#endif -+#endif -+ -+ /* -+ * OK, now restart the process with execute handler program's dentry. -+ */ -+ filp = open_exec(ee->handler_path); -+ if (IS_ERR(filp)) { -+ retval = PTR_ERR(filp); -+ goto out; -+ } -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) -+ ee->obj.path1 = filp->f_path; -+#else -+ ee->obj.path1.dentry = filp->f_dentry; -+ ee->obj.path1.mnt = filp->f_vfsmnt; -+#endif -+ bprm->file = filp; -+ bprm->filename = ee->handler_path; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) -+ bprm->interp = bprm->filename; -+#endif -+ retval = prepare_binprm(bprm); -+ if (retval < 0) -+ goto out; -+ ee->r.dont_sleep_on_enforce_error = true; -+ retval = ccs_find_next_domain(ee); -+ ee->r.dont_sleep_on_enforce_error = false; -+out: -+ return retval; -+} -+ -+/** -+ * ccs_find_execute_handler - Find an execute handler. -+ * -+ * @ee: Pointer to "struct ccs_execve". -+ * @type: Type of execute handler. -+ * -+ * Returns true if found, false otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static bool ccs_find_execute_handler(struct ccs_execve *ee, const u8 type) -+{ -+ struct ccs_request_info *r = &ee->r; -+ /* -+ * To avoid infinite execute handler loop, don't use execute handler -+ * if the current process is marked as execute handler. -+ */ -+ if (ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER) -+ return false; -+ r->param_type = type; -+ ccs_check_acl(r); -+ if (!r->granted) -+ return false; -+ ee->handler = container_of(r->matched_acl, struct ccs_handler_acl, -+ head)->handler; -+ ee->transition = r->matched_acl && r->matched_acl->cond && -+ r->matched_acl->cond->exec_transit ? -+ r->matched_acl->cond->transit : NULL; -+ return true; -+} -+ -+#endif -+ -+#ifdef CONFIG_MMU -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) -+#define CCS_BPRM_MMU -+#elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR >= 3 -+#define CCS_BPRM_MMU -+#elif defined(AX_MAJOR) && AX_MAJOR == 3 && defined(AX_MINOR) && AX_MINOR >= 2 -+#define CCS_BPRM_MMU -+#endif -+#endif -+ -+/** -+ * ccs_dump_page - Dump a page to buffer. -+ * -+ * @bprm: Pointer to "struct linux_binprm". -+ * @pos: Location to dump. -+ * @dump: Poiner to "struct ccs_page_dump". -+ * -+ * Returns true on success, false otherwise. -+ */ -+bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos, -+ struct ccs_page_dump *dump) -+{ -+ struct page *page; -+ /* dump->data is released by ccs_start_execve(). */ -+ if (!dump->data) { -+ dump->data = kzalloc(PAGE_SIZE, CCS_GFP_FLAGS); -+ if (!dump->data) -+ return false; -+ } -+ /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */ -+#ifdef CCS_BPRM_MMU -+ if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) -+ return false; -+#else -+ page = bprm->page[pos / PAGE_SIZE]; -+#endif -+ if (page != dump->page) { -+ const unsigned int offset = pos % PAGE_SIZE; -+ /* -+ * Maybe kmap()/kunmap() should be used here. -+ * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic(). -+ * So do I. -+ */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) -+ char *kaddr = kmap_atomic(page); -+#else -+ char *kaddr = kmap_atomic(page, KM_USER0); -+#endif -+ dump->page = page; -+ memcpy(dump->data + offset, kaddr + offset, -+ PAGE_SIZE - offset); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) -+ kunmap_atomic(kaddr); -+#else -+ kunmap_atomic(kaddr, KM_USER0); -+#endif -+ } -+ /* Same with put_arg_page(page) in fs/exec.c */ -+#ifdef CCS_BPRM_MMU -+ put_page(page); -+#endif -+ return true; -+} -+ -+/** -+ * ccs_start_execve - Prepare for execve() operation. -+ * -+ * @bprm: Pointer to "struct linux_binprm". -+ * @eep: Pointer to "struct ccs_execve *". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_start_execve(struct linux_binprm *bprm, -+ struct ccs_execve **eep) -+{ -+ int retval; -+ struct ccs_security *task = ccs_current_security(); -+ struct ccs_execve *ee; -+ int idx; -+ *eep = NULL; -+ ee = kzalloc(sizeof(*ee), CCS_GFP_FLAGS); -+ if (!ee) -+ return -ENOMEM; -+ ee->tmp = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS); -+ if (!ee->tmp) { -+ kfree(ee); -+ return -ENOMEM; -+ } -+ idx = ccs_read_lock(); -+ /* ee->dump->data is allocated by ccs_dump_page(). */ -+ ee->previous_domain = task->ccs_domain_info; -+ /* Clear manager flag. */ -+ task->ccs_flags &= ~CCS_TASK_IS_MANAGER; -+ *eep = ee; -+ ccs_init_request_info(&ee->r, CCS_MAC_FILE_EXECUTE); -+ ee->r.ee = ee; -+ ee->bprm = bprm; -+ ee->r.obj = &ee->obj; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) -+ ee->obj.path1 = bprm->file->f_path; -+#else -+ ee->obj.path1.dentry = bprm->file->f_dentry; -+ ee->obj.path1.mnt = bprm->file->f_vfsmnt; -+#endif -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ /* -+ * No need to call ccs_environ() for execute handler because envp[] is -+ * moved to argv[]. -+ */ -+ if (ccs_find_execute_handler(ee, CCS_TYPE_AUTO_EXECUTE_HANDLER)) { -+ retval = ccs_try_alt_exec(ee); -+ goto done; -+ } -+#endif -+ retval = ccs_find_next_domain(ee); -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ if (retval == -EPERM && -+ ccs_find_execute_handler(ee, CCS_TYPE_DENIED_EXECUTE_HANDLER)) { -+ retval = ccs_try_alt_exec(ee); -+ goto done; -+ } -+#endif -+#ifdef CONFIG_CCSECURITY_MISC -+ if (!retval) -+ retval = ccs_environ(ee); -+#endif -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+done: -+#endif -+ ccs_read_unlock(idx); -+ kfree(ee->tmp); -+ ee->tmp = NULL; -+ kfree(ee->dump.data); -+ ee->dump.data = NULL; -+ return retval; -+} -+ -+/** -+ * ccs_finish_execve - Clean up execve() operation. -+ * -+ * @retval: Return code of an execve() operation. -+ * @ee: Pointer to "struct ccs_execve". -+ * -+ * Returns nothing. -+ */ -+static void ccs_finish_execve(int retval, struct ccs_execve *ee) -+{ -+ struct ccs_security *task = ccs_current_security(); -+ if (!ee) -+ return; -+ if (retval < 0) { -+ task->ccs_domain_info = ee->previous_domain; -+ /* -+ * Make task->ccs_domain_info visible to GC before changing -+ * task->ccs_flags. -+ */ -+ smp_wmb(); -+ } else { -+ /* Mark the current process as execute handler. */ -+ if (ee->handler) -+ task->ccs_flags |= CCS_TASK_IS_EXECUTE_HANDLER; -+ /* Mark the current process as normal process. */ -+ else -+ task->ccs_flags &= ~CCS_TASK_IS_EXECUTE_HANDLER; -+ } -+ /* Tell GC that I finished execve(). */ -+ task->ccs_flags &= ~CCS_TASK_IS_IN_EXECVE; -+ kfree(ee->handler_path); -+ kfree(ee); -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) -+ -+/** -+ * __ccs_search_binary_handler - Main routine for do_execve(). -+ * -+ * @bprm: Pointer to "struct linux_binprm". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Performs permission checks for do_execve() and domain transition. -+ * Domain transition by "struct ccs_domain_transition_control" and -+ * "auto_domain_transition=" parameter of "struct ccs_condition" are reverted -+ * if do_execve() failed. -+ * Garbage collector does not remove "struct ccs_domain_info" from -+ * ccs_domain_list nor kfree("struct ccs_domain_info") if the current thread is -+ * marked as CCS_TASK_IS_IN_EXECVE. -+ */ -+static int __ccs_search_binary_handler(struct linux_binprm *bprm) -+{ -+ struct ccs_execve *ee; -+ int retval; -+#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER -+ if (!ccs_policy_loaded) -+ ccsecurity_exports.load_policy(bprm->filename); -+#endif -+ retval = ccs_start_execve(bprm, &ee); -+ if (!retval) -+ retval = search_binary_handler(bprm); -+ ccs_finish_execve(retval, ee); -+ return retval; -+} -+ -+#else -+ -+/** -+ * __ccs_search_binary_handler - Main routine for do_execve(). -+ * -+ * @bprm: Pointer to "struct linux_binprm". -+ * @regs: Pointer to "struct pt_regs". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Performs permission checks for do_execve() and domain transition. -+ * Domain transition by "struct ccs_domain_transition_control" and -+ * "auto_domain_transition=" parameter of "struct ccs_condition" are reverted -+ * if do_execve() failed. -+ * Garbage collector does not remove "struct ccs_domain_info" from -+ * ccs_domain_list nor kfree("struct ccs_domain_info") if the current thread is -+ * marked as CCS_TASK_IS_IN_EXECVE. -+ */ -+static int __ccs_search_binary_handler(struct linux_binprm *bprm, -+ struct pt_regs *regs) -+{ -+ struct ccs_execve *ee; -+ int retval; -+#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER -+ if (!ccs_policy_loaded) -+ ccsecurity_exports.load_policy(bprm->filename); -+#endif -+ retval = ccs_start_execve(bprm, &ee); -+ if (!retval) -+ retval = search_binary_handler(bprm, regs); -+ ccs_finish_execve(retval, ee); -+ return retval; -+} -+ -+#endif -+ -+/** -+ * ccs_permission_init - Register permission check hooks. -+ * -+ * Returns nothing. -+ */ -+void __init ccs_permission_init(void) -+{ -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) -+ ccsecurity_ops.save_open_mode = __ccs_save_open_mode; -+ ccsecurity_ops.clear_open_mode = __ccs_clear_open_mode; -+ ccsecurity_ops.open_permission = __ccs_open_permission; -+#else -+ ccsecurity_ops.open_permission = ccs_new_open_permission; -+#endif -+ ccsecurity_ops.fcntl_permission = __ccs_fcntl_permission; -+ ccsecurity_ops.ioctl_permission = __ccs_ioctl_permission; -+ ccsecurity_ops.chmod_permission = __ccs_chmod_permission; -+ ccsecurity_ops.chown_permission = __ccs_chown_permission; -+#ifdef CONFIG_CCSECURITY_FILE_GETATTR -+ ccsecurity_ops.getattr_permission = __ccs_getattr_permission; -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) -+ ccsecurity_ops.pivot_root_permission = __ccs_pivot_root_permission; -+ ccsecurity_ops.chroot_permission = __ccs_chroot_permission; -+#else -+ ccsecurity_ops.pivot_root_permission = ccs_old_pivot_root_permission; -+ ccsecurity_ops.chroot_permission = ccs_old_chroot_permission; -+#endif -+ ccsecurity_ops.umount_permission = __ccs_umount_permission; -+ ccsecurity_ops.mknod_permission = __ccs_mknod_permission; -+ ccsecurity_ops.mkdir_permission = __ccs_mkdir_permission; -+ ccsecurity_ops.rmdir_permission = __ccs_rmdir_permission; -+ ccsecurity_ops.unlink_permission = __ccs_unlink_permission; -+ ccsecurity_ops.symlink_permission = __ccs_symlink_permission; -+ ccsecurity_ops.truncate_permission = __ccs_truncate_permission; -+ ccsecurity_ops.rename_permission = __ccs_rename_permission; -+ ccsecurity_ops.link_permission = __ccs_link_permission; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) -+ ccsecurity_ops.open_exec_permission = __ccs_open_exec_permission; -+ ccsecurity_ops.uselib_permission = __ccs_uselib_permission; -+#endif -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) || (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL)) -+ ccsecurity_ops.parse_table = __ccs_parse_table; -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) -+ ccsecurity_ops.mount_permission = __ccs_mount_permission; -+#else -+ ccsecurity_ops.mount_permission = ccs_old_mount_permission; -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ ccsecurity_ops.socket_create_permission = -+ __ccs_socket_create_permission; -+#endif -+#ifdef CONFIG_CCSECURITY_NETWORK -+ ccsecurity_ops.socket_listen_permission = -+ __ccs_socket_listen_permission; -+ ccsecurity_ops.socket_connect_permission = -+ __ccs_socket_connect_permission; -+ ccsecurity_ops.socket_bind_permission = __ccs_socket_bind_permission; -+ ccsecurity_ops.socket_post_accept_permission = -+ __ccs_socket_post_accept_permission; -+ ccsecurity_ops.socket_sendmsg_permission = -+ __ccs_socket_sendmsg_permission; -+#endif -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ ccsecurity_ops.socket_post_recvmsg_permission = -+ __ccs_socket_post_recvmsg_permission; -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ ccsecurity_ops.kill_permission = ccs_signal_acl; -+ ccsecurity_ops.tgkill_permission = ccs_signal_acl0; -+ ccsecurity_ops.tkill_permission = ccs_signal_acl; -+ ccsecurity_ops.sigqueue_permission = ccs_signal_acl; -+ ccsecurity_ops.tgsigqueue_permission = ccs_signal_acl0; -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ ccsecurity_ops.capable = __ccs_capable; -+ ccsecurity_ops.ptrace_permission = __ccs_ptrace_permission; -+#endif -+ ccsecurity_ops.search_binary_handler = __ccs_search_binary_handler; -+} -+ -+/** -+ * ccs_kern_path - Wrapper for kern_path(). -+ * -+ * @pathname: Pathname to resolve. Maybe NULL. -+ * @flags: Lookup flags. -+ * @path: Pointer to "struct path". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_kern_path(const char *pathname, int flags, struct path *path) -+{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) -+ if (!pathname || kern_path(pathname, flags, path)) -+ return -ENOENT; -+#else -+ struct nameidata nd; -+ if (!pathname || path_lookup(pathname, flags, &nd)) -+ return -ENOENT; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) -+ *path = nd.path; -+#else -+ path->dentry = nd.dentry; -+ path->mnt = nd.mnt; -+#endif -+#endif -+ return 0; -+} -+ -+/** -+ * ccs_get_path - Get dentry/vfsmmount of a pathname. -+ * -+ * @pathname: The pathname to solve. Maybe NULL. -+ * @path: Pointer to "struct path". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_get_path(const char *pathname, struct path *path) -+{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) -+ return ccs_kern_path(pathname, LOOKUP_FOLLOW, path); -+#else -+ return ccs_kern_path(pathname, LOOKUP_FOLLOW | LOOKUP_POSITIVE, path); -+#endif -+} -+ -+/** -+ * ccs_symlink_path - Get symlink's pathname. -+ * -+ * @pathname: The pathname to solve. Maybe NULL. -+ * @name: Pointer to "struct ccs_path_info". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * This function uses kzalloc(), so caller must kfree() if this function -+ * didn't return NULL. -+ */ -+static int ccs_symlink_path(const char *pathname, struct ccs_path_info *name) -+{ -+ char *buf; -+ struct path path; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) -+ if (ccs_kern_path(pathname, 0, &path)) -+ return -ENOENT; -+#else -+ if (ccs_kern_path(pathname, LOOKUP_POSITIVE, &path)) -+ return -ENOENT; -+#endif -+ buf = ccs_realpath(&path); -+ path_put(&path); -+ if (buf) { -+ name->name = buf; -+ ccs_fill_path_info(name); -+ return 0; -+ } -+ return -ENOMEM; -+} -+ -+/** -+ * ccs_check_mount_acl - Check permission for path path path number operation. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @ptr: Pointer to "struct ccs_acl_info". -+ * -+ * Returns true if granted, false otherwise. -+ */ -+static bool ccs_check_mount_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr) -+{ -+ const struct ccs_mount_acl *acl = -+ container_of(ptr, typeof(*acl), head); -+ return ccs_compare_number_union(r->param.mount.flags, &acl->flags) && -+ ccs_compare_name_union(r->param.mount.type, &acl->fs_type) && -+ ccs_compare_name_union(r->param.mount.dir, &acl->dir_name) && -+ (!r->param.mount.need_dev || -+ ccs_compare_name_union(r->param.mount.dev, &acl->dev_name)); -+} -+ -+/** -+ * ccs_mount_acl - Check permission for mount() operation. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @dev_name: Name of device file. Maybe NULL. -+ * @dir: Pointer to "struct path". -+ * @type: Name of filesystem type. -+ * @flags: Mount options. -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_mount_acl(struct ccs_request_info *r, const char *dev_name, -+ struct path *dir, const char *type, -+ unsigned long flags) -+{ -+ struct ccs_obj_info obj = { }; -+ struct file_system_type *fstype = NULL; -+ const char *requested_type = NULL; -+ const char *requested_dir_name = NULL; -+ const char *requested_dev_name = NULL; -+ struct ccs_path_info rtype; -+ struct ccs_path_info rdev; -+ struct ccs_path_info rdir; -+ int need_dev = 0; -+ int error = -ENOMEM; -+ r->obj = &obj; -+ -+ /* Get fstype. */ -+ requested_type = ccs_encode(type); -+ if (!requested_type) -+ goto out; -+ rtype.name = requested_type; -+ ccs_fill_path_info(&rtype); -+ -+ /* Get mount point. */ -+ obj.path2 = *dir; -+ requested_dir_name = ccs_realpath(dir); -+ if (!requested_dir_name) { -+ error = -ENOMEM; -+ goto out; -+ } -+ rdir.name = requested_dir_name; -+ ccs_fill_path_info(&rdir); -+ -+ /* Compare fs name. */ -+ if (type == ccs_mounts[CCS_MOUNT_REMOUNT]) { -+ /* dev_name is ignored. */ -+ } else if (type == ccs_mounts[CCS_MOUNT_MAKE_UNBINDABLE] || -+ type == ccs_mounts[CCS_MOUNT_MAKE_PRIVATE] || -+ type == ccs_mounts[CCS_MOUNT_MAKE_SLAVE] || -+ type == ccs_mounts[CCS_MOUNT_MAKE_SHARED]) { -+ /* dev_name is ignored. */ -+ } else if (type == ccs_mounts[CCS_MOUNT_BIND] || -+ type == ccs_mounts[CCS_MOUNT_MOVE]) { -+ need_dev = -1; /* dev_name is a directory */ -+ } else { -+ fstype = get_fs_type(type); -+ if (!fstype) { -+ error = -ENODEV; -+ goto out; -+ } -+ if (fstype->fs_flags & FS_REQUIRES_DEV) -+ /* dev_name is a block device file. */ -+ need_dev = 1; -+ } -+ if (need_dev) { -+ /* Get mount point or device file. */ -+ if (ccs_get_path(dev_name, &obj.path1)) { -+ error = -ENOENT; -+ goto out; -+ } -+ requested_dev_name = ccs_realpath(&obj.path1); -+ if (!requested_dev_name) { -+ error = -ENOENT; -+ goto out; -+ } -+ } else { -+ /* Map dev_name to "" if no dev_name given. */ -+ if (!dev_name) -+ dev_name = ""; -+ requested_dev_name = ccs_encode(dev_name); -+ if (!requested_dev_name) { -+ error = -ENOMEM; -+ goto out; -+ } -+ } -+ rdev.name = requested_dev_name; -+ ccs_fill_path_info(&rdev); -+ r->param_type = CCS_TYPE_MOUNT_ACL; -+ r->param.mount.need_dev = need_dev; -+ r->param.mount.dev = &rdev; -+ r->param.mount.dir = &rdir; -+ r->param.mount.type = &rtype; -+ r->param.mount.flags = flags; -+ error = ccs_check_acl(r); -+out: -+ kfree(requested_dev_name); -+ kfree(requested_dir_name); -+ if (fstype) -+ ccs_put_filesystem(fstype); -+ kfree(requested_type); -+ /* Drop refcount obtained by ccs_get_path(). */ -+ if (obj.path1.dentry) -+ path_put(&obj.path1); -+ return error; -+} -+ -+/** -+ * __ccs_mount_permission - Check permission for mount() operation. -+ * -+ * @dev_name: Name of device file. Maybe NULL. -+ * @path: Pointer to "struct path". -+ * @type: Name of filesystem type. Maybe NULL. -+ * @flags: Mount options. -+ * @data_page: Optional data. Maybe NULL. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_mount_permission(const char *dev_name, struct path *path, -+ const char *type, unsigned long flags, -+ void *data_page) -+{ -+ struct ccs_request_info r; -+ int error = 0; -+ int idx; -+ if ((flags & MS_MGC_MSK) == MS_MGC_VAL) -+ flags &= ~MS_MGC_MSK; -+ if (flags & MS_REMOUNT) { -+ type = ccs_mounts[CCS_MOUNT_REMOUNT]; -+ flags &= ~MS_REMOUNT; -+ } else if (flags & MS_BIND) { -+ type = ccs_mounts[CCS_MOUNT_BIND]; -+ flags &= ~MS_BIND; -+ } else if (flags & MS_SHARED) { -+ if (flags & (MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) -+ return -EINVAL; -+ type = ccs_mounts[CCS_MOUNT_MAKE_SHARED]; -+ flags &= ~MS_SHARED; -+ } else if (flags & MS_PRIVATE) { -+ if (flags & (MS_SHARED | MS_SLAVE | MS_UNBINDABLE)) -+ return -EINVAL; -+ type = ccs_mounts[CCS_MOUNT_MAKE_PRIVATE]; -+ flags &= ~MS_PRIVATE; -+ } else if (flags & MS_SLAVE) { -+ if (flags & (MS_SHARED | MS_PRIVATE | MS_UNBINDABLE)) -+ return -EINVAL; -+ type = ccs_mounts[CCS_MOUNT_MAKE_SLAVE]; -+ flags &= ~MS_SLAVE; -+ } else if (flags & MS_UNBINDABLE) { -+ if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE)) -+ return -EINVAL; -+ type = ccs_mounts[CCS_MOUNT_MAKE_UNBINDABLE]; -+ flags &= ~MS_UNBINDABLE; -+ } else if (flags & MS_MOVE) { -+ type = ccs_mounts[CCS_MOUNT_MOVE]; -+ flags &= ~MS_MOVE; -+ } -+ if (!type) -+ type = ""; -+ idx = ccs_read_lock(); -+ if (ccs_init_request_info(&r, CCS_MAC_FILE_MOUNT) -+ != CCS_CONFIG_DISABLED) -+ error = ccs_mount_acl(&r, dev_name, path, type, flags); -+ ccs_read_unlock(idx); -+ return error; -+} -+ -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24) -+ -+/** -+ * ccs_old_mount_permission - Check permission for mount() operation. -+ * -+ * @dev_name: Name of device file. -+ * @nd: Pointer to "struct nameidata". -+ * @type: Name of filesystem type. Maybe NULL. -+ * @flags: Mount options. -+ * @data_page: Optional data. Maybe NULL. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_old_mount_permission(const char *dev_name, struct nameidata *nd, -+ const char *type, unsigned long flags, -+ void *data_page) -+{ -+ struct path path = { nd->mnt, nd->dentry }; -+ return __ccs_mount_permission(dev_name, &path, type, flags, data_page); -+} -+ -+#endif -+ -+/** -+ * ccs_compare_number_union - Check whether a value matches "struct ccs_number_union" or not. -+ * -+ * @value: Number to check. -+ * @ptr: Pointer to "struct ccs_number_union". -+ * -+ * Returns true if @value matches @ptr, false otherwise. -+ */ -+static bool ccs_compare_number_union(const unsigned long value, -+ const struct ccs_number_union *ptr) -+{ -+ if (ptr->group) -+ return ccs_number_matches_group(value, value, ptr->group); -+ return value >= ptr->values[0] && value <= ptr->values[1]; -+} -+ -+/** -+ * ccs_compare_name_union - Check whether a name matches "struct ccs_name_union" or not. -+ * -+ * @name: Pointer to "struct ccs_path_info". -+ * @ptr: Pointer to "struct ccs_name_union". -+ * -+ * Returns "struct ccs_path_info" if @name matches @ptr, NULL otherwise. -+ */ -+static const struct ccs_path_info *ccs_compare_name_union -+(const struct ccs_path_info *name, const struct ccs_name_union *ptr) -+{ -+ if (ptr->group) -+ return ccs_path_matches_group(name, ptr->group); -+ if (ccs_path_matches_pattern(name, ptr->filename)) -+ return ptr->filename; -+ return NULL; -+} -+ -+/** -+ * ccs_add_slash - Add trailing '/' if needed. -+ * -+ * @buf: Pointer to "struct ccs_path_info". -+ * -+ * Returns nothing. -+ * -+ * @buf must be generated by ccs_encode() because this function does not -+ * allocate memory for adding '/'. -+ */ -+static void ccs_add_slash(struct ccs_path_info *buf) -+{ -+ if (buf->is_dir) -+ return; -+ /* This is OK because ccs_encode() reserves space for appending "/". */ -+ strcat((char *) buf->name, "/"); -+ ccs_fill_path_info(buf); -+} -+ -+/** -+ * ccs_get_realpath - Get realpath. -+ * -+ * @buf: Pointer to "struct ccs_path_info". -+ * @path: Pointer to "struct path". @path->mnt may be NULL. -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool ccs_get_realpath(struct ccs_path_info *buf, struct path *path) -+{ -+ buf->name = ccs_realpath(path); -+ if (buf->name) { -+ ccs_fill_path_info(buf); -+ return true; -+ } -+ return false; -+} -+ -+/** -+ * ccs_check_path_acl - Check permission for path operation. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @ptr: Pointer to "struct ccs_acl_info". -+ * -+ * Returns true if granted, false otherwise. -+ * -+ * To be able to use wildcard for domain transition, this function sets -+ * matching entry on success. Since the caller holds ccs_read_lock(), -+ * it is safe to set matching entry. -+ */ -+static bool ccs_check_path_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr) -+{ -+ const struct ccs_path_acl *acl = container_of(ptr, typeof(*acl), head); -+ if (ptr->perm & (1 << r->param.path.operation)) { -+ r->param.path.matched_path = -+ ccs_compare_name_union(r->param.path.filename, -+ &acl->name); -+ return r->param.path.matched_path != NULL; -+ } -+ return false; -+} -+ -+/** -+ * ccs_check_path_number_acl - Check permission for path number operation. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @ptr: Pointer to "struct ccs_acl_info". -+ * -+ * Returns true if granted, false otherwise. -+ */ -+static bool ccs_check_path_number_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr) -+{ -+ const struct ccs_path_number_acl *acl = -+ container_of(ptr, typeof(*acl), head); -+ return (ptr->perm & (1 << r->param.path_number.operation)) && -+ ccs_compare_number_union(r->param.path_number.number, -+ &acl->number) && -+ ccs_compare_name_union(r->param.path_number.filename, -+ &acl->name); -+} -+ -+/** -+ * ccs_check_path2_acl - Check permission for path path operation. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @ptr: Pointer to "struct ccs_acl_info". -+ * -+ * Returns true if granted, false otherwise. -+ */ -+static bool ccs_check_path2_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr) -+{ -+ const struct ccs_path2_acl *acl = -+ container_of(ptr, typeof(*acl), head); -+ return (ptr->perm & (1 << r->param.path2.operation)) && -+ ccs_compare_name_union(r->param.path2.filename1, &acl->name1) -+ && ccs_compare_name_union(r->param.path2.filename2, -+ &acl->name2); -+} -+ -+/** -+ * ccs_check_mkdev_acl - Check permission for path number number number operation. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @ptr: Pointer to "struct ccs_acl_info". -+ * -+ * Returns true if granted, false otherwise. -+ */ -+static bool ccs_check_mkdev_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr) -+{ -+ const struct ccs_mkdev_acl *acl = -+ container_of(ptr, typeof(*acl), head); -+ return (ptr->perm & (1 << r->param.mkdev.operation)) && -+ ccs_compare_number_union(r->param.mkdev.mode, &acl->mode) && -+ ccs_compare_number_union(r->param.mkdev.major, &acl->major) && -+ ccs_compare_number_union(r->param.mkdev.minor, &acl->minor) && -+ ccs_compare_name_union(r->param.mkdev.filename, &acl->name); -+} -+ -+/** -+ * ccs_path_permission - Check permission for path operation. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @operation: Type of operation. -+ * @filename: Filename to check. -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_path_permission(struct ccs_request_info *r, u8 operation, -+ const struct ccs_path_info *filename) -+{ -+ r->type = ccs_p2mac[operation]; -+ r->mode = ccs_get_mode(r->profile, r->type); -+ if (r->mode == CCS_CONFIG_DISABLED) -+ return 0; -+ r->param_type = CCS_TYPE_PATH_ACL; -+ r->param.path.filename = filename; -+ r->param.path.operation = operation; -+ return ccs_check_acl(r); -+} -+ -+/** -+ * ccs_execute_permission - Check permission for execute operation. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @filename: Filename to check. -+ * -+ * Returns 0 on success, CCS_RETRY_REQUEST on retry, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_execute_permission(struct ccs_request_info *r, -+ const struct ccs_path_info *filename) -+{ -+ int error; -+ /* -+ * Unlike other permission checks, this check is done regardless of -+ * profile mode settings in order to check for domain transition -+ * preference. -+ */ -+ r->type = CCS_MAC_FILE_EXECUTE; -+ r->mode = ccs_get_mode(r->profile, r->type); -+ r->param_type = CCS_TYPE_PATH_ACL; -+ r->param.path.filename = filename; -+ r->param.path.operation = CCS_TYPE_EXECUTE; -+ error = ccs_check_acl(r); -+ r->ee->transition = r->matched_acl && r->matched_acl->cond && -+ r->matched_acl->cond->exec_transit ? -+ r->matched_acl->cond->transit : NULL; -+ return error; -+} -+ -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) -+ -+/** -+ * __ccs_save_open_mode - Remember original flags passed to sys_open(). -+ * -+ * @mode: Flags passed to sys_open(). -+ * -+ * Returns nothing. -+ * -+ * TOMOYO does not check "file write" if open(path, O_TRUNC | O_RDONLY) was -+ * requested because write() is not permitted. Instead, TOMOYO checks -+ * "file truncate" if O_TRUNC is passed. -+ * -+ * TOMOYO does not check "file read" and "file write" if open(path, 3) was -+ * requested because read()/write() are not permitted. Instead, TOMOYO checks -+ * "file ioctl" when ioctl() is requested. -+ */ -+static void __ccs_save_open_mode(int mode) -+{ -+ if ((mode & 3) == 3) -+ ccs_current_security()->ccs_flags |= CCS_OPEN_FOR_IOCTL_ONLY; -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 14) -+ /* O_TRUNC passes MAY_WRITE to ccs_open_permission(). */ -+ else if (!(mode & 3) && (mode & O_TRUNC)) -+ ccs_current_security()->ccs_flags |= -+ CCS_OPEN_FOR_READ_TRUNCATE; -+#endif -+} -+ -+/** -+ * __ccs_clear_open_mode - Forget original flags passed to sys_open(). -+ * -+ * Returns nothing. -+ */ -+static void __ccs_clear_open_mode(void) -+{ -+ ccs_current_security()->ccs_flags &= ~(CCS_OPEN_FOR_IOCTL_ONLY | -+ CCS_OPEN_FOR_READ_TRUNCATE); -+} -+ -+#endif -+ -+/** -+ * __ccs_open_permission - Check permission for "read" and "write". -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @mnt: Pointer to "struct vfsmount". Maybe NULL. -+ * @flag: Flags for open(). -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_open_permission(struct dentry *dentry, struct vfsmount *mnt, -+ const int flag) -+{ -+ struct ccs_request_info r; -+ struct ccs_obj_info obj = { -+ .path1.dentry = dentry, -+ .path1.mnt = mnt, -+ }; -+ const u32 ccs_flags = ccs_current_flags(); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) -+ const u8 acc_mode = (flag & 3) == 3 ? 0 : ACC_MODE(flag); -+#else -+ const u8 acc_mode = (ccs_flags & CCS_OPEN_FOR_IOCTL_ONLY) ? 0 : -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 14) -+ (ccs_flags & CCS_OPEN_FOR_READ_TRUNCATE) ? 4 : -+#endif -+ ACC_MODE(flag); -+#endif -+ int error = 0; -+ struct ccs_path_info buf; -+ int idx; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) -+ if (current->in_execve && !(ccs_flags & CCS_TASK_IS_IN_EXECVE)) -+ return 0; -+#endif -+#ifndef CONFIG_CCSECURITY_FILE_READDIR -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) -+ if (d_is_dir(dentry)) -+ return 0; -+#else -+ if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) -+ return 0; -+#endif -+#endif -+ buf.name = NULL; -+ r.mode = CCS_CONFIG_DISABLED; -+ idx = ccs_read_lock(); -+ if (acc_mode && ccs_init_request_info(&r, CCS_MAC_FILE_OPEN) -+ != CCS_CONFIG_DISABLED) { -+ if (!ccs_get_realpath(&buf, &obj.path1)) { -+ error = -ENOMEM; -+ goto out; -+ } -+ r.obj = &obj; -+ if (acc_mode & MAY_READ) -+ error = ccs_path_permission(&r, CCS_TYPE_READ, &buf); -+ if (!error && (acc_mode & MAY_WRITE)) -+ error = ccs_path_permission(&r, (flag & O_APPEND) ? -+ CCS_TYPE_APPEND : -+ CCS_TYPE_WRITE, &buf); -+ } -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) -+ if (!error && (flag & O_TRUNC) && -+ ccs_init_request_info(&r, CCS_MAC_FILE_TRUNCATE) -+ != CCS_CONFIG_DISABLED) { -+ if (!buf.name && !ccs_get_realpath(&buf, &obj.path1)) { -+ error = -ENOMEM; -+ goto out; -+ } -+ r.obj = &obj; -+ error = ccs_path_permission(&r, CCS_TYPE_TRUNCATE, &buf); -+ } -+#endif -+out: -+ kfree(buf.name); -+ ccs_read_unlock(idx); -+ if (r.mode != CCS_CONFIG_ENFORCING) -+ error = 0; -+ return error; -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) -+ -+/** -+ * ccs_new_open_permission - Check permission for "read" and "write". -+ * -+ * @filp: Pointer to "struct file". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_new_open_permission(struct file *filp) -+{ -+ return __ccs_open_permission(filp->f_path.dentry, filp->f_path.mnt, -+ filp->f_flags); -+} -+ -+#endif -+ -+/** -+ * ccs_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "append", "getattr", "chroot" and "unmount". -+ * -+ * @operation: Type of operation. -+ * @dentry: Pointer to "struct dentry". -+ * @mnt: Pointer to "struct vfsmount". Maybe NULL. -+ * @target: Symlink's target if @operation is CCS_TYPE_SYMLINK, -+ * NULL otherwise. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_path_perm(const u8 operation, struct dentry *dentry, -+ struct vfsmount *mnt, const char *target) -+{ -+ struct ccs_request_info r; -+ struct ccs_obj_info obj = { -+ .path1.dentry = dentry, -+ .path1.mnt = mnt, -+ }; -+ int error = 0; -+ struct ccs_path_info buf; -+ bool is_enforce = false; -+ struct ccs_path_info symlink_target; -+ int idx; -+ buf.name = NULL; -+ symlink_target.name = NULL; -+ idx = ccs_read_lock(); -+ if (ccs_init_request_info(&r, ccs_p2mac[operation]) -+ == CCS_CONFIG_DISABLED) -+ goto out; -+ is_enforce = (r.mode == CCS_CONFIG_ENFORCING); -+ error = -ENOMEM; -+ if (!ccs_get_realpath(&buf, &obj.path1)) -+ goto out; -+ r.obj = &obj; -+ switch (operation) { -+ case CCS_TYPE_RMDIR: -+ case CCS_TYPE_CHROOT: -+ ccs_add_slash(&buf); -+ break; -+ case CCS_TYPE_SYMLINK: -+ symlink_target.name = ccs_encode(target); -+ if (!symlink_target.name) -+ goto out; -+ ccs_fill_path_info(&symlink_target); -+ obj.symlink_target = &symlink_target; -+ break; -+ } -+ error = ccs_path_permission(&r, operation, &buf); -+ if (operation == CCS_TYPE_SYMLINK) -+ kfree(symlink_target.name); -+out: -+ kfree(buf.name); -+ ccs_read_unlock(idx); -+ if (!is_enforce) -+ error = 0; -+ return error; -+} -+ -+/** -+ * ccs_mkdev_perm - Check permission for "mkblock" and "mkchar". -+ * -+ * @operation: Type of operation. (CCS_TYPE_MKCHAR or CCS_TYPE_MKBLOCK) -+ * @dentry: Pointer to "struct dentry". -+ * @mnt: Pointer to "struct vfsmount". Maybe NULL. -+ * @mode: Create mode. -+ * @dev: Device number. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_mkdev_perm(const u8 operation, struct dentry *dentry, -+ struct vfsmount *mnt, const unsigned int mode, -+ unsigned int dev) -+{ -+ struct ccs_request_info r; -+ struct ccs_obj_info obj = { -+ .path1.dentry = dentry, -+ .path1.mnt = mnt, -+ }; -+ int error = 0; -+ struct ccs_path_info buf; -+ bool is_enforce = false; -+ int idx; -+ idx = ccs_read_lock(); -+ if (ccs_init_request_info(&r, ccs_pnnn2mac[operation]) -+ == CCS_CONFIG_DISABLED) -+ goto out; -+ is_enforce = (r.mode == CCS_CONFIG_ENFORCING); -+ error = -EPERM; -+ if (!capable(CAP_MKNOD)) -+ goto out; -+ error = -ENOMEM; -+ if (!ccs_get_realpath(&buf, &obj.path1)) -+ goto out; -+ r.obj = &obj; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) -+ dev = new_decode_dev(dev); -+#endif -+ r.param_type = CCS_TYPE_MKDEV_ACL; -+ r.param.mkdev.filename = &buf; -+ r.param.mkdev.operation = operation; -+ r.param.mkdev.mode = mode; -+ r.param.mkdev.major = MAJOR(dev); -+ r.param.mkdev.minor = MINOR(dev); -+ error = ccs_check_acl(&r); -+ kfree(buf.name); -+out: -+ ccs_read_unlock(idx); -+ if (!is_enforce) -+ error = 0; -+ return error; -+} -+ -+/** -+ * ccs_path2_perm - Check permission for "rename", "link" and "pivot_root". -+ * -+ * @operation: Type of operation. -+ * @dentry1: Pointer to "struct dentry". -+ * @mnt1: Pointer to "struct vfsmount". Maybe NULL. -+ * @dentry2: Pointer to "struct dentry". -+ * @mnt2: Pointer to "struct vfsmount". Maybe NULL. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_path2_perm(const u8 operation, struct dentry *dentry1, -+ struct vfsmount *mnt1, struct dentry *dentry2, -+ struct vfsmount *mnt2) -+{ -+ struct ccs_request_info r; -+ int error = 0; -+ struct ccs_path_info buf1; -+ struct ccs_path_info buf2; -+ bool is_enforce = false; -+ struct ccs_obj_info obj = { -+ .path1.dentry = dentry1, -+ .path1.mnt = mnt1, -+ .path2.dentry = dentry2, -+ .path2.mnt = mnt2, -+ }; -+ int idx; -+ buf1.name = NULL; -+ buf2.name = NULL; -+ idx = ccs_read_lock(); -+ if (ccs_init_request_info(&r, ccs_pp2mac[operation]) -+ == CCS_CONFIG_DISABLED) -+ goto out; -+ is_enforce = (r.mode == CCS_CONFIG_ENFORCING); -+ error = -ENOMEM; -+ if (!ccs_get_realpath(&buf1, &obj.path1) || -+ !ccs_get_realpath(&buf2, &obj.path2)) -+ goto out; -+ switch (operation) { -+ case CCS_TYPE_RENAME: -+ case CCS_TYPE_LINK: -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) -+ if (!d_is_dir(dentry1)) -+ break; -+#else -+ if (!dentry1->d_inode || !S_ISDIR(dentry1->d_inode->i_mode)) -+ break; -+#endif -+ /* fall through */ -+ case CCS_TYPE_PIVOT_ROOT: -+ ccs_add_slash(&buf1); -+ ccs_add_slash(&buf2); -+ break; -+ } -+ r.obj = &obj; -+ r.param_type = CCS_TYPE_PATH2_ACL; -+ r.param.path2.operation = operation; -+ r.param.path2.filename1 = &buf1; -+ r.param.path2.filename2 = &buf2; -+ error = ccs_check_acl(&r); -+out: -+ kfree(buf1.name); -+ kfree(buf2.name); -+ ccs_read_unlock(idx); -+ if (!is_enforce) -+ error = 0; -+ return error; -+} -+ -+/** -+ * ccs_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp". -+ * -+ * @type: Type of operation. -+ * @dentry: Pointer to "struct dentry". -+ * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. -+ * @number: Number. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_path_number_perm(const u8 type, struct dentry *dentry, -+ struct vfsmount *vfsmnt, unsigned long number) -+{ -+ struct ccs_request_info r; -+ struct ccs_obj_info obj = { -+ .path1.dentry = dentry, -+ .path1.mnt = vfsmnt, -+ }; -+ int error = 0; -+ struct ccs_path_info buf; -+ int idx; -+ if (!dentry) -+ return 0; -+ idx = ccs_read_lock(); -+ if (ccs_init_request_info(&r, ccs_pn2mac[type]) == CCS_CONFIG_DISABLED) -+ goto out; -+ error = -ENOMEM; -+ if (!ccs_get_realpath(&buf, &obj.path1)) -+ goto out; -+ r.obj = &obj; -+ if (type == CCS_TYPE_MKDIR) -+ ccs_add_slash(&buf); -+ r.param_type = CCS_TYPE_PATH_NUMBER_ACL; -+ r.param.path_number.operation = type; -+ r.param.path_number.filename = &buf; -+ r.param.path_number.number = number; -+ error = ccs_check_acl(&r); -+ kfree(buf.name); -+out: -+ ccs_read_unlock(idx); -+ if (r.mode != CCS_CONFIG_ENFORCING) -+ error = 0; -+ return error; -+} -+ -+/** -+ * __ccs_ioctl_permission - Check permission for "ioctl". -+ * -+ * @filp: Pointer to "struct file". -+ * @cmd: Ioctl command number. -+ * @arg: Param for @cmd. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_ioctl_permission(struct file *filp, unsigned int cmd, -+ unsigned long arg) -+{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) -+ return ccs_path_number_perm(CCS_TYPE_IOCTL, filp->f_path.dentry, -+ filp->f_path.mnt, cmd); -+#else -+ return ccs_path_number_perm(CCS_TYPE_IOCTL, filp->f_dentry, -+ filp->f_vfsmnt, cmd); -+#endif -+} -+ -+/** -+ * __ccs_chmod_permission - Check permission for "chmod". -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. -+ * @mode: Mode. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_chmod_permission(struct dentry *dentry, -+ struct vfsmount *vfsmnt, mode_t mode) -+{ -+ return ccs_path_number_perm(CCS_TYPE_CHMOD, dentry, vfsmnt, -+ mode & S_IALLUGO); -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) -+ -+/** -+ * __ccs_chown_permission - Check permission for "chown/chgrp". -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. -+ * @user: User ID. -+ * @group: Group ID. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_chown_permission(struct dentry *dentry, -+ struct vfsmount *vfsmnt, kuid_t user, -+ kgid_t group) -+{ -+ int error = 0; -+ if (uid_valid(user)) -+ error = ccs_path_number_perm(CCS_TYPE_CHOWN, dentry, vfsmnt, -+ from_kuid(&init_user_ns, user)); -+ if (!error && gid_valid(group)) -+ error = ccs_path_number_perm(CCS_TYPE_CHGRP, dentry, vfsmnt, -+ from_kgid(&init_user_ns, group)); -+ return error; -+} -+ -+#else -+ -+/** -+ * __ccs_chown_permission - Check permission for "chown/chgrp". -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. -+ * @user: User ID. -+ * @group: Group ID. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_chown_permission(struct dentry *dentry, -+ struct vfsmount *vfsmnt, uid_t user, -+ gid_t group) -+{ -+ int error = 0; -+ if (user == (uid_t) -1 && group == (gid_t) -1) -+ return 0; -+ if (user != (uid_t) -1) -+ error = ccs_path_number_perm(CCS_TYPE_CHOWN, dentry, vfsmnt, -+ user); -+ if (!error && group != (gid_t) -1) -+ error = ccs_path_number_perm(CCS_TYPE_CHGRP, dentry, vfsmnt, -+ group); -+ return error; -+} -+ -+#endif -+ -+/** -+ * __ccs_fcntl_permission - Check permission for changing O_APPEND flag. -+ * -+ * @file: Pointer to "struct file". -+ * @cmd: Command number. -+ * @arg: Value for @cmd. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_fcntl_permission(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ if (!(cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))) -+ return 0; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) -+ return __ccs_open_permission(file->f_path.dentry, file->f_path.mnt, -+ O_WRONLY | (arg & O_APPEND)); -+#elif defined(RHEL_MAJOR) && RHEL_MAJOR == 6 -+ return __ccs_open_permission(file->f_dentry, file->f_vfsmnt, -+ O_WRONLY | (arg & O_APPEND)); -+#else -+ return __ccs_open_permission(file->f_dentry, file->f_vfsmnt, -+ (O_WRONLY + 1) | (arg & O_APPEND)); -+#endif -+} -+ -+/** -+ * __ccs_pivot_root_permission - Check permission for pivot_root(). -+ * -+ * @old_path: Pointer to "struct path". -+ * @new_path: Pointer to "struct path". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_pivot_root_permission(struct path *old_path, -+ struct path *new_path) -+{ -+ return ccs_path2_perm(CCS_TYPE_PIVOT_ROOT, new_path->dentry, -+ new_path->mnt, old_path->dentry, old_path->mnt); -+} -+ -+/** -+ * __ccs_chroot_permission - Check permission for chroot(). -+ * -+ * @path: Pointer to "struct path". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_chroot_permission(struct path *path) -+{ -+ return ccs_path_perm(CCS_TYPE_CHROOT, path->dentry, path->mnt, NULL); -+} -+ -+/** -+ * __ccs_umount_permission - Check permission for unmount. -+ * -+ * @mnt: Pointer to "struct vfsmount". -+ * @flags: Unused. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_umount_permission(struct vfsmount *mnt, int flags) -+{ -+ return ccs_path_perm(CCS_TYPE_UMOUNT, mnt->mnt_root, mnt, NULL); -+} -+ -+/** -+ * __ccs_mknod_permission - Check permission for vfs_mknod(). -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @mnt: Pointer to "struct vfsmount". Maybe NULL. -+ * @mode: Device type and permission. -+ * @dev: Device number for block or character device. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_mknod_permission(struct dentry *dentry, struct vfsmount *mnt, -+ const unsigned int mode, unsigned int dev) -+{ -+ int error = 0; -+ const unsigned int perm = mode & S_IALLUGO; -+ switch (mode & S_IFMT) { -+ case S_IFCHR: -+ error = ccs_mkdev_perm(CCS_TYPE_MKCHAR, dentry, mnt, perm, -+ dev); -+ break; -+ case S_IFBLK: -+ error = ccs_mkdev_perm(CCS_TYPE_MKBLOCK, dentry, mnt, perm, -+ dev); -+ break; -+ case S_IFIFO: -+ error = ccs_path_number_perm(CCS_TYPE_MKFIFO, dentry, mnt, -+ perm); -+ break; -+ case S_IFSOCK: -+ error = ccs_path_number_perm(CCS_TYPE_MKSOCK, dentry, mnt, -+ perm); -+ break; -+ case 0: -+ case S_IFREG: -+ error = ccs_path_number_perm(CCS_TYPE_CREATE, dentry, mnt, -+ perm); -+ break; -+ } -+ return error; -+} -+ -+/** -+ * __ccs_mkdir_permission - Check permission for vfs_mkdir(). -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @mnt: Pointer to "struct vfsmount". Maybe NULL. -+ * @mode: Create mode. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_mkdir_permission(struct dentry *dentry, struct vfsmount *mnt, -+ unsigned int mode) -+{ -+ return ccs_path_number_perm(CCS_TYPE_MKDIR, dentry, mnt, mode); -+} -+ -+/** -+ * __ccs_rmdir_permission - Check permission for vfs_rmdir(). -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @mnt: Pointer to "struct vfsmount". Maybe NULL. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_rmdir_permission(struct dentry *dentry, struct vfsmount *mnt) -+{ -+ return ccs_path_perm(CCS_TYPE_RMDIR, dentry, mnt, NULL); -+} -+ -+/** -+ * __ccs_unlink_permission - Check permission for vfs_unlink(). -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @mnt: Pointer to "struct vfsmount". Maybe NULL. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_unlink_permission(struct dentry *dentry, struct vfsmount *mnt) -+{ -+ return ccs_path_perm(CCS_TYPE_UNLINK, dentry, mnt, NULL); -+} -+ -+#ifdef CONFIG_CCSECURITY_FILE_GETATTR -+ -+/** -+ * __ccs_getattr_permission - Check permission for vfs_getattr(). -+ * -+ * @mnt: Pointer to "struct vfsmount". Maybe NULL. -+ * @dentry: Pointer to "struct dentry". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_getattr_permission(struct vfsmount *mnt, -+ struct dentry *dentry) -+{ -+ return ccs_path_perm(CCS_TYPE_GETATTR, dentry, mnt, NULL); -+} -+ -+#endif -+ -+/** -+ * __ccs_symlink_permission - Check permission for vfs_symlink(). -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @mnt: Pointer to "struct vfsmount". Maybe NULL. -+ * @from: Content of symlink. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_symlink_permission(struct dentry *dentry, -+ struct vfsmount *mnt, const char *from) -+{ -+ return ccs_path_perm(CCS_TYPE_SYMLINK, dentry, mnt, from); -+} -+ -+/** -+ * __ccs_truncate_permission - Check permission for notify_change(). -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @mnt: Pointer to "struct vfsmount". Maybe NULL. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_truncate_permission(struct dentry *dentry, -+ struct vfsmount *mnt) -+{ -+ return ccs_path_perm(CCS_TYPE_TRUNCATE, dentry, mnt, NULL); -+} -+ -+/** -+ * __ccs_rename_permission - Check permission for vfs_rename(). -+ * -+ * @old_dentry: Pointer to "struct dentry". -+ * @new_dentry: Pointer to "struct dentry". -+ * @mnt: Pointer to "struct vfsmount". Maybe NULL. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_rename_permission(struct dentry *old_dentry, -+ struct dentry *new_dentry, -+ struct vfsmount *mnt) -+{ -+ return ccs_path2_perm(CCS_TYPE_RENAME, old_dentry, mnt, new_dentry, -+ mnt); -+} -+ -+/** -+ * __ccs_link_permission - Check permission for vfs_link(). -+ * -+ * @old_dentry: Pointer to "struct dentry". -+ * @new_dentry: Pointer to "struct dentry". -+ * @mnt: Pointer to "struct vfsmount". Maybe NULL. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_link_permission(struct dentry *old_dentry, -+ struct dentry *new_dentry, -+ struct vfsmount *mnt) -+{ -+ return ccs_path2_perm(CCS_TYPE_LINK, old_dentry, mnt, new_dentry, mnt); -+} -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) -+ -+/** -+ * __ccs_open_exec_permission - Check permission for open_exec(). -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @mnt: Pointer to "struct vfsmount". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_open_exec_permission(struct dentry *dentry, -+ struct vfsmount *mnt) -+{ -+ return (ccs_current_flags() & CCS_TASK_IS_IN_EXECVE) ? -+ __ccs_open_permission(dentry, mnt, O_RDONLY + 1) : 0; -+} -+ -+/** -+ * __ccs_uselib_permission - Check permission for sys_uselib(). -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @mnt: Pointer to "struct vfsmount". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_uselib_permission(struct dentry *dentry, struct vfsmount *mnt) -+{ -+ return __ccs_open_permission(dentry, mnt, O_RDONLY + 1); -+} -+ -+#endif -+ -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) || (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL)) -+ -+/** -+ * __ccs_parse_table - Check permission for parse_table(). -+ * -+ * @name: Pointer to "int __user". -+ * @nlen: Number of elements in @name. -+ * @oldval: Pointer to "void __user". -+ * @newval: Pointer to "void __user". -+ * @table: Pointer to "struct ctl_table". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Note that this function is racy because this function checks values in -+ * userspace memory which could be changed after permission check. -+ */ -+static int __ccs_parse_table(int __user *name, int nlen, void __user *oldval, -+ void __user *newval, struct ctl_table *table) -+{ -+ int n; -+ int error = -ENOMEM; -+ int op = 0; -+ struct ccs_path_info buf; -+ char *buffer = NULL; -+ struct ccs_request_info r; -+ int idx; -+ if (oldval) -+ op |= 004; -+ if (newval) -+ op |= 002; -+ if (!op) /* Neither read nor write */ -+ return 0; -+ idx = ccs_read_lock(); -+ if (ccs_init_request_info(&r, CCS_MAC_FILE_OPEN) -+ == CCS_CONFIG_DISABLED) { -+ error = 0; -+ goto out; -+ } -+ buffer = kmalloc(PAGE_SIZE, CCS_GFP_FLAGS); -+ if (!buffer) -+ goto out; -+ snprintf(buffer, PAGE_SIZE - 1, "proc:/sys"); -+repeat: -+ if (!nlen) { -+ error = -ENOTDIR; -+ goto out; -+ } -+ if (get_user(n, name)) { -+ error = -EFAULT; -+ goto out; -+ } -+ for ( ; table->ctl_name -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21) -+ || table->procname -+#endif -+ ; table++) { -+ int pos; -+ const char *cp; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21) -+ if (n != table->ctl_name && table->ctl_name != CTL_ANY) -+ continue; -+#else -+ if (!n || n != table->ctl_name) -+ continue; -+#endif -+ pos = strlen(buffer); -+ cp = table->procname; -+ error = -ENOMEM; -+ if (cp) { -+ int len = strlen(cp); -+ if (len + 2 > PAGE_SIZE - 1) -+ goto out; -+ buffer[pos++] = '/'; -+ memmove(buffer + pos, cp, len + 1); -+ } else { -+ /* Assume nobody assigns "=\$=" for procname. */ -+ snprintf(buffer + pos, PAGE_SIZE - pos - 1, -+ "/=%d=", table->ctl_name); -+ if (!memchr(buffer, '\0', PAGE_SIZE - 2)) -+ goto out; -+ } -+ if (!table->child) -+ goto no_child; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21) -+ if (!table->strategy) -+ goto no_strategy; -+ /* printk("sysctl='%s'\n", buffer); */ -+ buf.name = ccs_encode(buffer); -+ if (!buf.name) -+ goto out; -+ ccs_fill_path_info(&buf); -+ if (op & MAY_READ) -+ error = ccs_path_permission(&r, CCS_TYPE_READ, &buf); -+ else -+ error = 0; -+ if (!error && (op & MAY_WRITE)) -+ error = ccs_path_permission(&r, CCS_TYPE_WRITE, &buf); -+ kfree(buf.name); -+ if (error) -+ goto out; -+no_strategy: -+#endif -+ name++; -+ nlen--; -+ table = table->child; -+ goto repeat; -+no_child: -+ /* printk("sysctl='%s'\n", buffer); */ -+ buf.name = ccs_encode(buffer); -+ if (!buf.name) -+ goto out; -+ ccs_fill_path_info(&buf); -+ if (op & MAY_READ) -+ error = ccs_path_permission(&r, CCS_TYPE_READ, &buf); -+ else -+ error = 0; -+ if (!error && (op & MAY_WRITE)) -+ error = ccs_path_permission(&r, CCS_TYPE_WRITE, &buf); -+ kfree(buf.name); -+ goto out; -+ } -+ error = -ENOTDIR; -+out: -+ ccs_read_unlock(idx); -+ kfree(buffer); -+ return error; -+} -+ -+#endif -+ -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24) -+ -+/** -+ * ccs_old_pivot_root_permission - Check permission for pivot_root(). -+ * -+ * @old_nd: Pointer to "struct nameidata". -+ * @new_nd: Pointer to "struct nameidata". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_old_pivot_root_permission(struct nameidata *old_nd, -+ struct nameidata *new_nd) -+{ -+ struct path old_path = { old_nd->mnt, old_nd->dentry }; -+ struct path new_path = { new_nd->mnt, new_nd->dentry }; -+ return __ccs_pivot_root_permission(&old_path, &new_path); -+} -+ -+/** -+ * ccs_old_chroot_permission - Check permission for chroot(). -+ * -+ * @nd: Pointer to "struct nameidata". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_old_chroot_permission(struct nameidata *nd) -+{ -+ struct path path = { nd->mnt, nd->dentry }; -+ return __ccs_chroot_permission(&path); -+} -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_NETWORK -+ -+/** -+ * ccs_address_matches_group - Check whether the given address matches members of the given address group. -+ * -+ * @is_ipv6: True if @address is an IPv6 address. -+ * @address: An IPv4 or IPv6 address. -+ * @group: Pointer to "struct ccs_address_group". -+ * -+ * Returns true if @address matches addresses in @group group, false otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static bool ccs_address_matches_group(const bool is_ipv6, const u32 *address, -+ const struct ccs_group *group) -+{ -+ struct ccs_address_group *member; -+ bool matched = false; -+ const u8 size = is_ipv6 ? 16 : 4; -+ list_for_each_entry_srcu(member, &group->member_list, head.list, -+ &ccs_ss) { -+ if (member->head.is_deleted) -+ continue; -+ if (member->address.is_ipv6 != is_ipv6) -+ continue; -+ if (memcmp(&member->address.ip[0], address, size) > 0 || -+ memcmp(address, &member->address.ip[1], size) > 0) -+ continue; -+ matched = true; -+ break; -+ } -+ return matched; -+} -+ -+/** -+ * ccs_check_inet_acl - Check permission for inet domain socket operation. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @ptr: Pointer to "struct ccs_acl_info". -+ * -+ * Returns true if granted, false otherwise. -+ */ -+static bool ccs_check_inet_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr) -+{ -+ const struct ccs_inet_acl *acl = container_of(ptr, typeof(*acl), head); -+ const u8 size = r->param.inet_network.is_ipv6 ? 16 : 4; -+ if (!(ptr->perm & (1 << r->param.inet_network.operation)) || -+ !ccs_compare_number_union(r->param.inet_network.port, &acl->port)) -+ return false; -+ if (acl->address.group) -+ return ccs_address_matches_group(r->param.inet_network.is_ipv6, -+ r->param.inet_network.address, -+ acl->address.group); -+ return acl->address.is_ipv6 == r->param.inet_network.is_ipv6 && -+ memcmp(&acl->address.ip[0], -+ r->param.inet_network.address, size) <= 0 && -+ memcmp(r->param.inet_network.address, -+ &acl->address.ip[1], size) <= 0; -+} -+ -+/** -+ * ccs_check_unix_acl - Check permission for unix domain socket operation. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @ptr: Pointer to "struct ccs_acl_info". -+ * -+ * Returns true if granted, false otherwise. -+ */ -+static bool ccs_check_unix_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr) -+{ -+ const struct ccs_unix_acl *acl = container_of(ptr, typeof(*acl), head); -+ return (ptr->perm & (1 << r->param.unix_network.operation)) && -+ ccs_compare_name_union(r->param.unix_network.address, -+ &acl->name); -+} -+ -+/** -+ * ccs_inet_entry - Check permission for INET network operation. -+ * -+ * @address: Pointer to "struct ccs_addr_info". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_inet_entry(const struct ccs_addr_info *address) -+{ -+ const int idx = ccs_read_lock(); -+ struct ccs_request_info r; -+ int error = 0; -+ const u8 type = ccs_inet2mac[address->protocol][address->operation]; -+ if (type && ccs_init_request_info(&r, type) != CCS_CONFIG_DISABLED) { -+ r.param_type = CCS_TYPE_INET_ACL; -+ r.param.inet_network.protocol = address->protocol; -+ r.param.inet_network.operation = address->operation; -+ r.param.inet_network.is_ipv6 = address->inet.is_ipv6; -+ r.param.inet_network.address = address->inet.address; -+ r.param.inet_network.port = ntohs(address->inet.port); -+ r.dont_sleep_on_enforce_error = -+ address->operation == CCS_NETWORK_ACCEPT -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ || address->operation == CCS_NETWORK_RECV -+#endif -+ ; -+ error = ccs_check_acl(&r); -+ } -+ ccs_read_unlock(idx); -+ return error; -+} -+ -+/** -+ * ccs_check_inet_address - Check permission for inet domain socket's operation. -+ * -+ * @addr: Pointer to "struct sockaddr". -+ * @addr_len: Size of @addr. -+ * @port: Port number. -+ * @address: Pointer to "struct ccs_addr_info". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_check_inet_address(const struct sockaddr *addr, -+ const unsigned int addr_len, const u16 port, -+ struct ccs_addr_info *address) -+{ -+ struct ccs_inet_addr_info *i = &address->inet; -+ switch (addr->sa_family) { -+ case AF_INET6: -+ if (addr_len < SIN6_LEN_RFC2133) -+ goto skip; -+ i->is_ipv6 = true; -+ i->address = (u32 *) -+ ((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr; -+ i->port = ((struct sockaddr_in6 *) addr)->sin6_port; -+ break; -+ case AF_INET: -+ if (addr_len < sizeof(struct sockaddr_in)) -+ goto skip; -+ i->is_ipv6 = false; -+ i->address = (u32 *) &((struct sockaddr_in *) addr)->sin_addr; -+ i->port = ((struct sockaddr_in *) addr)->sin_port; -+ break; -+ default: -+ goto skip; -+ } -+ if (address->protocol == SOCK_RAW) -+ i->port = htons(port); -+ return ccs_inet_entry(address); -+skip: -+ return 0; -+} -+ -+/** -+ * ccs_unix_entry - Check permission for UNIX network operation. -+ * -+ * @address: Pointer to "struct ccs_addr_info". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_unix_entry(const struct ccs_addr_info *address) -+{ -+ const int idx = ccs_read_lock(); -+ struct ccs_request_info r; -+ int error = 0; -+ const u8 type = ccs_unix2mac[address->protocol][address->operation]; -+ if (type && ccs_init_request_info(&r, type) != CCS_CONFIG_DISABLED) { -+ char *buf = address->unix0.addr; -+ int len = address->unix0.addr_len - sizeof(sa_family_t); -+ if (len <= 0) { -+ buf = "anonymous"; -+ len = 9; -+ } else if (buf[0]) { -+ len = strnlen(buf, len); -+ } -+ buf = ccs_encode2(buf, len); -+ if (buf) { -+ struct ccs_path_info addr; -+ addr.name = buf; -+ ccs_fill_path_info(&addr); -+ r.param_type = CCS_TYPE_UNIX_ACL; -+ r.param.unix_network.protocol = address->protocol; -+ r.param.unix_network.operation = address->operation; -+ r.param.unix_network.address = &addr; -+ r.dont_sleep_on_enforce_error = -+ address->operation == CCS_NETWORK_ACCEPT -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ || address->operation == CCS_NETWORK_RECV -+#endif -+ ; -+ error = ccs_check_acl(&r); -+ kfree(buf); -+ } else -+ error = -ENOMEM; -+ } -+ ccs_read_unlock(idx); -+ return error; -+} -+ -+/** -+ * ccs_check_unix_address - Check permission for unix domain socket's operation. -+ * -+ * @addr: Pointer to "struct sockaddr". -+ * @addr_len: Size of @addr. -+ * @address: Pointer to "struct ccs_addr_info". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_check_unix_address(struct sockaddr *addr, -+ const unsigned int addr_len, -+ struct ccs_addr_info *address) -+{ -+ struct ccs_unix_addr_info *u = &address->unix0; -+ if (addr->sa_family != AF_UNIX) -+ return 0; -+ u->addr = ((struct sockaddr_un *) addr)->sun_path; -+ u->addr_len = addr_len; -+ return ccs_unix_entry(address); -+} -+ -+/** -+ * ccs_sock_family - Get socket's family. -+ * -+ * @sk: Pointer to "struct sock". -+ * -+ * Returns one of PF_INET, PF_INET6, PF_UNIX or 0. -+ */ -+static u8 ccs_sock_family(struct sock *sk) -+{ -+ u8 family; -+ if (ccs_kernel_service()) -+ return 0; -+ family = sk->sk_family; -+ switch (family) { -+ case PF_INET: -+ case PF_INET6: -+ case PF_UNIX: -+ return family; -+ default: -+ return 0; -+ } -+} -+ -+/** -+ * __ccs_socket_listen_permission - Check permission for listening a socket. -+ * -+ * @sock: Pointer to "struct socket". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_socket_listen_permission(struct socket *sock) -+{ -+ struct ccs_addr_info address; -+ const u8 family = ccs_sock_family(sock->sk); -+ const unsigned int type = sock->type; -+ struct sockaddr_storage addr; -+ int addr_len; -+ if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET)) -+ return 0; -+ { -+ const int error = sock->ops->getname(sock, (struct sockaddr *) -+ &addr, &addr_len, 0); -+ if (error) -+ return error; -+ } -+ address.protocol = type; -+ address.operation = CCS_NETWORK_LISTEN; -+ if (family == PF_UNIX) -+ return ccs_check_unix_address((struct sockaddr *) &addr, -+ addr_len, &address); -+ return ccs_check_inet_address((struct sockaddr *) &addr, addr_len, 0, -+ &address); -+} -+ -+/** -+ * __ccs_socket_connect_permission - Check permission for setting the remote address of a socket. -+ * -+ * @sock: Pointer to "struct socket". -+ * @addr: Pointer to "struct sockaddr". -+ * @addr_len: Size of @addr. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_socket_connect_permission(struct socket *sock, -+ struct sockaddr *addr, int addr_len) -+{ -+ struct ccs_addr_info address; -+ const u8 family = ccs_sock_family(sock->sk); -+ const unsigned int type = sock->type; -+ if (!family) -+ return 0; -+ address.protocol = type; -+ switch (type) { -+ case SOCK_DGRAM: -+ case SOCK_RAW: -+ address.operation = CCS_NETWORK_SEND; -+ break; -+ case SOCK_STREAM: -+ case SOCK_SEQPACKET: -+ address.operation = CCS_NETWORK_CONNECT; -+ break; -+ default: -+ return 0; -+ } -+ if (family == PF_UNIX) -+ return ccs_check_unix_address(addr, addr_len, &address); -+ return ccs_check_inet_address(addr, addr_len, sock->sk->sk_protocol, -+ &address); -+} -+ -+/** -+ * __ccs_socket_bind_permission - Check permission for setting the local address of a socket. -+ * -+ * @sock: Pointer to "struct socket". -+ * @addr: Pointer to "struct sockaddr". -+ * @addr_len: Size of @addr. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_socket_bind_permission(struct socket *sock, -+ struct sockaddr *addr, int addr_len) -+{ -+ struct ccs_addr_info address; -+ const u8 family = ccs_sock_family(sock->sk); -+ const unsigned int type = sock->type; -+ if (!family) -+ return 0; -+ switch (type) { -+ case SOCK_STREAM: -+ case SOCK_DGRAM: -+ case SOCK_RAW: -+ case SOCK_SEQPACKET: -+ address.protocol = type; -+ address.operation = CCS_NETWORK_BIND; -+ break; -+ default: -+ return 0; -+ } -+ if (family == PF_UNIX) -+ return ccs_check_unix_address(addr, addr_len, &address); -+ return ccs_check_inet_address(addr, addr_len, sock->sk->sk_protocol, -+ &address); -+} -+ -+/** -+ * __ccs_socket_sendmsg_permission - Check permission for sending a datagram. -+ * -+ * @sock: Pointer to "struct socket". -+ * @msg: Pointer to "struct msghdr". -+ * @size: Unused. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_socket_sendmsg_permission(struct socket *sock, -+ struct msghdr *msg, int size) -+{ -+ struct ccs_addr_info address; -+ const u8 family = ccs_sock_family(sock->sk); -+ const unsigned int type = sock->type; -+ if (!msg->msg_name || !family || -+ (type != SOCK_DGRAM && type != SOCK_RAW)) -+ return 0; -+ address.protocol = type; -+ address.operation = CCS_NETWORK_SEND; -+ if (family == PF_UNIX) -+ return ccs_check_unix_address((struct sockaddr *) -+ msg->msg_name, msg->msg_namelen, -+ &address); -+ return ccs_check_inet_address((struct sockaddr *) msg->msg_name, -+ msg->msg_namelen, sock->sk->sk_protocol, -+ &address); -+} -+ -+/** -+ * __ccs_socket_post_accept_permission - Check permission for accepting a socket. -+ * -+ * @sock: Pointer to "struct socket". -+ * @newsock: Pointer to "struct socket". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_socket_post_accept_permission(struct socket *sock, -+ struct socket *newsock) -+{ -+ struct ccs_addr_info address; -+ const u8 family = ccs_sock_family(sock->sk); -+ const unsigned int type = sock->type; -+ struct sockaddr_storage addr; -+ int addr_len; -+ if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET)) -+ return 0; -+ { -+ const int error = newsock->ops->getname(newsock, -+ (struct sockaddr *) -+ &addr, &addr_len, 2); -+ if (error) -+ return error; -+ } -+ address.protocol = type; -+ address.operation = CCS_NETWORK_ACCEPT; -+ if (family == PF_UNIX) -+ return ccs_check_unix_address((struct sockaddr *) &addr, -+ addr_len, &address); -+ return ccs_check_inet_address((struct sockaddr *) &addr, addr_len, 0, -+ &address); -+} -+ -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ -+/** -+ * __ccs_socket_post_recvmsg_permission - Check permission for receiving a datagram. -+ * -+ * @sk: Pointer to "struct sock". -+ * @skb: Pointer to "struct sk_buff". -+ * @flags: Flags passed to skb_recv_datagram(). -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_socket_post_recvmsg_permission(struct sock *sk, -+ struct sk_buff *skb, int flags) -+{ -+ struct ccs_addr_info address; -+ const u8 family = ccs_sock_family(sk); -+ const unsigned int type = sk->sk_type; -+ struct sockaddr_storage addr; -+ if (!family) -+ return 0; -+ switch (type) { -+ case SOCK_DGRAM: -+ case SOCK_RAW: -+ address.protocol = type; -+ break; -+ default: -+ return 0; -+ } -+ address.operation = CCS_NETWORK_RECV; -+ switch (family) { -+ case PF_INET6: -+ { -+ struct in6_addr *sin6 = (struct in6_addr *) &addr; -+ address.inet.is_ipv6 = true; -+ if (type == SOCK_DGRAM && -+ skb->protocol == htons(ETH_P_IP)) -+ ipv6_addr_set(sin6, 0, 0, htonl(0xffff), -+ ip_hdr(skb)->saddr); -+ else -+ *sin6 = ipv6_hdr(skb)->saddr; -+ break; -+ } -+ case PF_INET: -+ { -+ struct in_addr *sin4 = (struct in_addr *) &addr; -+ address.inet.is_ipv6 = false; -+ sin4->s_addr = ip_hdr(skb)->saddr; -+ break; -+ } -+ default: /* == PF_UNIX */ -+ { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) -+ struct unix_address *u = unix_sk(skb->sk)->addr; -+#else -+ struct unix_address *u = -+ skb->sk->protinfo.af_unix.addr; -+#endif -+ unsigned int addr_len; -+ if (u && u->len <= sizeof(addr)) { -+ addr_len = u->len; -+ memcpy(&addr, u->name, addr_len); -+ } else { -+ addr_len = 0; -+ addr.ss_family = AF_UNIX; -+ } -+ if (ccs_check_unix_address((struct sockaddr *) &addr, -+ addr_len, &address)) -+ goto out; -+ return 0; -+ } -+ } -+ address.inet.address = (u32 *) &addr; -+ if (type == SOCK_DGRAM) -+ address.inet.port = udp_hdr(skb)->source; -+ else -+ address.inet.port = htons(sk->sk_protocol); -+ if (ccs_inet_entry(&address)) -+ goto out; -+ return 0; -+out: -+ /* -+ * Remove from queue if MSG_PEEK is used so that -+ * the head message from unwanted source in receive queue will not -+ * prevent the caller from picking up next message from wanted source -+ * when the caller is using MSG_PEEK flag for picking up. -+ */ -+ { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) -+ bool slow = false; -+ if (type == SOCK_DGRAM && family != PF_UNIX) -+ slow = lock_sock_fast(sk); -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) -+ if (type == SOCK_DGRAM && family != PF_UNIX) -+ lock_sock(sk); -+#elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR >= 2 -+ if (type == SOCK_DGRAM && family != PF_UNIX) -+ lock_sock(sk); -+#endif -+ skb_kill_datagram(sk, skb, flags); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) -+ if (type == SOCK_DGRAM && family != PF_UNIX) -+ unlock_sock_fast(sk, slow); -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) -+ if (type == SOCK_DGRAM && family != PF_UNIX) -+ release_sock(sk); -+#elif defined(RHEL_MAJOR) && RHEL_MAJOR == 5 && defined(RHEL_MINOR) && RHEL_MINOR >= 2 -+ if (type == SOCK_DGRAM && family != PF_UNIX) -+ release_sock(sk); -+#endif -+ } -+ return -EPERM; -+} -+ -+#endif -+ -+#endif -+ -+#if defined(CONFIG_CCSECURITY_CAPABILITY) || defined(CONFIG_CCSECURITY_NETWORK) -+ -+/** -+ * ccs_kernel_service - Check whether I'm kernel service or not. -+ * -+ * Returns true if I'm kernel service, false otherwise. -+ */ -+static bool ccs_kernel_service(void) -+{ -+ /* Nothing to do if I am a kernel service. */ -+ return segment_eq(get_fs(), KERNEL_DS); -+} -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ -+/** -+ * ccs_check_capability_acl - Check permission for capability operation. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @ptr: Pointer to "struct ccs_acl_info". -+ * -+ * Returns true if granted, false otherwise. -+ */ -+static bool ccs_check_capability_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr) -+{ -+ const struct ccs_capability_acl *acl = -+ container_of(ptr, typeof(*acl), head); -+ return acl->operation == r->param.capability.operation; -+} -+ -+/** -+ * ccs_capable - Check permission for capability. -+ * -+ * @operation: Type of operation. -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool __ccs_capable(const u8 operation) -+{ -+ struct ccs_request_info r; -+ int error = 0; -+ const int idx = ccs_read_lock(); -+ if (ccs_init_request_info(&r, ccs_c2mac[operation]) -+ != CCS_CONFIG_DISABLED) { -+ r.param_type = CCS_TYPE_CAPABILITY_ACL; -+ r.param.capability.operation = operation; -+ error = ccs_check_acl(&r); -+ } -+ ccs_read_unlock(idx); -+ return !error; -+} -+ -+/** -+ * __ccs_socket_create_permission - Check permission for creating a socket. -+ * -+ * @family: Protocol family. -+ * @type: Unused. -+ * @protocol: Unused. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __ccs_socket_create_permission(int family, int type, int protocol) -+{ -+ if (ccs_kernel_service()) -+ return 0; -+ if (family == PF_PACKET && !ccs_capable(CCS_USE_PACKET_SOCKET)) -+ return -EPERM; -+ if (family == PF_ROUTE && !ccs_capable(CCS_USE_ROUTE_SOCKET)) -+ return -EPERM; -+ return 0; -+} -+ -+/** -+ * __ccs_ptrace_permission - Check permission for ptrace(). -+ * -+ * @request: Unused. -+ * @pid: Unused. -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Since this function is called from location where it is permitted to sleep, -+ * it is racy to check target process's domainname anyway. Therefore, we don't -+ * use target process's domainname. -+ */ -+static int __ccs_ptrace_permission(long request, long pid) -+{ -+ return __ccs_capable(CCS_SYS_PTRACE) ? 0 : -EPERM; -+} -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_IPC -+ -+/** -+ * ccs_check_signal_acl - Check permission for signal operation. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @ptr: Pointer to "struct ccs_acl_info". -+ * -+ * Returns true if granted, false otherwise. -+ */ -+static bool ccs_check_signal_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr) -+{ -+ const struct ccs_signal_acl *acl = -+ container_of(ptr, typeof(*acl), head); -+ if (ccs_compare_number_union(r->param.signal.sig, &acl->sig)) { -+ const int len = acl->domainname->total_len; -+ if (!strncmp(acl->domainname->name, -+ r->param.signal.dest_pattern, len)) { -+ switch (r->param.signal.dest_pattern[len]) { -+ case ' ': -+ case '\0': -+ return true; -+ } -+ } -+ } -+ return false; -+} -+ -+/** -+ * ccs_signal_acl2 - Check permission for signal. -+ * -+ * @sig: Signal number. -+ * @pid: Target's PID. -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_signal_acl2(const int sig, const int pid) -+{ -+ struct ccs_request_info r; -+ struct ccs_domain_info *dest = NULL; -+ const struct ccs_domain_info * const domain = ccs_current_domain(); -+ if (ccs_init_request_info(&r, CCS_MAC_SIGNAL) == CCS_CONFIG_DISABLED) -+ return 0; -+ if (!sig) -+ return 0; /* No check for NULL signal. */ -+ r.param_type = CCS_TYPE_SIGNAL_ACL; -+ r.param.signal.sig = sig; -+ r.param.signal.dest_pattern = domain->domainname->name; -+ r.granted = true; -+ if (ccs_sys_getpid() == pid) { -+ ccs_audit_log(&r); -+ return 0; /* No check for self process. */ -+ } -+ { /* Simplified checking. */ -+ struct task_struct *p = NULL; -+ ccs_tasklist_lock(); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) -+ if (pid > 0) -+ p = ccsecurity_exports.find_task_by_vpid((pid_t) pid); -+ else if (pid == 0) -+ p = current; -+ else if (pid == -1) -+ dest = &ccs_kernel_domain; -+ else -+ p = ccsecurity_exports.find_task_by_vpid((pid_t) -pid); -+#else -+ if (pid > 0) -+ p = find_task_by_pid((pid_t) pid); -+ else if (pid == 0) -+ p = current; -+ else if (pid == -1) -+ dest = &ccs_kernel_domain; -+ else -+ p = find_task_by_pid((pid_t) -pid); -+#endif -+ if (p) -+ dest = ccs_task_domain(p); -+ ccs_tasklist_unlock(); -+ } -+ if (!dest) -+ return 0; /* I can't find destinatioin. */ -+ if (domain == dest) { -+ ccs_audit_log(&r); -+ return 0; /* No check for self domain. */ -+ } -+ r.param.signal.dest_pattern = dest->domainname->name; -+ return ccs_check_acl(&r); -+} -+ -+/** -+ * ccs_signal_acl - Check permission for signal. -+ * -+ * @pid: Target's PID. -+ * @sig: Signal number. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_signal_acl(const int pid, const int sig) -+{ -+ int error; -+ if (!sig) -+ error = 0; -+ else { -+ const int idx = ccs_read_lock(); -+ error = ccs_signal_acl2(sig, pid); -+ ccs_read_unlock(idx); -+ } -+ return error; -+} -+ -+/** -+ * ccs_signal_acl0 - Permission check for signal(). -+ * -+ * @tgid: Unused. -+ * @pid: Target's PID. -+ * @sig: Signal number. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_signal_acl0(pid_t tgid, pid_t pid, int sig) -+{ -+ return ccs_signal_acl(pid, sig); -+} -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_MISC -+ -+/** -+ * ccs_check_env_acl - Check permission for environment variable's name. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @ptr: Pointer to "struct ccs_acl_info". -+ * -+ * Returns true if granted, false otherwise. -+ */ -+static bool ccs_check_env_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr) -+{ -+ const struct ccs_env_acl *acl = container_of(ptr, typeof(*acl), head); -+ return ccs_path_matches_pattern(r->param.environ.name, acl->env); -+} -+ -+/** -+ * ccs_env_perm - Check permission for environment variable's name. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @env: The name of environment variable. -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_env_perm(struct ccs_request_info *r, const char *env) -+{ -+ struct ccs_path_info environ; -+ if (!env || !*env) -+ return 0; -+ environ.name = env; -+ ccs_fill_path_info(&environ); -+ r->param_type = CCS_TYPE_ENV_ACL; -+ r->param.environ.name = &environ; -+ return ccs_check_acl(r); -+} -+ -+/** -+ * ccs_environ - Check permission for environment variable names. -+ * -+ * @ee: Pointer to "struct ccs_execve". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_environ(struct ccs_execve *ee) -+{ -+ struct ccs_request_info *r = &ee->r; -+ struct linux_binprm *bprm = ee->bprm; -+ /* env_page.data is allocated by ccs_dump_page(). */ -+ struct ccs_page_dump env_page = { }; -+ char *arg_ptr; /* Size is CCS_EXEC_TMPSIZE bytes */ -+ int arg_len = 0; -+ unsigned long pos = bprm->p; -+ int offset = pos % PAGE_SIZE; -+ int argv_count = bprm->argc; -+ int envp_count = bprm->envc; -+ /* printk(KERN_DEBUG "start %d %d\n", argv_count, envp_count); */ -+ int error = -ENOMEM; -+ ee->r.type = CCS_MAC_ENVIRON; -+ ee->r.profile = ccs_current_domain()->profile; -+ ee->r.mode = ccs_get_mode(ee->r.profile, CCS_MAC_ENVIRON); -+ if (!r->mode || !envp_count) -+ return 0; -+ arg_ptr = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS); -+ if (!arg_ptr) -+ goto out; -+ while (error == -ENOMEM) { -+ if (!ccs_dump_page(bprm, pos, &env_page)) -+ goto out; -+ pos += PAGE_SIZE - offset; -+ /* Read. */ -+ while (argv_count && offset < PAGE_SIZE) { -+ if (!env_page.data[offset++]) -+ argv_count--; -+ } -+ if (argv_count) { -+ offset = 0; -+ continue; -+ } -+ while (offset < PAGE_SIZE) { -+ const unsigned char c = env_page.data[offset++]; -+ if (c && arg_len < CCS_EXEC_TMPSIZE - 10) { -+ if (c == '=') { -+ arg_ptr[arg_len++] = '\0'; -+ } else if (c == '\\') { -+ arg_ptr[arg_len++] = '\\'; -+ arg_ptr[arg_len++] = '\\'; -+ } else if (c > ' ' && c < 127) { -+ arg_ptr[arg_len++] = c; -+ } else { -+ arg_ptr[arg_len++] = '\\'; -+ arg_ptr[arg_len++] = (c >> 6) + '0'; -+ arg_ptr[arg_len++] -+ = ((c >> 3) & 7) + '0'; -+ arg_ptr[arg_len++] = (c & 7) + '0'; -+ } -+ } else { -+ arg_ptr[arg_len] = '\0'; -+ } -+ if (c) -+ continue; -+ if (ccs_env_perm(r, arg_ptr)) { -+ error = -EPERM; -+ break; -+ } -+ if (!--envp_count) { -+ error = 0; -+ break; -+ } -+ arg_len = 0; -+ } -+ offset = 0; -+ } -+out: -+ if (r->mode != CCS_CONFIG_ENFORCING) -+ error = 0; -+ kfree(env_page.data); -+ kfree(arg_ptr); -+ return error; -+} -+ -+#endif -+ -+/** -+ * ccs_argv - Check argv[] in "struct linux_binbrm". -+ * -+ * @index: Index number of @arg_ptr. -+ * @arg_ptr: Contents of argv[@index]. -+ * @argc: Length of @argv. -+ * @argv: Pointer to "struct ccs_argv". -+ * @checked: Set to true if @argv[@index] was found. -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool ccs_argv(const unsigned int index, const char *arg_ptr, -+ const int argc, const struct ccs_argv *argv, -+ u8 *checked) -+{ -+ int i; -+ struct ccs_path_info arg; -+ arg.name = arg_ptr; -+ for (i = 0; i < argc; argv++, checked++, i++) { -+ bool result; -+ if (index != argv->index) -+ continue; -+ *checked = 1; -+ ccs_fill_path_info(&arg); -+ result = ccs_path_matches_pattern(&arg, argv->value); -+ if (argv->is_not) -+ result = !result; -+ if (!result) -+ return false; -+ } -+ return true; -+} -+ -+/** -+ * ccs_envp - Check envp[] in "struct linux_binbrm". -+ * -+ * @env_name: The name of environment variable. -+ * @env_value: The value of environment variable. -+ * @envc: Length of @envp. -+ * @envp: Pointer to "struct ccs_envp". -+ * @checked: Set to true if @envp[@env_name] was found. -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool ccs_envp(const char *env_name, const char *env_value, -+ const int envc, const struct ccs_envp *envp, -+ u8 *checked) -+{ -+ int i; -+ struct ccs_path_info name; -+ struct ccs_path_info value; -+ name.name = env_name; -+ ccs_fill_path_info(&name); -+ value.name = env_value; -+ ccs_fill_path_info(&value); -+ for (i = 0; i < envc; envp++, checked++, i++) { -+ bool result; -+ if (!ccs_path_matches_pattern(&name, envp->name)) -+ continue; -+ *checked = 1; -+ if (envp->value) { -+ result = ccs_path_matches_pattern(&value, envp->value); -+ if (envp->is_not) -+ result = !result; -+ } else { -+ result = true; -+ if (!envp->is_not) -+ result = !result; -+ } -+ if (!result) -+ return false; -+ } -+ return true; -+} -+ -+/** -+ * ccs_scan_bprm - Scan "struct linux_binprm". -+ * -+ * @ee: Pointer to "struct ccs_execve". -+ * @argc: Length of @argc. -+ * @argv: Pointer to "struct ccs_argv". -+ * @envc: Length of @envp. -+ * @envp: Poiner to "struct ccs_envp". -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool ccs_scan_bprm(struct ccs_execve *ee, -+ const u16 argc, const struct ccs_argv *argv, -+ const u16 envc, const struct ccs_envp *envp) -+{ -+ struct linux_binprm *bprm = ee->bprm; -+ struct ccs_page_dump *dump = &ee->dump; -+ char *arg_ptr = ee->tmp; -+ int arg_len = 0; -+ unsigned long pos = bprm->p; -+ int offset = pos % PAGE_SIZE; -+ int argv_count = bprm->argc; -+ int envp_count = bprm->envc; -+ bool result = true; -+ u8 local_checked[32]; -+ u8 *checked; -+ if (argc + envc <= sizeof(local_checked)) { -+ checked = local_checked; -+ memset(local_checked, 0, sizeof(local_checked)); -+ } else { -+ checked = kzalloc(argc + envc, CCS_GFP_FLAGS); -+ if (!checked) -+ return false; -+ } -+ while (argv_count || envp_count) { -+ if (!ccs_dump_page(bprm, pos, dump)) { -+ result = false; -+ goto out; -+ } -+ pos += PAGE_SIZE - offset; -+ while (offset < PAGE_SIZE) { -+ /* Read. */ -+ const char *kaddr = dump->data; -+ const unsigned char c = kaddr[offset++]; -+ if (c && arg_len < CCS_EXEC_TMPSIZE - 10) { -+ if (c == '\\') { -+ arg_ptr[arg_len++] = '\\'; -+ arg_ptr[arg_len++] = '\\'; -+ } else if (c > ' ' && c < 127) { -+ arg_ptr[arg_len++] = c; -+ } else { -+ arg_ptr[arg_len++] = '\\'; -+ arg_ptr[arg_len++] = (c >> 6) + '0'; -+ arg_ptr[arg_len++] = -+ ((c >> 3) & 7) + '0'; -+ arg_ptr[arg_len++] = (c & 7) + '0'; -+ } -+ } else { -+ arg_ptr[arg_len] = '\0'; -+ } -+ if (c) -+ continue; -+ /* Check. */ -+ if (argv_count) { -+ if (!ccs_argv(bprm->argc - argv_count, -+ arg_ptr, argc, argv, -+ checked)) { -+ result = false; -+ break; -+ } -+ argv_count--; -+ } else if (envp_count) { -+ char *cp = strchr(arg_ptr, '='); -+ if (cp) { -+ *cp = '\0'; -+ if (!ccs_envp(arg_ptr, cp + 1, -+ envc, envp, -+ checked + argc)) { -+ result = false; -+ break; -+ } -+ } -+ envp_count--; -+ } else { -+ break; -+ } -+ arg_len = 0; -+ } -+ offset = 0; -+ if (!result) -+ break; -+ } -+out: -+ if (result) { -+ int i; -+ /* Check not-yet-checked entries. */ -+ for (i = 0; i < argc; i++) { -+ if (checked[i]) -+ continue; -+ /* -+ * Return true only if all unchecked indexes in -+ * bprm->argv[] are not matched. -+ */ -+ if (argv[i].is_not) -+ continue; -+ result = false; -+ break; -+ } -+ for (i = 0; i < envc; envp++, i++) { -+ if (checked[argc + i]) -+ continue; -+ /* -+ * Return true only if all unchecked environ variables -+ * in bprm->envp[] are either undefined or not matched. -+ */ -+ if ((!envp->value && !envp->is_not) || -+ (envp->value && envp->is_not)) -+ continue; -+ result = false; -+ break; -+ } -+ } -+ if (checked != local_checked) -+ kfree(checked); -+ return result; -+} -+ -+/** -+ * ccs_scan_exec_realpath - Check "exec.realpath" parameter of "struct ccs_condition". -+ * -+ * @file: Pointer to "struct file". -+ * @ptr: Pointer to "struct ccs_name_union". -+ * @match: True if "exec.realpath=", false if "exec.realpath!=". -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool ccs_scan_exec_realpath(struct file *file, -+ const struct ccs_name_union *ptr, -+ const bool match) -+{ -+ bool result; -+ struct ccs_path_info exe; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) -+ struct path path; -+#endif -+ if (!file) -+ return false; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) -+ exe.name = ccs_realpath(&file->f_path); -+#else -+ path.mnt = file->f_vfsmnt; -+ path.dentry = file->f_dentry; -+ exe.name = ccs_realpath(&path); -+#endif -+ if (!exe.name) -+ return false; -+ ccs_fill_path_info(&exe); -+ result = ccs_compare_name_union(&exe, ptr); -+ kfree(exe.name); -+ return result == match; -+} -+ -+/** -+ * ccs_get_attributes - Revalidate "struct inode". -+ * -+ * @obj: Pointer to "struct ccs_obj_info". -+ * -+ * Returns nothing. -+ */ -+void ccs_get_attributes(struct ccs_obj_info *obj) -+{ -+ u8 i; -+ struct dentry *dentry = NULL; -+ -+ for (i = 0; i < CCS_MAX_PATH_STAT; i++) { -+ struct inode *inode; -+ switch (i) { -+ case CCS_PATH1: -+ dentry = obj->path1.dentry; -+ if (!dentry) -+ continue; -+ break; -+ case CCS_PATH2: -+ dentry = obj->path2.dentry; -+ if (!dentry) -+ continue; -+ break; -+ default: -+ if (!dentry) -+ continue; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+ spin_lock(&dcache_lock); -+ dentry = dget(dentry->d_parent); -+ spin_unlock(&dcache_lock); -+#else -+ dentry = dget_parent(dentry); -+#endif -+ break; -+ } -+ inode = dentry->d_inode; -+ if (inode) { -+ struct ccs_mini_stat *stat = &obj->stat[i]; -+ stat->uid = inode->i_uid; -+ stat->gid = inode->i_gid; -+ stat->ino = inode->i_ino; -+ stat->mode = inode->i_mode; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+ stat->dev = inode->i_dev; -+#else -+ stat->dev = inode->i_sb->s_dev; -+#endif -+ stat->rdev = inode->i_rdev; -+ obj->stat_valid[i] = true; -+ } -+ if (i & 1) /* i == CCS_PATH1_PARENT || i == CCS_PATH2_PARENT */ -+ dput(dentry); -+ } -+} -+ -+/** -+ * ccs_condition - Check condition part. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @cond: Pointer to "struct ccs_condition". Maybe NULL. -+ * -+ * Returns true on success, false otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static bool ccs_condition(struct ccs_request_info *r, -+ const struct ccs_condition *cond) -+{ -+ const u32 ccs_flags = ccs_current_flags(); -+ u32 i; -+ unsigned long min_v[2] = { 0, 0 }; -+ unsigned long max_v[2] = { 0, 0 }; -+ const struct ccs_condition_element *condp; -+ const struct ccs_number_union *numbers_p; -+ const struct ccs_name_union *names_p; -+ const struct ccs_argv *argv; -+ const struct ccs_envp *envp; -+ struct ccs_obj_info *obj; -+ u16 condc; -+ u16 argc; -+ u16 envc; -+ struct linux_binprm *bprm = NULL; -+ if (!cond) -+ return true; -+ condc = cond->condc; -+ argc = cond->argc; -+ envc = cond->envc; -+ obj = r->obj; -+ if (r->ee) -+ bprm = r->ee->bprm; -+ if (!bprm && (argc || envc)) -+ return false; -+ condp = (struct ccs_condition_element *) (cond + 1); -+ numbers_p = (const struct ccs_number_union *) (condp + condc); -+ names_p = (const struct ccs_name_union *) -+ (numbers_p + cond->numbers_count); -+ argv = (const struct ccs_argv *) (names_p + cond->names_count); -+ envp = (const struct ccs_envp *) (argv + argc); -+ for (i = 0; i < condc; i++) { -+ const bool match = condp->equals; -+ const u8 left = condp->left; -+ const u8 right = condp->right; -+ bool is_bitop[2] = { false, false }; -+ u8 j; -+ condp++; -+ /* Check argv[] and envp[] later. */ -+ if (left == CCS_ARGV_ENTRY || left == CCS_ENVP_ENTRY) -+ continue; -+ /* Check string expressions. */ -+ if (right == CCS_NAME_UNION) { -+ const struct ccs_name_union *ptr = names_p++; -+ switch (left) { -+ struct ccs_path_info *symlink; -+ struct ccs_execve *ee; -+ struct file *file; -+ case CCS_SYMLINK_TARGET: -+ symlink = obj ? obj->symlink_target : NULL; -+ if (!symlink || -+ !ccs_compare_name_union(symlink, ptr) -+ == match) -+ goto out; -+ break; -+ case CCS_EXEC_REALPATH: -+ ee = r->ee; -+ file = ee ? ee->bprm->file : NULL; -+ if (!ccs_scan_exec_realpath(file, ptr, match)) -+ goto out; -+ break; -+ } -+ continue; -+ } -+ /* Check numeric or bit-op expressions. */ -+ for (j = 0; j < 2; j++) { -+ const u8 index = j ? right : left; -+ unsigned long value = 0; -+ switch (index) { -+ case CCS_TASK_UID: -+ value = from_kuid(&init_user_ns, -+ current_uid()); -+ break; -+ case CCS_TASK_EUID: -+ value = from_kuid(&init_user_ns, -+ current_euid()); -+ break; -+ case CCS_TASK_SUID: -+ value = from_kuid(&init_user_ns, -+ current_suid()); -+ break; -+ case CCS_TASK_FSUID: -+ value = from_kuid(&init_user_ns, -+ current_fsuid()); -+ break; -+ case CCS_TASK_GID: -+ value = from_kgid(&init_user_ns, -+ current_gid()); -+ break; -+ case CCS_TASK_EGID: -+ value = from_kgid(&init_user_ns, -+ current_egid()); -+ break; -+ case CCS_TASK_SGID: -+ value = from_kgid(&init_user_ns, -+ current_sgid()); -+ break; -+ case CCS_TASK_FSGID: -+ value = from_kgid(&init_user_ns, -+ current_fsgid()); -+ break; -+ case CCS_TASK_PID: -+ value = ccs_sys_getpid(); -+ break; -+ case CCS_TASK_PPID: -+ value = ccs_sys_getppid(); -+ break; -+ case CCS_TYPE_IS_SOCKET: -+ value = S_IFSOCK; -+ break; -+ case CCS_TYPE_IS_SYMLINK: -+ value = S_IFLNK; -+ break; -+ case CCS_TYPE_IS_FILE: -+ value = S_IFREG; -+ break; -+ case CCS_TYPE_IS_BLOCK_DEV: -+ value = S_IFBLK; -+ break; -+ case CCS_TYPE_IS_DIRECTORY: -+ value = S_IFDIR; -+ break; -+ case CCS_TYPE_IS_CHAR_DEV: -+ value = S_IFCHR; -+ break; -+ case CCS_TYPE_IS_FIFO: -+ value = S_IFIFO; -+ break; -+ case CCS_MODE_SETUID: -+ value = S_ISUID; -+ break; -+ case CCS_MODE_SETGID: -+ value = S_ISGID; -+ break; -+ case CCS_MODE_STICKY: -+ value = S_ISVTX; -+ break; -+ case CCS_MODE_OWNER_READ: -+ value = S_IRUSR; -+ break; -+ case CCS_MODE_OWNER_WRITE: -+ value = S_IWUSR; -+ break; -+ case CCS_MODE_OWNER_EXECUTE: -+ value = S_IXUSR; -+ break; -+ case CCS_MODE_GROUP_READ: -+ value = S_IRGRP; -+ break; -+ case CCS_MODE_GROUP_WRITE: -+ value = S_IWGRP; -+ break; -+ case CCS_MODE_GROUP_EXECUTE: -+ value = S_IXGRP; -+ break; -+ case CCS_MODE_OTHERS_READ: -+ value = S_IROTH; -+ break; -+ case CCS_MODE_OTHERS_WRITE: -+ value = S_IWOTH; -+ break; -+ case CCS_MODE_OTHERS_EXECUTE: -+ value = S_IXOTH; -+ break; -+ case CCS_EXEC_ARGC: -+ if (!bprm) -+ goto out; -+ value = bprm->argc; -+ break; -+ case CCS_EXEC_ENVC: -+ if (!bprm) -+ goto out; -+ value = bprm->envc; -+ break; -+ case CCS_TASK_TYPE: -+ value = ((u8) ccs_flags) -+ & CCS_TASK_IS_EXECUTE_HANDLER; -+ break; -+ case CCS_TASK_EXECUTE_HANDLER: -+ value = CCS_TASK_IS_EXECUTE_HANDLER; -+ break; -+ case CCS_NUMBER_UNION: -+ /* Fetch values later. */ -+ break; -+ default: -+ if (!obj) -+ goto out; -+ if (!obj->validate_done) { -+ ccs_get_attributes(obj); -+ obj->validate_done = true; -+ } -+ { -+ u8 stat_index; -+ struct ccs_mini_stat *stat; -+ switch (index) { -+ case CCS_PATH1_UID: -+ case CCS_PATH1_GID: -+ case CCS_PATH1_INO: -+ case CCS_PATH1_MAJOR: -+ case CCS_PATH1_MINOR: -+ case CCS_PATH1_TYPE: -+ case CCS_PATH1_DEV_MAJOR: -+ case CCS_PATH1_DEV_MINOR: -+ case CCS_PATH1_PERM: -+ stat_index = CCS_PATH1; -+ break; -+ case CCS_PATH2_UID: -+ case CCS_PATH2_GID: -+ case CCS_PATH2_INO: -+ case CCS_PATH2_MAJOR: -+ case CCS_PATH2_MINOR: -+ case CCS_PATH2_TYPE: -+ case CCS_PATH2_DEV_MAJOR: -+ case CCS_PATH2_DEV_MINOR: -+ case CCS_PATH2_PERM: -+ stat_index = CCS_PATH2; -+ break; -+ case CCS_PATH1_PARENT_UID: -+ case CCS_PATH1_PARENT_GID: -+ case CCS_PATH1_PARENT_INO: -+ case CCS_PATH1_PARENT_PERM: -+ stat_index = CCS_PATH1_PARENT; -+ break; -+ case CCS_PATH2_PARENT_UID: -+ case CCS_PATH2_PARENT_GID: -+ case CCS_PATH2_PARENT_INO: -+ case CCS_PATH2_PARENT_PERM: -+ stat_index = CCS_PATH2_PARENT; -+ break; -+ default: -+ goto out; -+ } -+ if (!obj->stat_valid[stat_index]) -+ goto out; -+ stat = &obj->stat[stat_index]; -+ switch (index) { -+ case CCS_PATH1_UID: -+ case CCS_PATH2_UID: -+ case CCS_PATH1_PARENT_UID: -+ case CCS_PATH2_PARENT_UID: -+ value = from_kuid -+ (&init_user_ns, -+ stat->uid); -+ break; -+ case CCS_PATH1_GID: -+ case CCS_PATH2_GID: -+ case CCS_PATH1_PARENT_GID: -+ case CCS_PATH2_PARENT_GID: -+ value = from_kgid -+ (&init_user_ns, -+ stat->gid); -+ break; -+ case CCS_PATH1_INO: -+ case CCS_PATH2_INO: -+ case CCS_PATH1_PARENT_INO: -+ case CCS_PATH2_PARENT_INO: -+ value = stat->ino; -+ break; -+ case CCS_PATH1_MAJOR: -+ case CCS_PATH2_MAJOR: -+ value = MAJOR(stat->dev); -+ break; -+ case CCS_PATH1_MINOR: -+ case CCS_PATH2_MINOR: -+ value = MINOR(stat->dev); -+ break; -+ case CCS_PATH1_TYPE: -+ case CCS_PATH2_TYPE: -+ value = stat->mode & S_IFMT; -+ break; -+ case CCS_PATH1_DEV_MAJOR: -+ case CCS_PATH2_DEV_MAJOR: -+ value = MAJOR(stat->rdev); -+ break; -+ case CCS_PATH1_DEV_MINOR: -+ case CCS_PATH2_DEV_MINOR: -+ value = MINOR(stat->rdev); -+ break; -+ case CCS_PATH1_PERM: -+ case CCS_PATH2_PERM: -+ case CCS_PATH1_PARENT_PERM: -+ case CCS_PATH2_PARENT_PERM: -+ value = stat->mode & S_IALLUGO; -+ break; -+ } -+ } -+ break; -+ } -+ max_v[j] = value; -+ min_v[j] = value; -+ switch (index) { -+ case CCS_MODE_SETUID: -+ case CCS_MODE_SETGID: -+ case CCS_MODE_STICKY: -+ case CCS_MODE_OWNER_READ: -+ case CCS_MODE_OWNER_WRITE: -+ case CCS_MODE_OWNER_EXECUTE: -+ case CCS_MODE_GROUP_READ: -+ case CCS_MODE_GROUP_WRITE: -+ case CCS_MODE_GROUP_EXECUTE: -+ case CCS_MODE_OTHERS_READ: -+ case CCS_MODE_OTHERS_WRITE: -+ case CCS_MODE_OTHERS_EXECUTE: -+ is_bitop[j] = true; -+ } -+ } -+ if (left == CCS_NUMBER_UNION) { -+ /* Fetch values now. */ -+ const struct ccs_number_union *ptr = numbers_p++; -+ min_v[0] = ptr->values[0]; -+ max_v[0] = ptr->values[1]; -+ } -+ if (right == CCS_NUMBER_UNION) { -+ /* Fetch values now. */ -+ const struct ccs_number_union *ptr = numbers_p++; -+ if (ptr->group) { -+ if (ccs_number_matches_group(min_v[0], -+ max_v[0], -+ ptr->group) -+ == match) -+ continue; -+ } else { -+ if ((min_v[0] <= ptr->values[1] && -+ max_v[0] >= ptr->values[0]) == match) -+ continue; -+ } -+ goto out; -+ } -+ /* -+ * Bit operation is valid only when counterpart value -+ * represents permission. -+ */ -+ if (is_bitop[0] && is_bitop[1]) { -+ goto out; -+ } else if (is_bitop[0]) { -+ switch (right) { -+ case CCS_PATH1_PERM: -+ case CCS_PATH1_PARENT_PERM: -+ case CCS_PATH2_PERM: -+ case CCS_PATH2_PARENT_PERM: -+ if (!(max_v[0] & max_v[1]) == !match) -+ continue; -+ } -+ goto out; -+ } else if (is_bitop[1]) { -+ switch (left) { -+ case CCS_PATH1_PERM: -+ case CCS_PATH1_PARENT_PERM: -+ case CCS_PATH2_PERM: -+ case CCS_PATH2_PARENT_PERM: -+ if (!(max_v[0] & max_v[1]) == !match) -+ continue; -+ } -+ goto out; -+ } -+ /* Normal value range comparison. */ -+ if ((min_v[0] <= max_v[1] && max_v[0] >= min_v[1]) == match) -+ continue; -+out: -+ return false; -+ } -+ /* Check argv[] and envp[] now. */ -+ if (r->ee && (argc || envc)) -+ return ccs_scan_bprm(r->ee, argc, argv, envc, envp); -+ return true; -+} -+ -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+ -+/** -+ * ccs_check_task_acl - Check permission for task operation. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @ptr: Pointer to "struct ccs_acl_info". -+ * -+ * Returns true if granted, false otherwise. -+ */ -+static bool ccs_check_task_acl(struct ccs_request_info *r, -+ const struct ccs_acl_info *ptr) -+{ -+ const struct ccs_task_acl *acl = container_of(ptr, typeof(*acl), head); -+ return !ccs_pathcmp(r->param.task.domainname, acl->domainname); -+} -+ -+#endif -+ -+/** -+ * ccs_init_request_info - Initialize "struct ccs_request_info" members. -+ * -+ * @r: Pointer to "struct ccs_request_info" to initialize. -+ * @index: Index number of functionality. -+ * -+ * Returns mode. -+ * -+ * "task auto_domain_transition" keyword is evaluated before returning mode for -+ * @index. If "task auto_domain_transition" keyword was specified and -+ * transition to that domain failed, the current thread will be killed by -+ * SIGKILL. Note that if current->pid == 1, sending SIGKILL won't work. -+ */ -+int ccs_init_request_info(struct ccs_request_info *r, const u8 index) -+{ -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+ u8 i; -+ const char *buf; -+ for (i = 0; i < 255; i++) { -+ const u8 profile = ccs_current_domain()->profile; -+ memset(r, 0, sizeof(*r)); -+ r->profile = profile; -+ r->type = index; -+ r->mode = ccs_get_mode(profile, index); -+ r->param_type = CCS_TYPE_AUTO_TASK_ACL; -+ ccs_check_acl(r); -+ if (!r->granted) -+ return r->mode; -+ buf = container_of(r->matched_acl, typeof(struct ccs_task_acl), -+ head)->domainname->name; -+ if (!ccs_assign_domain(buf, true)) -+ break; -+ } -+ ccs_transition_failed(buf); -+ return CCS_CONFIG_DISABLED; -+#else -+ const u8 profile = ccs_current_domain()->profile; -+ memset(r, 0, sizeof(*r)); -+ r->profile = profile; -+ r->type = index; -+ r->mode = ccs_get_mode(profile, index); -+ return r->mode; -+#endif -+} -+ -+/** -+ * ccs_byte_range - Check whether the string is a \ooo style octal value. -+ * -+ * @str: Pointer to the string. -+ * -+ * Returns true if @str is a \ooo style octal value, false otherwise. -+ */ -+static bool ccs_byte_range(const char *str) -+{ -+ return *str >= '0' && *str++ <= '3' && -+ *str >= '0' && *str++ <= '7' && -+ *str >= '0' && *str <= '7'; -+} -+ -+/** -+ * ccs_decimal - Check whether the character is a decimal character. -+ * -+ * @c: The character to check. -+ * -+ * Returns true if @c is a decimal character, false otherwise. -+ */ -+static bool ccs_decimal(const char c) -+{ -+ return c >= '0' && c <= '9'; -+} -+ -+/** -+ * ccs_hexadecimal - Check whether the character is a hexadecimal character. -+ * -+ * @c: The character to check. -+ * -+ * Returns true if @c is a hexadecimal character, false otherwise. -+ */ -+static bool ccs_hexadecimal(const char c) -+{ -+ return (c >= '0' && c <= '9') || -+ (c >= 'A' && c <= 'F') || -+ (c >= 'a' && c <= 'f'); -+} -+ -+/** -+ * ccs_alphabet_char - Check whether the character is an alphabet. -+ * -+ * @c: The character to check. -+ * -+ * Returns true if @c is an alphabet character, false otherwise. -+ */ -+static bool ccs_alphabet_char(const char c) -+{ -+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); -+} -+ -+/** -+ * ccs_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern. -+ * -+ * @filename: The start of string to check. -+ * @filename_end: The end of string to check. -+ * @pattern: The start of pattern to compare. -+ * @pattern_end: The end of pattern to compare. -+ * -+ * Returns true if @filename matches @pattern, false otherwise. -+ */ -+static bool ccs_file_matches_pattern2(const char *filename, -+ const char *filename_end, -+ const char *pattern, -+ const char *pattern_end) -+{ -+ while (filename < filename_end && pattern < pattern_end) { -+ char c; -+ if (*pattern != '\\') { -+ if (*filename++ != *pattern++) -+ return false; -+ continue; -+ } -+ c = *filename; -+ pattern++; -+ switch (*pattern) { -+ int i; -+ int j; -+ case '?': -+ if (c == '/') { -+ return false; -+ } else if (c == '\\') { -+ if (filename[1] == '\\') -+ filename++; -+ else if (ccs_byte_range(filename + 1)) -+ filename += 3; -+ else -+ return false; -+ } -+ break; -+ case '\\': -+ if (c != '\\') -+ return false; -+ if (*++filename != '\\') -+ return false; -+ break; -+ case '+': -+ if (!ccs_decimal(c)) -+ return false; -+ break; -+ case 'x': -+ if (!ccs_hexadecimal(c)) -+ return false; -+ break; -+ case 'a': -+ if (!ccs_alphabet_char(c)) -+ return false; -+ break; -+ case '0': -+ case '1': -+ case '2': -+ case '3': -+ if (c == '\\' && ccs_byte_range(filename + 1) -+ && !strncmp(filename + 1, pattern, 3)) { -+ filename += 3; -+ pattern += 2; -+ break; -+ } -+ return false; /* Not matched. */ -+ case '*': -+ case '@': -+ for (i = 0; i <= filename_end - filename; i++) { -+ if (ccs_file_matches_pattern2(filename + i, -+ filename_end, -+ pattern + 1, -+ pattern_end)) -+ return true; -+ c = filename[i]; -+ if (c == '.' && *pattern == '@') -+ break; -+ if (c != '\\') -+ continue; -+ if (filename[i + 1] == '\\') -+ i++; -+ else if (ccs_byte_range(filename + i + 1)) -+ i += 3; -+ else -+ break; /* Bad pattern. */ -+ } -+ return false; /* Not matched. */ -+ default: -+ j = 0; -+ c = *pattern; -+ if (c == '$') { -+ while (ccs_decimal(filename[j])) -+ j++; -+ } else if (c == 'X') { -+ while (ccs_hexadecimal(filename[j])) -+ j++; -+ } else if (c == 'A') { -+ while (ccs_alphabet_char(filename[j])) -+ j++; -+ } -+ for (i = 1; i <= j; i++) { -+ if (ccs_file_matches_pattern2(filename + i, -+ filename_end, -+ pattern + 1, -+ pattern_end)) -+ return true; -+ } -+ return false; /* Not matched or bad pattern. */ -+ } -+ filename++; -+ pattern++; -+ } -+ while (*pattern == '\\' && -+ (*(pattern + 1) == '*' || *(pattern + 1) == '@')) -+ pattern += 2; -+ return filename == filename_end && pattern == pattern_end; -+} -+ -+/** -+ * ccs_file_matches_pattern - Pattern matching without '/' character. -+ * -+ * @filename: The start of string to check. -+ * @filename_end: The end of string to check. -+ * @pattern: The start of pattern to compare. -+ * @pattern_end: The end of pattern to compare. -+ * -+ * Returns true if @filename matches @pattern, false otherwise. -+ */ -+static bool ccs_file_matches_pattern(const char *filename, -+ const char *filename_end, -+ const char *pattern, -+ const char *pattern_end) -+{ -+ const char *pattern_start = pattern; -+ bool first = true; -+ bool result; -+ while (pattern < pattern_end - 1) { -+ /* Split at "\-" pattern. */ -+ if (*pattern++ != '\\' || *pattern++ != '-') -+ continue; -+ result = ccs_file_matches_pattern2(filename, filename_end, -+ pattern_start, pattern - 2); -+ if (first) -+ result = !result; -+ if (result) -+ return false; -+ first = false; -+ pattern_start = pattern; -+ } -+ result = ccs_file_matches_pattern2(filename, filename_end, -+ pattern_start, pattern_end); -+ return first ? result : !result; -+} -+ -+/** -+ * ccs_path_matches_pattern2 - Do pathname pattern matching. -+ * -+ * @f: The start of string to check. -+ * @p: The start of pattern to compare. -+ * -+ * Returns true if @f matches @p, false otherwise. -+ */ -+static bool ccs_path_matches_pattern2(const char *f, const char *p) -+{ -+ const char *f_delimiter; -+ const char *p_delimiter; -+ while (*f && *p) { -+ f_delimiter = strchr(f, '/'); -+ if (!f_delimiter) -+ f_delimiter = f + strlen(f); -+ p_delimiter = strchr(p, '/'); -+ if (!p_delimiter) -+ p_delimiter = p + strlen(p); -+ if (*p == '\\' && *(p + 1) == '{') -+ goto recursive; -+ if (!ccs_file_matches_pattern(f, f_delimiter, p, p_delimiter)) -+ return false; -+ f = f_delimiter; -+ if (*f) -+ f++; -+ p = p_delimiter; -+ if (*p) -+ p++; -+ } -+ /* Ignore trailing "\*" and "\@" in @pattern. */ -+ while (*p == '\\' && -+ (*(p + 1) == '*' || *(p + 1) == '@')) -+ p += 2; -+ return !*f && !*p; -+recursive: -+ /* -+ * The "\{" pattern is permitted only after '/' character. -+ * This guarantees that below "*(p - 1)" is safe. -+ * Also, the "\}" pattern is permitted only before '/' character -+ * so that "\{" + "\}" pair will not break the "\-" operator. -+ */ -+ if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || -+ *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') -+ return false; /* Bad pattern. */ -+ do { -+ /* Compare current component with pattern. */ -+ if (!ccs_file_matches_pattern(f, f_delimiter, p + 2, -+ p_delimiter - 2)) -+ break; -+ /* Proceed to next component. */ -+ f = f_delimiter; -+ if (!*f) -+ break; -+ f++; -+ /* Continue comparison. */ -+ if (ccs_path_matches_pattern2(f, p_delimiter + 1)) -+ return true; -+ f_delimiter = strchr(f, '/'); -+ } while (f_delimiter); -+ return false; /* Not matched. */ -+} -+ -+/** -+ * ccs_path_matches_pattern - Check whether the given filename matches the given pattern. -+ * -+ * @filename: The filename to check. -+ * @pattern: The pattern to compare. -+ * -+ * Returns true if matches, false otherwise. -+ * -+ * The following patterns are available. -+ * \\ \ itself. -+ * \ooo Octal representation of a byte. -+ * \* Zero or more repetitions of characters other than '/'. -+ * \@ Zero or more repetitions of characters other than '/' or '.'. -+ * \? 1 byte character other than '/'. -+ * \$ One or more repetitions of decimal digits. -+ * \+ 1 decimal digit. -+ * \X One or more repetitions of hexadecimal digits. -+ * \x 1 hexadecimal digit. -+ * \A One or more repetitions of alphabet characters. -+ * \a 1 alphabet character. -+ * -+ * \- Subtraction operator. -+ * -+ * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ -+ * /dir/dir/dir/ ). -+ */ -+static bool ccs_path_matches_pattern(const struct ccs_path_info *filename, -+ const struct ccs_path_info *pattern) -+{ -+ const char *f = filename->name; -+ const char *p = pattern->name; -+ const int len = pattern->const_len; -+ /* If @pattern doesn't contain pattern, I can use strcmp(). */ -+ if (!pattern->is_patterned) -+ return !ccs_pathcmp(filename, pattern); -+ /* Don't compare directory and non-directory. */ -+ if (filename->is_dir != pattern->is_dir) -+ return false; -+ /* Compare the initial length without patterns. */ -+ if (strncmp(f, p, len)) -+ return false; -+ f += len; -+ p += len; -+ return ccs_path_matches_pattern2(f, p); -+} -diff --git a/security/ccsecurity/policy_io.c b/security/ccsecurity/policy_io.c -new file mode 100644 -index 0000000..67adb50 ---- /dev/null -+++ b/security/ccsecurity/policy_io.c -@@ -0,0 +1,6484 @@ -+/* -+ * security/ccsecurity/policy_io.c -+ * -+ * Copyright (C) 2005-2012 NTT DATA CORPORATION -+ * -+ * Version: 1.8.4 2015/05/05 -+ */ -+ -+#include "internal.h" -+ -+/***** SECTION1: Constants definition *****/ -+ -+/* Define this to enable debug mode. */ -+/* #define DEBUG_CONDITION */ -+ -+#ifdef DEBUG_CONDITION -+#define dprintk printk -+#else -+#define dprintk(...) do { } while (0) -+#endif -+ -+/* Mapping table from "enum ccs_mac_index" to "enum ccs_mac_category_index". */ -+static const u8 ccs_index2category[CCS_MAX_MAC_INDEX] = { -+ /* CONFIG::file group */ -+ [CCS_MAC_FILE_EXECUTE] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_OPEN] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_CREATE] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_UNLINK] = CCS_MAC_CATEGORY_FILE, -+#ifdef CONFIG_CCSECURITY_FILE_GETATTR -+ [CCS_MAC_FILE_GETATTR] = CCS_MAC_CATEGORY_FILE, -+#endif -+ [CCS_MAC_FILE_MKDIR] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_RMDIR] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_MKFIFO] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_MKSOCK] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_TRUNCATE] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_SYMLINK] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_MKBLOCK] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_MKCHAR] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_LINK] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_RENAME] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_CHMOD] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_CHOWN] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_CHGRP] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_IOCTL] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_CHROOT] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_MOUNT] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_UMOUNT] = CCS_MAC_CATEGORY_FILE, -+ [CCS_MAC_FILE_PIVOT_ROOT] = CCS_MAC_CATEGORY_FILE, -+#ifdef CONFIG_CCSECURITY_MISC -+ /* CONFIG::misc group */ -+ [CCS_MAC_ENVIRON] = CCS_MAC_CATEGORY_MISC, -+#endif -+#ifdef CONFIG_CCSECURITY_NETWORK -+ /* CONFIG::network group */ -+ [CCS_MAC_NETWORK_INET_STREAM_BIND] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_INET_STREAM_LISTEN] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_INET_STREAM_CONNECT] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_INET_STREAM_ACCEPT] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_INET_DGRAM_BIND] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_INET_DGRAM_SEND] = CCS_MAC_CATEGORY_NETWORK, -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ [CCS_MAC_NETWORK_INET_DGRAM_RECV] = CCS_MAC_CATEGORY_NETWORK, -+#endif -+ [CCS_MAC_NETWORK_INET_RAW_BIND] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_INET_RAW_SEND] = CCS_MAC_CATEGORY_NETWORK, -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ [CCS_MAC_NETWORK_INET_RAW_RECV] = CCS_MAC_CATEGORY_NETWORK, -+#endif -+ [CCS_MAC_NETWORK_UNIX_STREAM_BIND] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_UNIX_STREAM_LISTEN] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_UNIX_STREAM_CONNECT] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_UNIX_DGRAM_BIND] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_UNIX_DGRAM_SEND] = CCS_MAC_CATEGORY_NETWORK, -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ [CCS_MAC_NETWORK_UNIX_DGRAM_RECV] = CCS_MAC_CATEGORY_NETWORK, -+#endif -+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = CCS_MAC_CATEGORY_NETWORK, -+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT] = CCS_MAC_CATEGORY_NETWORK, -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ /* CONFIG::ipc group */ -+ [CCS_MAC_SIGNAL] = CCS_MAC_CATEGORY_IPC, -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ /* CONFIG::capability group */ -+ [CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET] = CCS_MAC_CATEGORY_CAPABILITY, -+ [CCS_MAC_CAPABILITY_USE_PACKET_SOCKET] = CCS_MAC_CATEGORY_CAPABILITY, -+ [CCS_MAC_CAPABILITY_SYS_REBOOT] = CCS_MAC_CATEGORY_CAPABILITY, -+ [CCS_MAC_CAPABILITY_SYS_VHANGUP] = CCS_MAC_CATEGORY_CAPABILITY, -+ [CCS_MAC_CAPABILITY_SYS_SETTIME] = CCS_MAC_CATEGORY_CAPABILITY, -+ [CCS_MAC_CAPABILITY_SYS_NICE] = CCS_MAC_CATEGORY_CAPABILITY, -+ [CCS_MAC_CAPABILITY_SYS_SETHOSTNAME] = CCS_MAC_CATEGORY_CAPABILITY, -+ [CCS_MAC_CAPABILITY_USE_KERNEL_MODULE] = CCS_MAC_CATEGORY_CAPABILITY, -+ [CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD] = CCS_MAC_CATEGORY_CAPABILITY, -+ [CCS_MAC_CAPABILITY_SYS_PTRACE] = CCS_MAC_CATEGORY_CAPABILITY, -+#endif -+}; -+ -+/* String table for operation mode. */ -+static const char * const ccs_mode[CCS_CONFIG_MAX_MODE] = { -+ [CCS_CONFIG_DISABLED] = "disabled", -+ [CCS_CONFIG_LEARNING] = "learning", -+ [CCS_CONFIG_PERMISSIVE] = "permissive", -+ [CCS_CONFIG_ENFORCING] = "enforcing" -+}; -+ -+/* String table for /proc/ccs/profile interface. */ -+static const char * const ccs_mac_keywords[CCS_MAX_MAC_INDEX -+ + CCS_MAX_MAC_CATEGORY_INDEX] = { -+ /* CONFIG::file group */ -+ [CCS_MAC_FILE_EXECUTE] = "execute", -+ [CCS_MAC_FILE_OPEN] = "open", -+ [CCS_MAC_FILE_CREATE] = "create", -+ [CCS_MAC_FILE_UNLINK] = "unlink", -+#ifdef CONFIG_CCSECURITY_FILE_GETATTR -+ [CCS_MAC_FILE_GETATTR] = "getattr", -+#endif -+ [CCS_MAC_FILE_MKDIR] = "mkdir", -+ [CCS_MAC_FILE_RMDIR] = "rmdir", -+ [CCS_MAC_FILE_MKFIFO] = "mkfifo", -+ [CCS_MAC_FILE_MKSOCK] = "mksock", -+ [CCS_MAC_FILE_TRUNCATE] = "truncate", -+ [CCS_MAC_FILE_SYMLINK] = "symlink", -+ [CCS_MAC_FILE_MKBLOCK] = "mkblock", -+ [CCS_MAC_FILE_MKCHAR] = "mkchar", -+ [CCS_MAC_FILE_LINK] = "link", -+ [CCS_MAC_FILE_RENAME] = "rename", -+ [CCS_MAC_FILE_CHMOD] = "chmod", -+ [CCS_MAC_FILE_CHOWN] = "chown", -+ [CCS_MAC_FILE_CHGRP] = "chgrp", -+ [CCS_MAC_FILE_IOCTL] = "ioctl", -+ [CCS_MAC_FILE_CHROOT] = "chroot", -+ [CCS_MAC_FILE_MOUNT] = "mount", -+ [CCS_MAC_FILE_UMOUNT] = "unmount", -+ [CCS_MAC_FILE_PIVOT_ROOT] = "pivot_root", -+#ifdef CONFIG_CCSECURITY_MISC -+ /* CONFIG::misc group */ -+ [CCS_MAC_ENVIRON] = "env", -+#endif -+#ifdef CONFIG_CCSECURITY_NETWORK -+ /* CONFIG::network group */ -+ [CCS_MAC_NETWORK_INET_STREAM_BIND] = "inet_stream_bind", -+ [CCS_MAC_NETWORK_INET_STREAM_LISTEN] = "inet_stream_listen", -+ [CCS_MAC_NETWORK_INET_STREAM_CONNECT] = "inet_stream_connect", -+ [CCS_MAC_NETWORK_INET_STREAM_ACCEPT] = "inet_stream_accept", -+ [CCS_MAC_NETWORK_INET_DGRAM_BIND] = "inet_dgram_bind", -+ [CCS_MAC_NETWORK_INET_DGRAM_SEND] = "inet_dgram_send", -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ [CCS_MAC_NETWORK_INET_DGRAM_RECV] = "inet_dgram_recv", -+#endif -+ [CCS_MAC_NETWORK_INET_RAW_BIND] = "inet_raw_bind", -+ [CCS_MAC_NETWORK_INET_RAW_SEND] = "inet_raw_send", -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ [CCS_MAC_NETWORK_INET_RAW_RECV] = "inet_raw_recv", -+#endif -+ [CCS_MAC_NETWORK_UNIX_STREAM_BIND] = "unix_stream_bind", -+ [CCS_MAC_NETWORK_UNIX_STREAM_LISTEN] = "unix_stream_listen", -+ [CCS_MAC_NETWORK_UNIX_STREAM_CONNECT] = "unix_stream_connect", -+ [CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT] = "unix_stream_accept", -+ [CCS_MAC_NETWORK_UNIX_DGRAM_BIND] = "unix_dgram_bind", -+ [CCS_MAC_NETWORK_UNIX_DGRAM_SEND] = "unix_dgram_send", -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ [CCS_MAC_NETWORK_UNIX_DGRAM_RECV] = "unix_dgram_recv", -+#endif -+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind", -+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen", -+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect", -+ [CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT] = "unix_seqpacket_accept", -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ /* CONFIG::ipc group */ -+ [CCS_MAC_SIGNAL] = "signal", -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ /* CONFIG::capability group */ -+ [CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET] = "use_route", -+ [CCS_MAC_CAPABILITY_USE_PACKET_SOCKET] = "use_packet", -+ [CCS_MAC_CAPABILITY_SYS_REBOOT] = "SYS_REBOOT", -+ [CCS_MAC_CAPABILITY_SYS_VHANGUP] = "SYS_VHANGUP", -+ [CCS_MAC_CAPABILITY_SYS_SETTIME] = "SYS_TIME", -+ [CCS_MAC_CAPABILITY_SYS_NICE] = "SYS_NICE", -+ [CCS_MAC_CAPABILITY_SYS_SETHOSTNAME] = "SYS_SETHOSTNAME", -+ [CCS_MAC_CAPABILITY_USE_KERNEL_MODULE] = "use_kernel_module", -+ [CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD] = "SYS_KEXEC_LOAD", -+ [CCS_MAC_CAPABILITY_SYS_PTRACE] = "SYS_PTRACE", -+#endif -+ /* CONFIG group */ -+ [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_FILE] = "file", -+#ifdef CONFIG_CCSECURITY_NETWORK -+ [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_NETWORK] = "network", -+#endif -+#ifdef CONFIG_CCSECURITY_MISC -+ [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_MISC] = "misc", -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_IPC] = "ipc", -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_CAPABILITY] = "capability", -+#endif -+}; -+ -+/* String table for path operation. */ -+static const char * const ccs_path_keyword[CCS_MAX_PATH_OPERATION] = { -+ [CCS_TYPE_EXECUTE] = "execute", -+ [CCS_TYPE_READ] = "read", -+ [CCS_TYPE_WRITE] = "write", -+ [CCS_TYPE_APPEND] = "append", -+ [CCS_TYPE_UNLINK] = "unlink", -+#ifdef CONFIG_CCSECURITY_FILE_GETATTR -+ [CCS_TYPE_GETATTR] = "getattr", -+#endif -+ [CCS_TYPE_RMDIR] = "rmdir", -+ [CCS_TYPE_TRUNCATE] = "truncate", -+ [CCS_TYPE_SYMLINK] = "symlink", -+ [CCS_TYPE_CHROOT] = "chroot", -+ [CCS_TYPE_UMOUNT] = "unmount", -+}; -+ -+#ifdef CONFIG_CCSECURITY_NETWORK -+ -+/* String table for socket's operation. */ -+static const char * const ccs_socket_keyword[CCS_MAX_NETWORK_OPERATION] = { -+ [CCS_NETWORK_BIND] = "bind", -+ [CCS_NETWORK_LISTEN] = "listen", -+ [CCS_NETWORK_CONNECT] = "connect", -+ [CCS_NETWORK_ACCEPT] = "accept", -+ [CCS_NETWORK_SEND] = "send", -+#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG -+ [CCS_NETWORK_RECV] = "recv", -+#endif -+}; -+ -+/* String table for socket's protocols. */ -+static const char * const ccs_proto_keyword[CCS_SOCK_MAX] = { -+ [SOCK_STREAM] = "stream", -+ [SOCK_DGRAM] = "dgram", -+ [SOCK_RAW] = "raw", -+ [SOCK_SEQPACKET] = "seqpacket", -+ [0] = " ", /* Dummy for avoiding NULL pointer dereference. */ -+ [4] = " ", /* Dummy for avoiding NULL pointer dereference. */ -+}; -+ -+#endif -+ -+/* String table for categories. */ -+static const char * const ccs_category_keywords[CCS_MAX_MAC_CATEGORY_INDEX] = { -+ [CCS_MAC_CATEGORY_FILE] = "file", -+#ifdef CONFIG_CCSECURITY_NETWORK -+ [CCS_MAC_CATEGORY_NETWORK] = "network", -+#endif -+#ifdef CONFIG_CCSECURITY_MISC -+ [CCS_MAC_CATEGORY_MISC] = "misc", -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ [CCS_MAC_CATEGORY_IPC] = "ipc", -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ [CCS_MAC_CATEGORY_CAPABILITY] = "capability", -+#endif -+}; -+ -+/* String table for conditions. */ -+static const char * const ccs_condition_keyword[CCS_MAX_CONDITION_KEYWORD] = { -+ [CCS_TASK_UID] = "task.uid", -+ [CCS_TASK_EUID] = "task.euid", -+ [CCS_TASK_SUID] = "task.suid", -+ [CCS_TASK_FSUID] = "task.fsuid", -+ [CCS_TASK_GID] = "task.gid", -+ [CCS_TASK_EGID] = "task.egid", -+ [CCS_TASK_SGID] = "task.sgid", -+ [CCS_TASK_FSGID] = "task.fsgid", -+ [CCS_TASK_PID] = "task.pid", -+ [CCS_TASK_PPID] = "task.ppid", -+ [CCS_EXEC_ARGC] = "exec.argc", -+ [CCS_EXEC_ENVC] = "exec.envc", -+ [CCS_TYPE_IS_SOCKET] = "socket", -+ [CCS_TYPE_IS_SYMLINK] = "symlink", -+ [CCS_TYPE_IS_FILE] = "file", -+ [CCS_TYPE_IS_BLOCK_DEV] = "block", -+ [CCS_TYPE_IS_DIRECTORY] = "directory", -+ [CCS_TYPE_IS_CHAR_DEV] = "char", -+ [CCS_TYPE_IS_FIFO] = "fifo", -+ [CCS_MODE_SETUID] = "setuid", -+ [CCS_MODE_SETGID] = "setgid", -+ [CCS_MODE_STICKY] = "sticky", -+ [CCS_MODE_OWNER_READ] = "owner_read", -+ [CCS_MODE_OWNER_WRITE] = "owner_write", -+ [CCS_MODE_OWNER_EXECUTE] = "owner_execute", -+ [CCS_MODE_GROUP_READ] = "group_read", -+ [CCS_MODE_GROUP_WRITE] = "group_write", -+ [CCS_MODE_GROUP_EXECUTE] = "group_execute", -+ [CCS_MODE_OTHERS_READ] = "others_read", -+ [CCS_MODE_OTHERS_WRITE] = "others_write", -+ [CCS_MODE_OTHERS_EXECUTE] = "others_execute", -+ [CCS_TASK_TYPE] = "task.type", -+ [CCS_TASK_EXECUTE_HANDLER] = "execute_handler", -+ [CCS_EXEC_REALPATH] = "exec.realpath", -+ [CCS_SYMLINK_TARGET] = "symlink.target", -+ [CCS_PATH1_UID] = "path1.uid", -+ [CCS_PATH1_GID] = "path1.gid", -+ [CCS_PATH1_INO] = "path1.ino", -+ [CCS_PATH1_MAJOR] = "path1.major", -+ [CCS_PATH1_MINOR] = "path1.minor", -+ [CCS_PATH1_PERM] = "path1.perm", -+ [CCS_PATH1_TYPE] = "path1.type", -+ [CCS_PATH1_DEV_MAJOR] = "path1.dev_major", -+ [CCS_PATH1_DEV_MINOR] = "path1.dev_minor", -+ [CCS_PATH2_UID] = "path2.uid", -+ [CCS_PATH2_GID] = "path2.gid", -+ [CCS_PATH2_INO] = "path2.ino", -+ [CCS_PATH2_MAJOR] = "path2.major", -+ [CCS_PATH2_MINOR] = "path2.minor", -+ [CCS_PATH2_PERM] = "path2.perm", -+ [CCS_PATH2_TYPE] = "path2.type", -+ [CCS_PATH2_DEV_MAJOR] = "path2.dev_major", -+ [CCS_PATH2_DEV_MINOR] = "path2.dev_minor", -+ [CCS_PATH1_PARENT_UID] = "path1.parent.uid", -+ [CCS_PATH1_PARENT_GID] = "path1.parent.gid", -+ [CCS_PATH1_PARENT_INO] = "path1.parent.ino", -+ [CCS_PATH1_PARENT_PERM] = "path1.parent.perm", -+ [CCS_PATH2_PARENT_UID] = "path2.parent.uid", -+ [CCS_PATH2_PARENT_GID] = "path2.parent.gid", -+ [CCS_PATH2_PARENT_INO] = "path2.parent.ino", -+ [CCS_PATH2_PARENT_PERM] = "path2.parent.perm", -+}; -+ -+/* String table for PREFERENCE keyword. */ -+static const char * const ccs_pref_keywords[CCS_MAX_PREF] = { -+ [CCS_PREF_MAX_AUDIT_LOG] = "max_audit_log", -+ [CCS_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry", -+ [CCS_PREF_ENFORCING_PENALTY] = "enforcing_penalty", -+}; -+ -+/* String table for domain flags. */ -+const char * const ccs_dif[CCS_MAX_DOMAIN_INFO_FLAGS] = { -+ [CCS_DIF_QUOTA_WARNED] = "quota_exceeded\n", -+ [CCS_DIF_TRANSITION_FAILED] = "transition_failed\n", -+}; -+ -+/* String table for domain transition control keywords. */ -+static const char * const ccs_transition_type[CCS_MAX_TRANSITION_TYPE] = { -+ [CCS_TRANSITION_CONTROL_NO_RESET] = "no_reset_domain ", -+ [CCS_TRANSITION_CONTROL_RESET] = "reset_domain ", -+ [CCS_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ", -+ [CCS_TRANSITION_CONTROL_INITIALIZE] = "initialize_domain ", -+ [CCS_TRANSITION_CONTROL_NO_KEEP] = "no_keep_domain ", -+ [CCS_TRANSITION_CONTROL_KEEP] = "keep_domain ", -+}; -+ -+/* String table for grouping keywords. */ -+static const char * const ccs_group_name[CCS_MAX_GROUP] = { -+ [CCS_PATH_GROUP] = "path_group ", -+ [CCS_NUMBER_GROUP] = "number_group ", -+#ifdef CONFIG_CCSECURITY_NETWORK -+ [CCS_ADDRESS_GROUP] = "address_group ", -+#endif -+}; -+ -+/* String table for /proc/ccs/stat interface. */ -+static const char * const ccs_policy_headers[CCS_MAX_POLICY_STAT] = { -+ [CCS_STAT_POLICY_UPDATES] = "update:", -+ [CCS_STAT_POLICY_LEARNING] = "violation in learning mode:", -+ [CCS_STAT_POLICY_PERMISSIVE] = "violation in permissive mode:", -+ [CCS_STAT_POLICY_ENFORCING] = "violation in enforcing mode:", -+}; -+ -+/* String table for /proc/ccs/stat interface. */ -+static const char * const ccs_memory_headers[CCS_MAX_MEMORY_STAT] = { -+ [CCS_MEMORY_POLICY] = "policy:", -+ [CCS_MEMORY_AUDIT] = "audit log:", -+ [CCS_MEMORY_QUERY] = "query message:", -+}; -+ -+/***** SECTION2: Structure definition *****/ -+ -+struct iattr; -+ -+/* Structure for query. */ -+struct ccs_query { -+ struct list_head list; -+ struct ccs_domain_info *domain; -+ char *query; -+ size_t query_len; -+ unsigned int serial; -+ u8 timer; -+ u8 answer; -+ u8 retry; -+}; -+ -+/* Structure for audit log. */ -+struct ccs_log { -+ struct list_head list; -+ char *log; -+ int size; -+}; -+ -+/***** SECTION3: Prototype definition section *****/ -+ -+int ccs_audit_log(struct ccs_request_info *r); -+struct ccs_domain_info *ccs_assign_domain(const char *domainname, -+ const bool transit); -+u8 ccs_get_config(const u8 profile, const u8 index); -+void ccs_transition_failed(const char *domainname); -+void ccs_write_log(struct ccs_request_info *r, const char *fmt, ...); -+ -+static bool ccs_correct_domain(const unsigned char *domainname); -+static bool ccs_correct_path(const char *filename); -+static bool ccs_correct_word(const char *string); -+static bool ccs_correct_word2(const char *string, size_t len); -+static bool ccs_domain_def(const unsigned char *buffer); -+static bool ccs_domain_quota_ok(struct ccs_request_info *r); -+static bool ccs_flush(struct ccs_io_buffer *head); -+static bool ccs_get_audit(const struct ccs_request_info *r); -+static bool ccs_has_more_namespace(struct ccs_io_buffer *head); -+static bool ccs_manager(void); -+static bool ccs_namespace_jump(const char *domainname); -+static bool ccs_parse_argv(char *left, char *right, struct ccs_argv *argv); -+static bool ccs_parse_envp(char *left, char *right, struct ccs_envp *envp); -+static bool ccs_parse_name_union(struct ccs_acl_param *param, -+ struct ccs_name_union *ptr); -+static bool ccs_parse_name_union_quoted(struct ccs_acl_param *param, -+ struct ccs_name_union *ptr); -+static bool ccs_parse_number_union(struct ccs_acl_param *param, -+ struct ccs_number_union *ptr); -+static bool ccs_permstr(const char *string, const char *keyword); -+static bool ccs_print_condition(struct ccs_io_buffer *head, -+ const struct ccs_condition *cond); -+static bool ccs_print_entry(struct ccs_io_buffer *head, -+ const struct ccs_acl_info *acl); -+static bool ccs_print_group(struct ccs_io_buffer *head, -+ const struct ccs_group *group); -+static bool ccs_read_acl(struct ccs_io_buffer *head, struct list_head *list); -+static bool ccs_read_group(struct ccs_io_buffer *head, const int idx); -+static bool ccs_read_policy(struct ccs_io_buffer *head, const int idx); -+static bool ccs_same_condition(const struct ccs_condition *a, -+ const struct ccs_condition *b); -+static bool ccs_select_domain(struct ccs_io_buffer *head, const char *data); -+static bool ccs_set_lf(struct ccs_io_buffer *head); -+static bool ccs_str_starts(char **src, const char *find); -+static char *ccs_get_transit_preference(struct ccs_acl_param *param, -+ struct ccs_condition *e); -+static char *ccs_init_log(struct ccs_request_info *r, int len, const char *fmt, -+ va_list args); -+static char *ccs_print_bprm(struct linux_binprm *bprm, -+ struct ccs_page_dump *dump); -+static char *ccs_print_header(struct ccs_request_info *r); -+static char *ccs_read_token(struct ccs_acl_param *param); -+static const char *ccs_yesno(const unsigned int value); -+static const struct ccs_path_info *ccs_get_domainname -+(struct ccs_acl_param *param); -+static const struct ccs_path_info *ccs_get_dqword(char *start); -+static int __init ccs_init_module(void); -+static int ccs_delete_domain(char *domainname); -+static int ccs_open(struct inode *inode, struct file *file); -+static int ccs_parse_policy(struct ccs_io_buffer *head, char *line); -+static int ccs_release(struct inode *inode, struct file *file); -+static int ccs_set_mode(char *name, const char *value, -+ struct ccs_profile *profile); -+static int ccs_supervisor(struct ccs_request_info *r, const char *fmt, ...) -+ __printf(2, 3); -+static int ccs_truncate(char *str); -+static int ccs_update_acl(const int size, struct ccs_acl_param *param); -+static int ccs_update_manager_entry(const char *manager, const bool is_delete); -+static int ccs_update_policy(const int size, struct ccs_acl_param *param); -+static int ccs_write_acl(struct ccs_policy_namespace *ns, -+ struct list_head *list, char *data, -+ const bool is_delete); -+static int ccs_write_aggregator(struct ccs_acl_param *param); -+static int ccs_write_answer(struct ccs_io_buffer *head); -+static int ccs_write_domain(struct ccs_io_buffer *head); -+static int ccs_write_exception(struct ccs_io_buffer *head); -+static int ccs_write_file(struct ccs_acl_param *param); -+static int ccs_write_group(struct ccs_acl_param *param, const u8 type); -+static int ccs_write_manager(struct ccs_io_buffer *head); -+static int ccs_write_pid(struct ccs_io_buffer *head); -+static int ccs_write_profile(struct ccs_io_buffer *head); -+static int ccs_write_stat(struct ccs_io_buffer *head); -+static int ccs_write_task(struct ccs_acl_param *param); -+static int ccs_write_transition_control(struct ccs_acl_param *param, -+ const u8 type); -+static s8 ccs_find_yesno(const char *string, const char *find); -+static ssize_t ccs_read(struct file *file, char __user *buf, size_t count, -+ loff_t *ppos); -+static ssize_t ccs_read_self(struct file *file, char __user *buf, size_t count, -+ loff_t *ppos); -+static ssize_t ccs_write(struct file *file, const char __user *buf, -+ size_t count, loff_t *ppos); -+static struct ccs_condition *ccs_commit_condition(struct ccs_condition *entry); -+static struct ccs_condition *ccs_get_condition(struct ccs_acl_param *param); -+static struct ccs_domain_info *ccs_find_domain(const char *domainname); -+static struct ccs_domain_info *ccs_find_domain_by_qid(unsigned int serial); -+static struct ccs_group *ccs_get_group(struct ccs_acl_param *param, -+ const u8 idx); -+static struct ccs_policy_namespace *ccs_assign_namespace -+(const char *domainname); -+static struct ccs_policy_namespace *ccs_find_namespace(const char *name, -+ const unsigned int len); -+static struct ccs_profile *ccs_assign_profile(struct ccs_policy_namespace *ns, -+ const unsigned int profile); -+static struct ccs_profile *ccs_profile(const u8 profile); -+static u8 ccs_condition_type(const char *word); -+static u8 ccs_make_byte(const u8 c1, const u8 c2, const u8 c3); -+static u8 ccs_parse_ulong(unsigned long *result, char **str); -+static unsigned int ccs_poll(struct file *file, poll_table *wait); -+static void __init ccs_create_entry(const char *name, const umode_t mode, -+ struct proc_dir_entry *parent, -+ const u8 key); -+static void __init ccs_load_builtin_policy(void); -+static void __init ccs_policy_io_init(void); -+static void __init ccs_proc_init(void); -+static void ccs_add_entry(char *header); -+static void ccs_addprintf(char *buffer, int len, const char *fmt, ...) -+ __printf(3, 4); -+static void ccs_addprintf(char *buffer, int len, const char *fmt, ...); -+static void ccs_check_profile(void); -+static void ccs_convert_time(time_t time, struct ccs_time *stamp); -+static void ccs_init_policy_namespace(struct ccs_policy_namespace *ns); -+static void ccs_io_printf(struct ccs_io_buffer *head, const char *fmt, ...) -+ __printf(2, 3); -+static void ccs_normalize_line(unsigned char *buffer); -+static void ccs_print_config(struct ccs_io_buffer *head, const u8 config); -+static void ccs_print_name_union(struct ccs_io_buffer *head, -+ const struct ccs_name_union *ptr); -+static void ccs_print_name_union_quoted(struct ccs_io_buffer *head, -+ const struct ccs_name_union *ptr); -+static void ccs_print_namespace(struct ccs_io_buffer *head); -+static void ccs_print_number_union(struct ccs_io_buffer *head, -+ const struct ccs_number_union *ptr); -+static void ccs_print_number_union_nospace(struct ccs_io_buffer *head, -+ const struct ccs_number_union *ptr); -+static void ccs_read_domain(struct ccs_io_buffer *head); -+static void ccs_read_exception(struct ccs_io_buffer *head); -+static void ccs_read_log(struct ccs_io_buffer *head); -+static void ccs_read_manager(struct ccs_io_buffer *head); -+static void ccs_read_pid(struct ccs_io_buffer *head); -+static void ccs_read_profile(struct ccs_io_buffer *head); -+static void ccs_read_query(struct ccs_io_buffer *head); -+static void ccs_read_stat(struct ccs_io_buffer *head); -+static void ccs_read_version(struct ccs_io_buffer *head); -+static void ccs_set_group(struct ccs_io_buffer *head, const char *category); -+static void ccs_set_namespace_cursor(struct ccs_io_buffer *head); -+static void ccs_set_slash(struct ccs_io_buffer *head); -+static void ccs_set_space(struct ccs_io_buffer *head); -+static void ccs_set_string(struct ccs_io_buffer *head, const char *string); -+static void ccs_set_uint(unsigned int *i, const char *string, -+ const char *find); -+static void ccs_update_stat(const u8 index); -+static void ccs_update_task_domain(struct ccs_request_info *r); -+static void ccs_write_log2(struct ccs_request_info *r, int len, -+ const char *fmt, va_list args); -+ -+#ifdef CONFIG_CCSECURITY_PORTRESERVE -+static bool __ccs_lport_reserved(const u16 port); -+static int ccs_write_reserved_port(struct ccs_acl_param *param); -+#endif -+ -+#ifdef CONFIG_CCSECURITY_NETWORK -+static bool ccs_parse_ipaddr_union(struct ccs_acl_param *param, -+ struct ccs_ipaddr_union *ptr); -+static int ccs_print_ipv4(char *buffer, const unsigned int buffer_len, -+ const u32 *ip); -+static int ccs_print_ipv6(char *buffer, const unsigned int buffer_len, -+ const struct in6_addr *ip); -+static int ccs_write_inet_network(struct ccs_acl_param *param); -+static int ccs_write_unix_network(struct ccs_acl_param *param); -+static void ccs_print_ip(char *buf, const unsigned int size, -+ const struct ccs_ipaddr_union *ptr); -+#endif -+ -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+static int ccs_write_capability(struct ccs_acl_param *param); -+#endif -+ -+#ifdef CONFIG_CCSECURITY_MISC -+static int ccs_write_misc(struct ccs_acl_param *param); -+#endif -+ -+#ifdef CONFIG_CCSECURITY_IPC -+static int ccs_write_ipc(struct ccs_acl_param *param); -+#endif -+ -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+static ssize_t ccs_write_self(struct file *file, const char __user *buf, -+ size_t count, loff_t *ppos); -+#endif -+ -+/***** SECTION4: Standalone functions section *****/ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) -+ -+/** -+ * fatal_signal_pending - Check whether SIGKILL is pending or not. -+ * -+ * @p: Pointer to "struct task_struct". -+ * -+ * Returns true if SIGKILL is pending on @p, false otherwise. -+ * -+ * This is for compatibility with older kernels. -+ */ -+#define fatal_signal_pending(p) (signal_pending(p) && \ -+ sigismember(&p->pending.signal, SIGKILL)) -+ -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+ -+/** -+ * __wait_event_interruptible_timeout - Sleep until a condition gets true or a timeout elapses. -+ * -+ * @wq: The waitqueue to wait on. -+ * @condition: A C expression for the event to wait for. -+ * @ret: Timeout, in jiffies. -+ * -+ * Returns 0 if the @timeout elapsed, -ERESTARTSYS if it was interrupted by a -+ * signal, and the remaining jiffies otherwise if the condition evaluated to -+ * true before the timeout elapsed. -+ * -+ * This is for compatibility with older kernels. -+ */ -+#define __wait_event_interruptible_timeout(wq, condition, ret) \ -+do { \ -+ wait_queue_t __wait; \ -+ init_waitqueue_entry(&__wait, current); \ -+ \ -+ add_wait_queue(&wq, &__wait); \ -+ for (;;) { \ -+ set_current_state(TASK_INTERRUPTIBLE); \ -+ if (condition) \ -+ break; \ -+ if (!signal_pending(current)) { \ -+ ret = schedule_timeout(ret); \ -+ if (!ret) \ -+ break; \ -+ continue; \ -+ } \ -+ ret = -ERESTARTSYS; \ -+ break; \ -+ } \ -+ current->state = TASK_RUNNING; \ -+ remove_wait_queue(&wq, &__wait); \ -+} while (0) -+ -+/** -+ * wait_event_interruptible_timeout - Sleep until a condition gets true or a timeout elapses. -+ * -+ * @wq: The waitqueue to wait on. -+ * @condition: A C expression for the event to wait for. -+ * @timeout: Timeout, in jiffies. -+ * -+ * Returns 0 if the @timeout elapsed, -ERESTARTSYS if it was interrupted by a -+ * signal, and the remaining jiffies otherwise if the condition evaluated to -+ * true before the timeout elapsed. -+ * -+ * This is for compatibility with older kernels. -+ */ -+#define wait_event_interruptible_timeout(wq, condition, timeout) \ -+({ \ -+ long __ret = timeout; \ -+ if (!(condition)) \ -+ __wait_event_interruptible_timeout(wq, condition, __ret); \ -+ __ret; \ -+}) -+ -+#endif -+ -+/** -+ * ccs_convert_time - Convert time_t to YYYY/MM/DD hh/mm/ss. -+ * -+ * @time: Seconds since 1970/01/01 00:00:00. -+ * @stamp: Pointer to "struct ccs_time". -+ * -+ * Returns nothing. -+ * -+ * This function does not handle Y2038 problem. -+ */ -+static void ccs_convert_time(time_t time, struct ccs_time *stamp) -+{ -+ static const u16 ccs_eom[2][12] = { -+ { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, -+ { 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } -+ }; -+ u16 y; -+ u8 m; -+ bool r; -+ stamp->sec = time % 60; -+ time /= 60; -+ stamp->min = time % 60; -+ time /= 60; -+ stamp->hour = time % 24; -+ time /= 24; -+ for (y = 1970; ; y++) { -+ const unsigned short days = (y & 3) ? 365 : 366; -+ if (time < days) -+ break; -+ time -= days; -+ } -+ r = (y & 3) == 0; -+ for (m = 0; m < 11 && time >= ccs_eom[r][m]; m++); -+ if (m) -+ time -= ccs_eom[r][m - 1]; -+ stamp->year = y; -+ stamp->month = ++m; -+ stamp->day = ++time; -+} -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 23) -+#if !defined(RHEL_VERSION) || RHEL_VERSION != 3 -+ -+/** -+ * PDE - Get "struct proc_dir_entry". -+ * -+ * @inode: Pointer to "struct inode". -+ * -+ * Returns pointer to "struct proc_dir_entry". -+ * -+ * This is for compatibility with older kernels. -+ */ -+static inline struct proc_dir_entry *PDE(const struct inode *inode) -+{ -+ return (struct proc_dir_entry *) inode->u.generic_ip; -+} -+ -+#endif -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+ -+/** -+ * proc_notify_change - Update inode's attributes and reflect to the dentry. -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @iattr: Pointer to "struct iattr". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * The 2.4 kernels don't allow chmod()/chown() for files in /proc, -+ * while the 2.6 kernels allow. -+ * To permit management of /proc/ccs/ interface by non-root user, -+ * I modified to allow chmod()/chown() of /proc/ccs/ interface like 2.6 kernels -+ * by adding "struct inode_operations"->setattr hook. -+ */ -+static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) -+{ -+ struct inode *inode = dentry->d_inode; -+ struct proc_dir_entry *de = PDE(inode); -+ int error; -+ -+ error = inode_change_ok(inode, iattr); -+ if (error) -+ goto out; -+ -+ error = inode_setattr(inode, iattr); -+ if (error) -+ goto out; -+ -+ de->uid = inode->i_uid; -+ de->gid = inode->i_gid; -+ de->mode = inode->i_mode; -+out: -+ return error; -+} -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_NETWORK -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) && defined(CONFIG_NET) -+#define ccs_in4_pton in4_pton -+#define ccs_in6_pton in6_pton -+#else -+/* -+ * Routines for parsing IPv4 or IPv6 address. -+ * These are copied from lib/hexdump.c net/core/utils.c . -+ */ -+#include -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -+static int hex_to_bin(char ch) -+{ -+ if ((ch >= '0') && (ch <= '9')) -+ return ch - '0'; -+ ch = tolower(ch); -+ if ((ch >= 'a') && (ch <= 'f')) -+ return ch - 'a' + 10; -+ return -1; -+} -+#endif -+ -+#define IN6PTON_XDIGIT 0x00010000 -+#define IN6PTON_DIGIT 0x00020000 -+#define IN6PTON_COLON_MASK 0x00700000 -+#define IN6PTON_COLON_1 0x00100000 /* single : requested */ -+#define IN6PTON_COLON_2 0x00200000 /* second : requested */ -+#define IN6PTON_COLON_1_2 0x00400000 /* :: requested */ -+#define IN6PTON_DOT 0x00800000 /* . */ -+#define IN6PTON_DELIM 0x10000000 -+#define IN6PTON_NULL 0x20000000 /* first/tail */ -+#define IN6PTON_UNKNOWN 0x40000000 -+ -+static inline int xdigit2bin(char c, int delim) -+{ -+ int val; -+ -+ if (c == delim || c == '\0') -+ return IN6PTON_DELIM; -+ if (c == ':') -+ return IN6PTON_COLON_MASK; -+ if (c == '.') -+ return IN6PTON_DOT; -+ -+ val = hex_to_bin(c); -+ if (val >= 0) -+ return val | IN6PTON_XDIGIT | (val < 10 ? IN6PTON_DIGIT : 0); -+ -+ if (delim == -1) -+ return IN6PTON_DELIM; -+ return IN6PTON_UNKNOWN; -+} -+ -+static int ccs_in4_pton(const char *src, int srclen, u8 *dst, int delim, -+ const char **end) -+{ -+ const char *s; -+ u8 *d; -+ u8 dbuf[4]; -+ int ret = 0; -+ int i; -+ int w = 0; -+ -+ if (srclen < 0) -+ srclen = strlen(src); -+ s = src; -+ d = dbuf; -+ i = 0; -+ while (1) { -+ int c; -+ c = xdigit2bin(srclen > 0 ? *s : '\0', delim); -+ if (!(c & (IN6PTON_DIGIT | IN6PTON_DOT | IN6PTON_DELIM | -+ IN6PTON_COLON_MASK))) -+ goto out; -+ if (c & (IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK)) { -+ if (w == 0) -+ goto out; -+ *d++ = w & 0xff; -+ w = 0; -+ i++; -+ if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { -+ if (i != 4) -+ goto out; -+ break; -+ } -+ goto cont; -+ } -+ w = (w * 10) + c; -+ if ((w & 0xffff) > 255) -+ goto out; -+cont: -+ if (i >= 4) -+ goto out; -+ s++; -+ srclen--; -+ } -+ ret = 1; -+ memcpy(dst, dbuf, sizeof(dbuf)); -+out: -+ if (end) -+ *end = s; -+ return ret; -+} -+ -+static int ccs_in6_pton(const char *src, int srclen, u8 *dst, int delim, -+ const char **end) -+{ -+ const char *s, *tok = NULL; -+ u8 *d, *dc = NULL; -+ u8 dbuf[16]; -+ int ret = 0; -+ int i; -+ int state = IN6PTON_COLON_1_2 | IN6PTON_XDIGIT | IN6PTON_NULL; -+ int w = 0; -+ -+ memset(dbuf, 0, sizeof(dbuf)); -+ -+ s = src; -+ d = dbuf; -+ if (srclen < 0) -+ srclen = strlen(src); -+ -+ while (1) { -+ int c; -+ -+ c = xdigit2bin(srclen > 0 ? *s : '\0', delim); -+ if (!(c & state)) -+ goto out; -+ if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { -+ /* process one 16-bit word */ -+ if (!(state & IN6PTON_NULL)) { -+ *d++ = (w >> 8) & 0xff; -+ *d++ = w & 0xff; -+ } -+ w = 0; -+ if (c & IN6PTON_DELIM) { -+ /* We've processed last word */ -+ break; -+ } -+ /* -+ * COLON_1 => XDIGIT -+ * COLON_2 => XDIGIT|DELIM -+ * COLON_1_2 => COLON_2 -+ */ -+ switch (state & IN6PTON_COLON_MASK) { -+ case IN6PTON_COLON_2: -+ dc = d; -+ state = IN6PTON_XDIGIT | IN6PTON_DELIM; -+ if (dc - dbuf >= sizeof(dbuf)) -+ state |= IN6PTON_NULL; -+ break; -+ case IN6PTON_COLON_1|IN6PTON_COLON_1_2: -+ state = IN6PTON_XDIGIT | IN6PTON_COLON_2; -+ break; -+ case IN6PTON_COLON_1: -+ state = IN6PTON_XDIGIT; -+ break; -+ case IN6PTON_COLON_1_2: -+ state = IN6PTON_COLON_2; -+ break; -+ default: -+ state = 0; -+ } -+ tok = s + 1; -+ goto cont; -+ } -+ -+ if (c & IN6PTON_DOT) { -+ ret = ccs_in4_pton(tok ? tok : s, srclen + -+ (int)(s - tok), d, delim, &s); -+ if (ret > 0) { -+ d += 4; -+ break; -+ } -+ goto out; -+ } -+ -+ w = (w << 4) | (0xff & c); -+ state = IN6PTON_COLON_1 | IN6PTON_DELIM; -+ if (!(w & 0xf000)) -+ state |= IN6PTON_XDIGIT; -+ if (!dc && d + 2 < dbuf + sizeof(dbuf)) { -+ state |= IN6PTON_COLON_1_2; -+ state &= ~IN6PTON_DELIM; -+ } -+ if (d + 2 >= dbuf + sizeof(dbuf)) -+ state &= ~(IN6PTON_COLON_1|IN6PTON_COLON_1_2); -+cont: -+ if ((dc && d + 4 < dbuf + sizeof(dbuf)) || -+ d + 4 == dbuf + sizeof(dbuf)) -+ state |= IN6PTON_DOT; -+ if (d >= dbuf + sizeof(dbuf)) -+ state &= ~(IN6PTON_XDIGIT|IN6PTON_COLON_MASK); -+ s++; -+ srclen--; -+ } -+ -+ i = 15; d--; -+ -+ if (dc) { -+ while (d >= dc) -+ dst[i--] = *d--; -+ while (i >= dc - dbuf) -+ dst[i--] = 0; -+ while (i >= 0) -+ dst[i--] = *d--; -+ } else -+ memcpy(dst, dbuf, sizeof(dbuf)); -+ -+ ret = 1; -+out: -+ if (end) -+ *end = s; -+ return ret; -+} -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) -+ -+/* -+ * Routines for printing IPv4 or IPv6 address. -+ * These are copied from include/linux/kernel.h include/net/ipv6.h -+ * include/net/addrconf.h lib/hexdump.c lib/vsprintf.c and simplified. -+ */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) -+#if !defined(RHEL_MAJOR) || RHEL_MAJOR != 5 || !defined(RHEL_MINOR) || RHEL_MINOR < 9 -+static const char hex_asc[] = "0123456789abcdef"; -+#define hex_asc_lo(x) hex_asc[((x) & 0x0f)] -+#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4] -+ -+static inline char *pack_hex_byte(char *buf, u8 byte) -+{ -+ *buf++ = hex_asc_hi(byte); -+ *buf++ = hex_asc_lo(byte); -+ return buf; -+} -+#endif -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) -+static inline int ipv6_addr_v4mapped(const struct in6_addr *a) -+{ -+ return (a->s6_addr32[0] | a->s6_addr32[1] | -+ (a->s6_addr32[2] ^ htonl(0x0000ffff))) == 0; -+} -+#endif -+ -+static inline int ipv6_addr_is_isatap(const struct in6_addr *addr) -+{ -+ return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE); -+} -+ -+static char *ip4_string(char *p, const u8 *addr) -+{ -+ /* -+ * Since this function is called outside vsnprintf(), I can use -+ * sprintf() here. -+ */ -+ return p + -+ sprintf(p, "%u.%u.%u.%u", addr[0], addr[1], addr[2], addr[3]); -+} -+ -+static char *ip6_compressed_string(char *p, const char *addr) -+{ -+ int i, j, range; -+ unsigned char zerolength[8]; -+ int longest = 1; -+ int colonpos = -1; -+ u16 word; -+ u8 hi, lo; -+ bool needcolon = false; -+ bool useIPv4; -+ struct in6_addr in6; -+ -+ memcpy(&in6, addr, sizeof(struct in6_addr)); -+ -+ useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6); -+ -+ memset(zerolength, 0, sizeof(zerolength)); -+ -+ if (useIPv4) -+ range = 6; -+ else -+ range = 8; -+ -+ /* find position of longest 0 run */ -+ for (i = 0; i < range; i++) { -+ for (j = i; j < range; j++) { -+ if (in6.s6_addr16[j] != 0) -+ break; -+ zerolength[i]++; -+ } -+ } -+ for (i = 0; i < range; i++) { -+ if (zerolength[i] > longest) { -+ longest = zerolength[i]; -+ colonpos = i; -+ } -+ } -+ if (longest == 1) /* don't compress a single 0 */ -+ colonpos = -1; -+ -+ /* emit address */ -+ for (i = 0; i < range; i++) { -+ if (i == colonpos) { -+ if (needcolon || i == 0) -+ *p++ = ':'; -+ *p++ = ':'; -+ needcolon = false; -+ i += longest - 1; -+ continue; -+ } -+ if (needcolon) { -+ *p++ = ':'; -+ needcolon = false; -+ } -+ /* hex u16 without leading 0s */ -+ word = ntohs(in6.s6_addr16[i]); -+ hi = word >> 8; -+ lo = word & 0xff; -+ if (hi) { -+ if (hi > 0x0f) -+ p = pack_hex_byte(p, hi); -+ else -+ *p++ = hex_asc_lo(hi); -+ p = pack_hex_byte(p, lo); -+ } else if (lo > 0x0f) -+ p = pack_hex_byte(p, lo); -+ else -+ *p++ = hex_asc_lo(lo); -+ needcolon = true; -+ } -+ -+ if (useIPv4) { -+ if (needcolon) -+ *p++ = ':'; -+ p = ip4_string(p, &in6.s6_addr[12]); -+ } -+ *p = '\0'; -+ -+ return p; -+} -+#endif -+ -+/** -+ * ccs_print_ipv4 - Print an IPv4 address. -+ * -+ * @buffer: Buffer to write to. -+ * @buffer_len: Size of @buffer. -+ * @ip: Pointer to "u32 in network byte order". -+ * -+ * Returns written length. -+ */ -+static int ccs_print_ipv4(char *buffer, const unsigned int buffer_len, -+ const u32 *ip) -+{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) -+ return snprintf(buffer, buffer_len, "%pI4", ip); -+#else -+ char addr[sizeof("255.255.255.255")]; -+ ip4_string(addr, (const u8 *) ip); -+ return snprintf(buffer, buffer_len, "%s", addr); -+#endif -+} -+ -+/** -+ * ccs_print_ipv6 - Print an IPv6 address. -+ * -+ * @buffer: Buffer to write to. -+ * @buffer_len: Size of @buffer. -+ * @ip: Pointer to "struct in6_addr". -+ * -+ * Returns written length. -+ */ -+static int ccs_print_ipv6(char *buffer, const unsigned int buffer_len, -+ const struct in6_addr *ip) -+{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) -+ return snprintf(buffer, buffer_len, "%pI6c", ip); -+#else -+ char addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")]; -+ ip6_compressed_string(addr, (const u8 *) ip); -+ return snprintf(buffer, buffer_len, "%s", addr); -+#endif -+} -+ -+/** -+ * ccs_print_ip - Print an IP address. -+ * -+ * @buf: Buffer to write to. -+ * @size: Size of @buf. -+ * @ptr: Pointer to "struct ipaddr_union". -+ * -+ * Returns nothing. -+ */ -+static void ccs_print_ip(char *buf, const unsigned int size, -+ const struct ccs_ipaddr_union *ptr) -+{ -+ int len; -+ if (ptr->is_ipv6) -+ len = ccs_print_ipv6(buf, size, &ptr->ip[0]); -+ else -+ len = ccs_print_ipv4(buf, size, &ptr->ip[0].s6_addr32[0]); -+ if (!memcmp(&ptr->ip[0], &ptr->ip[1], 16) || len >= size / 2) -+ return; -+ buf[len++] = '-'; -+ if (ptr->is_ipv6) -+ ccs_print_ipv6(buf + len, size - len, &ptr->ip[1]); -+ else -+ ccs_print_ipv4(buf + len, size - len, -+ &ptr->ip[1].s6_addr32[0]); -+} -+ -+#endif -+ -+/***** SECTION5: Variables definition section *****/ -+ -+/* Permit policy management by non-root user? */ -+static bool ccs_manage_by_non_root; -+ -+/* Lock for protecting policy. */ -+DEFINE_MUTEX(ccs_policy_lock); -+ -+/* Has /sbin/init started? */ -+bool ccs_policy_loaded; -+ -+/* List of namespaces. */ -+LIST_HEAD(ccs_namespace_list); -+/* True if namespace other than ccs_kernel_namespace is defined. */ -+static bool ccs_namespace_enabled; -+ -+/* Initial namespace.*/ -+static struct ccs_policy_namespace ccs_kernel_namespace; -+ -+/* List of "struct ccs_condition". */ -+LIST_HEAD(ccs_condition_list); -+ -+#ifdef CONFIG_CCSECURITY_PORTRESERVE -+/* Bitmap for reserved local port numbers.*/ -+static u8 ccs_reserved_port_map[8192]; -+#endif -+ -+/* Wait queue for kernel -> userspace notification. */ -+static DECLARE_WAIT_QUEUE_HEAD(ccs_query_wait); -+/* Wait queue for userspace -> kernel notification. */ -+static DECLARE_WAIT_QUEUE_HEAD(ccs_answer_wait); -+ -+/* The list for "struct ccs_query". */ -+static LIST_HEAD(ccs_query_list); -+ -+/* Lock for manipulating ccs_query_list. */ -+static DEFINE_SPINLOCK(ccs_query_list_lock); -+ -+/* Number of "struct file" referring /proc/ccs/query interface. */ -+static atomic_t ccs_query_observers = ATOMIC_INIT(0); -+ -+/* Wait queue for /proc/ccs/audit. */ -+static DECLARE_WAIT_QUEUE_HEAD(ccs_log_wait); -+ -+/* The list for "struct ccs_log". */ -+static LIST_HEAD(ccs_log); -+ -+/* Lock for "struct list_head ccs_log". */ -+static DEFINE_SPINLOCK(ccs_log_lock); -+ -+/* Length of "stuct list_head ccs_log". */ -+static unsigned int ccs_log_count; -+ -+/* Timestamp counter for last updated. */ -+static unsigned int ccs_stat_updated[CCS_MAX_POLICY_STAT]; -+ -+/* Counter for number of updates. */ -+static unsigned int ccs_stat_modified[CCS_MAX_POLICY_STAT]; -+ -+/* Operations for /proc/ccs/self_domain interface. */ -+static -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) -+const -+#endif -+struct file_operations ccs_self_operations = { -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+ .write = ccs_write_self, -+#endif -+ .read = ccs_read_self, -+}; -+ -+/* Operations for /proc/ccs/ interface. */ -+static -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) -+const -+#endif -+struct file_operations ccs_operations = { -+ .open = ccs_open, -+ .release = ccs_release, -+ .poll = ccs_poll, -+ .read = ccs_read, -+ .write = ccs_write, -+}; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+ -+/* The inode operations for /proc/ccs/ directory. */ -+static struct inode_operations ccs_dir_inode_operations; -+ -+/* The inode operations for files under /proc/ccs/ directory. */ -+static struct inode_operations ccs_file_inode_operations; -+ -+#endif -+ -+/***** SECTION6: Dependent functions section *****/ -+ -+/** -+ * list_for_each_cookie - iterate over a list with cookie. -+ * -+ * @pos: Pointer to "struct list_head". -+ * @head: Pointer to "struct list_head". -+ */ -+#define list_for_each_cookie(pos, head) \ -+ for (pos = pos ? pos : srcu_dereference((head)->next, &ccs_ss); \ -+ pos != (head); pos = srcu_dereference(pos->next, &ccs_ss)) -+ -+/** -+ * ccs_read_token - Read a word from a line. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns a word on success, "" otherwise. -+ * -+ * To allow the caller to skip NULL check, this function returns "" rather than -+ * NULL if there is no more words to read. -+ */ -+static char *ccs_read_token(struct ccs_acl_param *param) -+{ -+ char *pos = param->data; -+ char *del = strchr(pos, ' '); -+ if (del) -+ *del++ = '\0'; -+ else -+ del = pos + strlen(pos); -+ param->data = del; -+ return pos; -+} -+ -+/** -+ * ccs_make_byte - Make byte value from three octal characters. -+ * -+ * @c1: The first character. -+ * @c2: The second character. -+ * @c3: The third character. -+ * -+ * Returns byte value. -+ */ -+static u8 ccs_make_byte(const u8 c1, const u8 c2, const u8 c3) -+{ -+ return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); -+} -+ -+/** -+ * ccs_correct_word2 - Check whether the given string follows the naming rules. -+ * -+ * @string: The byte sequence to check. Not '\0'-terminated. -+ * @len: Length of @string. -+ * -+ * Returns true if @string follows the naming rules, false otherwise. -+ */ -+static bool ccs_correct_word2(const char *string, size_t len) -+{ -+ const char *const start = string; -+ bool in_repetition = false; -+ unsigned char c; -+ unsigned char d; -+ unsigned char e; -+ if (!len) -+ goto out; -+ while (len--) { -+ c = *string++; -+ if (c == '\\') { -+ if (!len--) -+ goto out; -+ c = *string++; -+ switch (c) { -+ case '\\': /* "\\" */ -+ continue; -+ case '$': /* "\$" */ -+ case '+': /* "\+" */ -+ case '?': /* "\?" */ -+ case '*': /* "\*" */ -+ case '@': /* "\@" */ -+ case 'x': /* "\x" */ -+ case 'X': /* "\X" */ -+ case 'a': /* "\a" */ -+ case 'A': /* "\A" */ -+ case '-': /* "\-" */ -+ continue; -+ case '{': /* "/\{" */ -+ if (string - 3 < start || *(string - 3) != '/') -+ break; -+ in_repetition = true; -+ continue; -+ case '}': /* "\}/" */ -+ if (*string != '/') -+ break; -+ if (!in_repetition) -+ break; -+ in_repetition = false; -+ continue; -+ case '0': /* "\ooo" */ -+ case '1': -+ case '2': -+ case '3': -+ if (!len-- || !len--) -+ break; -+ d = *string++; -+ e = *string++; -+ if (d < '0' || d > '7' || e < '0' || e > '7') -+ break; -+ c = ccs_make_byte(c, d, e); -+ if (c <= ' ' || c >= 127) -+ continue; -+ } -+ goto out; -+ } else if (in_repetition && c == '/') { -+ goto out; -+ } else if (c <= ' ' || c >= 127) { -+ goto out; -+ } -+ } -+ if (in_repetition) -+ goto out; -+ return true; -+out: -+ return false; -+} -+ -+/** -+ * ccs_correct_word - Check whether the given string follows the naming rules. -+ * -+ * @string: The string to check. -+ * -+ * Returns true if @string follows the naming rules, false otherwise. -+ */ -+static bool ccs_correct_word(const char *string) -+{ -+ return ccs_correct_word2(string, strlen(string)); -+} -+ -+/** -+ * ccs_get_group - Allocate memory for "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group". -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * @idx: Index number. -+ * -+ * Returns pointer to "struct ccs_group" on success, NULL otherwise. -+ */ -+static struct ccs_group *ccs_get_group(struct ccs_acl_param *param, -+ const u8 idx) -+{ -+ struct ccs_group e = { }; -+ struct ccs_group *group = NULL; -+ struct list_head *list; -+ const char *group_name = ccs_read_token(param); -+ bool found = false; -+ if (!ccs_correct_word(group_name) || idx >= CCS_MAX_GROUP) -+ return NULL; -+ e.group_name = ccs_get_name(group_name); -+ if (!e.group_name) -+ return NULL; -+ if (mutex_lock_interruptible(&ccs_policy_lock)) -+ goto out; -+ list = ¶m->ns->group_list[idx]; -+ list_for_each_entry(group, list, head.list) { -+ if (e.group_name != group->group_name || -+ atomic_read(&group->head.users) == CCS_GC_IN_PROGRESS) -+ continue; -+ atomic_inc(&group->head.users); -+ found = true; -+ break; -+ } -+ if (!found) { -+ struct ccs_group *entry = ccs_commit_ok(&e, sizeof(e)); -+ if (entry) { -+ INIT_LIST_HEAD(&entry->member_list); -+ atomic_set(&entry->head.users, 1); -+ list_add_tail_rcu(&entry->head.list, list); -+ group = entry; -+ found = true; -+ } -+ } -+ mutex_unlock(&ccs_policy_lock); -+out: -+ ccs_put_name(e.group_name); -+ return found ? group : NULL; -+} -+ -+/** -+ * ccs_parse_name_union - Parse a ccs_name_union. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * @ptr: Pointer to "struct ccs_name_union". -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool ccs_parse_name_union(struct ccs_acl_param *param, -+ struct ccs_name_union *ptr) -+{ -+ char *filename; -+ if (param->data[0] == '@') { -+ param->data++; -+ ptr->group = ccs_get_group(param, CCS_PATH_GROUP); -+ return ptr->group != NULL; -+ } -+ filename = ccs_read_token(param); -+ if (!ccs_correct_word(filename)) -+ return false; -+ ptr->filename = ccs_get_name(filename); -+ return ptr->filename != NULL; -+} -+ -+/** -+ * ccs_parse_ulong - Parse an "unsigned long" value. -+ * -+ * @result: Pointer to "unsigned long". -+ * @str: Pointer to string to parse. -+ * -+ * Returns one of values in "enum ccs_value_type". -+ * -+ * The @src is updated to point the first character after the value -+ * on success. -+ */ -+static u8 ccs_parse_ulong(unsigned long *result, char **str) -+{ -+ const char *cp = *str; -+ char *ep; -+ int base = 10; -+ if (*cp == '0') { -+ char c = *(cp + 1); -+ if (c == 'x' || c == 'X') { -+ base = 16; -+ cp += 2; -+ } else if (c >= '0' && c <= '7') { -+ base = 8; -+ cp++; -+ } -+ } -+ *result = simple_strtoul(cp, &ep, base); -+ if (cp == ep) -+ return CCS_VALUE_TYPE_INVALID; -+ *str = ep; -+ switch (base) { -+ case 16: -+ return CCS_VALUE_TYPE_HEXADECIMAL; -+ case 8: -+ return CCS_VALUE_TYPE_OCTAL; -+ default: -+ return CCS_VALUE_TYPE_DECIMAL; -+ } -+} -+ -+/** -+ * ccs_parse_number_union - Parse a ccs_number_union. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * @ptr: Pointer to "struct ccs_number_union". -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool ccs_parse_number_union(struct ccs_acl_param *param, -+ struct ccs_number_union *ptr) -+{ -+ char *data; -+ u8 type; -+ unsigned long v; -+ memset(ptr, 0, sizeof(*ptr)); -+ if (param->data[0] == '@') { -+ param->data++; -+ ptr->group = ccs_get_group(param, CCS_NUMBER_GROUP); -+ return ptr->group != NULL; -+ } -+ data = ccs_read_token(param); -+ type = ccs_parse_ulong(&v, &data); -+ if (type == CCS_VALUE_TYPE_INVALID) -+ return false; -+ ptr->values[0] = v; -+ ptr->value_type[0] = type; -+ if (!*data) { -+ ptr->values[1] = v; -+ ptr->value_type[1] = type; -+ return true; -+ } -+ if (*data++ != '-') -+ return false; -+ type = ccs_parse_ulong(&v, &data); -+ if (type == CCS_VALUE_TYPE_INVALID || *data || ptr->values[0] > v) -+ return false; -+ ptr->values[1] = v; -+ ptr->value_type[1] = type; -+ return true; -+} -+ -+#ifdef CONFIG_CCSECURITY_NETWORK -+ -+/** -+ * ccs_parse_ipaddr_union - Parse an IP address. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * @ptr: Pointer to "struct ccs_ipaddr_union". -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool ccs_parse_ipaddr_union(struct ccs_acl_param *param, -+ struct ccs_ipaddr_union *ptr) -+{ -+ u8 * const min = ptr->ip[0].in6_u.u6_addr8; -+ u8 * const max = ptr->ip[1].in6_u.u6_addr8; -+ char *address = ccs_read_token(param); -+ const char *end; -+ if (!strchr(address, ':') && -+ ccs_in4_pton(address, -1, min, '-', &end) > 0) { -+ ptr->is_ipv6 = false; -+ if (!*end) -+ ptr->ip[1].s6_addr32[0] = ptr->ip[0].s6_addr32[0]; -+ else if (*end++ != '-' || -+ ccs_in4_pton(end, -1, max, '\0', &end) <= 0 || *end) -+ return false; -+ return true; -+ } -+ if (ccs_in6_pton(address, -1, min, '-', &end) > 0) { -+ ptr->is_ipv6 = true; -+ if (!*end) -+ memmove(max, min, sizeof(u16) * 8); -+ else if (*end++ != '-' || -+ ccs_in6_pton(end, -1, max, '\0', &end) <= 0 || *end) -+ return false; -+ return true; -+ } -+ return false; -+} -+ -+#endif -+ -+/** -+ * ccs_get_dqword - ccs_get_name() for a quoted string. -+ * -+ * @start: String to save. -+ * -+ * Returns pointer to "struct ccs_path_info" on success, NULL otherwise. -+ */ -+static const struct ccs_path_info *ccs_get_dqword(char *start) -+{ -+ char *cp = start + strlen(start) - 1; -+ if (cp == start || *start++ != '"' || *cp != '"') -+ return NULL; -+ *cp = '\0'; -+ if (*start && !ccs_correct_word(start)) -+ return NULL; -+ return ccs_get_name(start); -+} -+ -+/** -+ * ccs_parse_name_union_quoted - Parse a quoted word. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * @ptr: Pointer to "struct ccs_name_union". -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool ccs_parse_name_union_quoted(struct ccs_acl_param *param, -+ struct ccs_name_union *ptr) -+{ -+ char *filename = param->data; -+ if (*filename == '@') -+ return ccs_parse_name_union(param, ptr); -+ ptr->filename = ccs_get_dqword(filename); -+ return ptr->filename != NULL; -+} -+ -+/** -+ * ccs_parse_argv - Parse an argv[] condition part. -+ * -+ * @left: Lefthand value. -+ * @right: Righthand value. -+ * @argv: Pointer to "struct ccs_argv". -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool ccs_parse_argv(char *left, char *right, struct ccs_argv *argv) -+{ -+ if (ccs_parse_ulong(&argv->index, &left) != CCS_VALUE_TYPE_DECIMAL || -+ *left++ != ']' || *left) -+ return false; -+ argv->value = ccs_get_dqword(right); -+ return argv->value != NULL; -+} -+ -+/** -+ * ccs_parse_envp - Parse an envp[] condition part. -+ * -+ * @left: Lefthand value. -+ * @right: Righthand value. -+ * @envp: Pointer to "struct ccs_envp". -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool ccs_parse_envp(char *left, char *right, struct ccs_envp *envp) -+{ -+ const struct ccs_path_info *name; -+ const struct ccs_path_info *value; -+ char *cp = left + strlen(left) - 1; -+ if (*cp-- != ']' || *cp != '"') -+ goto out; -+ *cp = '\0'; -+ if (!ccs_correct_word(left)) -+ goto out; -+ name = ccs_get_name(left); -+ if (!name) -+ goto out; -+ if (!strcmp(right, "NULL")) { -+ value = NULL; -+ } else { -+ value = ccs_get_dqword(right); -+ if (!value) { -+ ccs_put_name(name); -+ goto out; -+ } -+ } -+ envp->name = name; -+ envp->value = value; -+ return true; -+out: -+ return false; -+} -+ -+/** -+ * ccs_same_condition - Check for duplicated "struct ccs_condition" entry. -+ * -+ * @a: Pointer to "struct ccs_condition". -+ * @b: Pointer to "struct ccs_condition". -+ * -+ * Returns true if @a == @b, false otherwise. -+ */ -+static bool ccs_same_condition(const struct ccs_condition *a, -+ const struct ccs_condition *b) -+{ -+ return a->size == b->size && a->condc == b->condc && -+ a->numbers_count == b->numbers_count && -+ a->names_count == b->names_count && -+ a->argc == b->argc && a->envc == b->envc && -+ a->grant_log == b->grant_log && -+ a->exec_transit == b->exec_transit && a->transit == b->transit -+ && !memcmp(a + 1, b + 1, a->size - sizeof(*a)); -+} -+ -+/** -+ * ccs_condition_type - Get condition type. -+ * -+ * @word: Keyword string. -+ * -+ * Returns one of values in "enum ccs_conditions_index" on success, -+ * CCS_MAX_CONDITION_KEYWORD otherwise. -+ */ -+static u8 ccs_condition_type(const char *word) -+{ -+ u8 i; -+ for (i = 0; i < CCS_MAX_CONDITION_KEYWORD; i++) { -+ if (!strcmp(word, ccs_condition_keyword[i])) -+ break; -+ } -+ return i; -+} -+ -+/** -+ * ccs_commit_condition - Commit "struct ccs_condition". -+ * -+ * @entry: Pointer to "struct ccs_condition". -+ * -+ * Returns pointer to "struct ccs_condition" on success, NULL otherwise. -+ * -+ * This function merges duplicated entries. This function returns NULL if -+ * @entry is not duplicated but memory quota for policy has exceeded. -+ */ -+static struct ccs_condition *ccs_commit_condition(struct ccs_condition *entry) -+{ -+ struct ccs_condition *ptr; -+ bool found = false; -+ if (mutex_lock_interruptible(&ccs_policy_lock)) { -+ dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__); -+ ptr = NULL; -+ found = true; -+ goto out; -+ } -+ list_for_each_entry(ptr, &ccs_condition_list, head.list) { -+ if (!ccs_same_condition(ptr, entry) || -+ atomic_read(&ptr->head.users) == CCS_GC_IN_PROGRESS) -+ continue; -+ /* Same entry found. Share this entry. */ -+ atomic_inc(&ptr->head.users); -+ found = true; -+ break; -+ } -+ if (!found) { -+ if (ccs_memory_ok(entry, entry->size)) { -+ atomic_set(&entry->head.users, 1); -+ list_add(&entry->head.list, &ccs_condition_list); -+ } else { -+ found = true; -+ ptr = NULL; -+ } -+ } -+ mutex_unlock(&ccs_policy_lock); -+out: -+ if (found) { -+ ccs_del_condition(&entry->head.list); -+ kfree(entry); -+ entry = ptr; -+ } -+ return entry; -+} -+ -+/** -+ * ccs_correct_path - Check whether the given pathname follows the naming rules. -+ * -+ * @filename: The pathname to check. -+ * -+ * Returns true if @filename follows the naming rules, false otherwise. -+ */ -+static bool ccs_correct_path(const char *filename) -+{ -+ return *filename == '/' && ccs_correct_word(filename); -+} -+ -+/** -+ * ccs_domain_def - Check whether the given token can be a domainname. -+ * -+ * @buffer: The token to check. -+ * -+ * Returns true if @buffer possibly be a domainname, false otherwise. -+ */ -+static bool ccs_domain_def(const unsigned char *buffer) -+{ -+ const unsigned char *cp; -+ int len; -+ if (*buffer != '<') -+ return false; -+ cp = strchr(buffer, ' '); -+ if (!cp) -+ len = strlen(buffer); -+ else -+ len = cp - buffer; -+ if (buffer[len - 1] != '>' || !ccs_correct_word2(buffer + 1, len - 2)) -+ return false; -+ return true; -+} -+ -+/** -+ * ccs_correct_domain - Check whether the given domainname follows the naming rules. -+ * -+ * @domainname: The domainname to check. -+ * -+ * Returns true if @domainname follows the naming rules, false otherwise. -+ */ -+static bool ccs_correct_domain(const unsigned char *domainname) -+{ -+ if (!domainname || !ccs_domain_def(domainname)) -+ return false; -+ domainname = strchr(domainname, ' '); -+ if (!domainname++) -+ return true; -+ while (1) { -+ const unsigned char *cp = strchr(domainname, ' '); -+ if (!cp) -+ break; -+ if (*domainname != '/' || -+ !ccs_correct_word2(domainname, cp - domainname)) -+ return false; -+ domainname = cp + 1; -+ } -+ return ccs_correct_path(domainname); -+} -+ -+/** -+ * ccs_normalize_line - Format string. -+ * -+ * @buffer: The line to normalize. -+ * -+ * Returns nothing. -+ * -+ * Leading and trailing whitespaces are removed. -+ * Multiple whitespaces are packed into single space. -+ */ -+static void ccs_normalize_line(unsigned char *buffer) -+{ -+ unsigned char *sp = buffer; -+ unsigned char *dp = buffer; -+ bool first = true; -+ while (*sp && (*sp <= ' ' || *sp >= 127)) -+ sp++; -+ while (*sp) { -+ if (!first) -+ *dp++ = ' '; -+ first = false; -+ while (*sp > ' ' && *sp < 127) -+ *dp++ = *sp++; -+ while (*sp && (*sp <= ' ' || *sp >= 127)) -+ sp++; -+ } -+ *dp = '\0'; -+} -+ -+/** -+ * ccs_get_domainname - Read a domainname from a line. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns a domainname on success, NULL otherwise. -+ */ -+static const struct ccs_path_info *ccs_get_domainname -+(struct ccs_acl_param *param) -+{ -+ char *start = param->data; -+ char *pos = start; -+ while (*pos) { -+ if (*pos++ != ' ' || *pos++ == '/') -+ continue; -+ pos -= 2; -+ *pos++ = '\0'; -+ break; -+ } -+ param->data = pos; -+ if (ccs_correct_domain(start)) -+ return ccs_get_name(start); -+ return NULL; -+} -+ -+/** -+ * ccs_get_transit_preference - Parse domain transition preference for execve(). -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * @e: Pointer to "struct ccs_condition". -+ * -+ * Returns the condition string part. -+ */ -+static char *ccs_get_transit_preference(struct ccs_acl_param *param, -+ struct ccs_condition *e) -+{ -+ char * const pos = param->data; -+ bool flag; -+ if (*pos == '<') { -+ e->transit = ccs_get_domainname(param); -+ goto done; -+ } -+ { -+ char *cp = strchr(pos, ' '); -+ if (cp) -+ *cp = '\0'; -+ flag = ccs_correct_path(pos) || !strcmp(pos, "keep") || -+ !strcmp(pos, "initialize") || !strcmp(pos, "reset") || -+ !strcmp(pos, "child") || !strcmp(pos, "parent"); -+ if (cp) -+ *cp = ' '; -+ } -+ if (!flag) -+ return pos; -+ e->transit = ccs_get_name(ccs_read_token(param)); -+done: -+ if (e->transit) { -+ e->exec_transit = true; -+ return param->data; -+ } -+ /* -+ * Return a bad read-only condition string that will let -+ * ccs_get_condition() return NULL. -+ */ -+ return "/"; -+} -+ -+/** -+ * ccs_get_condition - Parse condition part. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns pointer to "struct ccs_condition" on success, NULL otherwise. -+ */ -+struct ccs_condition *ccs_get_condition(struct ccs_acl_param *param) -+{ -+ struct ccs_condition *entry = NULL; -+ struct ccs_condition_element *condp = NULL; -+ struct ccs_number_union *numbers_p = NULL; -+ struct ccs_name_union *names_p = NULL; -+ struct ccs_argv *argv = NULL; -+ struct ccs_envp *envp = NULL; -+ struct ccs_condition e = { }; -+ char * const start_of_string = ccs_get_transit_preference(param, &e); -+ char * const end_of_string = start_of_string + strlen(start_of_string); -+ char *pos; -+rerun: -+ pos = start_of_string; -+ while (1) { -+ u8 left = -1; -+ u8 right = -1; -+ char *left_word = pos; -+ char *cp; -+ char *right_word; -+ bool is_not; -+ if (!*left_word) -+ break; -+ /* -+ * Since left-hand condition does not allow use of "path_group" -+ * or "number_group" and environment variable's names do not -+ * accept '=', it is guaranteed that the original line consists -+ * of one or more repetition of $left$operator$right blocks -+ * where "$left is free from '=' and ' '" and "$operator is -+ * either '=' or '!='" and "$right is free from ' '". -+ * Therefore, we can reconstruct the original line at the end -+ * of dry run even if we overwrite $operator with '\0'. -+ */ -+ cp = strchr(pos, ' '); -+ if (cp) { -+ *cp = '\0'; /* Will restore later. */ -+ pos = cp + 1; -+ } else { -+ pos = ""; -+ } -+ right_word = strchr(left_word, '='); -+ if (!right_word || right_word == left_word) -+ goto out; -+ is_not = *(right_word - 1) == '!'; -+ if (is_not) -+ *(right_word++ - 1) = '\0'; /* Will restore later. */ -+ else if (*(right_word + 1) != '=') -+ *right_word++ = '\0'; /* Will restore later. */ -+ else -+ goto out; -+ dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word, -+ is_not ? "!" : "", right_word); -+ if (!strcmp(left_word, "grant_log")) { -+ if (entry) { -+ if (is_not || -+ entry->grant_log != CCS_GRANTLOG_AUTO) -+ goto out; -+ else if (!strcmp(right_word, "yes")) -+ entry->grant_log = CCS_GRANTLOG_YES; -+ else if (!strcmp(right_word, "no")) -+ entry->grant_log = CCS_GRANTLOG_NO; -+ else -+ goto out; -+ } -+ continue; -+ } -+ if (!strcmp(left_word, "auto_domain_transition")) { -+ if (entry) { -+ if (is_not || entry->transit) -+ goto out; -+ entry->transit = ccs_get_dqword(right_word); -+ if (!entry->transit || -+ (entry->transit->name[0] != '/' && -+ !ccs_domain_def(entry->transit->name))) -+ goto out; -+ } -+ continue; -+ } -+ if (!strncmp(left_word, "exec.argv[", 10)) { -+ if (!argv) { -+ e.argc++; -+ e.condc++; -+ } else { -+ e.argc--; -+ e.condc--; -+ left = CCS_ARGV_ENTRY; -+ argv->is_not = is_not; -+ if (!ccs_parse_argv(left_word + 10, -+ right_word, argv++)) -+ goto out; -+ } -+ goto store_value; -+ } -+ if (!strncmp(left_word, "exec.envp[\"", 11)) { -+ if (!envp) { -+ e.envc++; -+ e.condc++; -+ } else { -+ e.envc--; -+ e.condc--; -+ left = CCS_ENVP_ENTRY; -+ envp->is_not = is_not; -+ if (!ccs_parse_envp(left_word + 11, -+ right_word, envp++)) -+ goto out; -+ } -+ goto store_value; -+ } -+ left = ccs_condition_type(left_word); -+ dprintk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__, left_word, -+ left); -+ if (left == CCS_MAX_CONDITION_KEYWORD) { -+ if (!numbers_p) { -+ e.numbers_count++; -+ } else { -+ e.numbers_count--; -+ left = CCS_NUMBER_UNION; -+ param->data = left_word; -+ if (*left_word == '@' || -+ !ccs_parse_number_union(param, -+ numbers_p++)) -+ goto out; -+ } -+ } -+ if (!condp) -+ e.condc++; -+ else -+ e.condc--; -+ if (left == CCS_EXEC_REALPATH || left == CCS_SYMLINK_TARGET) { -+ if (!names_p) { -+ e.names_count++; -+ } else { -+ e.names_count--; -+ right = CCS_NAME_UNION; -+ param->data = right_word; -+ if (!ccs_parse_name_union_quoted(param, -+ names_p++)) -+ goto out; -+ } -+ goto store_value; -+ } -+ right = ccs_condition_type(right_word); -+ if (right == CCS_MAX_CONDITION_KEYWORD) { -+ if (!numbers_p) { -+ e.numbers_count++; -+ } else { -+ e.numbers_count--; -+ right = CCS_NUMBER_UNION; -+ param->data = right_word; -+ if (!ccs_parse_number_union(param, -+ numbers_p++)) -+ goto out; -+ } -+ } -+store_value: -+ if (!condp) { -+ dprintk(KERN_WARNING "%u: dry_run left=%u right=%u " -+ "match=%u\n", __LINE__, left, right, !is_not); -+ continue; -+ } -+ condp->left = left; -+ condp->right = right; -+ condp->equals = !is_not; -+ dprintk(KERN_WARNING "%u: left=%u right=%u match=%u\n", -+ __LINE__, condp->left, condp->right, -+ condp->equals); -+ condp++; -+ } -+ dprintk(KERN_INFO "%u: cond=%u numbers=%u names=%u ac=%u ec=%u\n", -+ __LINE__, e.condc, e.numbers_count, e.names_count, e.argc, -+ e.envc); -+ if (entry) { -+ BUG_ON(e.names_count | e.numbers_count | e.argc | e.envc | -+ e.condc); -+ return ccs_commit_condition(entry); -+ } -+ e.size = sizeof(*entry) -+ + e.condc * sizeof(struct ccs_condition_element) -+ + e.numbers_count * sizeof(struct ccs_number_union) -+ + e.names_count * sizeof(struct ccs_name_union) -+ + e.argc * sizeof(struct ccs_argv) -+ + e.envc * sizeof(struct ccs_envp); -+ entry = kzalloc(e.size, CCS_GFP_FLAGS); -+ if (!entry) -+ goto out2; -+ *entry = e; -+ e.transit = NULL; -+ condp = (struct ccs_condition_element *) (entry + 1); -+ numbers_p = (struct ccs_number_union *) (condp + e.condc); -+ names_p = (struct ccs_name_union *) (numbers_p + e.numbers_count); -+ argv = (struct ccs_argv *) (names_p + e.names_count); -+ envp = (struct ccs_envp *) (argv + e.argc); -+ { -+ bool flag = false; -+ for (pos = start_of_string; pos < end_of_string; pos++) { -+ if (*pos) -+ continue; -+ if (flag) /* Restore " ". */ -+ *pos = ' '; -+ else if (*(pos + 1) == '=') /* Restore "!=". */ -+ *pos = '!'; -+ else /* Restore "=". */ -+ *pos = '='; -+ flag = !flag; -+ } -+ } -+ goto rerun; -+out: -+ dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__); -+ if (entry) { -+ ccs_del_condition(&entry->head.list); -+ kfree(entry); -+ } -+out2: -+ ccs_put_name(e.transit); -+ return NULL; -+} -+ -+/** -+ * ccs_yesno - Return "yes" or "no". -+ * -+ * @value: Bool value. -+ * -+ * Returns "yes" if @value is not 0, "no" otherwise. -+ */ -+static const char *ccs_yesno(const unsigned int value) -+{ -+ return value ? "yes" : "no"; -+} -+ -+/** -+ * ccs_addprintf - strncat()-like-snprintf(). -+ * -+ * @buffer: Buffer to write to. Must be '\0'-terminated. -+ * @len: Size of @buffer. -+ * @fmt: The printf()'s format string, followed by parameters. -+ * -+ * Returns nothing. -+ */ -+static void ccs_addprintf(char *buffer, int len, const char *fmt, ...) -+{ -+ va_list args; -+ const int pos = strlen(buffer); -+ va_start(args, fmt); -+ vsnprintf(buffer + pos, len - pos - 1, fmt, args); -+ va_end(args); -+} -+ -+/** -+ * ccs_flush - Flush queued string to userspace's buffer. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns true if all data was flushed, false otherwise. -+ */ -+static bool ccs_flush(struct ccs_io_buffer *head) -+{ -+ while (head->r.w_pos) { -+ const char *w = head->r.w[0]; -+ size_t len = strlen(w); -+ if (len) { -+ if (len > head->read_user_buf_avail) -+ len = head->read_user_buf_avail; -+ if (!len) -+ return false; -+ if (copy_to_user(head->read_user_buf, w, len)) -+ return false; -+ head->read_user_buf_avail -= len; -+ head->read_user_buf += len; -+ w += len; -+ } -+ head->r.w[0] = w; -+ if (*w) -+ return false; -+ /* Add '\0' for audit logs and query. */ -+ if (head->type == CCS_AUDIT || head->type == CCS_QUERY) { -+ if (!head->read_user_buf_avail || -+ copy_to_user(head->read_user_buf, "", 1)) -+ return false; -+ head->read_user_buf_avail--; -+ head->read_user_buf++; -+ } -+ head->r.w_pos--; -+ for (len = 0; len < head->r.w_pos; len++) -+ head->r.w[len] = head->r.w[len + 1]; -+ } -+ head->r.avail = 0; -+ return true; -+} -+ -+/** -+ * ccs_set_string - Queue string to "struct ccs_io_buffer" structure. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @string: String to print. -+ * -+ * Returns nothing. -+ * -+ * Note that @string has to be kept valid until @head is kfree()d. -+ * This means that char[] allocated on stack memory cannot be passed to -+ * this function. Use ccs_io_printf() for char[] allocated on stack memory. -+ */ -+static void ccs_set_string(struct ccs_io_buffer *head, const char *string) -+{ -+ if (head->r.w_pos < CCS_MAX_IO_READ_QUEUE) { -+ head->r.w[head->r.w_pos++] = string; -+ ccs_flush(head); -+ } else -+ printk(KERN_WARNING "Too many words in a line.\n"); -+} -+ -+/** -+ * ccs_io_printf - printf() to "struct ccs_io_buffer" structure. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @fmt: The printf()'s format string, followed by parameters. -+ * -+ * Returns nothing. -+ */ -+static void ccs_io_printf(struct ccs_io_buffer *head, const char *fmt, ...) -+{ -+ va_list args; -+ size_t len; -+ size_t pos = head->r.avail; -+ int size = head->readbuf_size - pos; -+ if (size <= 0) -+ return; -+ va_start(args, fmt); -+ len = vsnprintf(head->read_buf + pos, size, fmt, args) + 1; -+ va_end(args); -+ if (pos + len >= head->readbuf_size) { -+ printk(KERN_WARNING "Too many words in a line.\n"); -+ return; -+ } -+ head->r.avail += len; -+ ccs_set_string(head, head->read_buf + pos); -+} -+ -+/** -+ * ccs_set_space - Put a space to "struct ccs_io_buffer" structure. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns nothing. -+ */ -+static void ccs_set_space(struct ccs_io_buffer *head) -+{ -+ ccs_set_string(head, " "); -+} -+ -+/** -+ * ccs_set_lf - Put a line feed to "struct ccs_io_buffer" structure. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns true if all data was flushed, false otherwise. -+ */ -+static bool ccs_set_lf(struct ccs_io_buffer *head) -+{ -+ ccs_set_string(head, "\n"); -+ return !head->r.w_pos; -+} -+ -+/** -+ * ccs_set_slash - Put a shash to "struct ccs_io_buffer" structure. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns nothing. -+ */ -+static void ccs_set_slash(struct ccs_io_buffer *head) -+{ -+ ccs_set_string(head, "/"); -+} -+ -+/** -+ * ccs_init_policy_namespace - Initialize namespace. -+ * -+ * @ns: Pointer to "struct ccs_policy_namespace". -+ * -+ * Returns nothing. -+ */ -+static void ccs_init_policy_namespace(struct ccs_policy_namespace *ns) -+{ -+ unsigned int idx; -+ for (idx = 0; idx < CCS_MAX_ACL_GROUPS; idx++) -+ INIT_LIST_HEAD(&ns->acl_group[idx]); -+ for (idx = 0; idx < CCS_MAX_GROUP; idx++) -+ INIT_LIST_HEAD(&ns->group_list[idx]); -+ for (idx = 0; idx < CCS_MAX_POLICY; idx++) -+ INIT_LIST_HEAD(&ns->policy_list[idx]); -+ ns->profile_version = 20150505; -+ ccs_namespace_enabled = !list_empty(&ccs_namespace_list); -+ list_add_tail_rcu(&ns->namespace_list, &ccs_namespace_list); -+} -+ -+/** -+ * ccs_print_namespace - Print namespace header. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns nothing. -+ */ -+static void ccs_print_namespace(struct ccs_io_buffer *head) -+{ -+ if (!ccs_namespace_enabled) -+ return; -+ ccs_set_string(head, -+ container_of(head->r.ns, struct ccs_policy_namespace, -+ namespace_list)->name); -+ ccs_set_space(head); -+} -+ -+/** -+ * ccs_assign_profile - Create a new profile. -+ * -+ * @ns: Pointer to "struct ccs_policy_namespace". -+ * @profile: Profile number to create. -+ * -+ * Returns pointer to "struct ccs_profile" on success, NULL otherwise. -+ */ -+static struct ccs_profile *ccs_assign_profile(struct ccs_policy_namespace *ns, -+ const unsigned int profile) -+{ -+ struct ccs_profile *ptr; -+ struct ccs_profile *entry; -+ if (profile >= CCS_MAX_PROFILES) -+ return NULL; -+ ptr = ns->profile_ptr[profile]; -+ if (ptr) -+ return ptr; -+ entry = kzalloc(sizeof(*entry), CCS_GFP_FLAGS); -+ if (mutex_lock_interruptible(&ccs_policy_lock)) -+ goto out; -+ ptr = ns->profile_ptr[profile]; -+ if (!ptr && ccs_memory_ok(entry, sizeof(*entry))) { -+ ptr = entry; -+ ptr->default_config = CCS_CONFIG_DISABLED | -+ CCS_CONFIG_WANT_GRANT_LOG | CCS_CONFIG_WANT_REJECT_LOG; -+ memset(ptr->config, CCS_CONFIG_USE_DEFAULT, -+ sizeof(ptr->config)); -+ ptr->pref[CCS_PREF_MAX_AUDIT_LOG] = -+ CONFIG_CCSECURITY_MAX_AUDIT_LOG; -+ ptr->pref[CCS_PREF_MAX_LEARNING_ENTRY] = -+ CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY; -+ mb(); /* Avoid out-of-order execution. */ -+ ns->profile_ptr[profile] = ptr; -+ entry = NULL; -+ } -+ mutex_unlock(&ccs_policy_lock); -+out: -+ kfree(entry); -+ return ptr; -+} -+ -+/** -+ * ccs_check_profile - Check all profiles currently assigned to domains are defined. -+ * -+ * Returns nothing. -+ */ -+static void ccs_check_profile(void) -+{ -+ struct ccs_domain_info *domain; -+ const int idx = ccs_read_lock(); -+ ccs_policy_loaded = true; -+ printk(KERN_INFO "CCSecurity: 1.8.4 2015/05/05\n"); -+ list_for_each_entry_srcu(domain, &ccs_domain_list, list, &ccs_ss) { -+ const u8 profile = domain->profile; -+ struct ccs_policy_namespace *ns = domain->ns; -+ if (ns->profile_version == 20100903) { -+ static bool done; -+ if (!done) -+ printk(KERN_INFO "Converting profile version " -+ "from %u to %u.\n", 20100903, 20150505); -+ done = true; -+ ns->profile_version = 20150505; -+ } -+ if (ns->profile_version != 20150505) -+ printk(KERN_ERR -+ "Profile version %u is not supported.\n", -+ ns->profile_version); -+ else if (!ns->profile_ptr[profile]) -+ printk(KERN_ERR -+ "Profile %u (used by '%s') is not defined.\n", -+ profile, domain->domainname->name); -+ else -+ continue; -+ printk(KERN_ERR -+ "Userland tools for TOMOYO 1.8 must be installed and " -+ "policy must be initialized.\n"); -+ printk(KERN_ERR "Please see http://tomoyo.osdn.jp/1.8/ " -+ "for more information.\n"); -+ panic("STOP!"); -+ } -+ ccs_read_unlock(idx); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) && defined(CONFIG_SECURITY) -+ ccsecurity_exports.add_hooks(); -+#endif -+ printk(KERN_INFO "Mandatory Access Control activated.\n"); -+} -+ -+/** -+ * ccs_profile - Find a profile. -+ * -+ * @profile: Profile number to find. -+ * -+ * Returns pointer to "struct ccs_profile". -+ */ -+static struct ccs_profile *ccs_profile(const u8 profile) -+{ -+ static struct ccs_profile ccs_null_profile; -+ struct ccs_profile *ptr = ccs_current_namespace()-> -+ profile_ptr[profile]; -+ if (!ptr) -+ ptr = &ccs_null_profile; -+ return ptr; -+} -+ -+/** -+ * ccs_get_config - Get config for specified profile's specified functionality. -+ * -+ * @profile: Profile number. -+ * @index: Index number of functionality. -+ * -+ * Returns config. -+ * -+ * First, check for CONFIG::category::functionality. -+ * If CONFIG::category::functionality is set to use default, then check -+ * CONFIG::category. If CONFIG::category is set to use default, then use -+ * CONFIG. CONFIG cannot be set to use default. -+ */ -+u8 ccs_get_config(const u8 profile, const u8 index) -+{ -+ u8 config; -+ const struct ccs_profile *p; -+ if (!ccs_policy_loaded) -+ return CCS_CONFIG_DISABLED; -+ p = ccs_profile(profile); -+ config = p->config[index]; -+ if (config == CCS_CONFIG_USE_DEFAULT) -+ config = p->config[ccs_index2category[index] -+ + CCS_MAX_MAC_INDEX]; -+ if (config == CCS_CONFIG_USE_DEFAULT) -+ config = p->default_config; -+ return config; -+} -+ -+/** -+ * ccs_find_yesno - Find values for specified keyword. -+ * -+ * @string: String to check. -+ * @find: Name of keyword. -+ * -+ * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise. -+ */ -+static s8 ccs_find_yesno(const char *string, const char *find) -+{ -+ const char *cp = strstr(string, find); -+ if (cp) { -+ cp += strlen(find); -+ if (!strncmp(cp, "=yes", 4)) -+ return 1; -+ else if (!strncmp(cp, "=no", 3)) -+ return 0; -+ } -+ return -1; -+} -+ -+/** -+ * ccs_set_uint - Set value for specified preference. -+ * -+ * @i: Pointer to "unsigned int". -+ * @string: String to check. -+ * @find: Name of keyword. -+ * -+ * Returns nothing. -+ */ -+static void ccs_set_uint(unsigned int *i, const char *string, const char *find) -+{ -+ const char *cp = strstr(string, find); -+ if (cp) -+ sscanf(cp + strlen(find), "=%u", i); -+} -+ -+/** -+ * ccs_str_starts - Check whether the given string starts with the given keyword. -+ * -+ * @src: Pointer to pointer to the string. -+ * @find: Pointer to the keyword. -+ * -+ * Returns true if @src starts with @find, false otherwise. -+ * -+ * The @src is updated to point the first character after the @find -+ * if @src starts with @find. -+ */ -+static bool ccs_str_starts(char **src, const char *find) -+{ -+ const int len = strlen(find); -+ char *tmp = *src; -+ if (strncmp(tmp, find, len)) -+ return false; -+ tmp += len; -+ *src = tmp; -+ return true; -+} -+ -+/** -+ * ccs_print_group - Print group's name. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @group: Pointer to "struct ccsgroup". Maybe NULL. -+ * -+ * Returns true if @group is not NULL. false otherwise. -+ */ -+static bool ccs_print_group(struct ccs_io_buffer *head, -+ const struct ccs_group *group) -+{ -+ if (group) { -+ ccs_set_string(head, "@"); -+ ccs_set_string(head, group->group_name->name); -+ return true; -+ } -+ return false; -+} -+ -+/** -+ * ccs_set_mode - Set mode for specified profile. -+ * -+ * @name: Name of functionality. -+ * @value: Mode for @name. -+ * @profile: Pointer to "struct ccs_profile". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_set_mode(char *name, const char *value, -+ struct ccs_profile *profile) -+{ -+ u8 i; -+ u8 config; -+ if (!strcmp(name, "CONFIG")) { -+ i = CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX; -+ config = profile->default_config; -+ } else if (ccs_str_starts(&name, "CONFIG::")) { -+ config = 0; -+ for (i = 0; i < CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX; -+ i++) { -+ int len = 0; -+ if (i < CCS_MAX_MAC_INDEX) { -+ const u8 c = ccs_index2category[i]; -+ const char *category = -+ ccs_category_keywords[c]; -+ len = strlen(category); -+ if (strncmp(name, category, len) || -+ name[len++] != ':' || name[len++] != ':') -+ continue; -+ } -+ if (strcmp(name + len, ccs_mac_keywords[i])) -+ continue; -+ config = profile->config[i]; -+ break; -+ } -+ if (i == CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX) -+ return -EINVAL; -+ } else { -+ return -EINVAL; -+ } -+ if (strstr(value, "use_default")) { -+ config = CCS_CONFIG_USE_DEFAULT; -+ } else { -+ u8 mode; -+ for (mode = 0; mode < CCS_CONFIG_MAX_MODE; mode++) -+ if (strstr(value, ccs_mode[mode])) -+ /* -+ * Update lower 3 bits in order to distinguish -+ * 'config' from 'CCS_CONFIG_USE_DEAFULT'. -+ */ -+ config = (config & ~7) | mode; -+ if (config != CCS_CONFIG_USE_DEFAULT) { -+ switch (ccs_find_yesno(value, "grant_log")) { -+ case 1: -+ config |= CCS_CONFIG_WANT_GRANT_LOG; -+ break; -+ case 0: -+ config &= ~CCS_CONFIG_WANT_GRANT_LOG; -+ break; -+ } -+ switch (ccs_find_yesno(value, "reject_log")) { -+ case 1: -+ config |= CCS_CONFIG_WANT_REJECT_LOG; -+ break; -+ case 0: -+ config &= ~CCS_CONFIG_WANT_REJECT_LOG; -+ break; -+ } -+ } -+ } -+ if (i < CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX) -+ profile->config[i] = config; -+ else if (config != CCS_CONFIG_USE_DEFAULT) -+ profile->default_config = config; -+ return 0; -+} -+ -+/** -+ * ccs_write_profile - Write profile table. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_write_profile(struct ccs_io_buffer *head) -+{ -+ char *data = head->write_buf; -+ unsigned int i; -+ char *cp; -+ struct ccs_profile *profile; -+ if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version) -+ == 1) -+ return 0; -+ i = simple_strtoul(data, &cp, 10); -+ if (*cp != '-') -+ return -EINVAL; -+ data = cp + 1; -+ profile = ccs_assign_profile(head->w.ns, i); -+ if (!profile) -+ return -EINVAL; -+ cp = strchr(data, '='); -+ if (!cp) -+ return -EINVAL; -+ *cp++ = '\0'; -+ if (!strcmp(data, "COMMENT")) { -+ static DEFINE_SPINLOCK(lock); -+ const struct ccs_path_info *new_comment = ccs_get_name(cp); -+ const struct ccs_path_info *old_comment; -+ if (!new_comment) -+ return -ENOMEM; -+ spin_lock(&lock); -+ old_comment = profile->comment; -+ profile->comment = new_comment; -+ spin_unlock(&lock); -+ ccs_put_name(old_comment); -+ return 0; -+ } -+ if (!strcmp(data, "PREFERENCE")) { -+ for (i = 0; i < CCS_MAX_PREF; i++) -+ ccs_set_uint(&profile->pref[i], cp, -+ ccs_pref_keywords[i]); -+ return 0; -+ } -+ return ccs_set_mode(data, cp, profile); -+} -+ -+/** -+ * ccs_print_config - Print mode for specified functionality. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @config: Mode for that functionality. -+ * -+ * Returns nothing. -+ * -+ * Caller prints functionality's name. -+ */ -+static void ccs_print_config(struct ccs_io_buffer *head, const u8 config) -+{ -+ ccs_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n", -+ ccs_mode[config & 3], -+ ccs_yesno(config & CCS_CONFIG_WANT_GRANT_LOG), -+ ccs_yesno(config & CCS_CONFIG_WANT_REJECT_LOG)); -+} -+ -+/** -+ * ccs_read_profile - Read profile table. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns nothing. -+ */ -+static void ccs_read_profile(struct ccs_io_buffer *head) -+{ -+ u8 index; -+ struct ccs_policy_namespace *ns = container_of(head->r.ns, typeof(*ns), -+ namespace_list); -+ const struct ccs_profile *profile; -+ if (head->r.eof) -+ return; -+next: -+ index = head->r.index; -+ profile = ns->profile_ptr[index]; -+ switch (head->r.step) { -+ case 0: -+ ccs_print_namespace(head); -+ ccs_io_printf(head, "PROFILE_VERSION=%u\n", -+ ns->profile_version); -+ head->r.step++; -+ break; -+ case 1: -+ for ( ; head->r.index < CCS_MAX_PROFILES; head->r.index++) -+ if (ns->profile_ptr[head->r.index]) -+ break; -+ if (head->r.index == CCS_MAX_PROFILES) { -+ head->r.eof = true; -+ return; -+ } -+ head->r.step++; -+ break; -+ case 2: -+ { -+ u8 i; -+ const struct ccs_path_info *comment = profile->comment; -+ ccs_print_namespace(head); -+ ccs_io_printf(head, "%u-COMMENT=", index); -+ ccs_set_string(head, comment ? comment->name : ""); -+ ccs_set_lf(head); -+ ccs_print_namespace(head); -+ ccs_io_printf(head, "%u-PREFERENCE={ ", index); -+ for (i = 0; i < CCS_MAX_PREF; i++) -+ ccs_io_printf(head, "%s=%u ", -+ ccs_pref_keywords[i], -+ profile->pref[i]); -+ ccs_set_string(head, "}\n"); -+ head->r.step++; -+ } -+ break; -+ case 3: -+ { -+ ccs_print_namespace(head); -+ ccs_io_printf(head, "%u-%s", index, "CONFIG"); -+ ccs_print_config(head, profile->default_config); -+ head->r.bit = 0; -+ head->r.step++; -+ } -+ break; -+ case 4: -+ for ( ; head->r.bit < CCS_MAX_MAC_INDEX -+ + CCS_MAX_MAC_CATEGORY_INDEX; head->r.bit++) { -+ const u8 i = head->r.bit; -+ const u8 config = profile->config[i]; -+ if (config == CCS_CONFIG_USE_DEFAULT) -+ continue; -+ ccs_print_namespace(head); -+ if (i < CCS_MAX_MAC_INDEX) -+ ccs_io_printf(head, "%u-CONFIG::%s::%s", index, -+ ccs_category_keywords -+ [ccs_index2category[i]], -+ ccs_mac_keywords[i]); -+ else -+ ccs_io_printf(head, "%u-CONFIG::%s", index, -+ ccs_mac_keywords[i]); -+ ccs_print_config(head, config); -+ head->r.bit++; -+ break; -+ } -+ if (head->r.bit == CCS_MAX_MAC_INDEX -+ + CCS_MAX_MAC_CATEGORY_INDEX) { -+ head->r.index++; -+ head->r.step = 1; -+ } -+ break; -+ } -+ if (ccs_flush(head)) -+ goto next; -+} -+ -+/** -+ * ccs_update_policy - Update an entry for exception policy. -+ * -+ * @size: Size of new entry in bytes. -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_update_policy(const int size, struct ccs_acl_param *param) -+{ -+ struct ccs_acl_head *new_entry = ¶m->e.acl_head; -+ int error = param->is_delete ? -ENOENT : -ENOMEM; -+ struct ccs_acl_head *entry; -+ struct list_head *list = param->list; -+ BUG_ON(size < sizeof(*entry)); -+ if (mutex_lock_interruptible(&ccs_policy_lock)) -+ return -ENOMEM; -+ list_for_each_entry_srcu(entry, list, list, &ccs_ss) { -+ if (entry->is_deleted == CCS_GC_IN_PROGRESS) -+ continue; -+ if (memcmp(entry + 1, new_entry + 1, size - sizeof(*entry))) -+ continue; -+ entry->is_deleted = param->is_delete; -+ error = 0; -+ break; -+ } -+ if (error && !param->is_delete) { -+ entry = ccs_commit_ok(new_entry, size); -+ if (entry) { -+ list_add_tail_rcu(&entry->list, list); -+ error = 0; -+ } -+ } -+ mutex_unlock(&ccs_policy_lock); -+ return error; -+} -+ -+/** -+ * ccs_update_manager_entry - Add a manager entry. -+ * -+ * @manager: The path to manager or the domainnamme. -+ * @is_delete: True if it is a delete request. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_update_manager_entry(const char *manager, -+ const bool is_delete) -+{ -+ struct ccs_acl_param param = { -+ /* .ns = &ccs_kernel_namespace, */ -+ .is_delete = is_delete, -+ .list = &ccs_kernel_namespace.policy_list[CCS_ID_MANAGER], -+ }; -+ struct ccs_manager *e = ¶m.e.manager; -+ int error = is_delete ? -ENOENT : -ENOMEM; -+ /* Forced zero clear for using memcmp() at ccs_update_policy(). */ -+ memset(¶m.e, 0, sizeof(param.e)); -+ if (!ccs_correct_domain(manager) && !ccs_correct_word(manager)) -+ return -EINVAL; -+ e->manager = ccs_get_name(manager); -+ if (e->manager) { -+ error = ccs_update_policy(sizeof(*e), ¶m); -+ ccs_put_name(e->manager); -+ } -+ return error; -+} -+ -+/** -+ * ccs_write_manager - Write manager policy. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_write_manager(struct ccs_io_buffer *head) -+{ -+ const char *data = head->write_buf; -+ if (!strcmp(data, "manage_by_non_root")) { -+ ccs_manage_by_non_root = !head->w.is_delete; -+ return 0; -+ } -+ return ccs_update_manager_entry(data, head->w.is_delete); -+} -+ -+/** -+ * ccs_read_manager - Read manager policy. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns nothing. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static void ccs_read_manager(struct ccs_io_buffer *head) -+{ -+ if (head->r.eof) -+ return; -+ list_for_each_cookie(head->r.acl, &ccs_kernel_namespace. -+ policy_list[CCS_ID_MANAGER]) { -+ struct ccs_manager *ptr = -+ list_entry(head->r.acl, typeof(*ptr), head.list); -+ if (ptr->head.is_deleted) -+ continue; -+ if (!ccs_flush(head)) -+ return; -+ ccs_set_string(head, ptr->manager->name); -+ ccs_set_lf(head); -+ } -+ head->r.eof = true; -+} -+ -+/** -+ * ccs_manager - Check whether the current process is a policy manager. -+ * -+ * Returns true if the current process is permitted to modify policy -+ * via /proc/ccs/ interface. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static bool ccs_manager(void) -+{ -+ struct ccs_manager *ptr; -+ struct ccs_path_info exe; -+ struct ccs_security *task = ccs_current_security(); -+ const struct ccs_path_info *domainname -+ = ccs_current_domain()->domainname; -+ bool found = false; -+ if (!ccs_policy_loaded) -+ return true; -+ if (task->ccs_flags & CCS_TASK_IS_MANAGER) -+ return true; -+ if (!ccs_manage_by_non_root && -+ (!uid_eq(current_uid(), GLOBAL_ROOT_UID) || -+ !uid_eq(current_euid(), GLOBAL_ROOT_UID))) -+ return false; -+ exe.name = ccs_get_exe(); -+ if (!exe.name) -+ return false; -+ ccs_fill_path_info(&exe); -+ list_for_each_entry_srcu(ptr, &ccs_kernel_namespace. -+ policy_list[CCS_ID_MANAGER], head.list, -+ &ccs_ss) { -+ if (ptr->head.is_deleted) -+ continue; -+ if (ccs_pathcmp(domainname, ptr->manager) && -+ ccs_pathcmp(&exe, ptr->manager)) -+ continue; -+ /* Set manager flag. */ -+ task->ccs_flags |= CCS_TASK_IS_MANAGER; -+ found = true; -+ break; -+ } -+ if (!found) { /* Reduce error messages. */ -+ static pid_t ccs_last_pid; -+ const pid_t pid = current->pid; -+ if (ccs_last_pid != pid) { -+ printk(KERN_WARNING "%s ( %s ) is not permitted to " -+ "update policies.\n", domainname->name, -+ exe.name); -+ ccs_last_pid = pid; -+ } -+ } -+ kfree(exe.name); -+ return found; -+} -+ -+/** -+ * ccs_find_domain - Find a domain by the given name. -+ * -+ * @domainname: The domainname to find. -+ * -+ * Returns pointer to "struct ccs_domain_info" if found, NULL otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static struct ccs_domain_info *ccs_find_domain(const char *domainname) -+{ -+ struct ccs_domain_info *domain; -+ struct ccs_path_info name; -+ name.name = domainname; -+ ccs_fill_path_info(&name); -+ list_for_each_entry_srcu(domain, &ccs_domain_list, list, &ccs_ss) { -+ if (!domain->is_deleted && -+ !ccs_pathcmp(&name, domain->domainname)) -+ return domain; -+ } -+ return NULL; -+} -+ -+/** -+ * ccs_select_domain - Parse select command. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @data: String to parse. -+ * -+ * Returns true on success, false otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static bool ccs_select_domain(struct ccs_io_buffer *head, const char *data) -+{ -+ unsigned int pid; -+ struct ccs_domain_info *domain = NULL; -+ bool global_pid = false; -+ if (strncmp(data, "select ", 7)) -+ return false; -+ data += 7; -+ if (sscanf(data, "pid=%u", &pid) == 1 || -+ (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { -+ struct task_struct *p; -+ ccs_tasklist_lock(); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) -+ if (global_pid) -+ p = ccsecurity_exports.find_task_by_pid_ns(pid, -+ &init_pid_ns); -+ else -+ p = ccsecurity_exports.find_task_by_vpid(pid); -+#else -+ p = find_task_by_pid(pid); -+#endif -+ if (p) -+ domain = ccs_task_domain(p); -+ ccs_tasklist_unlock(); -+ } else if (!strncmp(data, "domain=", 7)) { -+ if (*(data + 7) == '<') -+ domain = ccs_find_domain(data + 7); -+ } else if (sscanf(data, "Q=%u", &pid) == 1) { -+ domain = ccs_find_domain_by_qid(pid); -+ } else -+ return false; -+ head->w.domain = domain; -+ /* Accessing read_buf is safe because head->io_sem is held. */ -+ if (!head->read_buf) -+ return true; /* Do nothing if open(O_WRONLY). */ -+ memset(&head->r, 0, sizeof(head->r)); -+ head->r.print_this_domain_only = true; -+ if (domain) -+ head->r.domain = &domain->list; -+ else -+ head->r.eof = true; -+ ccs_io_printf(head, "# select %s\n", data); -+ if (domain && domain->is_deleted) -+ ccs_set_string(head, "# This is a deleted domain.\n"); -+ return true; -+} -+ -+/** -+ * ccs_update_acl - Update "struct ccs_acl_info" entry. -+ * -+ * @size: Size of new entry in bytes. -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_update_acl(const int size, struct ccs_acl_param *param) -+{ -+ struct ccs_acl_info *new_entry = ¶m->e.acl_info; -+ const bool is_delete = param->is_delete; -+ int error = is_delete ? -ENOENT : -ENOMEM; -+ struct ccs_acl_info *entry; -+ struct list_head * const list = param->list; -+ BUG_ON(size < sizeof(*entry)); -+ if (param->data[0]) { -+ new_entry->cond = ccs_get_condition(param); -+ if (!new_entry->cond) -+ return -EINVAL; -+ /* -+ * Domain transition preference is allowed for only -+ * "file execute"/"task auto_execute_handler"/ -+ * "task denied_auto_execute_handler" entries. -+ */ -+ if (new_entry->cond->exec_transit && -+ !(new_entry->type == CCS_TYPE_PATH_ACL && -+ new_entry->perm == 1 << CCS_TYPE_EXECUTE) -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ && new_entry->type != CCS_TYPE_AUTO_EXECUTE_HANDLER && -+ new_entry->type != CCS_TYPE_DENIED_EXECUTE_HANDLER -+#endif -+ ) -+ return -EINVAL; -+ } -+ if (mutex_lock_interruptible(&ccs_policy_lock)) -+ return -ENOMEM; -+ list_for_each_entry_srcu(entry, list, list, &ccs_ss) { -+ if (entry->is_deleted == CCS_GC_IN_PROGRESS) -+ continue; -+ if (entry->type != new_entry->type || -+ entry->cond != new_entry->cond || -+ memcmp(entry + 1, new_entry + 1, size - sizeof(*entry))) -+ continue; -+ if (is_delete) -+ entry->perm &= ~new_entry->perm; -+ else -+ entry->perm |= new_entry->perm; -+ entry->is_deleted = !entry->perm; -+ error = 0; -+ break; -+ } -+ if (error && !is_delete) { -+ entry = ccs_commit_ok(new_entry, size); -+ if (entry) { -+ list_add_tail_rcu(&entry->list, list); -+ error = 0; -+ } -+ } -+ mutex_unlock(&ccs_policy_lock); -+ return error; -+} -+ -+/** -+ * ccs_permstr - Find permission keywords. -+ * -+ * @string: String representation for permissions in foo/bar/buz format. -+ * @keyword: Keyword to find from @string/ -+ * -+ * Returns ture if @keyword was found in @string, false otherwise. -+ * -+ * This function assumes that strncmp(w1, w2, strlen(w1)) != 0 if w1 != w2. -+ */ -+static bool ccs_permstr(const char *string, const char *keyword) -+{ -+ const char *cp = strstr(string, keyword); -+ if (cp) -+ return cp == string || *(cp - 1) == '/'; -+ return false; -+} -+ -+/** -+ * ccs_write_task - Update task related list. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_write_task(struct ccs_acl_param *param) -+{ -+ int error; -+ const bool is_auto = ccs_str_starts(¶m->data, -+ "auto_domain_transition "); -+ if (!is_auto && !ccs_str_starts(¶m->data, -+ "manual_domain_transition ")) { -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ struct ccs_handler_acl *e = ¶m->e.handler_acl; -+ char *handler; -+ if (ccs_str_starts(¶m->data, "auto_execute_handler ")) -+ e->head.type = CCS_TYPE_AUTO_EXECUTE_HANDLER; -+ else if (ccs_str_starts(¶m->data, -+ "denied_execute_handler ")) -+ e->head.type = CCS_TYPE_DENIED_EXECUTE_HANDLER; -+ else -+ return -EINVAL; -+ handler = ccs_read_token(param); -+ if (!ccs_correct_path(handler)) -+ return -EINVAL; -+ e->handler = ccs_get_name(handler); -+ if (!e->handler) -+ return -ENOMEM; -+ if (e->handler->is_patterned) -+ return -EINVAL; /* No patterns allowed. */ -+ return ccs_update_acl(sizeof(*e), param); -+#else -+ error = -EINVAL; -+#endif -+ } else { -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+ struct ccs_task_acl *e = ¶m->e.task_acl; -+ e->head.type = is_auto ? -+ CCS_TYPE_AUTO_TASK_ACL : CCS_TYPE_MANUAL_TASK_ACL; -+ e->domainname = ccs_get_domainname(param); -+ if (!e->domainname) -+ return -EINVAL; -+ return ccs_update_acl(sizeof(*e), param); -+#else -+ error = -EINVAL; -+#endif -+ } -+ return error; -+} -+ -+#ifdef CONFIG_CCSECURITY_NETWORK -+ -+/** -+ * ccs_write_inet_network - Write "struct ccs_inet_acl" list. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_write_inet_network(struct ccs_acl_param *param) -+{ -+ struct ccs_inet_acl *e = ¶m->e.inet_acl; -+ u8 type; -+ const char *protocol = ccs_read_token(param); -+ const char *operation = ccs_read_token(param); -+ e->head.type = CCS_TYPE_INET_ACL; -+ for (type = 0; type < CCS_SOCK_MAX; type++) -+ if (!strcmp(protocol, ccs_proto_keyword[type])) -+ break; -+ if (type == CCS_SOCK_MAX) -+ return -EINVAL; -+ e->protocol = type; -+ e->head.perm = 0; -+ for (type = 0; type < CCS_MAX_NETWORK_OPERATION; type++) -+ if (ccs_permstr(operation, ccs_socket_keyword[type])) -+ e->head.perm |= 1 << type; -+ if (!e->head.perm) -+ return -EINVAL; -+ if (param->data[0] == '@') { -+ param->data++; -+ e->address.group = ccs_get_group(param, CCS_ADDRESS_GROUP); -+ if (!e->address.group) -+ return -ENOMEM; -+ } else { -+ if (!ccs_parse_ipaddr_union(param, &e->address)) -+ return -EINVAL; -+ } -+ if (!ccs_parse_number_union(param, &e->port) || -+ e->port.values[1] > 65535) -+ return -EINVAL; -+ return ccs_update_acl(sizeof(*e), param); -+} -+ -+/** -+ * ccs_write_unix_network - Write "struct ccs_unix_acl" list. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_write_unix_network(struct ccs_acl_param *param) -+{ -+ struct ccs_unix_acl *e = ¶m->e.unix_acl; -+ u8 type; -+ const char *protocol = ccs_read_token(param); -+ const char *operation = ccs_read_token(param); -+ e->head.type = CCS_TYPE_UNIX_ACL; -+ for (type = 0; type < CCS_SOCK_MAX; type++) -+ if (!strcmp(protocol, ccs_proto_keyword[type])) -+ break; -+ if (type == CCS_SOCK_MAX) -+ return -EINVAL; -+ e->protocol = type; -+ e->head.perm = 0; -+ for (type = 0; type < CCS_MAX_NETWORK_OPERATION; type++) -+ if (ccs_permstr(operation, ccs_socket_keyword[type])) -+ e->head.perm |= 1 << type; -+ if (!e->head.perm) -+ return -EINVAL; -+ if (!ccs_parse_name_union(param, &e->name)) -+ return -EINVAL; -+ return ccs_update_acl(sizeof(*e), param); -+} -+ -+#endif -+ -+/** -+ * ccs_write_file - Update file related list. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_write_file(struct ccs_acl_param *param) -+{ -+ u16 perm = 0; -+ u8 type; -+ const char *operation = ccs_read_token(param); -+ for (type = 0; type < CCS_MAX_PATH_OPERATION; type++) -+ if (ccs_permstr(operation, ccs_path_keyword[type])) -+ perm |= 1 << type; -+ if (perm) { -+ struct ccs_path_acl *e = ¶m->e.path_acl; -+ e->head.type = CCS_TYPE_PATH_ACL; -+ e->head.perm = perm; -+ if (!ccs_parse_name_union(param, &e->name)) -+ return -EINVAL; -+ return ccs_update_acl(sizeof(*e), param); -+ } -+ for (type = 0; type < CCS_MAX_PATH2_OPERATION; type++) -+ if (ccs_permstr(operation, ccs_mac_keywords[ccs_pp2mac[type]])) -+ perm |= 1 << type; -+ if (perm) { -+ struct ccs_path2_acl *e = ¶m->e.path2_acl; -+ e->head.type = CCS_TYPE_PATH2_ACL; -+ e->head.perm = perm; -+ if (!ccs_parse_name_union(param, &e->name1) || -+ !ccs_parse_name_union(param, &e->name2)) -+ return -EINVAL; -+ return ccs_update_acl(sizeof(*e), param); -+ } -+ for (type = 0; type < CCS_MAX_PATH_NUMBER_OPERATION; type++) -+ if (ccs_permstr(operation, ccs_mac_keywords[ccs_pn2mac[type]])) -+ perm |= 1 << type; -+ if (perm) { -+ struct ccs_path_number_acl *e = ¶m->e.path_number_acl; -+ e->head.type = CCS_TYPE_PATH_NUMBER_ACL; -+ e->head.perm = perm; -+ if (!ccs_parse_name_union(param, &e->name) || -+ !ccs_parse_number_union(param, &e->number)) -+ return -EINVAL; -+ return ccs_update_acl(sizeof(*e), param); -+ } -+ for (type = 0; type < CCS_MAX_MKDEV_OPERATION; type++) -+ if (ccs_permstr(operation, -+ ccs_mac_keywords[ccs_pnnn2mac[type]])) -+ perm |= 1 << type; -+ if (perm) { -+ struct ccs_mkdev_acl *e = ¶m->e.mkdev_acl; -+ e->head.type = CCS_TYPE_MKDEV_ACL; -+ e->head.perm = perm; -+ if (!ccs_parse_name_union(param, &e->name) || -+ !ccs_parse_number_union(param, &e->mode) || -+ !ccs_parse_number_union(param, &e->major) || -+ !ccs_parse_number_union(param, &e->minor)) -+ return -EINVAL; -+ return ccs_update_acl(sizeof(*e), param); -+ } -+ if (ccs_permstr(operation, ccs_mac_keywords[CCS_MAC_FILE_MOUNT])) { -+ struct ccs_mount_acl *e = ¶m->e.mount_acl; -+ e->head.type = CCS_TYPE_MOUNT_ACL; -+ if (!ccs_parse_name_union(param, &e->dev_name) || -+ !ccs_parse_name_union(param, &e->dir_name) || -+ !ccs_parse_name_union(param, &e->fs_type) || -+ !ccs_parse_number_union(param, &e->flags)) -+ return -EINVAL; -+ return ccs_update_acl(sizeof(*e), param); -+ } -+ return -EINVAL; -+} -+ -+#ifdef CONFIG_CCSECURITY_MISC -+ -+/** -+ * ccs_write_misc - Update environment variable list. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_write_misc(struct ccs_acl_param *param) -+{ -+ if (ccs_str_starts(¶m->data, "env ")) { -+ struct ccs_env_acl *e = ¶m->e.env_acl; -+ const char *data = ccs_read_token(param); -+ e->head.type = CCS_TYPE_ENV_ACL; -+ if (!ccs_correct_word(data) || strchr(data, '=')) -+ return -EINVAL; -+ e->env = ccs_get_name(data); -+ if (!e->env) -+ return -ENOMEM; -+ return ccs_update_acl(sizeof(*e), param); -+ } -+ return -EINVAL; -+} -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_IPC -+ -+/** -+ * ccs_write_ipc - Update "struct ccs_signal_acl" list. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_write_ipc(struct ccs_acl_param *param) -+{ -+ struct ccs_signal_acl *e = ¶m->e.signal_acl; -+ e->head.type = CCS_TYPE_SIGNAL_ACL; -+ if (!ccs_parse_number_union(param, &e->sig)) -+ return -EINVAL; -+ e->domainname = ccs_get_domainname(param); -+ if (!e->domainname) -+ return -EINVAL; -+ return ccs_update_acl(sizeof(*e), param); -+} -+ -+#endif -+ -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ -+/** -+ * ccs_write_capability - Write "struct ccs_capability_acl" list. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_write_capability(struct ccs_acl_param *param) -+{ -+ struct ccs_capability_acl *e = ¶m->e.capability_acl; -+ const char *operation = ccs_read_token(param); -+ u8 type; -+ e->head.type = CCS_TYPE_CAPABILITY_ACL; -+ for (type = 0; type < CCS_MAX_CAPABILITY_INDEX; type++) { -+ if (strcmp(operation, ccs_mac_keywords[ccs_c2mac[type]])) -+ continue; -+ e->operation = type; -+ return ccs_update_acl(sizeof(*e), param); -+ } -+ return -EINVAL; -+} -+ -+#endif -+ -+/** -+ * ccs_write_acl - Write "struct ccs_acl_info" list. -+ * -+ * @ns: Pointer to "struct ccs_policy_namespace". -+ * @list: Pointer to "struct list_head". -+ * @data: Policy to be interpreted. -+ * @is_delete: True if it is a delete request. -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_write_acl(struct ccs_policy_namespace *ns, -+ struct list_head *list, char *data, -+ const bool is_delete) -+{ -+ struct ccs_acl_param param = { -+ .ns = ns, -+ .list = list, -+ .data = data, -+ .is_delete = is_delete, -+ }; -+ static const struct { -+ const char *keyword; -+ int (*write) (struct ccs_acl_param *); -+ } ccs_callback[] = { -+ { "file ", ccs_write_file }, -+#ifdef CONFIG_CCSECURITY_NETWORK -+ { "network inet ", ccs_write_inet_network }, -+ { "network unix ", ccs_write_unix_network }, -+#endif -+#ifdef CONFIG_CCSECURITY_MISC -+ { "misc ", ccs_write_misc }, -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ { "capability ", ccs_write_capability }, -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ { "ipc signal ", ccs_write_ipc }, -+#endif -+ { "task ", ccs_write_task }, -+ }; -+ u8 i; -+ /* Forced zero clear for using memcmp() at ccs_update_acl(). */ -+ memset(¶m.e, 0, sizeof(param.e)); -+ param.e.acl_info.perm = 1; -+ for (i = 0; i < ARRAY_SIZE(ccs_callback); i++) { -+ int error; -+ if (!ccs_str_starts(¶m.data, ccs_callback[i].keyword)) -+ continue; -+ error = ccs_callback[i].write(¶m); -+ ccs_del_acl(¶m.e.acl_info.list); -+ return error; -+ } -+ return -EINVAL; -+} -+ -+/** -+ * ccs_delete_domain - Delete a domain. -+ * -+ * @domainname: The name of domain. -+ * -+ * Returns 0. -+ */ -+static int ccs_delete_domain(char *domainname) -+{ -+ struct ccs_domain_info *domain; -+ struct ccs_path_info name; -+ name.name = domainname; -+ ccs_fill_path_info(&name); -+ if (mutex_lock_interruptible(&ccs_policy_lock)) -+ return 0; -+ /* Is there an active domain? */ -+ list_for_each_entry_srcu(domain, &ccs_domain_list, list, &ccs_ss) { -+ /* Never delete ccs_kernel_domain. */ -+ if (domain == &ccs_kernel_domain) -+ continue; -+ if (domain->is_deleted || -+ ccs_pathcmp(domain->domainname, &name)) -+ continue; -+ domain->is_deleted = true; -+ break; -+ } -+ mutex_unlock(&ccs_policy_lock); -+ return 0; -+} -+ -+/** -+ * ccs_write_domain - Write domain policy. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_write_domain(struct ccs_io_buffer *head) -+{ -+ char *data = head->write_buf; -+ struct ccs_policy_namespace *ns; -+ struct ccs_domain_info *domain = head->w.domain; -+ const bool is_delete = head->w.is_delete; -+ const bool is_select = !is_delete && ccs_str_starts(&data, "select "); -+ unsigned int idx; -+ if (*data == '<') { -+ domain = NULL; -+ if (is_delete) -+ ccs_delete_domain(data); -+ else if (is_select) -+ domain = ccs_find_domain(data); -+ else -+ domain = ccs_assign_domain(data, false); -+ head->w.domain = domain; -+ return 0; -+ } -+ if (!domain) -+ return -EINVAL; -+ ns = domain->ns; -+ if (sscanf(data, "use_profile %u\n", &idx) == 1 && -+ idx < CCS_MAX_PROFILES) { -+ if (!ccs_policy_loaded || ns->profile_ptr[(u8) idx]) -+ if (!is_delete) -+ domain->profile = (u8) idx; -+ return 0; -+ } -+ if (sscanf(data, "use_group %u\n", &idx) == 1 && -+ idx < CCS_MAX_ACL_GROUPS) { -+ if (!is_delete) -+ set_bit(idx, domain->group); -+ else -+ clear_bit(idx, domain->group); -+ return 0; -+ } -+ for (idx = 0; idx < CCS_MAX_DOMAIN_INFO_FLAGS; idx++) { -+ const char *cp = ccs_dif[idx]; -+ if (strncmp(data, cp, strlen(cp) - 1)) -+ continue; -+ domain->flags[idx] = !is_delete; -+ return 0; -+ } -+ return ccs_write_acl(ns, &domain->acl_info_list, data, is_delete); -+} -+ -+/** -+ * ccs_print_name_union - Print a ccs_name_union. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @ptr: Pointer to "struct ccs_name_union". -+ * -+ * Returns nothing. -+ */ -+static void ccs_print_name_union(struct ccs_io_buffer *head, -+ const struct ccs_name_union *ptr) -+{ -+ ccs_set_space(head); -+ if (!ccs_print_group(head, ptr->group)) -+ ccs_set_string(head, ptr->filename->name); -+} -+ -+/** -+ * ccs_print_name_union_quoted - Print a ccs_name_union with a quote. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @ptr: Pointer to "struct ccs_name_union". -+ * -+ * Returns nothing. -+ */ -+static void ccs_print_name_union_quoted(struct ccs_io_buffer *head, -+ const struct ccs_name_union *ptr) -+{ -+ if (!ccs_print_group(head, ptr->group)) { -+ ccs_set_string(head, "\""); -+ ccs_set_string(head, ptr->filename->name); -+ ccs_set_string(head, "\""); -+ } -+} -+ -+/** -+ * ccs_print_number_union_nospace - Print a ccs_number_union without a space. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @ptr: Pointer to "struct ccs_number_union". -+ * -+ * Returns nothing. -+ */ -+static void ccs_print_number_union_nospace(struct ccs_io_buffer *head, -+ const struct ccs_number_union *ptr) -+{ -+ if (!ccs_print_group(head, ptr->group)) { -+ int i; -+ unsigned long min = ptr->values[0]; -+ const unsigned long max = ptr->values[1]; -+ u8 min_type = ptr->value_type[0]; -+ const u8 max_type = ptr->value_type[1]; -+ char buffer[128]; -+ buffer[0] = '\0'; -+ for (i = 0; i < 2; i++) { -+ switch (min_type) { -+ case CCS_VALUE_TYPE_HEXADECIMAL: -+ ccs_addprintf(buffer, sizeof(buffer), "0x%lX", -+ min); -+ break; -+ case CCS_VALUE_TYPE_OCTAL: -+ ccs_addprintf(buffer, sizeof(buffer), "0%lo", -+ min); -+ break; -+ default: -+ ccs_addprintf(buffer, sizeof(buffer), "%lu", -+ min); -+ break; -+ } -+ if (min == max && min_type == max_type) -+ break; -+ ccs_addprintf(buffer, sizeof(buffer), "-"); -+ min_type = max_type; -+ min = max; -+ } -+ ccs_io_printf(head, "%s", buffer); -+ } -+} -+ -+/** -+ * ccs_print_number_union - Print a ccs_number_union. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @ptr: Pointer to "struct ccs_number_union". -+ * -+ * Returns nothing. -+ */ -+static void ccs_print_number_union(struct ccs_io_buffer *head, -+ const struct ccs_number_union *ptr) -+{ -+ ccs_set_space(head); -+ ccs_print_number_union_nospace(head, ptr); -+} -+ -+/** -+ * ccs_print_condition - Print condition part. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @cond: Pointer to "struct ccs_condition". -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool ccs_print_condition(struct ccs_io_buffer *head, -+ const struct ccs_condition *cond) -+{ -+ switch (head->r.cond_step) { -+ case 0: -+ head->r.cond_index = 0; -+ head->r.cond_step++; -+ if (cond->transit && cond->exec_transit) { -+ ccs_set_space(head); -+ ccs_set_string(head, cond->transit->name); -+ } -+ /* fall through */ -+ case 1: -+ { -+ const u16 condc = cond->condc; -+ const struct ccs_condition_element *condp = -+ (typeof(condp)) (cond + 1); -+ const struct ccs_number_union *numbers_p = -+ (typeof(numbers_p)) (condp + condc); -+ const struct ccs_name_union *names_p = -+ (typeof(names_p)) -+ (numbers_p + cond->numbers_count); -+ const struct ccs_argv *argv = -+ (typeof(argv)) (names_p + cond->names_count); -+ const struct ccs_envp *envp = -+ (typeof(envp)) (argv + cond->argc); -+ u16 skip; -+ for (skip = 0; skip < head->r.cond_index; skip++) { -+ const u8 left = condp->left; -+ const u8 right = condp->right; -+ condp++; -+ switch (left) { -+ case CCS_ARGV_ENTRY: -+ argv++; -+ continue; -+ case CCS_ENVP_ENTRY: -+ envp++; -+ continue; -+ case CCS_NUMBER_UNION: -+ numbers_p++; -+ break; -+ } -+ switch (right) { -+ case CCS_NAME_UNION: -+ names_p++; -+ break; -+ case CCS_NUMBER_UNION: -+ numbers_p++; -+ break; -+ } -+ } -+ while (head->r.cond_index < condc) { -+ const u8 match = condp->equals; -+ const u8 left = condp->left; -+ const u8 right = condp->right; -+ if (!ccs_flush(head)) -+ return false; -+ condp++; -+ head->r.cond_index++; -+ ccs_set_space(head); -+ switch (left) { -+ case CCS_ARGV_ENTRY: -+ ccs_io_printf(head, -+ "exec.argv[%lu]%s=\"", -+ argv->index, -+ argv->is_not ? "!" : ""); -+ ccs_set_string(head, -+ argv->value->name); -+ ccs_set_string(head, "\""); -+ argv++; -+ continue; -+ case CCS_ENVP_ENTRY: -+ ccs_set_string(head, "exec.envp[\""); -+ ccs_set_string(head, envp->name->name); -+ ccs_io_printf(head, "\"]%s=", -+ envp->is_not ? "!" : ""); -+ if (envp->value) { -+ ccs_set_string(head, "\""); -+ ccs_set_string(head, envp-> -+ value->name); -+ ccs_set_string(head, "\""); -+ } else { -+ ccs_set_string(head, "NULL"); -+ } -+ envp++; -+ continue; -+ case CCS_NUMBER_UNION: -+ ccs_print_number_union_nospace -+ (head, numbers_p++); -+ break; -+ default: -+ ccs_set_string(head, -+ ccs_condition_keyword[left]); -+ break; -+ } -+ ccs_set_string(head, match ? "=" : "!="); -+ switch (right) { -+ case CCS_NAME_UNION: -+ ccs_print_name_union_quoted -+ (head, names_p++); -+ break; -+ case CCS_NUMBER_UNION: -+ ccs_print_number_union_nospace -+ (head, numbers_p++); -+ break; -+ default: -+ ccs_set_string(head, -+ ccs_condition_keyword[right]); -+ break; -+ } -+ } -+ } -+ head->r.cond_step++; -+ /* fall through */ -+ case 2: -+ if (!ccs_flush(head)) -+ break; -+ head->r.cond_step++; -+ /* fall through */ -+ case 3: -+ if (cond->grant_log != CCS_GRANTLOG_AUTO) -+ ccs_io_printf(head, " grant_log=%s", -+ ccs_yesno(cond->grant_log == -+ CCS_GRANTLOG_YES)); -+ if (cond->transit && !cond->exec_transit) { -+ const char *name = cond->transit->name; -+ ccs_set_string(head, " auto_domain_transition=\""); -+ ccs_set_string(head, name); -+ ccs_set_string(head, "\""); -+ } -+ ccs_set_lf(head); -+ return true; -+ } -+ return false; -+} -+ -+/** -+ * ccs_set_group - Print "acl_group " header keyword and category name. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @category: Category name. -+ * -+ * Returns nothing. -+ */ -+static void ccs_set_group(struct ccs_io_buffer *head, const char *category) -+{ -+ if (head->type == CCS_EXCEPTION_POLICY) { -+ ccs_print_namespace(head); -+ ccs_io_printf(head, "acl_group %u ", head->r.acl_group_index); -+ } -+ ccs_set_string(head, category); -+} -+ -+/** -+ * ccs_print_entry - Print an ACL entry. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @acl: Pointer to an ACL entry. -+ * -+ * Returns true on success, false otherwise. -+ */ -+static bool ccs_print_entry(struct ccs_io_buffer *head, -+ const struct ccs_acl_info *acl) -+{ -+ const u8 acl_type = acl->type; -+ const bool may_trigger_transition = acl->cond && acl->cond->transit; -+ bool first = true; -+ u8 bit; -+ if (head->r.print_cond_part) -+ goto print_cond_part; -+ if (acl->is_deleted) -+ return true; -+ if (!ccs_flush(head)) -+ return false; -+ else if (acl_type == CCS_TYPE_PATH_ACL) { -+ struct ccs_path_acl *ptr -+ = container_of(acl, typeof(*ptr), head); -+ for (bit = 0; bit < CCS_MAX_PATH_OPERATION; bit++) { -+ if (!(acl->perm & (1 << bit))) -+ continue; -+ if (head->r.print_transition_related_only && -+ bit != CCS_TYPE_EXECUTE && !may_trigger_transition) -+ continue; -+ if (first) { -+ ccs_set_group(head, "file "); -+ first = false; -+ } else { -+ ccs_set_slash(head); -+ } -+ ccs_set_string(head, ccs_path_keyword[bit]); -+ } -+ if (first) -+ return true; -+ ccs_print_name_union(head, &ptr->name); -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ } else if (acl_type == CCS_TYPE_AUTO_EXECUTE_HANDLER || -+ acl_type == CCS_TYPE_DENIED_EXECUTE_HANDLER) { -+ struct ccs_handler_acl *ptr -+ = container_of(acl, typeof(*ptr), head); -+ ccs_set_group(head, "task "); -+ ccs_set_string(head, acl_type == CCS_TYPE_AUTO_EXECUTE_HANDLER -+ ? "auto_execute_handler " : -+ "denied_execute_handler "); -+ ccs_set_string(head, ptr->handler->name); -+#endif -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+ } else if (acl_type == CCS_TYPE_AUTO_TASK_ACL || -+ acl_type == CCS_TYPE_MANUAL_TASK_ACL) { -+ struct ccs_task_acl *ptr = -+ container_of(acl, typeof(*ptr), head); -+ ccs_set_group(head, "task "); -+ ccs_set_string(head, acl_type == CCS_TYPE_AUTO_TASK_ACL ? -+ "auto_domain_transition " : -+ "manual_domain_transition "); -+ ccs_set_string(head, ptr->domainname->name); -+#endif -+ } else if (head->r.print_transition_related_only && -+ !may_trigger_transition) { -+ return true; -+ } else if (acl_type == CCS_TYPE_MKDEV_ACL) { -+ struct ccs_mkdev_acl *ptr = -+ container_of(acl, typeof(*ptr), head); -+ for (bit = 0; bit < CCS_MAX_MKDEV_OPERATION; bit++) { -+ if (!(acl->perm & (1 << bit))) -+ continue; -+ if (first) { -+ ccs_set_group(head, "file "); -+ first = false; -+ } else { -+ ccs_set_slash(head); -+ } -+ ccs_set_string(head, ccs_mac_keywords -+ [ccs_pnnn2mac[bit]]); -+ } -+ if (first) -+ return true; -+ ccs_print_name_union(head, &ptr->name); -+ ccs_print_number_union(head, &ptr->mode); -+ ccs_print_number_union(head, &ptr->major); -+ ccs_print_number_union(head, &ptr->minor); -+ } else if (acl_type == CCS_TYPE_PATH2_ACL) { -+ struct ccs_path2_acl *ptr = -+ container_of(acl, typeof(*ptr), head); -+ for (bit = 0; bit < CCS_MAX_PATH2_OPERATION; bit++) { -+ if (!(acl->perm & (1 << bit))) -+ continue; -+ if (first) { -+ ccs_set_group(head, "file "); -+ first = false; -+ } else { -+ ccs_set_slash(head); -+ } -+ ccs_set_string(head, ccs_mac_keywords -+ [ccs_pp2mac[bit]]); -+ } -+ if (first) -+ return true; -+ ccs_print_name_union(head, &ptr->name1); -+ ccs_print_name_union(head, &ptr->name2); -+ } else if (acl_type == CCS_TYPE_PATH_NUMBER_ACL) { -+ struct ccs_path_number_acl *ptr = -+ container_of(acl, typeof(*ptr), head); -+ for (bit = 0; bit < CCS_MAX_PATH_NUMBER_OPERATION; bit++) { -+ if (!(acl->perm & (1 << bit))) -+ continue; -+ if (first) { -+ ccs_set_group(head, "file "); -+ first = false; -+ } else { -+ ccs_set_slash(head); -+ } -+ ccs_set_string(head, ccs_mac_keywords -+ [ccs_pn2mac[bit]]); -+ } -+ if (first) -+ return true; -+ ccs_print_name_union(head, &ptr->name); -+ ccs_print_number_union(head, &ptr->number); -+#ifdef CONFIG_CCSECURITY_MISC -+ } else if (acl_type == CCS_TYPE_ENV_ACL) { -+ struct ccs_env_acl *ptr = -+ container_of(acl, typeof(*ptr), head); -+ ccs_set_group(head, "misc env "); -+ ccs_set_string(head, ptr->env->name); -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ } else if (acl_type == CCS_TYPE_CAPABILITY_ACL) { -+ struct ccs_capability_acl *ptr = -+ container_of(acl, typeof(*ptr), head); -+ ccs_set_group(head, "capability "); -+ ccs_set_string(head, ccs_mac_keywords -+ [ccs_c2mac[ptr->operation]]); -+#endif -+#ifdef CONFIG_CCSECURITY_NETWORK -+ } else if (acl_type == CCS_TYPE_INET_ACL) { -+ struct ccs_inet_acl *ptr = -+ container_of(acl, typeof(*ptr), head); -+ for (bit = 0; bit < CCS_MAX_NETWORK_OPERATION; bit++) { -+ if (!(acl->perm & (1 << bit))) -+ continue; -+ if (first) { -+ ccs_set_group(head, "network inet "); -+ ccs_set_string(head, ccs_proto_keyword -+ [ptr->protocol]); -+ ccs_set_space(head); -+ first = false; -+ } else { -+ ccs_set_slash(head); -+ } -+ ccs_set_string(head, ccs_socket_keyword[bit]); -+ } -+ if (first) -+ return true; -+ ccs_set_space(head); -+ if (!ccs_print_group(head, ptr->address.group)) { -+ char buf[128]; -+ ccs_print_ip(buf, sizeof(buf), &ptr->address); -+ ccs_io_printf(head, "%s", buf); -+ } -+ ccs_print_number_union(head, &ptr->port); -+ } else if (acl_type == CCS_TYPE_UNIX_ACL) { -+ struct ccs_unix_acl *ptr = -+ container_of(acl, typeof(*ptr), head); -+ for (bit = 0; bit < CCS_MAX_NETWORK_OPERATION; bit++) { -+ if (!(acl->perm & (1 << bit))) -+ continue; -+ if (first) { -+ ccs_set_group(head, "network unix "); -+ ccs_set_string(head, ccs_proto_keyword -+ [ptr->protocol]); -+ ccs_set_space(head); -+ first = false; -+ } else { -+ ccs_set_slash(head); -+ } -+ ccs_set_string(head, ccs_socket_keyword[bit]); -+ } -+ if (first) -+ return true; -+ ccs_print_name_union(head, &ptr->name); -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ } else if (acl_type == CCS_TYPE_SIGNAL_ACL) { -+ struct ccs_signal_acl *ptr = -+ container_of(acl, typeof(*ptr), head); -+ ccs_set_group(head, "ipc signal "); -+ ccs_print_number_union_nospace(head, &ptr->sig); -+ ccs_set_space(head); -+ ccs_set_string(head, ptr->domainname->name); -+#endif -+ } else if (acl_type == CCS_TYPE_MOUNT_ACL) { -+ struct ccs_mount_acl *ptr = -+ container_of(acl, typeof(*ptr), head); -+ ccs_set_group(head, "file mount"); -+ ccs_print_name_union(head, &ptr->dev_name); -+ ccs_print_name_union(head, &ptr->dir_name); -+ ccs_print_name_union(head, &ptr->fs_type); -+ ccs_print_number_union(head, &ptr->flags); -+ } -+ if (acl->cond) { -+ head->r.print_cond_part = true; -+ head->r.cond_step = 0; -+ if (!ccs_flush(head)) -+ return false; -+print_cond_part: -+ if (!ccs_print_condition(head, acl->cond)) -+ return false; -+ head->r.print_cond_part = false; -+ } else { -+ ccs_set_lf(head); -+ } -+ return true; -+} -+ -+/** -+ * ccs_read_acl - Read "struct ccs_acl_info" list. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @list: Pointer to "struct list_head". -+ * -+ * Returns true on success, false otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static bool ccs_read_acl(struct ccs_io_buffer *head, struct list_head *list) -+{ -+ list_for_each_cookie(head->r.acl, list) { -+ struct ccs_acl_info *ptr = -+ list_entry(head->r.acl, typeof(*ptr), list); -+ if (!ccs_print_entry(head, ptr)) -+ return false; -+ } -+ head->r.acl = NULL; -+ return true; -+} -+ -+/** -+ * ccs_read_domain - Read domain policy. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns nothing. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static void ccs_read_domain(struct ccs_io_buffer *head) -+{ -+ if (head->r.eof) -+ return; -+ list_for_each_cookie(head->r.domain, &ccs_domain_list) { -+ struct ccs_domain_info *domain = -+ list_entry(head->r.domain, typeof(*domain), list); -+ switch (head->r.step) { -+ u8 i; -+ case 0: -+ if (domain->is_deleted && -+ !head->r.print_this_domain_only) -+ continue; -+ /* Print domainname and flags. */ -+ ccs_set_string(head, domain->domainname->name); -+ ccs_set_lf(head); -+ ccs_io_printf(head, "use_profile %u\n", -+ domain->profile); -+ for (i = 0; i < CCS_MAX_DOMAIN_INFO_FLAGS; i++) -+ if (domain->flags[i]) -+ ccs_set_string(head, ccs_dif[i]); -+ head->r.index = 0; -+ head->r.step++; -+ /* fall through */ -+ case 1: -+ while (head->r.index < CCS_MAX_ACL_GROUPS) { -+ i = head->r.index++; -+ if (!test_bit(i, domain->group)) -+ continue; -+ ccs_io_printf(head, "use_group %u\n", i); -+ if (!ccs_flush(head)) -+ return; -+ } -+ head->r.index = 0; -+ head->r.step++; -+ ccs_set_lf(head); -+ /* fall through */ -+ case 2: -+ if (!ccs_read_acl(head, &domain->acl_info_list)) -+ return; -+ head->r.step++; -+ if (!ccs_set_lf(head)) -+ return; -+ /* fall through */ -+ case 3: -+ head->r.step = 0; -+ if (head->r.print_this_domain_only) -+ goto done; -+ } -+ } -+done: -+ head->r.eof = true; -+} -+ -+/** -+ * ccs_write_pid - Specify PID to obtain domainname. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns 0. -+ */ -+static int ccs_write_pid(struct ccs_io_buffer *head) -+{ -+ head->r.eof = false; -+ return 0; -+} -+ -+/** -+ * ccs_read_pid - Read information of a process. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns the domainname which the specified PID is in or -+ * process information of the specified PID on success, -+ * empty string otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static void ccs_read_pid(struct ccs_io_buffer *head) -+{ -+ char *buf = head->write_buf; -+ bool task_info = false; -+ bool global_pid = false; -+ unsigned int pid; -+ struct task_struct *p; -+ struct ccs_domain_info *domain = NULL; -+ u32 ccs_flags = 0; -+ /* Accessing write_buf is safe because head->io_sem is held. */ -+ if (!buf) { -+ head->r.eof = true; -+ return; /* Do nothing if open(O_RDONLY). */ -+ } -+ if (head->r.w_pos || head->r.eof) -+ return; -+ head->r.eof = true; -+ if (ccs_str_starts(&buf, "info ")) -+ task_info = true; -+ if (ccs_str_starts(&buf, "global-pid ")) -+ global_pid = true; -+ pid = (unsigned int) simple_strtoul(buf, NULL, 10); -+ ccs_tasklist_lock(); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) -+ if (global_pid) -+ p = ccsecurity_exports.find_task_by_pid_ns(pid, &init_pid_ns); -+ else -+ p = ccsecurity_exports.find_task_by_vpid(pid); -+#else -+ p = find_task_by_pid(pid); -+#endif -+ if (p) { -+ domain = ccs_task_domain(p); -+ ccs_flags = ccs_task_flags(p); -+ } -+ ccs_tasklist_unlock(); -+ if (!domain) -+ return; -+ if (!task_info) { -+ ccs_io_printf(head, "%u %u ", pid, domain->profile); -+ ccs_set_string(head, domain->domainname->name); -+ } else { -+ ccs_io_printf(head, "%u manager=%s execute_handler=%s ", pid, -+ ccs_yesno(ccs_flags & -+ CCS_TASK_IS_MANAGER), -+ ccs_yesno(ccs_flags & -+ CCS_TASK_IS_EXECUTE_HANDLER)); -+ } -+} -+ -+/** -+ * ccs_write_group - Write "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group" list. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * @type: Type of this group. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_write_group(struct ccs_acl_param *param, const u8 type) -+{ -+ struct ccs_group *group = ccs_get_group(param, type); -+ int error = -EINVAL; -+ if (!group) -+ return -ENOMEM; -+ param->list = &group->member_list; -+ if (type == CCS_PATH_GROUP) { -+ struct ccs_path_group *e = ¶m->e.path_group; -+ e->member_name = ccs_get_name(ccs_read_token(param)); -+ if (!e->member_name) { -+ error = -ENOMEM; -+ goto out; -+ } -+ error = ccs_update_policy(sizeof(*e), param); -+ ccs_put_name(e->member_name); -+ } else if (type == CCS_NUMBER_GROUP) { -+ struct ccs_number_group *e = ¶m->e.number_group; -+ if (param->data[0] == '@' || -+ !ccs_parse_number_union(param, &e->number)) -+ goto out; -+ error = ccs_update_policy(sizeof(*e), param); -+#ifdef CONFIG_CCSECURITY_NETWORK -+ } else { -+ struct ccs_address_group *e = ¶m->e.address_group; -+ if (param->data[0] == '@' || -+ !ccs_parse_ipaddr_union(param, &e->address)) -+ goto out; -+ error = ccs_update_policy(sizeof(*e), param); -+#endif -+ } -+out: -+ ccs_put_group(group); -+ return error; -+} -+ -+#ifdef CONFIG_CCSECURITY_PORTRESERVE -+/** -+ * ccs_lport_reserved - Check whether local port is reserved or not. -+ * -+ * @port: Port number. -+ * -+ * Returns true if local port is reserved, false otherwise. -+ */ -+static bool __ccs_lport_reserved(const u16 port) -+{ -+ return ccs_reserved_port_map[port >> 3] & (1 << (port & 7)) -+ ? true : false; -+} -+ -+/** -+ * ccs_write_reserved_port - Update "struct ccs_reserved" list. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_write_reserved_port(struct ccs_acl_param *param) -+{ -+ struct ccs_reserved *e = ¶m->e.reserved; -+ struct ccs_policy_namespace *ns = param->ns; -+ int error; -+ u8 *tmp; -+ if (param->data[0] == '@' || -+ !ccs_parse_number_union(param, &e->port) || -+ e->port.values[1] > 65535 || param->data[0]) -+ return -EINVAL; -+ param->list = &ns->policy_list[CCS_ID_RESERVEDPORT]; -+ error = ccs_update_policy(sizeof(*e), param); -+ if (error) -+ return error; -+ tmp = kzalloc(sizeof(ccs_reserved_port_map), CCS_GFP_FLAGS); -+ if (!tmp) -+ return -ENOMEM; -+ list_for_each_entry_srcu(ns, &ccs_namespace_list, namespace_list, -+ &ccs_ss) { -+ struct ccs_reserved *ptr; -+ struct list_head *list = &ns->policy_list[CCS_ID_RESERVEDPORT]; -+ list_for_each_entry_srcu(ptr, list, head.list, &ccs_ss) { -+ unsigned int port; -+ if (ptr->head.is_deleted) -+ continue; -+ for (port = ptr->port.values[0]; -+ port <= ptr->port.values[1]; port++) -+ tmp[port >> 3] |= 1 << (port & 7); -+ } -+ } -+ memmove(ccs_reserved_port_map, tmp, sizeof(ccs_reserved_port_map)); -+ kfree(tmp); -+ /* -+ * Since this feature is no-op by default, we don't need to register -+ * this callback hook unless the first entry is added. -+ */ -+ ccsecurity_ops.lport_reserved = __ccs_lport_reserved; -+ return 0; -+} -+#endif -+ -+/** -+ * ccs_write_aggregator - Write "struct ccs_aggregator" list. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_write_aggregator(struct ccs_acl_param *param) -+{ -+ struct ccs_aggregator *e = ¶m->e.aggregator; -+ int error = param->is_delete ? -ENOENT : -ENOMEM; -+ const char *original_name = ccs_read_token(param); -+ const char *aggregated_name = ccs_read_token(param); -+ if (!ccs_correct_word(original_name) || -+ !ccs_correct_path(aggregated_name)) -+ return -EINVAL; -+ e->original_name = ccs_get_name(original_name); -+ e->aggregated_name = ccs_get_name(aggregated_name); -+ if (!e->original_name || !e->aggregated_name || -+ e->aggregated_name->is_patterned) /* No patterns allowed. */ -+ goto out; -+ param->list = ¶m->ns->policy_list[CCS_ID_AGGREGATOR]; -+ error = ccs_update_policy(sizeof(*e), param); -+out: -+ ccs_put_name(e->original_name); -+ ccs_put_name(e->aggregated_name); -+ return error; -+} -+ -+/** -+ * ccs_write_transition_control - Write "struct ccs_transition_control" list. -+ * -+ * @param: Pointer to "struct ccs_acl_param". -+ * @type: Type of this entry. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_write_transition_control(struct ccs_acl_param *param, -+ const u8 type) -+{ -+ struct ccs_transition_control *e = ¶m->e.transition_control; -+ int error = param->is_delete ? -ENOENT : -ENOMEM; -+ char *program = param->data; -+ char *domainname = strstr(program, " from "); -+ e->type = type; -+ if (domainname) { -+ *domainname = '\0'; -+ domainname += 6; -+ } else if (type == CCS_TRANSITION_CONTROL_NO_KEEP || -+ type == CCS_TRANSITION_CONTROL_KEEP) { -+ domainname = program; -+ program = NULL; -+ } -+ if (program && strcmp(program, "any")) { -+ if (!ccs_correct_path(program)) -+ return -EINVAL; -+ e->program = ccs_get_name(program); -+ if (!e->program) -+ goto out; -+ } -+ if (domainname && strcmp(domainname, "any")) { -+ if (!ccs_correct_domain(domainname)) { -+ if (!ccs_correct_path(domainname)) -+ goto out; -+ e->is_last_name = true; -+ } -+ e->domainname = ccs_get_name(domainname); -+ if (!e->domainname) -+ goto out; -+ } -+ param->list = ¶m->ns->policy_list[CCS_ID_TRANSITION_CONTROL]; -+ error = ccs_update_policy(sizeof(*e), param); -+out: -+ ccs_put_name(e->domainname); -+ ccs_put_name(e->program); -+ return error; -+} -+ -+/** -+ * ccs_write_exception - Write exception policy. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_write_exception(struct ccs_io_buffer *head) -+{ -+ const bool is_delete = head->w.is_delete; -+ struct ccs_acl_param param = { -+ .ns = head->w.ns, -+ .is_delete = is_delete, -+ .data = head->write_buf, -+ }; -+ u8 i; -+ /* Forced zero clear for using memcmp() at ccs_update_policy(). */ -+ memset(¶m.e, 0, sizeof(param.e)); -+ if (ccs_str_starts(¶m.data, "aggregator ")) -+ return ccs_write_aggregator(¶m); -+#ifdef CONFIG_CCSECURITY_PORTRESERVE -+ if (ccs_str_starts(¶m.data, "deny_autobind ")) -+ return ccs_write_reserved_port(¶m); -+#endif -+ for (i = 0; i < CCS_MAX_TRANSITION_TYPE; i++) -+ if (ccs_str_starts(¶m.data, ccs_transition_type[i])) -+ return ccs_write_transition_control(¶m, i); -+ for (i = 0; i < CCS_MAX_GROUP; i++) -+ if (ccs_str_starts(¶m.data, ccs_group_name[i])) -+ return ccs_write_group(¶m, i); -+ if (ccs_str_starts(¶m.data, "acl_group ")) { -+ unsigned int group; -+ char *data; -+ group = simple_strtoul(param.data, &data, 10); -+ if (group < CCS_MAX_ACL_GROUPS && *data++ == ' ') -+ return ccs_write_acl(head->w.ns, -+ &head->w.ns->acl_group[group], -+ data, is_delete); -+ } -+ return -EINVAL; -+} -+ -+/** -+ * ccs_read_group - Read "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group" list. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @idx: Index number. -+ * -+ * Returns true on success, false otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static bool ccs_read_group(struct ccs_io_buffer *head, const int idx) -+{ -+ struct ccs_policy_namespace *ns = container_of(head->r.ns, typeof(*ns), -+ namespace_list); -+ struct list_head *list = &ns->group_list[idx]; -+ list_for_each_cookie(head->r.group, list) { -+ struct ccs_group *group = -+ list_entry(head->r.group, typeof(*group), head.list); -+ list_for_each_cookie(head->r.acl, &group->member_list) { -+ struct ccs_acl_head *ptr = -+ list_entry(head->r.acl, typeof(*ptr), list); -+ if (ptr->is_deleted) -+ continue; -+ if (!ccs_flush(head)) -+ return false; -+ ccs_print_namespace(head); -+ ccs_set_string(head, ccs_group_name[idx]); -+ ccs_set_string(head, group->group_name->name); -+ if (idx == CCS_PATH_GROUP) { -+ ccs_set_space(head); -+ ccs_set_string(head, container_of -+ (ptr, struct ccs_path_group, -+ head)->member_name->name); -+ } else if (idx == CCS_NUMBER_GROUP) { -+ ccs_print_number_union(head, &container_of -+ (ptr, struct ccs_number_group, -+ head)->number); -+#ifdef CONFIG_CCSECURITY_NETWORK -+ } else if (idx == CCS_ADDRESS_GROUP) { -+ char buffer[128]; -+ struct ccs_address_group *member = -+ container_of(ptr, typeof(*member), -+ head); -+ ccs_print_ip(buffer, sizeof(buffer), -+ &member->address); -+ ccs_io_printf(head, " %s", buffer); -+#endif -+ } -+ ccs_set_lf(head); -+ } -+ head->r.acl = NULL; -+ } -+ head->r.group = NULL; -+ return true; -+} -+ -+/** -+ * ccs_read_policy - Read "struct ccs_..._entry" list. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * @idx: Index number. -+ * -+ * Returns true on success, false otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static bool ccs_read_policy(struct ccs_io_buffer *head, const int idx) -+{ -+ struct ccs_policy_namespace *ns = container_of(head->r.ns, typeof(*ns), -+ namespace_list); -+ struct list_head *list = &ns->policy_list[idx]; -+ list_for_each_cookie(head->r.acl, list) { -+ struct ccs_acl_head *acl = -+ container_of(head->r.acl, typeof(*acl), list); -+ if (acl->is_deleted) -+ continue; -+ if (head->r.print_transition_related_only && -+ idx != CCS_ID_TRANSITION_CONTROL) -+ continue; -+ if (!ccs_flush(head)) -+ return false; -+ switch (idx) { -+ case CCS_ID_TRANSITION_CONTROL: -+ { -+ struct ccs_transition_control *ptr = -+ container_of(acl, typeof(*ptr), head); -+ ccs_print_namespace(head); -+ ccs_set_string(head, -+ ccs_transition_type[ptr->type]); -+ ccs_set_string(head, ptr->program ? -+ ptr->program->name : "any"); -+ ccs_set_string(head, " from "); -+ ccs_set_string(head, ptr->domainname ? -+ ptr->domainname->name : "any"); -+ } -+ break; -+ case CCS_ID_AGGREGATOR: -+ { -+ struct ccs_aggregator *ptr = -+ container_of(acl, typeof(*ptr), head); -+ ccs_print_namespace(head); -+ ccs_set_string(head, "aggregator "); -+ ccs_set_string(head, ptr->original_name->name); -+ ccs_set_space(head); -+ ccs_set_string(head, -+ ptr->aggregated_name->name); -+ } -+ break; -+#ifdef CONFIG_CCSECURITY_PORTRESERVE -+ case CCS_ID_RESERVEDPORT: -+ { -+ struct ccs_reserved *ptr = -+ container_of(acl, typeof(*ptr), head); -+ ccs_print_namespace(head); -+ ccs_set_string(head, "deny_autobind "); -+ ccs_print_number_union_nospace(head, -+ &ptr->port); -+ } -+ break; -+#endif -+ default: -+ continue; -+ } -+ ccs_set_lf(head); -+ } -+ head->r.acl = NULL; -+ return true; -+} -+ -+/** -+ * ccs_read_exception - Read exception policy. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns nothing. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static void ccs_read_exception(struct ccs_io_buffer *head) -+{ -+ struct ccs_policy_namespace *ns = container_of(head->r.ns, typeof(*ns), -+ namespace_list); -+ if (head->r.eof) -+ return; -+ while (head->r.step < CCS_MAX_POLICY && -+ ccs_read_policy(head, head->r.step)) -+ head->r.step++; -+ if (head->r.step < CCS_MAX_POLICY) -+ return; -+ while (head->r.step < CCS_MAX_POLICY + CCS_MAX_GROUP && -+ ccs_read_group(head, head->r.step - CCS_MAX_POLICY)) -+ head->r.step++; -+ if (head->r.step < CCS_MAX_POLICY + CCS_MAX_GROUP) -+ return; -+ while (head->r.step < CCS_MAX_POLICY + CCS_MAX_GROUP -+ + CCS_MAX_ACL_GROUPS) { -+ head->r.acl_group_index = -+ head->r.step - CCS_MAX_POLICY - CCS_MAX_GROUP; -+ if (!ccs_read_acl(head, &ns->acl_group -+ [head->r.acl_group_index])) -+ return; -+ head->r.step++; -+ } -+ head->r.eof = true; -+} -+ -+/** -+ * ccs_truncate - Truncate a line. -+ * -+ * @str: String to truncate. -+ * -+ * Returns length of truncated @str. -+ */ -+static int ccs_truncate(char *str) -+{ -+ char *start = str; -+ while (*(unsigned char *) str > (unsigned char) ' ') -+ str++; -+ *str = '\0'; -+ return strlen(start) + 1; -+} -+ -+/** -+ * ccs_add_entry - Add an ACL to current thread's domain. Used by learning mode. -+ * -+ * @header: Lines containing ACL. -+ * -+ * Returns nothing. -+ */ -+static void ccs_add_entry(char *header) -+{ -+ char *buffer; -+ char *realpath = NULL; -+ char *argv0 = NULL; -+ char *symlink = NULL; -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ char *handler; -+#endif -+ char *cp = strchr(header, '\n'); -+ int len; -+ if (!cp) -+ return; -+ cp = strchr(cp + 1, '\n'); -+ if (!cp) -+ return; -+ *cp++ = '\0'; -+ len = strlen(cp) + 1; -+ /* strstr() will return NULL if ordering is wrong. */ -+ if (*cp == 'f') { -+ argv0 = strstr(header, " argv[]={ \""); -+ if (argv0) { -+ argv0 += 10; -+ len += ccs_truncate(argv0) + 14; -+ } -+ realpath = strstr(header, " exec={ realpath=\""); -+ if (realpath) { -+ realpath += 8; -+ len += ccs_truncate(realpath) + 6; -+ } -+ symlink = strstr(header, " symlink.target=\""); -+ if (symlink) -+ len += ccs_truncate(symlink + 1) + 1; -+ } -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ handler = strstr(header, "type=execute_handler"); -+ if (handler) -+ len += ccs_truncate(handler) + 6; -+#endif -+ buffer = kmalloc(len, CCS_GFP_FLAGS); -+ if (!buffer) -+ return; -+ snprintf(buffer, len - 1, "%s", cp); -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ if (handler) -+ ccs_addprintf(buffer, len, " task.%s", handler); -+#endif -+ if (realpath) -+ ccs_addprintf(buffer, len, " exec.%s", realpath); -+ if (argv0) -+ ccs_addprintf(buffer, len, " exec.argv[0]=%s", argv0); -+ if (symlink) -+ ccs_addprintf(buffer, len, "%s", symlink); -+ ccs_normalize_line(buffer); -+ { -+ struct ccs_domain_info *domain = ccs_current_domain(); -+ if (!ccs_write_acl(domain->ns, &domain->acl_info_list, -+ buffer, false)) -+ ccs_update_stat(CCS_STAT_POLICY_UPDATES); -+ } -+ kfree(buffer); -+} -+ -+/** -+ * ccs_domain_quota_ok - Check for domain's quota. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * -+ * Returns true if the domain is not exceeded quota, false otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static bool ccs_domain_quota_ok(struct ccs_request_info *r) -+{ -+ unsigned int count = 0; -+ struct ccs_domain_info * const domain = ccs_current_domain(); -+ struct ccs_acl_info *ptr; -+ if (r->mode != CCS_CONFIG_LEARNING) -+ return false; -+ if (!domain) -+ return true; -+ list_for_each_entry_srcu(ptr, &domain->acl_info_list, list, &ccs_ss) { -+ u16 perm; -+ u8 i; -+ if (ptr->is_deleted) -+ continue; -+ switch (ptr->type) { -+ case CCS_TYPE_PATH_ACL: -+ case CCS_TYPE_PATH2_ACL: -+ case CCS_TYPE_PATH_NUMBER_ACL: -+ case CCS_TYPE_MKDEV_ACL: -+#ifdef CONFIG_CCSECURITY_NETWORK -+ case CCS_TYPE_INET_ACL: -+ case CCS_TYPE_UNIX_ACL: -+#endif -+ perm = ptr->perm; -+ break; -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ case CCS_TYPE_AUTO_EXECUTE_HANDLER: -+ case CCS_TYPE_DENIED_EXECUTE_HANDLER: -+#endif -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+ case CCS_TYPE_AUTO_TASK_ACL: -+ case CCS_TYPE_MANUAL_TASK_ACL: -+#endif -+ perm = 0; -+ break; -+ default: -+ perm = 1; -+ } -+ for (i = 0; i < 16; i++) -+ if (perm & (1 << i)) -+ count++; -+ } -+ if (count < ccs_profile(r->profile)->pref[CCS_PREF_MAX_LEARNING_ENTRY]) -+ return true; -+ if (!domain->flags[CCS_DIF_QUOTA_WARNED]) { -+ domain->flags[CCS_DIF_QUOTA_WARNED] = true; -+ /* r->granted = false; */ -+ ccs_write_log(r, "%s", ccs_dif[CCS_DIF_QUOTA_WARNED]); -+ printk(KERN_WARNING "WARNING: " -+ "Domain '%s' has too many ACLs to hold. " -+ "Stopped learning mode.\n", domain->domainname->name); -+ } -+ return false; -+} -+ -+/** -+ * ccs_supervisor - Ask for the supervisor's decision. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @fmt: The printf()'s format string, followed by parameters. -+ * -+ * Returns 0 if the supervisor decided to permit the access request which -+ * violated the policy in enforcing mode, CCS_RETRY_REQUEST if the supervisor -+ * decided to retry the access request which violated the policy in enforcing -+ * mode, 0 if it is not in enforcing mode, -EPERM otherwise. -+ */ -+static int ccs_supervisor(struct ccs_request_info *r, const char *fmt, ...) -+{ -+ va_list args; -+ int error; -+ int len; -+ static unsigned int ccs_serial; -+ struct ccs_query entry = { }; -+ bool quota_exceeded = false; -+ va_start(args, fmt); -+ len = vsnprintf((char *) &len, 1, fmt, args) + 1; -+ va_end(args); -+ /* Write /proc/ccs/audit. */ -+ va_start(args, fmt); -+ ccs_write_log2(r, len, fmt, args); -+ va_end(args); -+ /* Nothing more to do if granted. */ -+ if (r->granted) -+ return 0; -+ if (r->mode) -+ ccs_update_stat(r->mode); -+ switch (r->mode) { -+ int i; -+ struct ccs_profile *p; -+ case CCS_CONFIG_ENFORCING: -+ error = -EPERM; -+ if (atomic_read(&ccs_query_observers)) -+ break; -+ if (r->dont_sleep_on_enforce_error) -+ goto out; -+ p = ccs_profile(r->profile); -+ /* Check enforcing_penalty parameter. */ -+ for (i = 0; i < p->pref[CCS_PREF_ENFORCING_PENALTY]; i++) { -+ set_current_state(TASK_INTERRUPTIBLE); -+ schedule_timeout(HZ / 10); -+ } -+ goto out; -+ case CCS_CONFIG_LEARNING: -+ error = 0; -+ /* Check max_learning_entry parameter. */ -+ if (ccs_domain_quota_ok(r)) -+ break; -+ /* fall through */ -+ default: -+ return 0; -+ } -+ /* Get message. */ -+ va_start(args, fmt); -+ entry.query = ccs_init_log(r, len, fmt, args); -+ va_end(args); -+ if (!entry.query) -+ goto out; -+ entry.query_len = strlen(entry.query) + 1; -+ if (!error) { -+ ccs_add_entry(entry.query); -+ goto out; -+ } -+ len = ccs_round2(entry.query_len); -+ entry.domain = ccs_current_domain(); -+ spin_lock(&ccs_query_list_lock); -+ if (ccs_memory_quota[CCS_MEMORY_QUERY] && -+ ccs_memory_used[CCS_MEMORY_QUERY] + len -+ >= ccs_memory_quota[CCS_MEMORY_QUERY]) { -+ quota_exceeded = true; -+ } else { -+ entry.serial = ccs_serial++; -+ entry.retry = r->retry; -+ ccs_memory_used[CCS_MEMORY_QUERY] += len; -+ list_add_tail(&entry.list, &ccs_query_list); -+ } -+ spin_unlock(&ccs_query_list_lock); -+ if (quota_exceeded) -+ goto out; -+ /* Give 10 seconds for supervisor's opinion. */ -+ while (entry.timer < 10) { -+ wake_up_all(&ccs_query_wait); -+ if (wait_event_interruptible_timeout -+ (ccs_answer_wait, entry.answer || -+ !atomic_read(&ccs_query_observers), HZ)) -+ break; -+ else -+ entry.timer++; -+ } -+ spin_lock(&ccs_query_list_lock); -+ list_del(&entry.list); -+ ccs_memory_used[CCS_MEMORY_QUERY] -= len; -+ spin_unlock(&ccs_query_list_lock); -+ switch (entry.answer) { -+ case 3: /* Asked to retry by administrator. */ -+ error = CCS_RETRY_REQUEST; -+ r->retry++; -+ break; -+ case 1: -+ /* Granted by administrator. */ -+ error = 0; -+ break; -+ default: -+ /* Timed out or rejected by administrator. */ -+ break; -+ } -+out: -+ kfree(entry.query); -+ return error; -+} -+ -+/** -+ * ccs_audit_log - Audit permission check log. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * -+ * Returns return value of ccs_supervisor(). -+ */ -+int ccs_audit_log(struct ccs_request_info *r) -+{ -+ switch (r->param_type) { -+ u8 type; -+ char buf[48]; -+#ifdef CONFIG_CCSECURITY_NETWORK -+ const u32 *address; -+#endif -+ case CCS_TYPE_PATH_ACL: -+ return ccs_supervisor(r, "file %s %s\n", ccs_path_keyword -+ [r->param.path.operation], -+ r->param.path.filename->name); -+ case CCS_TYPE_PATH2_ACL: -+ return ccs_supervisor(r, "file %s %s %s\n", ccs_mac_keywords -+ [ccs_pp2mac[r->param.path2.operation]], -+ r->param.path2.filename1->name, -+ r->param.path2.filename2->name); -+ case CCS_TYPE_PATH_NUMBER_ACL: -+ type = r->param.path_number.operation; -+ switch (type) { -+ case CCS_TYPE_CREATE: -+ case CCS_TYPE_MKDIR: -+ case CCS_TYPE_MKFIFO: -+ case CCS_TYPE_MKSOCK: -+ case CCS_TYPE_CHMOD: -+ snprintf(buf, sizeof(buf), "0%lo", -+ r->param.path_number.number); -+ break; -+ case CCS_TYPE_IOCTL: -+ snprintf(buf, sizeof(buf), "0x%lX", -+ r->param.path_number.number); -+ break; -+ default: -+ snprintf(buf, sizeof(buf), "%lu", -+ r->param.path_number.number); -+ break; -+ } -+ return ccs_supervisor(r, "file %s %s %s\n", ccs_mac_keywords -+ [ccs_pn2mac[type]], -+ r->param.path_number.filename->name, -+ buf); -+ case CCS_TYPE_MKDEV_ACL: -+ return ccs_supervisor(r, "file %s %s 0%o %u %u\n", -+ ccs_mac_keywords -+ [ccs_pnnn2mac[r->param.mkdev.operation]], -+ r->param.mkdev.filename->name, -+ r->param.mkdev.mode, -+ r->param.mkdev.major, -+ r->param.mkdev.minor); -+ case CCS_TYPE_MOUNT_ACL: -+ return ccs_supervisor(r, "file mount %s %s %s 0x%lX\n", -+ r->param.mount.dev->name, -+ r->param.mount.dir->name, -+ r->param.mount.type->name, -+ r->param.mount.flags); -+#ifdef CONFIG_CCSECURITY_MISC -+ case CCS_TYPE_ENV_ACL: -+ return ccs_supervisor(r, "misc env %s\n", -+ r->param.environ.name->name); -+#endif -+#ifdef CONFIG_CCSECURITY_CAPABILITY -+ case CCS_TYPE_CAPABILITY_ACL: -+ return ccs_supervisor(r, "capability %s\n", ccs_mac_keywords -+ [ccs_c2mac[r->param.capability. -+ operation]]); -+#endif -+#ifdef CONFIG_CCSECURITY_NETWORK -+ case CCS_TYPE_INET_ACL: -+ address = r->param.inet_network.address; -+ if (r->param.inet_network.is_ipv6) -+ ccs_print_ipv6(buf, sizeof(buf), -+ (const struct in6_addr *) address); -+ else -+ ccs_print_ipv4(buf, sizeof(buf), address); -+ return ccs_supervisor(r, "network inet %s %s %s %u\n", -+ ccs_proto_keyword[r->param.inet_network. -+ protocol], -+ ccs_socket_keyword[r->param.inet_network. -+ operation], -+ buf, r->param.inet_network.port); -+ case CCS_TYPE_UNIX_ACL: -+ return ccs_supervisor(r, "network unix %s %s %s\n", -+ ccs_proto_keyword[r->param. -+ unix_network.protocol], -+ ccs_socket_keyword[r->param.unix_network. -+ operation], -+ r->param.unix_network.address->name); -+#endif -+#ifdef CONFIG_CCSECURITY_IPC -+ case CCS_TYPE_SIGNAL_ACL: -+ return ccs_supervisor(r, "ipc signal %d %s\n", -+ r->param.signal.sig, -+ r->param.signal.dest_pattern); -+#endif -+ } -+ return 0; -+} -+ -+/** -+ * ccs_find_domain_by_qid - Get domain by query id. -+ * -+ * @serial: Query ID assigned by ccs_supervisor(). -+ * -+ * Returns pointer to "struct ccs_domain_info" if found, NULL otherwise. -+ */ -+static struct ccs_domain_info *ccs_find_domain_by_qid(unsigned int serial) -+{ -+ struct ccs_query *ptr; -+ struct ccs_domain_info *domain = NULL; -+ spin_lock(&ccs_query_list_lock); -+ list_for_each_entry(ptr, &ccs_query_list, list) { -+ if (ptr->serial != serial) -+ continue; -+ domain = ptr->domain; -+ break; -+ } -+ spin_unlock(&ccs_query_list_lock); -+ return domain; -+} -+ -+/** -+ * ccs_read_query - Read access requests which violated policy in enforcing mode. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns nothing. -+ */ -+static void ccs_read_query(struct ccs_io_buffer *head) -+{ -+ struct list_head *tmp; -+ unsigned int pos = 0; -+ size_t len = 0; -+ char *buf; -+ if (head->r.w_pos) -+ return; -+ kfree(head->read_buf); -+ head->read_buf = NULL; -+ spin_lock(&ccs_query_list_lock); -+ list_for_each(tmp, &ccs_query_list) { -+ struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); -+ if (pos++ != head->r.query_index) -+ continue; -+ len = ptr->query_len; -+ break; -+ } -+ spin_unlock(&ccs_query_list_lock); -+ if (!len) { -+ head->r.query_index = 0; -+ return; -+ } -+ buf = kzalloc(len + 32, CCS_GFP_FLAGS); -+ if (!buf) -+ return; -+ pos = 0; -+ spin_lock(&ccs_query_list_lock); -+ list_for_each(tmp, &ccs_query_list) { -+ struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); -+ if (pos++ != head->r.query_index) -+ continue; -+ /* -+ * Some query can be skipped because ccs_query_list -+ * can change, but I don't care. -+ */ -+ if (len == ptr->query_len) -+ snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial, -+ ptr->retry, ptr->query); -+ break; -+ } -+ spin_unlock(&ccs_query_list_lock); -+ if (buf[0]) { -+ head->read_buf = buf; -+ head->r.w[head->r.w_pos++] = buf; -+ head->r.query_index++; -+ } else { -+ kfree(buf); -+ } -+} -+ -+/** -+ * ccs_write_answer - Write the supervisor's decision. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns 0 on success, -EINVAL otherwise. -+ */ -+static int ccs_write_answer(struct ccs_io_buffer *head) -+{ -+ char *data = head->write_buf; -+ struct list_head *tmp; -+ unsigned int serial; -+ unsigned int answer; -+ spin_lock(&ccs_query_list_lock); -+ list_for_each(tmp, &ccs_query_list) { -+ struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); -+ ptr->timer = 0; -+ } -+ spin_unlock(&ccs_query_list_lock); -+ if (sscanf(data, "A%u=%u", &serial, &answer) != 2) -+ return -EINVAL; -+ spin_lock(&ccs_query_list_lock); -+ list_for_each(tmp, &ccs_query_list) { -+ struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); -+ if (ptr->serial != serial) -+ continue; -+ ptr->answer = (u8) answer; -+ /* Remove from ccs_query_list. */ -+ if (ptr->answer) { -+ list_del(&ptr->list); -+ INIT_LIST_HEAD(&ptr->list); -+ } -+ break; -+ } -+ spin_unlock(&ccs_query_list_lock); -+ wake_up_all(&ccs_answer_wait); -+ return 0; -+} -+ -+/** -+ * ccs_read_version - Get version. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns nothing. -+ */ -+static void ccs_read_version(struct ccs_io_buffer *head) -+{ -+ if (head->r.eof) -+ return; -+ ccs_set_string(head, "1.8.4"); -+ head->r.eof = true; -+} -+ -+/** -+ * ccs_update_stat - Update statistic counters. -+ * -+ * @index: Index for policy type. -+ * -+ * Returns nothing. -+ */ -+static void ccs_update_stat(const u8 index) -+{ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) -+ struct timeval tv; -+ do_gettimeofday(&tv); -+ /* -+ * I don't use atomic operations because race condition is not fatal. -+ */ -+ ccs_stat_updated[index]++; -+ ccs_stat_modified[index] = tv.tv_sec; -+#else -+ /* -+ * I don't use atomic operations because race condition is not fatal. -+ */ -+ ccs_stat_updated[index]++; -+ ccs_stat_modified[index] = get_seconds(); -+#endif -+} -+ -+/** -+ * ccs_read_stat - Read statistic data. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns nothing. -+ */ -+static void ccs_read_stat(struct ccs_io_buffer *head) -+{ -+ u8 i; -+ unsigned int total = 0; -+ if (head->r.eof) -+ return; -+ for (i = 0; i < CCS_MAX_POLICY_STAT; i++) { -+ ccs_io_printf(head, "Policy %-30s %10u", ccs_policy_headers[i], -+ ccs_stat_updated[i]); -+ if (ccs_stat_modified[i]) { -+ struct ccs_time stamp; -+ ccs_convert_time(ccs_stat_modified[i], &stamp); -+ ccs_io_printf(head, " (Last: %04u/%02u/%02u " -+ "%02u:%02u:%02u)", -+ stamp.year, stamp.month, stamp.day, -+ stamp.hour, stamp.min, stamp.sec); -+ } -+ ccs_set_lf(head); -+ } -+ for (i = 0; i < CCS_MAX_MEMORY_STAT; i++) { -+ unsigned int used = ccs_memory_used[i]; -+ total += used; -+ ccs_io_printf(head, "Memory used by %-22s %10u", -+ ccs_memory_headers[i], used); -+ used = ccs_memory_quota[i]; -+ if (used) -+ ccs_io_printf(head, " (Quota: %10u)", used); -+ ccs_set_lf(head); -+ } -+ ccs_io_printf(head, "Total memory used: %10u\n", -+ total); -+ head->r.eof = true; -+} -+ -+/** -+ * ccs_write_stat - Set memory quota. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns 0. -+ */ -+static int ccs_write_stat(struct ccs_io_buffer *head) -+{ -+ char *data = head->write_buf; -+ u8 i; -+ if (ccs_str_starts(&data, "Memory used by ")) -+ for (i = 0; i < CCS_MAX_MEMORY_STAT; i++) -+ if (ccs_str_starts(&data, ccs_memory_headers[i])) { -+ if (*data == ' ') -+ data++; -+ ccs_memory_quota[i] = -+ simple_strtoul(data, NULL, 10); -+ } -+ return 0; -+} -+ -+/** -+ * ccs_print_bprm - Print "struct linux_binprm" for auditing. -+ * -+ * @bprm: Pointer to "struct linux_binprm". -+ * @dump: Pointer to "struct ccs_page_dump". -+ * -+ * Returns the contents of @bprm on success, NULL otherwise. -+ * -+ * This function uses kzalloc(), so caller must kfree() if this function -+ * didn't return NULL. -+ */ -+static char *ccs_print_bprm(struct linux_binprm *bprm, -+ struct ccs_page_dump *dump) -+{ -+ static const int ccs_buffer_len = 4096 * 2; -+ char *buffer = kzalloc(ccs_buffer_len, CCS_GFP_FLAGS); -+ char *cp; -+ char *last_start; -+ int len; -+ unsigned long pos = bprm->p; -+ int offset = pos % PAGE_SIZE; -+ int argv_count = bprm->argc; -+ int envp_count = bprm->envc; -+ bool truncated = false; -+ if (!buffer) -+ return NULL; -+ len = snprintf(buffer, ccs_buffer_len - 1, "argv[]={ "); -+ cp = buffer + len; -+ if (!argv_count) { -+ memmove(cp, "} envp[]={ ", 11); -+ cp += 11; -+ } -+ last_start = cp; -+ while (argv_count || envp_count) { -+ if (!ccs_dump_page(bprm, pos, dump)) -+ goto out; -+ pos += PAGE_SIZE - offset; -+ /* Read. */ -+ while (offset < PAGE_SIZE) { -+ const char *kaddr = dump->data; -+ const unsigned char c = kaddr[offset++]; -+ if (cp == last_start) -+ *cp++ = '"'; -+ if (cp >= buffer + ccs_buffer_len - 32) { -+ /* Reserve some room for "..." string. */ -+ truncated = true; -+ } else if (c == '\\') { -+ *cp++ = '\\'; -+ *cp++ = '\\'; -+ } else if (c > ' ' && c < 127) { -+ *cp++ = c; -+ } else if (!c) { -+ *cp++ = '"'; -+ *cp++ = ' '; -+ last_start = cp; -+ } else { -+ *cp++ = '\\'; -+ *cp++ = (c >> 6) + '0'; -+ *cp++ = ((c >> 3) & 7) + '0'; -+ *cp++ = (c & 7) + '0'; -+ } -+ if (c) -+ continue; -+ if (argv_count) { -+ if (--argv_count == 0) { -+ if (truncated) { -+ cp = last_start; -+ memmove(cp, "... ", 4); -+ cp += 4; -+ } -+ memmove(cp, "} envp[]={ ", 11); -+ cp += 11; -+ last_start = cp; -+ truncated = false; -+ } -+ } else if (envp_count) { -+ if (--envp_count == 0) { -+ if (truncated) { -+ cp = last_start; -+ memmove(cp, "... ", 4); -+ cp += 4; -+ } -+ } -+ } -+ if (!argv_count && !envp_count) -+ break; -+ } -+ offset = 0; -+ } -+ *cp++ = '}'; -+ *cp = '\0'; -+ return buffer; -+out: -+ snprintf(buffer, ccs_buffer_len - 1, "argv[]={ ... } envp[]= { ... }"); -+ return buffer; -+} -+ -+/** -+ * ccs_filetype - Get string representation of file type. -+ * -+ * @mode: Mode value for stat(). -+ * -+ * Returns file type string. -+ */ -+static inline const char *ccs_filetype(const umode_t mode) -+{ -+ switch (mode & S_IFMT) { -+ case S_IFREG: -+ case 0: -+ return ccs_condition_keyword[CCS_TYPE_IS_FILE]; -+ case S_IFDIR: -+ return ccs_condition_keyword[CCS_TYPE_IS_DIRECTORY]; -+ case S_IFLNK: -+ return ccs_condition_keyword[CCS_TYPE_IS_SYMLINK]; -+ case S_IFIFO: -+ return ccs_condition_keyword[CCS_TYPE_IS_FIFO]; -+ case S_IFSOCK: -+ return ccs_condition_keyword[CCS_TYPE_IS_SOCKET]; -+ case S_IFBLK: -+ return ccs_condition_keyword[CCS_TYPE_IS_BLOCK_DEV]; -+ case S_IFCHR: -+ return ccs_condition_keyword[CCS_TYPE_IS_CHAR_DEV]; -+ } -+ return "unknown"; /* This should not happen. */ -+} -+ -+/** -+ * ccs_print_header - Get header line of audit log. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * -+ * Returns string representation. -+ * -+ * This function uses kmalloc(), so caller must kfree() if this function -+ * didn't return NULL. -+ */ -+static char *ccs_print_header(struct ccs_request_info *r) -+{ -+ struct ccs_time stamp; -+ struct ccs_obj_info *obj = r->obj; -+ const u32 ccs_flags = ccs_current_flags(); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) -+ const pid_t gpid = ccs_sys_getpid(); -+#else -+ const pid_t gpid = task_pid_nr(current); -+#endif -+ static const int ccs_buffer_len = 4096; -+ char *buffer = kmalloc(ccs_buffer_len, CCS_GFP_FLAGS); -+ int pos; -+ u8 i; -+ if (!buffer) -+ return NULL; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) -+ { -+ struct timeval tv; -+ do_gettimeofday(&tv); -+ ccs_convert_time(tv.tv_sec, &stamp); -+ } -+#else -+ ccs_convert_time(get_seconds(), &stamp); -+#endif -+ pos = snprintf(buffer, ccs_buffer_len - 1, -+ "#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s " -+ "granted=%s (global-pid=%u) task={ pid=%u ppid=%u " -+ "uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u " -+ "fsuid=%u fsgid=%u type%s=execute_handler }", -+ stamp.year, stamp.month, stamp.day, stamp.hour, -+ stamp.min, stamp.sec, r->profile, ccs_mode[r->mode], -+ ccs_yesno(r->granted), gpid, ccs_sys_getpid(), -+ ccs_sys_getppid(), -+ from_kuid(&init_user_ns, current_uid()), -+ from_kgid(&init_user_ns, current_gid()), -+ from_kuid(&init_user_ns, current_euid()), -+ from_kgid(&init_user_ns, current_egid()), -+ from_kuid(&init_user_ns, current_suid()), -+ from_kgid(&init_user_ns, current_sgid()), -+ from_kuid(&init_user_ns, current_fsuid()), -+ from_kgid(&init_user_ns, current_fsgid()), -+ ccs_flags & CCS_TASK_IS_EXECUTE_HANDLER ? "" : "!"); -+ if (!obj) -+ goto no_obj_info; -+ if (!obj->validate_done) { -+ ccs_get_attributes(obj); -+ obj->validate_done = true; -+ } -+ for (i = 0; i < CCS_MAX_PATH_STAT; i++) { -+ struct ccs_mini_stat *stat; -+ unsigned int dev; -+ umode_t mode; -+ if (!obj->stat_valid[i]) -+ continue; -+ stat = &obj->stat[i]; -+ dev = stat->dev; -+ mode = stat->mode; -+ if (i & 1) { -+ pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, -+ " path%u.parent={ uid=%u gid=%u " -+ "ino=%lu perm=0%o }", (i >> 1) + 1, -+ from_kuid(&init_user_ns, stat->uid), -+ from_kgid(&init_user_ns, stat->gid), -+ (unsigned long) stat->ino, -+ stat->mode & S_IALLUGO); -+ continue; -+ } -+ pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, -+ " path%u={ uid=%u gid=%u ino=%lu major=%u" -+ " minor=%u perm=0%o type=%s", (i >> 1) + 1, -+ from_kuid(&init_user_ns, stat->uid), -+ from_kgid(&init_user_ns, stat->gid), -+ (unsigned long) stat->ino, MAJOR(dev), -+ MINOR(dev), mode & S_IALLUGO, -+ ccs_filetype(mode)); -+ if (S_ISCHR(mode) || S_ISBLK(mode)) { -+ dev = stat->rdev; -+ pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, -+ " dev_major=%u dev_minor=%u", -+ MAJOR(dev), MINOR(dev)); -+ } -+ pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, " }"); -+ } -+no_obj_info: -+ if (pos < ccs_buffer_len - 1) -+ return buffer; -+ kfree(buffer); -+ return NULL; -+} -+ -+/** -+ * ccs_init_log - Allocate buffer for audit logs. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @len: Buffer size needed for @fmt and @args. -+ * @fmt: The printf()'s format string. -+ * @args: va_list structure for @fmt. -+ * -+ * Returns pointer to allocated memory. -+ * -+ * This function uses kzalloc(), so caller must kfree() if this function -+ * didn't return NULL. -+ */ -+static char *ccs_init_log(struct ccs_request_info *r, int len, const char *fmt, -+ va_list args) -+{ -+ char *buf = NULL; -+ char *bprm_info = NULL; -+ char *realpath = NULL; -+ const char *symlink = NULL; -+ const char *header = NULL; -+ int pos; -+ const char *domainname = ccs_current_domain()->domainname->name; -+ header = ccs_print_header(r); -+ if (!header) -+ return NULL; -+ /* +10 is for '\n' etc. and '\0'. */ -+ len += strlen(domainname) + strlen(header) + 10; -+ if (r->ee) { -+ struct file *file = r->ee->bprm->file; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) -+ struct path path = { file->f_vfsmnt, file->f_dentry }; -+ realpath = ccs_realpath(&path); -+#else -+ realpath = ccs_realpath(&file->f_path); -+#endif -+ bprm_info = ccs_print_bprm(r->ee->bprm, &r->ee->dump); -+ if (!realpath || !bprm_info) -+ goto out; -+ /* +80 is for " exec={ realpath=\"%s\" argc=%d envc=%d %s }" */ -+ len += strlen(realpath) + 80 + strlen(bprm_info); -+ } else if (r->obj && r->obj->symlink_target) { -+ symlink = r->obj->symlink_target->name; -+ /* +18 is for " symlink.target=\"%s\"" */ -+ len += 18 + strlen(symlink); -+ } -+ len = ccs_round2(len); -+ buf = kzalloc(len, CCS_GFP_FLAGS); -+ if (!buf) -+ goto out; -+ len--; -+ pos = snprintf(buf, len, "%s", header); -+ if (realpath) { -+ struct linux_binprm *bprm = r->ee->bprm; -+ pos += snprintf(buf + pos, len - pos, -+ " exec={ realpath=\"%s\" argc=%d envc=%d %s }", -+ realpath, bprm->argc, bprm->envc, bprm_info); -+ } else if (symlink) -+ pos += snprintf(buf + pos, len - pos, " symlink.target=\"%s\"", -+ symlink); -+ pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname); -+ vsnprintf(buf + pos, len - pos, fmt, args); -+out: -+ kfree(realpath); -+ kfree(bprm_info); -+ kfree(header); -+ return buf; -+} -+ -+/** -+ * ccs_transition_failed - Print waning message and send signal when domain transition failed. -+ * -+ * @domainname: Name of domain to transit. -+ * -+ * Returns nothing. -+ * -+ * Note that if current->pid == 1, sending SIGKILL won't work. -+ */ -+void ccs_transition_failed(const char *domainname) -+{ -+ printk(KERN_WARNING -+ "ERROR: Unable to transit to '%s' domain.\n", domainname); -+ force_sig(SIGKILL, current); -+} -+ -+/** -+ * ccs_update_task_domain - Update task's domain. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * -+ * Returns nothing. -+ * -+ * The task will retry as hard as possible. But if domain transition failed, -+ * the task will be killed by SIGKILL. -+ */ -+static void ccs_update_task_domain(struct ccs_request_info *r) -+{ -+ char *buf; -+ const char *cp; -+ const struct ccs_acl_info *acl = r->matched_acl; -+ r->matched_acl = NULL; -+ if (!acl || !acl->cond || !acl->cond->transit || -+ acl->cond->exec_transit) -+ return; -+ while (1) { -+ buf = kzalloc(CCS_EXEC_TMPSIZE, CCS_GFP_FLAGS); -+ if (buf) -+ break; -+ ssleep(1); -+ if (fatal_signal_pending(current)) -+ return; -+ } -+ cp = acl->cond->transit->name; -+ if (*cp == '/') -+ snprintf(buf, CCS_EXEC_TMPSIZE - 1, "%s %s", -+ ccs_current_domain()->domainname->name, cp); -+ else -+ strncpy(buf, cp, CCS_EXEC_TMPSIZE - 1); -+ if (!ccs_assign_domain(buf, true)) -+ ccs_transition_failed(buf); -+ kfree(buf); -+} -+ -+/** -+ * ccs_get_audit - Get audit mode. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * -+ * Returns true if this request should be audited, false otherwise. -+ */ -+static bool ccs_get_audit(const struct ccs_request_info *r) -+{ -+ const struct ccs_acl_info *matched_acl = r->matched_acl; -+ const u8 profile = r->profile; -+ const u8 index = r->type; -+ const bool is_granted = r->granted; -+ u8 mode; -+ struct ccs_profile *p; -+ if (!ccs_policy_loaded) -+ return false; -+ p = ccs_profile(profile); -+ if (ccs_log_count >= p->pref[CCS_PREF_MAX_AUDIT_LOG]) -+ return false; -+ if (is_granted && matched_acl && matched_acl->cond && -+ matched_acl->cond->grant_log != CCS_GRANTLOG_AUTO) -+ return matched_acl->cond->grant_log == CCS_GRANTLOG_YES; -+ mode = p->config[index]; -+ if (mode == CCS_CONFIG_USE_DEFAULT) -+ mode = p->config -+ [ccs_index2category[index] + CCS_MAX_MAC_INDEX]; -+ if (mode == CCS_CONFIG_USE_DEFAULT) -+ mode = p->default_config; -+ if (is_granted) -+ return mode & CCS_CONFIG_WANT_GRANT_LOG; -+ return mode & CCS_CONFIG_WANT_REJECT_LOG; -+} -+ -+/** -+ * ccs_write_log2 - Write an audit log. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @len: Buffer size needed for @fmt and @args. -+ * @fmt: The printf()'s format string. -+ * @args: va_list structure for @fmt. -+ * -+ * Returns nothing. -+ */ -+static void ccs_write_log2(struct ccs_request_info *r, int len, -+ const char *fmt, va_list args) -+{ -+ char *buf; -+ struct ccs_log *entry; -+ bool quota_exceeded = false; -+ if (!ccs_get_audit(r)) -+ goto out; -+ buf = ccs_init_log(r, len, fmt, args); -+ if (!buf) -+ goto out; -+ entry = kzalloc(sizeof(*entry), CCS_GFP_FLAGS); -+ if (!entry) { -+ kfree(buf); -+ goto out; -+ } -+ entry->log = buf; -+ len = ccs_round2(strlen(buf) + 1); -+ /* -+ * The entry->size is used for memory quota checks. -+ * Don't go beyond strlen(entry->log). -+ */ -+ entry->size = len + ccs_round2(sizeof(*entry)); -+ spin_lock(&ccs_log_lock); -+ if (ccs_memory_quota[CCS_MEMORY_AUDIT] && -+ ccs_memory_used[CCS_MEMORY_AUDIT] + entry->size >= -+ ccs_memory_quota[CCS_MEMORY_AUDIT]) { -+ quota_exceeded = true; -+ } else { -+ ccs_memory_used[CCS_MEMORY_AUDIT] += entry->size; -+ list_add_tail(&entry->list, &ccs_log); -+ ccs_log_count++; -+ } -+ spin_unlock(&ccs_log_lock); -+ if (quota_exceeded) { -+ kfree(buf); -+ kfree(entry); -+ goto out; -+ } -+ wake_up(&ccs_log_wait); -+out: -+ ccs_update_task_domain(r); -+} -+ -+/** -+ * ccs_write_log - Write an audit log. -+ * -+ * @r: Pointer to "struct ccs_request_info". -+ * @fmt: The printf()'s format string, followed by parameters. -+ * -+ * Returns nothing. -+ */ -+void ccs_write_log(struct ccs_request_info *r, const char *fmt, ...) -+{ -+ va_list args; -+ int len; -+ va_start(args, fmt); -+ len = vsnprintf((char *) &len, 1, fmt, args) + 1; -+ va_end(args); -+ va_start(args, fmt); -+ ccs_write_log2(r, len, fmt, args); -+ va_end(args); -+} -+ -+/** -+ * ccs_read_log - Read an audit log. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns nothing. -+ */ -+static void ccs_read_log(struct ccs_io_buffer *head) -+{ -+ struct ccs_log *ptr = NULL; -+ if (head->r.w_pos) -+ return; -+ kfree(head->read_buf); -+ head->read_buf = NULL; -+ spin_lock(&ccs_log_lock); -+ if (!list_empty(&ccs_log)) { -+ ptr = list_entry(ccs_log.next, typeof(*ptr), list); -+ list_del(&ptr->list); -+ ccs_log_count--; -+ ccs_memory_used[CCS_MEMORY_AUDIT] -= ptr->size; -+ } -+ spin_unlock(&ccs_log_lock); -+ if (ptr) { -+ head->read_buf = ptr->log; -+ head->r.w[head->r.w_pos++] = head->read_buf; -+ kfree(ptr); -+ } -+} -+ -+/** -+ * ccs_set_namespace_cursor - Set namespace to read. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns nothing. -+ */ -+static void ccs_set_namespace_cursor(struct ccs_io_buffer *head) -+{ -+ struct list_head *ns; -+ if (head->type != CCS_EXCEPTION_POLICY && head->type != CCS_PROFILE) -+ return; -+ /* -+ * If this is the first read, or reading previous namespace finished -+ * and has more namespaces to read, update the namespace cursor. -+ */ -+ ns = head->r.ns; -+ if (!ns || (head->r.eof && ns->next != &ccs_namespace_list)) { -+ /* Clearing is OK because ccs_flush() returned true. */ -+ memset(&head->r, 0, sizeof(head->r)); -+ head->r.ns = ns ? ns->next : ccs_namespace_list.next; -+ } -+} -+ -+/** -+ * ccs_has_more_namespace - Check for unread namespaces. -+ * -+ * @head: Pointer to "struct ccs_io_buffer". -+ * -+ * Returns true if we have more entries to print, false otherwise. -+ */ -+static bool ccs_has_more_namespace(struct ccs_io_buffer *head) -+{ -+ return (head->type == CCS_EXCEPTION_POLICY || -+ head->type == CCS_PROFILE) && head->r.eof && -+ head->r.ns->next != &ccs_namespace_list; -+} -+ -+/** -+ * ccs_find_namespace - Find specified namespace. -+ * -+ * @name: Name of namespace to find. -+ * @len: Length of @name. -+ * -+ * Returns pointer to "struct ccs_policy_namespace" if found, NULL otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static struct ccs_policy_namespace *ccs_find_namespace(const char *name, -+ const unsigned int len) -+{ -+ struct ccs_policy_namespace *ns; -+ list_for_each_entry_srcu(ns, &ccs_namespace_list, namespace_list, -+ &ccs_ss) { -+ if (strncmp(name, ns->name, len) || -+ (name[len] && name[len] != ' ')) -+ continue; -+ return ns; -+ } -+ return NULL; -+} -+ -+/** -+ * ccs_assign_namespace - Create a new namespace. -+ * -+ * @domainname: Name of namespace to create. -+ * -+ * Returns pointer to "struct ccs_policy_namespace" on success, NULL otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static struct ccs_policy_namespace *ccs_assign_namespace -+(const char *domainname) -+{ -+ struct ccs_policy_namespace *ptr; -+ struct ccs_policy_namespace *entry; -+ const char *cp = domainname; -+ unsigned int len = 0; -+ while (*cp && *cp++ != ' ') -+ len++; -+ ptr = ccs_find_namespace(domainname, len); -+ if (ptr) -+ return ptr; -+ if (len >= CCS_EXEC_TMPSIZE - 10 || !ccs_domain_def(domainname)) -+ return NULL; -+ entry = kzalloc(sizeof(*entry) + len + 1, CCS_GFP_FLAGS); -+ if (!entry) -+ return NULL; -+ if (mutex_lock_interruptible(&ccs_policy_lock)) -+ goto out; -+ ptr = ccs_find_namespace(domainname, len); -+ if (!ptr && ccs_memory_ok(entry, sizeof(*entry) + len + 1)) { -+ char *name = (char *) (entry + 1); -+ ptr = entry; -+ memmove(name, domainname, len); -+ name[len] = '\0'; -+ entry->name = name; -+ ccs_init_policy_namespace(entry); -+ entry = NULL; -+ } -+ mutex_unlock(&ccs_policy_lock); -+out: -+ kfree(entry); -+ return ptr; -+} -+ -+/** -+ * ccs_namespace_jump - Check for namespace jump. -+ * -+ * @domainname: Name of domain. -+ * -+ * Returns true if namespace differs, false otherwise. -+ */ -+static bool ccs_namespace_jump(const char *domainname) -+{ -+ const char *namespace = ccs_current_namespace()->name; -+ const int len = strlen(namespace); -+ return strncmp(domainname, namespace, len) || -+ (domainname[len] && domainname[len] != ' '); -+} -+ -+/** -+ * ccs_assign_domain - Create a domain or a namespace. -+ * -+ * @domainname: The name of domain. -+ * @transit: True if transit to domain found or created. -+ * -+ * Returns pointer to "struct ccs_domain_info" on success, NULL otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+struct ccs_domain_info *ccs_assign_domain(const char *domainname, -+ const bool transit) -+{ -+ struct ccs_security *security = ccs_current_security(); -+ struct ccs_domain_info e = { }; -+ struct ccs_domain_info *entry = ccs_find_domain(domainname); -+ bool created = false; -+ if (entry) { -+ if (transit) { -+ /* -+ * Since namespace is created at runtime, profiles may -+ * not be created by the moment the process transits to -+ * that domain. Do not perform domain transition if -+ * profile for that domain is not yet created. -+ */ -+ if (ccs_policy_loaded && -+ !entry->ns->profile_ptr[entry->profile]) -+ return NULL; -+ security->ccs_domain_info = entry; -+ } -+ return entry; -+ } -+ /* Requested domain does not exist. */ -+ /* Don't create requested domain if domainname is invalid. */ -+ if (strlen(domainname) >= CCS_EXEC_TMPSIZE - 10 || -+ !ccs_correct_domain(domainname)) -+ return NULL; -+ /* -+ * Since definition of profiles and acl_groups may differ across -+ * namespaces, do not inherit "use_profile" and "use_group" settings -+ * by automatically creating requested domain upon domain transition. -+ */ -+ if (transit && ccs_namespace_jump(domainname)) -+ return NULL; -+ e.ns = ccs_assign_namespace(domainname); -+ if (!e.ns) -+ return NULL; -+ /* -+ * "use_profile" and "use_group" settings for automatically created -+ * domains are inherited from current domain. These are 0 for manually -+ * created domains. -+ */ -+ if (transit) { -+ const struct ccs_domain_info *domain = -+ security->ccs_domain_info; -+ e.profile = domain->profile; -+ memcpy(e.group, domain->group, sizeof(e.group)); -+ } -+ e.domainname = ccs_get_name(domainname); -+ if (!e.domainname) -+ return NULL; -+ if (mutex_lock_interruptible(&ccs_policy_lock)) -+ goto out; -+ entry = ccs_find_domain(domainname); -+ if (!entry) { -+ entry = ccs_commit_ok(&e, sizeof(e)); -+ if (entry) { -+ INIT_LIST_HEAD(&entry->acl_info_list); -+ list_add_tail_rcu(&entry->list, &ccs_domain_list); -+ created = true; -+ } -+ } -+ mutex_unlock(&ccs_policy_lock); -+out: -+ ccs_put_name(e.domainname); -+ if (entry && transit) { -+ security->ccs_domain_info = entry; -+ if (created) { -+ struct ccs_request_info r; -+ int i; -+ ccs_init_request_info(&r, CCS_MAC_FILE_EXECUTE); -+ r.granted = false; -+ ccs_write_log(&r, "use_profile %u\n", entry->profile); -+ for (i = 0; i < CCS_MAX_ACL_GROUPS; i++) -+ if (test_bit(i, entry->group)) -+ ccs_write_log(&r, "use_group %u\n", i); -+ ccs_update_stat(CCS_STAT_POLICY_UPDATES); -+ } -+ } -+ return entry; -+} -+ -+/** -+ * ccs_parse_policy - Parse a policy line. -+ * -+ * @head: Poiter to "struct ccs_io_buffer". -+ * @line: Line to parse. -+ * -+ * Returns 0 on success, negative value otherwise. -+ * -+ * Caller holds ccs_read_lock(). -+ */ -+static int ccs_parse_policy(struct ccs_io_buffer *head, char *line) -+{ -+ /* Delete request? */ -+ head->w.is_delete = !strncmp(line, "delete ", 7); -+ if (head->w.is_delete) -+ memmove(line, line + 7, strlen(line + 7) + 1); -+ /* Selecting namespace to update. */ -+ if (head->type == CCS_EXCEPTION_POLICY || head->type == CCS_PROFILE) { -+ if (*line == '<') { -+ char *cp = strchr(line, ' '); -+ if (cp) { -+ *cp++ = '\0'; -+ head->w.ns = ccs_assign_namespace(line); -+ memmove(line, cp, strlen(cp) + 1); -+ } else -+ head->w.ns = NULL; -+ } else -+ head->w.ns = &ccs_kernel_namespace; -+ /* Don't allow updating if namespace is invalid. */ -+ if (!head->w.ns) -+ return -ENOENT; -+ } -+ /* Do the update. */ -+ switch (head->type) { -+ case CCS_DOMAIN_POLICY: -+ return ccs_write_domain(head); -+ case CCS_EXCEPTION_POLICY: -+ return ccs_write_exception(head); -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ case CCS_EXECUTE_HANDLER: -+#endif -+ case CCS_PROCESS_STATUS: -+ return ccs_write_pid(head); -+ case CCS_STAT: -+ return ccs_write_stat(head); -+ case CCS_PROFILE: -+ return ccs_write_profile(head); -+ case CCS_QUERY: -+ return ccs_write_answer(head); -+ case CCS_MANAGER: -+ return ccs_write_manager(head); -+ default: -+ return -ENOSYS; -+ } -+} -+ -+/** -+ * ccs_policy_io_init - Register hooks for policy I/O. -+ * -+ * Returns nothing. -+ */ -+static void __init ccs_policy_io_init(void) -+{ -+ ccsecurity_ops.check_profile = ccs_check_profile; -+} -+ -+/** -+ * ccs_load_builtin_policy - Load built-in policy. -+ * -+ * Returns nothing. -+ */ -+static void __init ccs_load_builtin_policy(void) -+{ -+ /* -+ * This include file is manually created and contains built-in policy -+ * named "ccs_builtin_profile", "ccs_builtin_exception_policy", -+ * "ccs_builtin_domain_policy", "ccs_builtin_manager", -+ * "ccs_builtin_stat" in the form of "static char [] __initdata". -+ */ -+#include "builtin-policy.h" -+ u8 i; -+ const int idx = ccs_read_lock(); -+ for (i = 0; i < 5; i++) { -+ struct ccs_io_buffer head = { }; -+ char *start = ""; -+ switch (i) { -+ case 0: -+ start = ccs_builtin_profile; -+ head.type = CCS_PROFILE; -+ break; -+ case 1: -+ start = ccs_builtin_exception_policy; -+ head.type = CCS_EXCEPTION_POLICY; -+ break; -+ case 2: -+ start = ccs_builtin_domain_policy; -+ head.type = CCS_DOMAIN_POLICY; -+ break; -+ case 3: -+ start = ccs_builtin_manager; -+ head.type = CCS_MANAGER; -+ break; -+ case 4: -+ start = ccs_builtin_stat; -+ head.type = CCS_STAT; -+ break; -+ } -+ while (1) { -+ char *end = strchr(start, '\n'); -+ if (!end) -+ break; -+ *end = '\0'; -+ ccs_normalize_line(start); -+ head.write_buf = start; -+ ccs_parse_policy(&head, start); -+ start = end + 1; -+ } -+ } -+ ccs_read_unlock(idx); -+#ifdef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER -+ ccs_check_profile(); -+#endif -+} -+ -+/** -+ * ccs_read_self - read() for /proc/ccs/self_domain interface. -+ * -+ * @file: Pointer to "struct file". -+ * @buf: Domainname which current thread belongs to. -+ * @count: Size of @buf. -+ * @ppos: Bytes read by now. -+ * -+ * Returns read size on success, negative value otherwise. -+ */ -+static ssize_t ccs_read_self(struct file *file, char __user *buf, size_t count, -+ loff_t *ppos) -+{ -+ const char *domain = ccs_current_domain()->domainname->name; -+ loff_t len = strlen(domain); -+ loff_t pos = *ppos; -+ if (pos >= len || !count) -+ return 0; -+ len -= pos; -+ if (count < len) -+ len = count; -+ if (copy_to_user(buf, domain + pos, len)) -+ return -EFAULT; -+ *ppos += len; -+ return len; -+} -+ -+/** -+ * ccs_open - open() for /proc/ccs/ interface. -+ * -+ * @inode: Pointer to "struct inode". -+ * @file: Pointer to "struct file". -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int ccs_open(struct inode *inode, struct file *file) -+{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) -+ const u8 type = (unsigned long) PDE_DATA(inode); -+#else -+ const u8 type = (unsigned long) PDE(inode)->data; -+#endif -+ struct ccs_io_buffer *head = kzalloc(sizeof(*head), CCS_GFP_FLAGS); -+ if (!head) -+ return -ENOMEM; -+ mutex_init(&head->io_sem); -+ head->type = type; -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ if (type == CCS_EXECUTE_HANDLER) { -+ /* Allow execute_handler to read process's status. */ -+ if (!(ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER)) { -+ kfree(head); -+ return -EPERM; -+ } -+ } -+#endif -+ if ((file->f_mode & FMODE_READ) && type != CCS_AUDIT && -+ type != CCS_QUERY) { -+ /* Don't allocate read_buf for poll() access. */ -+ head->readbuf_size = 4096; -+ head->read_buf = kzalloc(head->readbuf_size, CCS_GFP_FLAGS); -+ if (!head->read_buf) { -+ kfree(head); -+ return -ENOMEM; -+ } -+ } -+ if (file->f_mode & FMODE_WRITE) { -+ head->writebuf_size = 4096; -+ head->write_buf = kzalloc(head->writebuf_size, CCS_GFP_FLAGS); -+ if (!head->write_buf) { -+ kfree(head->read_buf); -+ kfree(head); -+ return -ENOMEM; -+ } -+ } -+ /* -+ * If the file is /proc/ccs/query, increment the observer counter. -+ * The obserber counter is used by ccs_supervisor() to see if -+ * there is some process monitoring /proc/ccs/query. -+ */ -+ if (type == CCS_QUERY) -+ atomic_inc(&ccs_query_observers); -+ file->private_data = head; -+ ccs_notify_gc(head, true); -+ return 0; -+} -+ -+/** -+ * ccs_release - close() for /proc/ccs/ interface. -+ * -+ * @inode: Pointer to "struct inode". -+ * @file: Pointer to "struct file". -+ * -+ * Returns 0. -+ */ -+static int ccs_release(struct inode *inode, struct file *file) -+{ -+ struct ccs_io_buffer *head = file->private_data; -+ /* -+ * If the file is /proc/ccs/query, decrement the observer counter. -+ */ -+ if (head->type == CCS_QUERY && -+ atomic_dec_and_test(&ccs_query_observers)) -+ wake_up_all(&ccs_answer_wait); -+ ccs_notify_gc(head, false); -+ return 0; -+} -+ -+/** -+ * ccs_poll - poll() for /proc/ccs/ interface. -+ * -+ * @file: Pointer to "struct file". -+ * @wait: Pointer to "poll_table". Maybe NULL. -+ * -+ * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, -+ * POLLOUT | POLLWRNORM otherwise. -+ */ -+static unsigned int ccs_poll(struct file *file, poll_table *wait) -+{ -+ struct ccs_io_buffer *head = file->private_data; -+ if (head->type == CCS_AUDIT) { -+ if (!ccs_memory_used[CCS_MEMORY_AUDIT]) { -+ poll_wait(file, &ccs_log_wait, wait); -+ if (!ccs_memory_used[CCS_MEMORY_AUDIT]) -+ return POLLOUT | POLLWRNORM; -+ } -+ } else if (head->type == CCS_QUERY) { -+ if (list_empty(&ccs_query_list)) { -+ poll_wait(file, &ccs_query_wait, wait); -+ if (list_empty(&ccs_query_list)) -+ return POLLOUT | POLLWRNORM; -+ } -+ } -+ return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; -+} -+ -+/** -+ * ccs_read - read() for /proc/ccs/ interface. -+ * -+ * @file: Pointer to "struct file". -+ * @buf: Pointer to buffer. -+ * @count: Size of @buf. -+ * @ppos: Unused. -+ * -+ * Returns bytes read on success, negative value otherwise. -+ */ -+static ssize_t ccs_read(struct file *file, char __user *buf, size_t count, -+ loff_t *ppos) -+{ -+ struct ccs_io_buffer *head = file->private_data; -+ int len; -+ int idx; -+ if (mutex_lock_interruptible(&head->io_sem)) -+ return -EINTR; -+ head->read_user_buf = buf; -+ head->read_user_buf_avail = count; -+ idx = ccs_read_lock(); -+ if (ccs_flush(head)) -+ /* Call the policy handler. */ -+ do { -+ ccs_set_namespace_cursor(head); -+ switch (head->type) { -+ case CCS_DOMAIN_POLICY: -+ ccs_read_domain(head); -+ break; -+ case CCS_EXCEPTION_POLICY: -+ ccs_read_exception(head); -+ break; -+ case CCS_AUDIT: -+ ccs_read_log(head); -+ break; -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ case CCS_EXECUTE_HANDLER: -+#endif -+ case CCS_PROCESS_STATUS: -+ ccs_read_pid(head); -+ break; -+ case CCS_VERSION: -+ ccs_read_version(head); -+ break; -+ case CCS_STAT: -+ ccs_read_stat(head); -+ break; -+ case CCS_PROFILE: -+ ccs_read_profile(head); -+ break; -+ case CCS_QUERY: -+ ccs_read_query(head); -+ break; -+ case CCS_MANAGER: -+ ccs_read_manager(head); -+ break; -+ } -+ } while (ccs_flush(head) && ccs_has_more_namespace(head)); -+ ccs_read_unlock(idx); -+ len = head->read_user_buf - buf; -+ mutex_unlock(&head->io_sem); -+ return len; -+} -+ -+#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -+ -+/** -+ * ccs_write_self - write() for /proc/ccs/self_domain interface. -+ * -+ * @file: Pointer to "struct file". -+ * @buf: Domainname to transit to. -+ * @count: Size of @buf. -+ * @ppos: Unused. -+ * -+ * Returns @count on success, negative value otherwise. -+ * -+ * If domain transition was permitted but the domain transition failed, this -+ * function returns error rather than terminating current thread with SIGKILL. -+ */ -+static ssize_t ccs_write_self(struct file *file, const char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ char *data; -+ int error; -+ if (!count || count >= CCS_EXEC_TMPSIZE - 10) -+ return -ENOMEM; -+ data = kzalloc(count + 1, CCS_GFP_FLAGS); -+ if (!data) -+ return -ENOMEM; -+ if (copy_from_user(data, buf, count)) { -+ error = -EFAULT; -+ goto out; -+ } -+ ccs_normalize_line(data); -+ if (ccs_correct_domain(data)) { -+ const int idx = ccs_read_lock(); -+ struct ccs_path_info name; -+ struct ccs_request_info r; -+ name.name = data; -+ ccs_fill_path_info(&name); -+ /* Check "task manual_domain_transition" permission. */ -+ ccs_init_request_info(&r, CCS_MAC_FILE_EXECUTE); -+ r.param_type = CCS_TYPE_MANUAL_TASK_ACL; -+ r.param.task.domainname = &name; -+ ccs_check_acl(&r); -+ if (!r.granted) -+ error = -EPERM; -+ else -+ error = ccs_assign_domain(data, true) ? 0 : -ENOENT; -+ ccs_read_unlock(idx); -+ } else -+ error = -EINVAL; -+out: -+ kfree(data); -+ return error ? error : count; -+} -+ -+#endif -+ -+/** -+ * ccs_write - write() for /proc/ccs/ interface. -+ * -+ * @file: Pointer to "struct file". -+ * @buf: Pointer to buffer. -+ * @count: Size of @buf. -+ * @ppos: Unused. -+ * -+ * Returns @count on success, negative value otherwise. -+ */ -+static ssize_t ccs_write(struct file *file, const char __user *buf, -+ size_t count, loff_t *ppos) -+{ -+ struct ccs_io_buffer *head = file->private_data; -+ int error = count; -+ char *cp0 = head->write_buf; -+ int idx; -+ if (mutex_lock_interruptible(&head->io_sem)) -+ return -EINTR; -+ head->read_user_buf_avail = 0; -+ idx = ccs_read_lock(); -+ /* Read a line and dispatch it to the policy handler. */ -+ while (count) { -+ char c; -+ if (head->w.avail >= head->writebuf_size - 1) { -+ const int len = head->writebuf_size * 2; -+ char *cp = kzalloc(len, CCS_GFP_FLAGS); -+ if (!cp) { -+ error = -ENOMEM; -+ break; -+ } -+ memmove(cp, cp0, head->w.avail); -+ kfree(cp0); -+ head->write_buf = cp; -+ cp0 = cp; -+ head->writebuf_size = len; -+ } -+ if (get_user(c, buf)) { -+ error = -EFAULT; -+ break; -+ } -+ buf++; -+ count--; -+ cp0[head->w.avail++] = c; -+ if (c != '\n') -+ continue; -+ cp0[head->w.avail - 1] = '\0'; -+ head->w.avail = 0; -+ ccs_normalize_line(cp0); -+ if (!strcmp(cp0, "reset")) { -+ head->w.ns = &ccs_kernel_namespace; -+ head->w.domain = NULL; -+ memset(&head->r, 0, sizeof(head->r)); -+ continue; -+ } -+ /* Don't allow updating policies by non manager programs. */ -+ switch (head->type) { -+ case CCS_PROCESS_STATUS: -+ /* This does not write anything. */ -+ break; -+ case CCS_DOMAIN_POLICY: -+ if (ccs_select_domain(head, cp0)) -+ continue; -+ /* fall through */ -+ case CCS_EXCEPTION_POLICY: -+ if (!strcmp(cp0, "select transition_only")) { -+ head->r.print_transition_related_only = true; -+ continue; -+ } -+ /* fall through */ -+ default: -+ if (!ccs_manager()) { -+ error = -EPERM; -+ goto out; -+ } -+ } -+ switch (ccs_parse_policy(head, cp0)) { -+ case -EPERM: -+ error = -EPERM; -+ goto out; -+ case 0: -+ /* Update statistics. */ -+ switch (head->type) { -+ case CCS_DOMAIN_POLICY: -+ case CCS_EXCEPTION_POLICY: -+ case CCS_STAT: -+ case CCS_PROFILE: -+ case CCS_MANAGER: -+ ccs_update_stat(CCS_STAT_POLICY_UPDATES); -+ break; -+ default: -+ break; -+ } -+ break; -+ } -+ } -+out: -+ ccs_read_unlock(idx); -+ mutex_unlock(&head->io_sem); -+ return error; -+} -+ -+/** -+ * ccs_create_entry - Create interface files under /proc/ccs/ directory. -+ * -+ * @name: The name of the interface file. -+ * @mode: The permission of the interface file. -+ * @parent: The parent directory. -+ * @key: Type of interface. -+ * -+ * Returns nothing. -+ */ -+static void __init ccs_create_entry(const char *name, const umode_t mode, -+ struct proc_dir_entry *parent, -+ const u8 key) -+{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) -+ proc_create_data(name, mode, parent, &ccs_operations, -+ ((u8 *) NULL) + key); -+#else -+ struct proc_dir_entry *entry = create_proc_entry(name, mode, parent); -+ if (entry) { -+ entry->proc_fops = &ccs_operations; -+ entry->data = ((u8 *) NULL) + key; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+ if (entry->proc_iops) -+ ccs_file_inode_operations = *entry->proc_iops; -+ if (!ccs_file_inode_operations.setattr) -+ ccs_file_inode_operations.setattr = proc_notify_change; -+ entry->proc_iops = &ccs_file_inode_operations; -+#endif -+ } -+#endif -+} -+ -+/** -+ * ccs_proc_init - Initialize /proc/ccs/ interface. -+ * -+ * Returns nothing. -+ */ -+static void __init ccs_proc_init(void) -+{ -+ struct proc_dir_entry *ccs_dir = proc_mkdir("ccs", NULL); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+ if (ccs_dir->proc_iops) -+ ccs_dir_inode_operations = *ccs_dir->proc_iops; -+ if (!ccs_dir_inode_operations.setattr) -+ ccs_dir_inode_operations.setattr = proc_notify_change; -+ ccs_dir->proc_iops = &ccs_dir_inode_operations; -+#endif -+ ccs_create_entry("query", 0600, ccs_dir, CCS_QUERY); -+ ccs_create_entry("domain_policy", 0600, ccs_dir, CCS_DOMAIN_POLICY); -+ ccs_create_entry("exception_policy", 0600, ccs_dir, -+ CCS_EXCEPTION_POLICY); -+ ccs_create_entry("audit", 0400, ccs_dir, CCS_AUDIT); -+ ccs_create_entry(".process_status", 0600, ccs_dir, -+ CCS_PROCESS_STATUS); -+ ccs_create_entry("stat", 0644, ccs_dir, CCS_STAT); -+ ccs_create_entry("profile", 0600, ccs_dir, CCS_PROFILE); -+ ccs_create_entry("manager", 0600, ccs_dir, CCS_MANAGER); -+ ccs_create_entry("version", 0400, ccs_dir, CCS_VERSION); -+#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -+ ccs_create_entry(".execute_handler", 0666, ccs_dir, -+ CCS_EXECUTE_HANDLER); -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) -+ proc_create("self_domain", 0666, ccs_dir, &ccs_self_operations); -+#else -+ { -+ struct proc_dir_entry *e = create_proc_entry("self_domain", -+ 0666, ccs_dir); -+ if (e) -+ e->proc_fops = &ccs_self_operations; -+ } -+#endif -+} -+ -+/** -+ * ccs_init_module - Initialize this module. -+ * -+ * Returns 0 on success, negative value otherwise. -+ */ -+static int __init ccs_init_module(void) -+{ -+ if (ccsecurity_ops.disabled) -+ return -EINVAL; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) -+ MOD_INC_USE_COUNT; -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) -+ if (init_srcu_struct(&ccs_ss)) -+ panic("Out of memory."); -+#endif -+ ccs_kernel_namespace.name = ""; -+ ccs_init_policy_namespace(&ccs_kernel_namespace); -+ ccs_kernel_domain.ns = &ccs_kernel_namespace; -+ INIT_LIST_HEAD(&ccs_kernel_domain.acl_info_list); -+ ccs_mm_init(); -+ ccs_policy_io_init(); -+ ccs_permission_init(); -+ ccs_proc_init(); -+ ccs_load_builtin_policy(); -+ return 0; -+} -+ -+MODULE_LICENSE("GPL"); -+module_init(ccs_init_module); -diff --git a/security/ccsecurity/realpath.c b/security/ccsecurity/realpath.c -new file mode 100644 -index 0000000..df821ed ---- /dev/null -+++ b/security/ccsecurity/realpath.c -@@ -0,0 +1,767 @@ -+/* -+ * security/ccsecurity/realpath.c -+ * -+ * Copyright (C) 2005-2012 NTT DATA CORPORATION -+ * -+ * Version: 1.8.4 2015/05/05 -+ */ -+ -+#include "internal.h" -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) -+#include -+#include -+#endif -+ -+/***** SECTION1: Constants definition *****/ -+ -+#define SOCKFS_MAGIC 0x534F434B -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+#define s_fs_info u.generic_sbp -+#endif -+ -+/***** SECTION2: Structure definition *****/ -+ -+/***** SECTION3: Prototype definition section *****/ -+ -+char *ccs_encode(const char *str); -+char *ccs_encode2(const char *str, int str_len); -+char *ccs_realpath(const struct path *path); -+const char *ccs_get_exe(void); -+void ccs_fill_path_info(struct ccs_path_info *ptr); -+ -+static char *ccs_get_absolute_path(const struct path *path, -+ char * const buffer, const int buflen); -+static char *ccs_get_dentry_path(struct dentry *dentry, char * const buffer, -+ const int buflen); -+static char *ccs_get_local_path(struct dentry *dentry, char * const buffer, -+ const int buflen); -+static char *ccs_get_socket_name(const struct path *path, char * const buffer, -+ const int buflen); -+static int ccs_const_part_length(const char *filename); -+ -+/***** SECTION4: Standalone functions section *****/ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -+ -+/** -+ * SOCKET_I - Get "struct socket". -+ * -+ * @inode: Pointer to "struct inode". -+ * -+ * Returns pointer to "struct socket". -+ * -+ * This is for compatibility with older kernels. -+ */ -+static inline struct socket *SOCKET_I(struct inode *inode) -+{ -+ return inode->i_sock ? &inode->u.socket_i : NULL; -+} -+ -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) -+ -+/** -+ * ccs_realpath_lock - Take locks for __d_path(). -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_realpath_lock(void) -+{ -+ /* dcache_lock is locked by __d_path(). */ -+ /* vfsmount_lock is locked by __d_path(). */ -+} -+ -+/** -+ * ccs_realpath_unlock - Release locks for __d_path(). -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_realpath_unlock(void) -+{ -+ /* vfsmount_lock is unlocked by __d_path(). */ -+ /* dcache_lock is unlocked by __d_path(). */ -+} -+ -+#elif LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 36) -+ -+/** -+ * ccs_realpath_lock - Take locks for __d_path(). -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_realpath_lock(void) -+{ -+ spin_lock(&dcache_lock); -+ /* vfsmount_lock is locked by __d_path(). */ -+} -+ -+/** -+ * ccs_realpath_unlock - Release locks for __d_path(). -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_realpath_unlock(void) -+{ -+ /* vfsmount_lock is unlocked by __d_path(). */ -+ spin_unlock(&dcache_lock); -+} -+ -+#elif defined(D_PATH_DISCONNECT) && !defined(CONFIG_SUSE_KERNEL) -+ -+/** -+ * ccs_realpath_lock - Take locks for __d_path(). -+ * -+ * Returns nothing. -+ * -+ * Original unambiguous-__d_path.diff in patches.apparmor.tar.bz2 inversed the -+ * order of holding dcache_lock and vfsmount_lock. That patch was applied on -+ * (at least) SUSE 11.1 and Ubuntu 8.10 and Ubuntu 9.04 kernels. -+ * -+ * However, that patch was updated to use original order and the updated patch -+ * is applied to (as far as I know) only SUSE kernels. -+ * -+ * Therefore, I need to use original order for SUSE 11.1 kernels and inversed -+ * order for other kernels. I detect it by checking D_PATH_DISCONNECT and -+ * CONFIG_SUSE_KERNEL. I don't know whether other distributions are using the -+ * updated patch or not. If you got deadlock, check fs/dcache.c for locking -+ * order, and add " && 0" to this "#elif " block if fs/dcache.c uses original -+ * order. -+ */ -+static inline void ccs_realpath_lock(void) -+{ -+ spin_lock(ccsecurity_exports.vfsmount_lock); -+ spin_lock(&dcache_lock); -+} -+ -+/** -+ * ccs_realpath_unlock - Release locks for __d_path(). -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_realpath_unlock(void) -+{ -+ spin_unlock(&dcache_lock); -+ spin_unlock(ccsecurity_exports.vfsmount_lock); -+} -+ -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) -+ -+/** -+ * ccs_realpath_lock - Take locks for __d_path(). -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_realpath_lock(void) -+{ -+ spin_lock(&dcache_lock); -+ spin_lock(ccsecurity_exports.vfsmount_lock); -+} -+ -+/** -+ * ccs_realpath_unlock - Release locks for __d_path(). -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_realpath_unlock(void) -+{ -+ spin_unlock(ccsecurity_exports.vfsmount_lock); -+ spin_unlock(&dcache_lock); -+} -+ -+#else -+ -+/** -+ * ccs_realpath_lock - Take locks for __d_path(). -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_realpath_lock(void) -+{ -+ spin_lock(&dcache_lock); -+} -+ -+/** -+ * ccs_realpath_unlock - Release locks for __d_path(). -+ * -+ * Returns nothing. -+ */ -+static inline void ccs_realpath_unlock(void) -+{ -+ spin_unlock(&dcache_lock); -+} -+ -+#endif -+ -+/***** SECTION5: Variables definition section *****/ -+ -+/***** SECTION6: Dependent functions section *****/ -+ -+/** -+ * ccs_get_absolute_path - Get the path of a dentry but ignores chroot'ed root. -+ * -+ * @path: Pointer to "struct path". -+ * @buffer: Pointer to buffer to return value in. -+ * @buflen: Sizeof @buffer. -+ * -+ * Returns the buffer on success, an error code otherwise. -+ * -+ * Caller holds the dcache_lock and vfsmount_lock. -+ * Based on __d_path() in fs/dcache.c -+ * -+ * If dentry is a directory, trailing '/' is appended. -+ */ -+static char *ccs_get_absolute_path(const struct path *path, -+ char * const buffer, const int buflen) -+{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) -+ char *pos = ERR_PTR(-ENOMEM); -+ if (buflen >= 256) { -+ pos = ccsecurity_exports.d_absolute_path(path, buffer, -+ buflen - 1); -+ if (!IS_ERR(pos) && *pos == '/' && pos[1]) { -+ struct inode *inode = path->dentry->d_inode; -+ if (inode && S_ISDIR(inode->i_mode)) { -+ buffer[buflen - 2] = '/'; -+ buffer[buflen - 1] = '\0'; -+ } -+ } -+ } -+ return pos; -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) -+ /* -+ * __d_path() will start returning NULL by backporting commit 02125a82 -+ * "fix apparmor dereferencing potentially freed dentry, sanitize -+ * __d_path() API". -+ * -+ * Unfortunately, __d_path() after applying that commit always returns -+ * NULL when root is empty. d_absolute_path() is provided for TOMOYO -+ * 2.x and AppArmor but TOMOYO 1.x does not use it, for TOMOYO 1.x -+ * might be built as a loadable kernel module and there is no warrantee -+ * that TOMOYO 1.x is recompiled after applying that commit. Also, -+ * I don't want to search /proc/kallsyms for d_absolute_path() because -+ * I want to keep TOMOYO 1.x architecture independent. Thus, supply -+ * non empty root like AppArmor's d_namespace_path() did. -+ */ -+ char *pos = ERR_PTR(-ENOMEM); -+ if (buflen >= 256) { -+ static bool ccs_no_empty; -+ if (!ccs_no_empty) { -+ struct path root = { }; -+ pos = ccsecurity_exports.__d_path(path, &root, buffer, -+ buflen - 1); -+ } else { -+ pos = NULL; -+ } -+ if (!pos) { -+ struct task_struct *task = current; -+ struct path root; -+ struct path tmp; -+ spin_lock(&task->fs->lock); -+ root.mnt = task->nsproxy->mnt_ns->root; -+ root.dentry = root.mnt->mnt_root; -+ path_get(&root); -+ spin_unlock(&task->fs->lock); -+ tmp = root; -+ pos = ccsecurity_exports.__d_path(path, &tmp, buffer, -+ buflen - 1); -+ path_put(&root); -+ if (!pos) -+ return ERR_PTR(-EINVAL); -+ /* Remember if __d_path() needs non empty root. */ -+ ccs_no_empty = true; -+ } -+ if (!IS_ERR(pos) && *pos == '/' && pos[1]) { -+ struct inode *inode = path->dentry->d_inode; -+ if (inode && S_ISDIR(inode->i_mode)) { -+ buffer[buflen - 2] = '/'; -+ buffer[buflen - 1] = '\0'; -+ } -+ } -+ } -+ return pos; -+#else -+ char *pos = buffer + buflen - 1; -+ struct dentry *dentry = path->dentry; -+ struct vfsmount *vfsmnt = path->mnt; -+ const char *name; -+ int len; -+ -+ if (buflen < 256) -+ goto out; -+ -+ *pos = '\0'; -+ if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) -+ *--pos = '/'; -+ for (;;) { -+ struct dentry *parent; -+ if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { -+ if (vfsmnt->mnt_parent == vfsmnt) -+ break; -+ dentry = vfsmnt->mnt_mountpoint; -+ vfsmnt = vfsmnt->mnt_parent; -+ continue; -+ } -+ parent = dentry->d_parent; -+ name = dentry->d_name.name; -+ len = dentry->d_name.len; -+ pos -= len; -+ if (pos <= buffer) -+ goto out; -+ memmove(pos, name, len); -+ *--pos = '/'; -+ dentry = parent; -+ } -+ if (*pos == '/') -+ pos++; -+ len = dentry->d_name.len; -+ pos -= len; -+ if (pos < buffer) -+ goto out; -+ memmove(pos, dentry->d_name.name, len); -+ return pos; -+out: -+ return ERR_PTR(-ENOMEM); -+#endif -+} -+ -+/** -+ * ccs_get_dentry_path - Get the path of a dentry. -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @buffer: Pointer to buffer to return value in. -+ * @buflen: Sizeof @buffer. -+ * -+ * Returns the buffer on success, an error code otherwise. -+ * -+ * Based on dentry_path() in fs/dcache.c -+ * -+ * If dentry is a directory, trailing '/' is appended. -+ */ -+static char *ccs_get_dentry_path(struct dentry *dentry, char * const buffer, -+ const int buflen) -+{ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) -+ char *pos = ERR_PTR(-ENOMEM); -+ if (buflen >= 256) { -+ pos = dentry_path_raw(dentry, buffer, buflen - 1); -+ if (!IS_ERR(pos) && *pos == '/' && pos[1] && -+ d_is_dir(dentry)) { -+ buffer[buflen - 2] = '/'; -+ buffer[buflen - 1] = '\0'; -+ } -+ } -+ return pos; -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) -+ char *pos = ERR_PTR(-ENOMEM); -+ if (buflen >= 256) { -+ /* rename_lock is locked/unlocked by dentry_path_raw(). */ -+ pos = dentry_path_raw(dentry, buffer, buflen - 1); -+ if (!IS_ERR(pos) && *pos == '/' && pos[1]) { -+ struct inode *inode = dentry->d_inode; -+ if (inode && S_ISDIR(inode->i_mode)) { -+ buffer[buflen - 2] = '/'; -+ buffer[buflen - 1] = '\0'; -+ } -+ } -+ } -+ return pos; -+#else -+ char *pos = buffer + buflen - 1; -+ if (buflen < 256) -+ return ERR_PTR(-ENOMEM); -+ *pos = '\0'; -+ if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) -+ *--pos = '/'; -+ spin_lock(&dcache_lock); -+ while (!IS_ROOT(dentry)) { -+ struct dentry *parent = dentry->d_parent; -+ const char *name = dentry->d_name.name; -+ const int len = dentry->d_name.len; -+ pos -= len; -+ if (pos <= buffer) { -+ pos = ERR_PTR(-ENOMEM); -+ break; -+ } -+ memmove(pos, name, len); -+ *--pos = '/'; -+ dentry = parent; -+ } -+ spin_unlock(&dcache_lock); -+ return pos; -+#endif -+} -+ -+/** -+ * ccs_get_local_path - Get the path of a dentry. -+ * -+ * @dentry: Pointer to "struct dentry". -+ * @buffer: Pointer to buffer to return value in. -+ * @buflen: Sizeof @buffer. -+ * -+ * Returns the buffer on success, an error code otherwise. -+ */ -+static char *ccs_get_local_path(struct dentry *dentry, char * const buffer, -+ const int buflen) -+{ -+ struct super_block *sb = dentry->d_sb; -+ char *pos = ccs_get_dentry_path(dentry, buffer, buflen); -+ if (IS_ERR(pos)) -+ return pos; -+ /* Convert from $PID to self if $PID is current thread. */ -+ if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') { -+ char *ep; -+ const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) -+ if (*ep == '/' && pid && pid == -+ task_tgid_nr_ns(current, sb->s_fs_info)) { -+ pos = ep - 5; -+ if (pos < buffer) -+ goto out; -+ memmove(pos, "/self", 5); -+ } -+#else -+ if (*ep == '/' && pid == ccs_sys_getpid()) { -+ pos = ep - 5; -+ if (pos < buffer) -+ goto out; -+ memmove(pos, "/self", 5); -+ } -+#endif -+ goto prepend_filesystem_name; -+ } -+ /* Use filesystem name for unnamed devices. */ -+ if (!MAJOR(sb->s_dev)) -+ goto prepend_filesystem_name; -+ { -+ struct inode *inode = sb->s_root->d_inode; -+ /* -+ * Use filesystem name if filesystems does not support rename() -+ * operation. -+ */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) -+ if (inode->i_op && !inode->i_op->rename) -+ goto prepend_filesystem_name; -+#else -+ if (!inode->i_op->rename && !inode->i_op->rename2) -+ goto prepend_filesystem_name; -+#endif -+ } -+ /* Prepend device name. */ -+ { -+ char name[64]; -+ int name_len; -+ const dev_t dev = sb->s_dev; -+ name[sizeof(name) - 1] = '\0'; -+ snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev), -+ MINOR(dev)); -+ name_len = strlen(name); -+ pos -= name_len; -+ if (pos < buffer) -+ goto out; -+ memmove(pos, name, name_len); -+ return pos; -+ } -+ /* Prepend filesystem name. */ -+prepend_filesystem_name: -+ { -+ const char *name = sb->s_type->name; -+ const int name_len = strlen(name); -+ pos -= name_len + 1; -+ if (pos < buffer) -+ goto out; -+ memmove(pos, name, name_len); -+ pos[name_len] = ':'; -+ } -+ return pos; -+out: -+ return ERR_PTR(-ENOMEM); -+} -+ -+/** -+ * ccs_get_socket_name - Get the name of a socket. -+ * -+ * @path: Pointer to "struct path". -+ * @buffer: Pointer to buffer to return value in. -+ * @buflen: Sizeof @buffer. -+ * -+ * Returns the buffer. -+ */ -+static char *ccs_get_socket_name(const struct path *path, char * const buffer, -+ const int buflen) -+{ -+ struct inode *inode = path->dentry->d_inode; -+ struct socket *sock = inode ? SOCKET_I(inode) : NULL; -+ struct sock *sk = sock ? sock->sk : NULL; -+ if (sk) { -+ snprintf(buffer, buflen, "socket:[family=%u:type=%u:" -+ "protocol=%u]", sk->sk_family, sk->sk_type, -+ sk->sk_protocol); -+ } else { -+ snprintf(buffer, buflen, "socket:[unknown]"); -+ } -+ return buffer; -+} -+ -+#define SOCKFS_MAGIC 0x534F434B -+ -+/** -+ * ccs_realpath - Returns realpath(3) of the given pathname but ignores chroot'ed root. -+ * -+ * @path: Pointer to "struct path". -+ * -+ * Returns the realpath of the given @path on success, NULL otherwise. -+ * -+ * This function uses kzalloc(), so caller must kfree() if this function -+ * didn't return NULL. -+ */ -+char *ccs_realpath(const struct path *path) -+{ -+ char *buf = NULL; -+ char *name = NULL; -+ unsigned int buf_len = PAGE_SIZE / 2; -+ struct dentry *dentry = path->dentry; -+ struct super_block *sb; -+ if (!dentry) -+ return NULL; -+ sb = dentry->d_sb; -+ while (1) { -+ char *pos; -+ struct inode *inode; -+ buf_len <<= 1; -+ kfree(buf); -+ buf = kmalloc(buf_len, CCS_GFP_FLAGS); -+ if (!buf) -+ break; -+ /* To make sure that pos is '\0' terminated. */ -+ buf[buf_len - 1] = '\0'; -+ /* Get better name for socket. */ -+ if (sb->s_magic == SOCKFS_MAGIC) { -+ pos = ccs_get_socket_name(path, buf, buf_len - 1); -+ goto encode; -+ } -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) -+ /* For "pipe:[\$]". */ -+ if (dentry->d_op && dentry->d_op->d_dname) { -+ pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); -+ goto encode; -+ } -+#endif -+ inode = sb->s_root->d_inode; -+ /* -+ * Use local name for "filesystems without rename() operation" -+ * or "path without vfsmount" or "absolute name is unavailable" -+ * cases. -+ */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0) -+ if (!path->mnt || (inode->i_op && !inode->i_op->rename)) -+ pos = ERR_PTR(-EINVAL); -+ else { -+ /* Get absolute name for the rest. */ -+ ccs_realpath_lock(); -+ pos = ccs_get_absolute_path(path, buf, buf_len - 1); -+ ccs_realpath_unlock(); -+ } -+ if (pos == ERR_PTR(-EINVAL)) -+ pos = ccs_get_local_path(path->dentry, buf, -+ buf_len - 1); -+#else -+ if (!path->mnt || -+ (!inode->i_op->rename && !inode->i_op->rename2)) -+ pos = ccs_get_local_path(path->dentry, buf, -+ buf_len - 1); -+ else -+ pos = ccs_get_absolute_path(path, buf, buf_len - 1); -+#endif -+encode: -+ if (IS_ERR(pos)) -+ continue; -+ name = ccs_encode(pos); -+ break; -+ } -+ kfree(buf); -+ if (!name) -+ ccs_warn_oom(__func__); -+ return name; -+} -+ -+/** -+ * ccs_encode2 - Encode binary string to ascii string. -+ * -+ * @str: String in binary format. -+ * @str_len: Size of @str in byte. -+ * -+ * Returns pointer to @str in ascii format on success, NULL otherwise. -+ * -+ * This function uses kzalloc(), so caller must kfree() if this function -+ * didn't return NULL. -+ */ -+char *ccs_encode2(const char *str, int str_len) -+{ -+ int i; -+ int len = 0; -+ const char *p = str; -+ char *cp; -+ char *cp0; -+ if (!p) -+ return NULL; -+ for (i = 0; i < str_len; i++) { -+ const unsigned char c = p[i]; -+ if (c == '\\') -+ len += 2; -+ else if (c > ' ' && c < 127) -+ len++; -+ else -+ len += 4; -+ } -+ len++; -+ /* Reserve space for appending "/". */ -+ cp = kzalloc(len + 10, CCS_GFP_FLAGS); -+ if (!cp) -+ return NULL; -+ cp0 = cp; -+ p = str; -+ for (i = 0; i < str_len; i++) { -+ const unsigned char c = p[i]; -+ if (c == '\\') { -+ *cp++ = '\\'; -+ *cp++ = '\\'; -+ } else if (c > ' ' && c < 127) { -+ *cp++ = c; -+ } else { -+ *cp++ = '\\'; -+ *cp++ = (c >> 6) + '0'; -+ *cp++ = ((c >> 3) & 7) + '0'; -+ *cp++ = (c & 7) + '0'; -+ } -+ } -+ return cp0; -+} -+ -+/** -+ * ccs_encode - Encode binary string to ascii string. -+ * -+ * @str: String in binary format. -+ * -+ * Returns pointer to @str in ascii format on success, NULL otherwise. -+ * -+ * This function uses kzalloc(), so caller must kfree() if this function -+ * didn't return NULL. -+ */ -+char *ccs_encode(const char *str) -+{ -+ return str ? ccs_encode2(str, strlen(str)) : NULL; -+} -+ -+/** -+ * ccs_const_part_length - Evaluate the initial length without a pattern in a token. -+ * -+ * @filename: The string to evaluate. -+ * -+ * Returns the initial length without a pattern in @filename. -+ */ -+static int ccs_const_part_length(const char *filename) -+{ -+ char c; -+ int len = 0; -+ if (!filename) -+ return 0; -+ while (1) { -+ c = *filename++; -+ if (!c) -+ break; -+ if (c != '\\') { -+ len++; -+ continue; -+ } -+ c = *filename++; -+ switch (c) { -+ case '\\': /* "\\" */ -+ len += 2; -+ continue; -+ case '0': /* "\ooo" */ -+ case '1': -+ case '2': -+ case '3': -+ c = *filename++; -+ if (c < '0' || c > '7') -+ break; -+ c = *filename++; -+ if (c < '0' || c > '7') -+ break; -+ len += 4; -+ continue; -+ } -+ break; -+ } -+ return len; -+} -+ -+/** -+ * ccs_fill_path_info - Fill in "struct ccs_path_info" members. -+ * -+ * @ptr: Pointer to "struct ccs_path_info" to fill in. -+ * -+ * The caller sets "struct ccs_path_info"->name. -+ */ -+void ccs_fill_path_info(struct ccs_path_info *ptr) -+{ -+ const char *name = ptr->name; -+ const int len = strlen(name); -+ ptr->total_len = len; -+ ptr->const_len = ccs_const_part_length(name); -+ ptr->is_dir = len && (name[len - 1] == '/'); -+ ptr->is_patterned = (ptr->const_len < len); -+ ptr->hash = full_name_hash(name, len); -+} -+ -+/** -+ * ccs_get_exe - Get ccs_realpath() of current process. -+ * -+ * Returns the ccs_realpath() of current process on success, NULL otherwise. -+ * -+ * This function uses kzalloc(), so the caller must kfree() -+ * if this function didn't return NULL. -+ */ -+const char *ccs_get_exe(void) -+{ -+ struct mm_struct *mm = current->mm; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) -+ struct vm_area_struct *vma; -+#endif -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) -+ struct path path; -+#endif -+ struct file *exe_file = NULL; -+ const char *cp; -+ if (!mm) -+ return NULL; -+ down_read(&mm->mmap_sem); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) -+ /* Not using get_mm_exe_file() as it is not exported. */ -+ exe_file = mm->exe_file; -+#else -+ for (vma = mm->mmap; vma; vma = vma->vm_next) { -+ if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { -+ exe_file = vma->vm_file; -+ break; -+ } -+ } -+#endif -+ if (exe_file) -+ get_file(exe_file); -+ up_read(&mm->mmap_sem); -+ if (!exe_file) -+ return NULL; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) -+ cp = ccs_realpath(&exe_file->f_path); -+#else -+ path.mnt = exe_file->f_vfsmnt; -+ path.dentry = exe_file->f_dentry; -+ cp = ccs_realpath(&path); -+#endif -+ fput(exe_file); -+ return cp; -+} --- -1.9.1 - diff --git a/recipes-kernel/linux/linux-yocto-4.1/tomoyo.cfg b/recipes-kernel/linux/linux-yocto-4.1/tomoyo.cfg deleted file mode 100644 index 8f78270..0000000 --- a/recipes-kernel/linux/linux-yocto-4.1/tomoyo.cfg +++ /dev/null @@ -1,16 +0,0 @@ -CONFIG_CCSECURITY=y -CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY=y -CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY=2048 -CONFIG_CCSECURITY_MAX_AUDIT_LOG=1024 -CONFIG_CCSECURITY_POLICY_LOADER="/sbin/ccs-init" -CONFIG_CCSECURITY_ACTIVATION_TRIGGER="/sbin/init" -CONFIG_CCSECURITY_FILE_READDIR=y -CONFIG_CCSECURITY_FILE_GETATTR=y -CONFIG_CCSECURITY_NETWORK=y -CONFIG_CCSECURITY_NETWORK_RECVMSG=y -CONFIG_CCSECURITY_CAPABILITY=y -CONFIG_CCSECURITY_IPC=y -CONFIG_CCSECURITY_MISC=y -CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER=y -CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION=y -CONFIG_CCSECURITY_PORTRESERVE=y diff --git a/recipes-kernel/linux/linux-yocto-4.1/tomoyo.scc b/recipes-kernel/linux/linux-yocto-4.1/tomoyo.scc deleted file mode 100644 index 588864c..0000000 --- a/recipes-kernel/linux/linux-yocto-4.1/tomoyo.scc +++ /dev/null @@ -1,4 +0,0 @@ -define KFEATURE_DESCRIPTION "Tomoyo Kernel Support" -define KFEATURE_COMPATIBILITY arch - -kconf non-hardware tomoyo.cfg diff --git a/recipes-kernel/linux/linux-yocto_4.1.bbappend b/recipes-kernel/linux/linux-yocto_4.1.bbappend deleted file mode 100644 index dc90e31..0000000 --- a/recipes-kernel/linux/linux-yocto_4.1.bbappend +++ /dev/null @@ -1,9 +0,0 @@ -FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}-4.1:" - -# Tomoyo kernel support -SRC_URI += "\ - ${@bb.utils.contains('DISTRO_FEATURES', 'tomoyo', ' file://ccs-tools-yocto.4.1.patch', '', d)} \ - ${@bb.utils.contains('DISTRO_FEATURES', 'tomoyo', ' file://ccs-tools-yocto_security.patch', '', d)} \ - ${@bb.utils.contains('DISTRO_FEATURES', 'tomoyo', ' file://tomoyo.cfg', '', d)} \ - ${@bb.utils.contains('DISTRO_FEATURES', 'tomoyo', ' file://tomoyo.scc', '', d)} \ - " -- cgit v1.2.3-54-g00ecf