diff options
Diffstat (limited to 'recipes-kernel')
-rw-r--r-- | recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.1.diff | 968 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.2.diff | 681 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto.4.1.patch | 1029 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto_security.patch | 17920 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-yocto-4.1/tomoyo.cfg | 16 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-yocto-4.1/tomoyo.scc | 4 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-yocto_4.1.bbappend | 9 |
7 files changed, 20627 insertions, 0 deletions
diff --git a/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.1.diff b/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.1.diff new file mode 100644 index 0000000..9ad49fd --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.1.diff @@ -0,0 +1,968 @@ +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 <linux/sched/prio.h> + + ++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 <linux/ccsecurity.h> + + /* 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 <uapi/linux/fs.h>" . ++ */ ++ 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 <asm/uaccess.h> + #include <asm/io.h> + #include <asm/sections.h> ++#include <linux/ccsecurity.h> + + #include <crypto/hash.h> + #include <crypto/sha.h> +@@ -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 <linux/bsearch.h> + #include <uapi/linux/module.h> + #include "module-internal.h" ++#include <linux/ccsecurity.h> + + #define CREATE_TRACE_POINTS + #include <trace/events/module.h> +@@ -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 <linux/syscalls.h> + #include <linux/syscore_ops.h> + #include <linux/uaccess.h> ++#include <linux/ccsecurity.h> + + /* + * 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 <linux/mm.h> + #include <linux/module.h> + #include <linux/rtc.h> ++#include <linux/ccsecurity.h> + + #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 new file mode 100644 index 0000000..d1b021a --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-4.1/ccs-patch-4.2.diff @@ -0,0 +1,681 @@ +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 <linux/sched/prio.h> + + ++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 <linux/ccsecurity.h> + + /* 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 <uapi/linux/fs.h>" . ++ */ ++ 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 <asm/uaccess.h> + #include <asm/io.h> + #include <asm/sections.h> ++#include <linux/ccsecurity.h> + + #include <crypto/hash.h> + #include <crypto/sha.h> +@@ -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 <linux/bsearch.h> + #include <uapi/linux/module.h> + #include "module-internal.h" ++#include <linux/ccsecurity.h> + + #define CREATE_TRACE_POINTS + #include <trace/events/module.h> +@@ -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 <linux/syscalls.h> + #include <linux/syscore_ops.h> + #include <linux/uaccess.h> ++#include <linux/ccsecurity.h> + + /* + * 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 <linux/mm.h> + #include <linux/module.h> + #include <linux/rtc.h> ++#include <linux/ccsecurity.h> + + #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 new file mode 100644 index 0000000..611d396 --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto.4.1.patch @@ -0,0 +1,1029 @@ +From 13c3a21c549a87cf7410bd2ff88eeb6cb1ddda8c Mon Sep 17 00:00:00 2001 +From: invalid_git config <unknown@unknown> +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 <linux/sched/prio.h> + + ++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 <linux/ccsecurity.h> + + /* 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 <uapi/linux/fs.h>" . ++ */ ++ 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 <asm/uaccess.h> + #include <asm/io.h> + #include <asm/sections.h> ++#include <linux/ccsecurity.h> + + #include <crypto/hash.h> + #include <crypto/sha.h> +@@ -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 <linux/bsearch.h> + #include <uapi/linux/module.h> + #include "module-internal.h" ++#include <linux/ccsecurity.h> + + #define CREATE_TRACE_POINTS + #include <trace/events/module.h> +@@ -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 <linux/syscalls.h> + #include <linux/syscore_ops.h> + #include <linux/uaccess.h> ++#include <linux/ccsecurity.h> + + /* + * 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 <linux/mm.h> + #include <linux/module.h> + #include <linux/rtc.h> ++#include <linux/ccsecurity.h> + + #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 new file mode 100644 index 0000000..33a69a1 --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-4.1/ccs-tools-yocto_security.patch @@ -0,0 +1,17920 @@ +From d4a329ee417f1b75ba087828bb4b2f62c1ef57bb Mon Sep 17 00:00:00 2001 +From: Auto Configured <auto.configured> +Date: Sun, 25 Oct 2015 12:19:45 -0700 +Subject: [PATCH] Ci add ccsecuroty + +Signed-off-by: Auto Configured <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 <linux/version.h> ++ ++#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 <linux/lsm2ccsecurity.h> ++#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 <linux/dcache.h> ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) ++/* Obtain definition of kuid_t and kgid_t. */ ++#include <linux/uidgid.h> ++#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 <linux/version.h> ++#include <linux/uidgid.h> ++ ++#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 <linux/version.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <linux/string.h> ++#include <linux/mm.h> ++#include <linux/utime.h> ++#include <linux/file.h> ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 38) ++#include <linux/smp_lock.h> ++#endif ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/highmem.h> ++#include <linux/poll.h> ++#include <linux/binfmts.h> ++#include <linux/delay.h> ++#include <linux/sched.h> ++#include <linux/dcache.h> ++#include <linux/mount.h> ++#include <linux/net.h> ++#include <linux/inet.h> ++#include <linux/in.h> ++#include <linux/in6.h> ++#include <linux/un.h> ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) ++#include <linux/fs.h> ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) ++#include <linux/namei.h> ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) ++#include <linux/fs_struct.h> ++#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) ++#include <linux/namespace.h> ++#endif ++#include <linux/proc_fs.h> ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) || defined(RHEL_MAJOR) ++#include <linux/hash.h> ++#endif ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) || (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL)) ++#include <linux/sysctl.h> ++#endif ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 6) ++#include <linux/kthread.h> ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) ++#include <linux/magic.h> ++#endif ++#include <stdarg.h> ++#include <asm/uaccess.h> ++#include <net/sock.h> ++#include <net/af_unix.h> ++#include <net/ip.h> ++#include <net/ipv6.h> ++#include <net/udp.h> ++ ++#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 <linux/ccsecurity.h> ++ ++/* 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. "<kernel>", "</usr/sbin/httpd>" ). */ ++ 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 <linux/version.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/binfmts.h> ++#include <linux/sched.h> ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) ++#include <linux/kmod.h> ++/* ++ * 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 <linux/unistd.h> ++#else ++#include <linux/fs.h> ++#include <linux/namei.h> ++#endif ++#ifndef LOOKUP_POSITIVE ++#define LOOKUP_POSITIVE 0 ++#endif ++ ++/* ++ * TOMOYO specific part start. ++ */ ++ ++#include <linux/ccsecurity.h> ++ ++/** ++ * 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 <linux/path.h> ++#include <linux/security.h> ++#include <linux/ccsecurity.h> ++ ++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 <linux/lsm_hooks.h> ++ ++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("<kernel>"); ++ 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("<unknown>", bprm); ++#else ++ snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "<unknown>"); ++ 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 "<NULL>" if no dev_name given. */ ++ if (!dev_name) ++ dev_name = "<NULL>"; ++ 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 = "<NULL>"; ++ 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 <linux/ctype.h> ++ ++#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 = "<kernel>"; ++ 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 <linux/nsproxy.h> ++#include <linux/mnt_namespace.h> ++#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 new file mode 100644 index 0000000..8f78270 --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-4.1/tomoyo.cfg @@ -0,0 +1,16 @@ +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 new file mode 100644 index 0000000..588864c --- /dev/null +++ b/recipes-kernel/linux/linux-yocto-4.1/tomoyo.scc @@ -0,0 +1,4 @@ +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 new file mode 100644 index 0000000..1043d5c --- /dev/null +++ b/recipes-kernel/linux/linux-yocto_4.1.bbappend @@ -0,0 +1,9 @@ +FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}-4.1:" + +# Tomoyo kernel support +SRC_URI += "\ + ${@base_contains('DISTRO_FEATURES', 'tomoyo', ' file://ccs-tools-yocto.4.1.patch', '', d)} \ + ${@base_contains('DISTRO_FEATURES', 'tomoyo', ' file://ccs-tools-yocto_security.patch', '', d)} \ + ${@base_contains('DISTRO_FEATURES', 'tomoyo', ' file://tomoyo.cfg', '', d)} \ + ${@base_contains('DISTRO_FEATURES', 'tomoyo', ' file://tomoyo.scc', '', d)} \ + " |