diff --git a/akari/COPYING b/COPYING similarity index 100% rename from akari/COPYING rename to COPYING diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a53bc83 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +INSTALL := install +USRBINDIR := /usr/bin +USRDOCDIR := /usr/share/doc +ifndef CFLAGS +CFLAGS := -Wall -O2 -g -D_FORTIFY_SOURCE=2 +endif + +all: udplogger + +udplogger: udplogger.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o udplogger udplogger.c + +install: all + mkdir -p -m 0755 $(INSTALLDIR)$(USRBINDIR) + $(INSTALL) -m 0755 udplogger $(INSTALLDIR)$(USRBINDIR) + mkdir -p -m 0755 $(INSTALLDIR)$(USRDOCDIR)/udplogger/ + $(INSTALL) -m 0644 README COPYING $(INSTALLDIR)$(USRDOCDIR)/udplogger/ + +clean: + rm -f -- udplogger + +.PHONY: clean install diff --git a/README b/README new file mode 100644 index 0000000..8fb8416 --- /dev/null +++ b/README @@ -0,0 +1,126 @@ +About this package: + + When troubleshooting system freeze problems, obtaining SysRq messages + is commonly used. However, sometimes it is impossible to let syslog + daemon save the messages. In that case, serial console can be used. + + However, in already-deployed enterprise servers, it is difficult to + reboot the system in order to add the serial console to the list of + consoles. Also, speak of virtualized servers in the cloud environment, + it may be difficult to add the virtual serial console which would + require FTP-like access to the log files saved on the virtualization + host. In that case, netconsole can be used. + + It would be easy to configure the sender side; just load the netconsole + kernel module with appropriate arguments. However, it is not always easy + to configure the receiver side. + + This package contains a utility for receiving netconsole messages. + + This utility records timestamp and source IP:port in front of each line, + allowing you to monitor multiple servers with single log file. + You can easily pick up specific server's messages using awk(1). + + This utility automatically switches the log file if the latest log + crosses the midnight, allowing you to rotate log files without + restarting this utility. + + This utility waits for the newline of each message, allowing you to + record messages without emitting unnecessary timestamp and IP:port. + + This utility uses larger receive buffer, allowing you to catch + burst messages like SysRq-T. + + This utility can be run as an unprivileged user provided that the + receive buffer size is sufficient. + + This package is released under the GPLv2. + +How to use this utility: + + There are 10 command line arguments. They are all optional. + + ip=$listen_ip + + $listen_ip specifies the local IPv4 or IPv6 address to listen to. + Default value is :: if IPv6 is available, 0.0.0.0 otherwise. + + port=$listen_port + + $listen_port specifies the local port number to listen to. + Default value is 6666. + + You need to run this program as the root user (or with + CAP_NET_BIND_SERVICE capability) if you want to specify a value + between 1 and 1023. + + dir=$log_dir + + $log_dir specifies the directory to create log files. + Default value is current directory. + + The log files are created as YYYY-MM-DD.log where YYYY/MM/DD are + the date of the message received. + + timeout=$seconds_waiting_for_newline + + $seconds_waiting_for_newline specifies the max seconds to wait for + a new line if already received message does not have a new line. + The value should be between 5 and 600. Default value is 10. + + clients=$max_clients + + $max_clients specifies the max number of clients to accept. The value + should be between 10 and 65536. Default value is 1024. + + wbuf=$write_buffer_size + + $write_buffer_size specifies the max buffer size for receiving a line of + message. The value should be between 1024 and 1048576. Default value is + 65536. + + rbuf=$receive_buffer_size + + $receive_buffer_size specifies the max buffer size for receiving UDP + datagrams. The value should be between 65536 and 1073741824. Default + value is 8388608 but actual size might be adjusted by the kernel. + + You need to run this program as the root user (or with CAP_NET_ADMIN + capability) if you want to ignore /proc/sys/net/core/rmem_max limit. + + uid=$user_id + + $user_id specifies the UID to run this program and the owner of the log + files. Default value is the UID who executed this program. + + You need to run this program as the root user (or with CAP_SETUID + capability) if you want to change this value. + + gid=$group_id + + $group_id specifies the GID to run this program and the group of the log + files. Default value is the GID who executed this program. + + You need to run this program as the root user (or with CAP_SETGID + capability) if you want to change this value. + + perm=$log_perm + + $log_perm specifies the protection mode of the log files. + The value should be between 0000 and 0666. Default value is 0600. + +ChangeLog: + + Version 1.0 2014/03/10 Initial packaging. + + The following enhancements are added to the draft version posted to + https://lkml.org/lkml/2013/10/24/278 . + + Listen to IPv6 address. + Run with given UID and/or GID. + Create log files with given protection mode. + + Version 1.1 2014/04/20 Typo fix. + + The option name to print is not "user_id=" but "uid=" . + The option name to print is not "group_id=" but "gid=" . diff --git a/akari/Makefile b/akari/Makefile deleted file mode 100644 index 7500fb3..0000000 --- a/akari/Makefile +++ /dev/null @@ -1,63 +0,0 @@ -akari-objs := permission.o gc.o memory.o policy_io.o realpath.o load_policy.o probe.o -ifdef CONFIG_SECURITY_COMPOSER_MAX -akari-objs += mclsm.o -else -akari-objs += lsm.o -endif -akari_test-objs := test.o probe.o -obj-m += akari_test.o akari.o - -$(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 an empty policy/exception_policy.conf - @touch $@ - -$(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 $(obj)/config.h - -$(obj)/check_umode_t.h: - @echo Checking whether umode_t is used by include/linux/security.h or not. - @grep -q umode_t $(srctree)/include/linux/security.h && echo "#define USE_UMODE_T" > $@.tmp || echo "" > $@.tmp - @mv $@.tmp $@ - -$(obj)/lsm.o: $(obj)/check_umode_t.h $(obj)/config.h - -.o: $(obj)/config.h diff --git a/akari/README b/akari/README deleted file mode 100644 index 2dda9ac..0000000 --- a/akari/README +++ /dev/null @@ -1,266 +0,0 @@ -Notes for AKARI project - -AKARI is Access Keeping And Regulating Instrument for Linux 2.6 and later -kernels. - -You can use AKARI for analyzing your system's behavior (i.e. reports which -application accesses which resources like strace command does) and optionally -restricting your system's behavior (i.e. controls which application can -access which resources like TOMOYO/AppArmor does). - -AKARI is forked from TOMOYO 1.8 and made as a LKM (loadable kernel module) -so that you don't need to replace your kernels installed in your system. - -This patch is released under the GPLv2. - -Project URL: http://akari.sourceforge.jp/ - -ChangeLog: - -Version 1.0 2010/10/10 First release. - -Version 1.0.1 2010/10/18 Minor update release. - - Synchronize with TOMOYO revision 4069. - - Fix off-by-two in ccs_check_unix_address(). - - Implement post accept() LSM hook. - -Version 1.0.2 2010/10/25 Minor update release. - - Synchronize with TOMOYO revision 4090. - - Add getattr() and readdir() checks. - - Use "YYYY/MM/DD hh:mm:ss" format for /proc/ccs/ interface. - - Do not automatically add / for umount(). - -Version 1.0.3 2010/11/01 Minor update release. - - Synchronize with TOMOYO revision 4104. - - Fix pathname handling in ccs_unix_entry(). - -Version 1.0.4 2010/11/11 Minor update release. - - Synchronize with TOMOYO 1.8.0 release. - - Add sysctl() check for 2.6.21 to 2.6.32 kernels. - - Fix double new_decode_dev() bug for mknod(). - - Fix keyword typo. - - Fix build failure on some kernels. - - Changed pathname prefix priority. - - Use hash table for faster scan. - - Updated function comments. - -Version 1.0.5 2010/11/22 Minor update release. - - Make ccs_domain_info/ccs_flags inheritable for 2.6.29 and later kernels. - -Version 1.0.6 2010/12/31 Minor update release. - - Synchronize with TOMOYO revision 4280. - - Use same interface for audit logs. - - Split ccs_null_security into ccs_default_security and ccs_oom_security. - -Version 1.0.7 2011/01/21 Minor update release. - - Synchronize with TOMOYO revision 4400. - - Use filesystem name for unnamed devices when vfsmount is missing. - -Version 1.0.8 2011/02/07 Minor update release. - - Synchronize with TOMOYO revision 4545. - - Fix infinite loop bug when reading /proc/ccs/audit or /proc/ccs/query . - -Version 1.0.9 2011/02/14 Minor update release. - - Fix missing permission check for interpreters in 2.6.30 and later kernels. - -Version 1.0.10 2011/02/15 Minor update release. - - Fix missing permission check for interpreters in 2.6.23 and earlier kernels. - - Fix wrong execute permission check and domain transition in 2.6.28 and earlier kernels. - -Version 1.0.11 2010/04/01 Minor update release. - - Synchronize with TOMOYO 1.8.1 release. - - Run garbage collector without waiting for /proc/ccs/ users. - - Support built-in policy configuration. - - Remove /proc/ccs/meminfo interface. - - Pack policy when printing via /proc/ccs/ interface. - - Fix conditional policy parsing. - - Serialize updating profile's comment line. - -Version 1.0.12 2011/04/11 Minor update release. - - Synchronize with TOMOYO revision 4874. - - Fix fcntl(F_SETFL, O_APPEND) handling. - -Version 1.0.13 2011/05/05 Minor update release. - - Synchronize with TOMOYO revision 4963. - - Fix wrong profile number in audit logs for "misc env" permission. - -Version 1.0.14 2011/05/11 Minor update release. - - Synchronize with TOMOYO revision 4978. - - Fix wrong domainname validation. - -Version 1.0.15 2011/06/20 Minor update release. - - Synchronize with TOMOYO 1.8.2 release. - - Add policy namespace support. - -Version 1.0.16 2011/07/07 Minor update release. - - Synchronize with TOMOYO revision 5235. - - Remove /proc/ccs/.domain_status interface. - -Version 1.0.17 2011/07/13 Minor update release. - - Synchronize with TOMOYO revision 5266. - - Fix /proc/ccs/stat parser. - - Accept "::" notation for IPv6 address. - -Version 1.0.18 2011/09/03 Minor update release. - - Synchronize with TOMOYO revision 5401. - - Avoid race when retrying "file execute" permission check. - - Remove unneeded daemonize(). - - Fix load failure with !CONFIG_SMP && !CONFIG_DEBUG_SPINLOCK kernels. - -Version 1.0.19 2011/09/15 Minor update release. - - Use akari/config.h for choosing build options. - - Fix build error on CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER=y case. - - Use lookup_mnt() rather than __put_namespace(). (2.6.0 to 2.6.2 kernels) - - Fix unbalanced spin_lock()/spin_unlock() pair in lsm_pin(). - (2.6.15 to 2.6.35 kernels) - - Fix "struct task_struct" leaks of tasks created before loading akari.ko . - (2.6.28 and earlier kernels) - - Use "struct task_struct"->pids[PIDTYPE_PID].pid instead of - "struct task_struct" for associating with per "struct task_struct" variables - (i.e. "struct ccs_security") in order to reduce amount of dead memory - waiting for garbage collection. (2.6.29 and later kernels) - - Add akari_test.ko for checking whether akari.ko seems to work or not. - - Add SH and ARM architectures support. (Needs more testing.) - -Version 1.0.20 2011/09/29 Minor update release. - - Synchronize with TOMOYO 1.8.3 release. - - Allow specifying domain transition preference. - - Simplify garbage collector. - -Version 1.0.21 2011/10/25 Minor update release. - - Synchronize with TOMOYO revision 5569. - - Fix incomplete read after seek. - - Use query id for reaching target process's domain policy. - - Fix quota counting. - -Version 1.0.22 2011/11/11 Minor update release. - - Synchronize with TOMOYO revision 5625. - - Optimize for object's size. - -Version 1.0.23 2011/11/18 Minor update release. - - Synchronize with TOMOYO revision 5646. - - Fix kernel config mapping error. - -Version 1.0.24 2011/12/13 Minor update release. - - Synchronize with TOMOYO revision 5711. - - Follow __d_path() behavior change. (Only 2.6.36 and later) - -Version 1.0.25 2012/02/29 Minor update release. - - Synchronize with TOMOYO revision 5893. - - Follow UMH_WAIT_PROC constant renumbering. - - Fix mount flags checking order. - -Version 1.0.26 2012/04/01 Minor update release. - - Synchronize with TOMOYO revision 5973. - - Return appropriate value to poll(). - -Version 1.0.27 2012/05/05 Minor update release. - - Synchronize with TOMOYO revision 6035. - - Readd RHEL_MINOR/AX_MINOR checks. - - Accept manager programs which do not start with / . - -Version 1.0.28 2012/10/20 Security update release. - - Fix kernel panic caused by double kfree() bug when "struct ccs_execve" - pointer was by error duplicated at __ccs_alloc_task_security(). - This bug affects only 2.6.28 and earlier kernels. - -Version 1.0.29 2012/11/04 Minor update release. - - Use dummy pointer as needed in order to make sure that security_bprm_free() - (which is used for making the caller of do_execve() return to previous - domain when do_execve() failed after domain transition) is always called. - Without this fix, domain transition history on 2.6.28 and earlier kernels - becomes inaccurate. - -Version 1.0.30 2013/02/14 Minor update release. - - Commit a2a8474c "exec: do not sleep in TASK_TRACED under ->cred_guard_mutex" - moved "current->in_execve = 1;" from before prepare_bprm_creds() to after - prepare_bprm_creds(). It turned out that, as an unexpected bonus, we can use - security_prepare_creds() as a hook for emulating security_bprm_free() hook. - - I updated the logic for security_bprm_free() emulation, and now AKARI should - be able to coexist with other AKARI-like LKM-based LSM implementations (e.g. - CaitSith) on all kernel versions other than 2.6.29 and 2.6.30. diff --git a/akari/ccsecurity.h b/akari/ccsecurity.h deleted file mode 100644 index a33fbaa..0000000 --- a/akari/ccsecurity.h +++ /dev/null @@ -1,919 +0,0 @@ -/* - * include/linux/ccsecurity.h - * - * Copyright (C) 2005-2012 NTT DATA CORPORATION - * - * Version: 1.8.3+ 2012/05/05 - */ - -#ifndef _LINUX_CCSECURITY_H -#define _LINUX_CCSECURITY_H - -#include - -#ifndef __user -#define __user -#endif - -struct nameidata; -struct path; -struct dentry; -struct vfsmount; -struct linux_binprm; -struct pt_regs; -struct file; -struct ctl_table; -struct socket; -struct sockaddr; -struct sock; -struct sk_buff; -struct msghdr; -struct pid_namespace; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) -int search_binary_handler(struct linux_binprm *bprm); -#else -int search_binary_handler(struct linux_binprm *bprm, struct pt_regs *regs); -#endif - -#ifdef CONFIG_CCSECURITY - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) -/* Obtain prototype of __d_path(). */ -#include -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) -/* Obtain definition of kuid_t and kgid_t. */ -#include -#endif - -/* For exporting variables and functions. */ -struct ccsecurity_exports { - void (*load_policy) (const char *filename); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(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/akari/config.h b/akari/config.h deleted file mode 100644 index a527bb6..0000000 --- a/akari/config.h +++ /dev/null @@ -1,15 +0,0 @@ -#define CONFIG_CCSECURITY_MAX_AUDIT_LOG 1024 -#define CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY 2048 -//#define CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER -#define CONFIG_CCSECURITY_POLICY_LOADER "/sbin/ccs-init" -#define CONFIG_CCSECURITY_ACTIVATION_TRIGGER "/sbin/init" -//#define CONFIG_AKARI_DEBUG -//#define CONFIG_AKARI_TRACE_EXECVE_COUNT -#define CONFIG_CCSECURITY_FILE_READDIR -#define CONFIG_CCSECURITY_FILE_GETATTR -#ifdef CONFIG_SECURITY_NETWORK -#define CONFIG_CCSECURITY_NETWORK -#endif -#define CONFIG_CCSECURITY_MISC -#define CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -#define CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION diff --git a/akari/gc.c b/akari/gc.c deleted file mode 100644 index cd4eb5f..0000000 --- a/akari/gc.c +++ /dev/null @@ -1,1037 +0,0 @@ -/* - * security/ccsecurity/gc.c - * - * Copyright (C) 2005-2012 NTT DATA CORPORATION - * - * Version: 1.8.3+ 2012/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; - } - } - in_use = ccs_used_by_cred(domain); -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/akari/internal.h b/akari/internal.h deleted file mode 100644 index 8df8b5e..0000000 --- a/akari/internal.h +++ /dev/null @@ -1,2160 +0,0 @@ -/* - * security/ccsecurity/internal.h - * - * Copyright (C) 2005-2012 NTT DATA CORPORATION - * - * Version: 1.8.3+ 2012/05/05 - */ - -#ifndef _SECURITY_CCSECURITY_INTERNAL_H -#define _SECURITY_CCSECURITY_INTERNAL_H - -#include -#include -#include -#include -#include -#include -#include -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 38) -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -#include -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) -#include -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) -#include -#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) -#include -#endif -#include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) || defined(RHEL_MAJOR) -#include -#endif -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) || (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL)) -#include -#endif -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 6) -#include -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) -#include -#endif -#include -#include -#include -#include -#include -#include -#include - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -#define sk_family family -#define sk_protocol protocol -#define sk_type type -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - -/* Structure for holding "struct vfsmount *" and "struct dentry *". */ -struct path { - struct vfsmount *mnt; - struct dentry *dentry; -}; - -#endif - -#ifndef __printf -#define __printf(a,b) __attribute__((format(printf,a,b))) -#endif -#ifndef __packed -#define __packed __attribute__((__packed__)) -#endif -#ifndef bool -#define bool _Bool -#endif -#ifndef false -#define false 0 -#endif -#ifndef true -#define true 1 -#endif - -#ifndef __user -#define __user -#endif - -#ifndef current_uid -#define current_uid() (current->uid) -#endif -#ifndef current_gid -#define current_gid() (current->gid) -#endif -#ifndef current_euid -#define current_euid() (current->euid) -#endif -#ifndef current_egid -#define current_egid() (current->egid) -#endif -#ifndef current_suid -#define current_suid() (current->suid) -#endif -#ifndef current_sgid -#define current_sgid() (current->sgid) -#endif -#ifndef current_fsuid -#define current_fsuid() (current->fsuid) -#endif -#ifndef current_fsgid -#define current_fsgid() (current->fsgid) -#endif - -#ifndef DEFINE_SPINLOCK -#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) -#define mutex semaphore -#define mutex_init(mutex) init_MUTEX(mutex) -#define mutex_unlock(mutex) up(mutex) -#define mutex_lock(mutex) down(mutex) -#define mutex_lock_interruptible(mutex) down_interruptible(mutex) -#define mutex_trylock(mutex) (!down_trylock(mutex)) -#define DEFINE_MUTEX(mutexname) DECLARE_MUTEX(mutexname) -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) -#define MS_UNBINDABLE (1<<17) /* change to unbindable */ -#define MS_PRIVATE (1<<18) /* change to private */ -#define MS_SLAVE (1<<19) /* change to slave */ -#define MS_SHARED (1<<20) /* change to shared */ -#endif - -#ifndef container_of -#define container_of(ptr, type, member) ({ \ - const typeof(((type *)0)->member) *__mptr = (ptr); \ - (type *)((char *)__mptr - offsetof(type, member)); }) -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) -#define smp_read_barrier_depends smp_rmb -#endif - -#ifndef ACCESS_ONCE -#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) -#endif - -#ifndef rcu_dereference -#define rcu_dereference(p) ({ \ - typeof(p) _________p1 = ACCESS_ONCE(p); \ - smp_read_barrier_depends(); /* see RCU */ \ - (_________p1); \ - }) -#endif - -#ifndef rcu_assign_pointer -#define rcu_assign_pointer(p, v) \ - ({ \ - if (!__builtin_constant_p(v) || \ - ((v) != NULL)) \ - smp_wmb(); /* see RCU */ \ - (p) = (v); \ - }) -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) -#define f_vfsmnt f_path.mnt -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) - -/** - * kzalloc() - Allocate memory. The memory is set to zero. - * - * @size: Size to allocate. - * @flags: GFP flags. - * - * Returns pointer to allocated memory on success, NULL otherwise. - * - * This is for compatibility with older kernels. - * - * Since several distributions backported kzalloc(), I define it as a macro - * rather than an inlined function in order to avoid multiple definition error. - */ -#define kzalloc(size, flags) ({ \ - void *ret = kmalloc((size), (flags)); \ - if (ret) \ - memset(ret, 0, (size)); \ - ret; }) - -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) - -/** - * path_put - Drop reference on "struct path". - * - * @path: Pointer to "struct path". - * - * Returns nothing. - * - * This is for compatibility with older kernels. - */ -static inline void path_put(struct path *path) -{ - dput(path->dentry); - mntput(path->mnt); -} - -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) - -/** - * __list_add_rcu - Insert a new entry between two known consecutive entries. - * - * @new: Pointer to "struct list_head". - * @prev: Pointer to "struct list_head". - * @next: Pointer to "struct list_head". - * - * Returns nothing. - * - * This is for compatibility with older kernels. - */ -static inline void __list_add_rcu(struct list_head *new, - struct list_head *prev, - struct list_head *next) -{ - new->next = next; - new->prev = prev; - rcu_assign_pointer(prev->next, new); - next->prev = new; -} - -/** - * list_add_tail_rcu - Add a new entry to rcu-protected list. - * - * @new: Pointer to "struct list_head". - * @head: Pointer to "struct list_head". - * - * Returns nothing. - * - * This is for compatibility with older kernels. - */ -static inline void list_add_tail_rcu(struct list_head *new, - struct list_head *head) -{ - __list_add_rcu(new, head->prev, head); -} - -/** - * list_add_rcu - Add a new entry to rcu-protected list. - * - * @new: Pointer to "struct list_head". - * @head: Pointer to "struct list_head". - * - * Returns nothing. - * - * This is for compatibility with older kernels. - */ -static inline void list_add_rcu(struct list_head *new, struct list_head *head) -{ - __list_add_rcu(new, head, head->next); -} - -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) - -/** - * __list_del_entry - Deletes entry from list without re-initialization. - * - * @entry: Pointer to "struct list_head". - * - * Returns nothing. - * - * This is for compatibility with older kernels. - */ -static inline void __list_del_entry(struct list_head *entry) -{ - __list_del(entry->prev, entry->next); -} - -#endif - -#ifndef list_for_each_entry_safe - -/** - * list_for_each_entry_safe - Iterate over list of given type safe against removal of list entry. - * - * @pos: The "type *" to use as a loop cursor. - * @n: Another "type *" to use as temporary storage. - * @head: Pointer to "struct list_head". - * @member: The name of the list_struct within the struct. - * - * This is for compatibility with older kernels. - */ -#define list_for_each_entry_safe(pos, n, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, typeof(*n), member)) - -#endif - -#ifndef srcu_dereference - -/** - * srcu_dereference - Fetch SRCU-protected pointer with checking. - * - * @p: The pointer to read, prior to dereferencing. - * @ss: Pointer to "struct srcu_struct". - * - * Returns @p. - * - * This is for compatibility with older kernels. - */ -#define srcu_dereference(p, ss) rcu_dereference(p) - -#endif - -#ifndef list_for_each_entry_srcu - -/** - * list_for_each_entry_srcu - Iterate over rcu list of given type. - * - * @pos: The type * to use as a loop cursor. - * @head: The head for your list. - * @member: The name of the list_struct within the struct. - * @ss: Pointer to "struct srcu_struct". - * - * As of 2.6.36, this macro is not provided because only TOMOYO wants it. - */ -#define list_for_each_entry_srcu(pos, head, member, ss) \ - for (pos = list_entry(srcu_dereference((head)->next, ss), \ - typeof(*pos), member); \ - prefetch(pos->member.next), &pos->member != (head); \ - pos = list_entry(srcu_dereference(pos->member.next, ss), \ - typeof(*pos), member)) - -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 30) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 9)) - -#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. - */ - -/* Clear TOMOYO 1.8 config. */ -#undef CONFIG_CCSECURITY -#undef CONFIG_CCSECURITY_LKM -#undef CONFIG_CCSECURITY_DISABLE_BY_DEFAULT -#undef CONFIG_CCSECURITY_MAX_AUDIT_LOG -#undef CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY -#undef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER -#undef CONFIG_CCSECURITY_POLICY_LOADER -#undef CONFIG_CCSECURITY_ACTIVATION_TRIGGER -#undef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -#undef CONFIG_CCSECURITY_FILE_READDIR -#undef CONFIG_CCSECURITY_FILE_GETATTR -#undef CONFIG_CCSECURITY_NETWORK -#undef CONFIG_CCSECURITY_NETWORK_RECVMSG -#undef CONFIG_CCSECURITY_CAPABILITY -#undef CONFIG_CCSECURITY_IPC -#undef CONFIG_CCSECURITY_MISC -#undef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER -#undef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -#undef CONFIG_CCSECURITY_PORTRESERVE -/* Define AKARI 1.0 config. */ -#include "config.h" -#ifndef CONFIG_CCSECURITY -#define CONFIG_CCSECURITY -#endif -#ifndef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -#define CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -#endif -#ifndef CONFIG_CCSECURITY_MAX_AUDIT_LOG -#define CONFIG_CCSECURITY_MAX_AUDIT_LOG 1024 -#endif -#ifndef CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY -#define CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY 2048 -#endif -#ifndef CONFIG_CCSECURITY_POLICY_LOADER -#define CONFIG_CCSECURITY_POLICY_LOADER "/sbin/ccs-init" -#endif -#ifndef CONFIG_CCSECURITY_ACTIVATION_TRIGGER -#define CONFIG_CCSECURITY_ACTIVATION_TRIGGER "/sbin/init" -#endif -#include "ccsecurity.h" -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) -#error This module supports only 2.6.0 and later kernels. -#endif -#ifndef CONFIG_SECURITY -#error You must choose CONFIG_SECURITY=y for building this module. -#endif -#ifndef CONFIG_KALLSYMS -#error You must choose CONFIG_KALLSYMS=y for building this module. -#endif -#ifndef CONFIG_PROC_FS -#error You must choose CONFIG_PROC_FS=y for building this module. -#endif -#ifndef CONFIG_MODULES -#error You must choose CONFIG_MODULES=y for building this module. -#endif - -/* 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; - u8 profile; /* Profile number to use. */ - u8 group; /* Group 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 20100903 is defined. */ - unsigned int profile_version; - /* Name of this namespace (e.g. "", "" ). */ - const char *name; -}; - -/* Prototype definition for "struct ccsecurity_operations". */ - -void __init ccs_permission_init(void); -void __init ccs_mm_init(void); - -/* Prototype definition for internal use. */ - -bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos, - struct ccs_page_dump *dump); -bool ccs_memory_ok(const void *ptr, const unsigned int size); -char *ccs_encode(const char *str); -char *ccs_encode2(const char *str, int str_len); -char *ccs_realpath(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 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; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) - const struct task_struct *task; -#else - struct pid *pid; /* Maybe NULL. */ - const struct cred *cred; /* Maybe NULL. */ -#endif - struct ccs_domain_info *ccs_domain_info; - u32 ccs_flags; - struct ccs_execve *ee; /* Maybe NULL. */ - struct rcu_head rcu; -}; - -void __init ccs_main_init(void); -bool ccs_used_by_cred(const struct ccs_domain_info *domain); -int ccs_start_execve(struct linux_binprm *bprm, struct ccs_execve **eep); -void ccs_finish_execve(int retval, struct ccs_execve *ee); -void ccs_load_policy(const char *filename); -#ifndef CONFIG_AKARI_TRACE_EXECVE_COUNT -#define ccs_audit_alloc_execve(ee) do { } while (0) -#define ccs_audit_free_execve(ee, is_current) do { } while (0) -#else -void ccs_audit_alloc_execve(const struct ccs_execve * const ee); -void ccs_audit_free_execve(const struct ccs_execve * const ee, - const bool is_current); -#endif - -#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/akari/load_policy.c b/akari/load_policy.c deleted file mode 100644 index e6a609f..0000000 --- a/akari/load_policy.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * security/ccsecurity/load_policy.c - * - * Copyright (C) 2005-2012 NTT DATA CORPORATION - * - * Version: 1.8.3+ 2012/05/05 - */ - -#include -#include -#include -#include -#include -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -#include -/* - * Regarding 2.4 kernels, we need to define __KERNEL_SYSCALLS__ in order to use - * waitpid() because call_usermodehelper() does not support UMH_WAIT_PROC. - */ -#define __KERNEL_SYSCALLS__ -#include -#else -#include -#include -#endif -#ifndef LOOKUP_POSITIVE -#define LOOKUP_POSITIVE 0 -#endif - -/* - * TOMOYO specific part start. - */ - -#include "internal.h" - -#if 0 -/** - * 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); -#endif - -#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER - -/* Path to the policy loader. (default = CONFIG_CCSECURITY_POLICY_LOADER) */ -static const char *ccs_loader; - -#if 0 -/** - * 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); -#endif - -/** - * 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; - -#if 0 -/** - * 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); -#endif - -/** - * 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. - */ -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 0 -#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(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); -#endif diff --git a/akari/lsm.c b/akari/lsm.c deleted file mode 100644 index bce04b6..0000000 --- a/akari/lsm.c +++ /dev/null @@ -1,2766 +0,0 @@ -/* - * lsm.c - * - * Copyright (C) 2010-2013 Tetsuo Handa - * - * Version: 1.0.30 2013/02/14 - */ - -#include "internal.h" -#include "probe.h" -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) -#define USE_UMODE_T -#else -#include "check_umode_t.h" -#endif - -/* Prototype definition. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) -static void ccs_task_security_gc(void); -static int ccs_copy_cred_security(const struct cred *new, - const struct cred *old, gfp_t gfp); -static struct ccs_security *ccs_find_cred_security(const struct cred *cred); -static DEFINE_SPINLOCK(ccs_task_security_list_lock); -static atomic_t ccs_in_execve_tasks = ATOMIC_INIT(0); -/* - * List of "struct ccs_security" for "struct pid". - * - * All instances on this list is guaranteed that "struct ccs_security"->pid != - * NULL. Also, instances on this list that are in execve() are guaranteed that - * "struct ccs_security"->cred remembers "struct linux_binprm"->cred with a - * refcount on "struct linux_binprm"->cred. - */ -struct list_head ccs_task_security_list[CCS_MAX_TASK_SECURITY_HASH]; -/* - * List of "struct ccs_security" for "struct cred". - * - * Since the number of "struct cred" is nearly equals to the number of - * "struct pid", we allocate hash tables like ccs_task_security_list. - * - * All instances on this list are guaranteed that "struct ccs_security"->pid == - * NULL and "struct ccs_security"->cred != NULL. - */ -static struct list_head ccs_cred_security_list[CCS_MAX_TASK_SECURITY_HASH]; - -/* 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 -}; -#else -/* Dummy security context for avoiding NULL pointer dereference. */ -extern struct ccs_security ccs_oom_security; -/* Dummy security context for avoiding NULL pointer dereference. */ -extern struct ccs_security ccs_default_security; -/* Dummy marker for calling security_bprm_free(). */ -static const unsigned long ccs_bprm_security; -#endif - -/* For exporting variables and functions. */ -struct ccsecurity_exports ccsecurity_exports; -/* Members are updated by loadable kernel module. */ -struct ccsecurity_operations ccsecurity_ops; - -/* Function pointers originally registered by register_security(). */ -static struct security_operations original_security_ops /* = *security_ops; */; - -/* - * AppArmor patch added "struct vfsmount *" to security_inode_\*() hooks. - * Detect it by checking whether D_PATH_DISCONNECT is defined or not. - * Also, there may be other kernels with "struct vfsmount *" added. - * If you got build failure, check security_inode_\*() hooks in - * include/linux/security.h. - */ -#if defined(D_PATH_DISCONNECT) -#define CCS_INODE_HOOK_HAS_MNT -#elif defined(CONFIG_SUSE_KERNEL) && LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 25) -#define CCS_INODE_HOOK_HAS_MNT -#elif defined(CONFIG_SECURITY_APPARMOR) && LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 24) -#define CCS_INODE_HOOK_HAS_MNT -#endif - -#ifdef CONFIG_AKARI_TRACE_EXECVE_COUNT - -/** - * ccs_update_ee_counter - Update "struct ccs_execve" counter. - * - * @count: Count to increment or decrement. - * - * Returns updated counter. - */ -static unsigned int ccs_update_ee_counter(int count) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) || defined(atomic_add_return) - /* Debug counter for detecting "struct ccs_execve" memory leak. */ - static atomic_t ccs_ee_counter = ATOMIC_INIT(0); - return atomic_add_return(count, &ccs_ee_counter); -#else - static DEFINE_SPINLOCK(ccs_ee_lock); - static unsigned int ccs_ee_counter; - unsigned long flags; - spin_lock_irqsave(&ccs_ee_lock, flags); - ccs_ee_counter += count; - count = ccs_ee_counter; - spin_unlock_irqrestore(&ccs_ee_lock, flags); - return count; -#endif -} - -/** - * ccs_audit_alloc_execve - Audit allocation of "struct ccs_execve". - * - * @ee: Pointer to "struct ccs_execve". - * - * Returns nothing. - */ -void ccs_audit_alloc_execve(const struct ccs_execve * const ee) -{ - printk(KERN_INFO "AKARI: Allocated %p by pid=%u (count=%u)\n", ee, - current->pid, ccs_update_ee_counter(1) - 1); -} - -/** - * ccs_audit_free_execve - Audit release of "struct ccs_execve". - * - * @ee: Pointer to "struct ccs_execve". - * @task: True if released by current task, false otherwise. - * - * Returns nothing. - */ -void ccs_audit_free_execve(const struct ccs_execve * const ee, - const bool is_current) -{ - const unsigned int tmp = ccs_update_ee_counter(-1); - if (is_current) - printk(KERN_INFO "AKARI: Releasing %p by pid=%u (count=%u)\n", - ee, current->pid, tmp); - else - printk(KERN_INFO "AKARI: Releasing %p by kernel (count=%u)\n", - ee, tmp); -} - -#endif - -#if !defined(CONFIG_AKARI_DEBUG) -#define ccs_debug_trace(pos) do { } while (0) -#else -#define ccs_debug_trace(pos) \ - do { \ - static bool done; \ - if (!done) { \ - printk(KERN_INFO \ - "AKARI: Debug trace: " pos " of 4\n"); \ - done = true; \ - } \ - } while (0) -#endif - -/** - * ccs_clear_execve - Release memory used by do_execve(). - * - * @ret: 0 if do_execve() succeeded, negative value otherwise. - * @security: Pointer to "struct ccs_security". - * - * Returns nothing. - */ -static void ccs_clear_execve(int ret, struct ccs_security *security) -{ - struct ccs_execve *ee; - if (security == &ccs_default_security || security == &ccs_oom_security) - return; - ee = security->ee; - security->ee = NULL; - if (!ee) - return; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) - /* - * Drop refcount on "struct cred" in "struct linux_binprm" and forget - * it. - */ - put_cred(security->cred); - security->cred = NULL; -#endif - atomic_dec(&ccs_in_execve_tasks); -#endif - ccs_finish_execve(ret, ee); -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) - -/** - * 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); - struct ccs_execve *ee = ptr->ee; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) - /* - * If this security context was associated with "struct pid" and - * ptr->ccs_flags has CCS_TASK_IS_IN_EXECVE set, it indicates that a - * "struct task_struct" associated with this security context exited - * immediately after do_execve() has failed. - */ - if (ptr->pid && (ptr->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { - ccs_debug_trace("1"); - atomic_dec(&ccs_in_execve_tasks); - } -#else - /* - * If this security context was associated with "struct pid" and - * remembers "struct cred" in "struct linux_binprm", it indicates that - * a "struct task_struct" associated with this security context exited - * immediately after do_execve() has failed. - */ - if (ptr->pid && ptr->cred) { - ccs_debug_trace("1"); - put_cred(ptr->cred); - atomic_dec(&ccs_in_execve_tasks); - } -#endif - /* - * If this security context was associated with "struct pid", - * drop refcount obtained by get_pid() in ccs_find_task_security(). - */ - if (ptr->pid) { - ccs_debug_trace("2"); - put_pid(ptr->pid); - } - if (ee) { - ccs_debug_trace("3"); - ccs_audit_free_execve(ee, false); - kfree(ee->handler_path); - kfree(ee); - } - kfree(ptr); -} - -/** - * ccs_del_security - Release "struct ccs_security". - * - * @ptr: Pointer to "struct ccs_security". - * - * Returns nothing. - */ -static void ccs_del_security(struct ccs_security *ptr) -{ - unsigned long flags; - 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); - call_rcu(&ptr->rcu, ccs_rcu_free); -} - -/** - * ccs_add_cred_security - Add "struct ccs_security" to list. - * - * @ptr: Pointer to "struct ccs_security". - * - * Returns nothing. - */ -static void ccs_add_cred_security(struct ccs_security *ptr) -{ - unsigned long flags; - struct list_head *list = &ccs_cred_security_list - [hash_ptr((void *) ptr->cred, CCS_TASK_SECURITY_HASH_BITS)]; -#ifdef CONFIG_AKARI_DEBUG - if (ptr->pid) - printk(KERN_INFO "AKARI: \"struct ccs_security\"->pid != NULL" - "\n"); -#endif - ptr->pid = NULL; - 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_task_create - Make snapshot of security context for new task. - * - * @clone_flags: Flags passed to clone(). - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_task_create(unsigned long clone_flags) -{ - int rc; - struct ccs_security *old_security; - struct ccs_security *new_security; - struct cred *cred = prepare_creds(); - if (!cred) - return -ENOMEM; - while (!original_security_ops.task_create); - rc = original_security_ops.task_create(clone_flags); - if (rc) { - abort_creds(cred); - return rc; - } - old_security = ccs_find_task_security(current); - new_security = ccs_find_cred_security(cred); - new_security->ccs_domain_info = old_security->ccs_domain_info; - new_security->ccs_flags = old_security->ccs_flags; - return commit_creds(cred); -} - -/** - * ccs_cred_prepare - Allocate memory for new credentials. - * - * @new: Pointer to "struct cred". - * @old: Pointer to "struct cred". - * @gfp: Memory allocation flags. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_cred_prepare(struct cred *new, const struct cred *old, - gfp_t gfp) -{ - int rc; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) - /* - * For checking whether reverting domain transition is needed or not. - * - * See ccs_find_task_security() for reason. - */ - if (gfp == GFP_KERNEL) - ccs_find_task_security(current); -#endif - rc = ccs_copy_cred_security(new, old, gfp); - if (rc) - return rc; - if (gfp == GFP_KERNEL) - ccs_task_security_gc(); - while (!original_security_ops.cred_prepare); - rc = original_security_ops.cred_prepare(new, old, gfp); - if (rc) - ccs_del_security(ccs_find_cred_security(new)); - return rc; -} - -/** - * ccs_cred_free - Release memory used by credentials. - * - * @cred: Pointer to "struct cred". - * - * Returns nothing. - */ -static void ccs_cred_free(struct cred *cred) -{ - while (!original_security_ops.cred_free); - original_security_ops.cred_free(cred); - ccs_del_security(ccs_find_cred_security(cred)); -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) - -/** - * ccs_alloc_cred_security - Allocate memory for new credentials. - * - * @cred: Pointer to "struct cred". - * @gfp: Memory allocation flags. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_alloc_cred_security(const struct cred *cred, gfp_t gfp) -{ - struct ccs_security *new_security = kzalloc(sizeof(*new_security), - gfp); - if (!new_security) - return -ENOMEM; - new_security->cred = cred; - ccs_add_cred_security(new_security); - return 0; -} - -/** - * ccs_cred_alloc_blank - Allocate memory for new credentials. - * - * @new: Pointer to "struct cred". - * @gfp: Memory allocation flags. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_cred_alloc_blank(struct cred *new, gfp_t gfp) -{ - int rc = ccs_alloc_cred_security(new, gfp); - if (rc) - return rc; - while (!original_security_ops.cred_alloc_blank); - rc = original_security_ops.cred_alloc_blank(new, gfp); - if (rc) - ccs_del_security(ccs_find_cred_security(new)); - return rc; -} - -/** - * ccs_cred_transfer - Transfer "struct ccs_security" between credentials. - * - * @new: Pointer to "struct cred". - * @old: Pointer to "struct cred". - * - * Returns nothing. - */ -static void ccs_cred_transfer(struct cred *new, const struct cred *old) -{ - struct ccs_security *new_security; - struct ccs_security *old_security; - while (!original_security_ops.cred_transfer); - original_security_ops.cred_transfer(new, old); - new_security = ccs_find_cred_security(new); - old_security = ccs_find_cred_security(old); - if (new_security == &ccs_default_security || - new_security == &ccs_oom_security || - old_security == &ccs_default_security || - old_security == &ccs_oom_security) - return; - new_security->ccs_flags = old_security->ccs_flags; - new_security->ccs_domain_info = old_security->ccs_domain_info; -} - -#endif - -#else - -/** - * ccs_task_alloc_security - Allocate memory for new tasks. - * - * @p: Pointer to "struct task_struct". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_task_alloc_security(struct task_struct *p) -{ - int rc = ccs_alloc_task_security(p); - if (rc) - return rc; - while (!original_security_ops.task_alloc_security); - rc = original_security_ops.task_alloc_security(p); - if (rc) - ccs_free_task_security(p); - return rc; -} - -/** - * ccs_task_free_security - Release memory for "struct task_struct". - * - * @p: Pointer to "struct task_struct". - * - * Returns nothing. - */ -static void ccs_task_free_security(struct task_struct *p) -{ - while (!original_security_ops.task_free_security); - original_security_ops.task_free_security(p); - ccs_free_task_security(p); -} - -/** - * ccs_bprm_alloc_security - Allocate memory for "struct linux_binprm". - * - * @bprm: Pointer to "struct linux_binprm". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_bprm_alloc_security(struct linux_binprm *bprm) -{ - int rc; - while (!original_security_ops.bprm_alloc_security); - rc = original_security_ops.bprm_alloc_security(bprm); - if (bprm->security || rc) - return rc; - /* - * Update bprm->security to &ccs_bprm_security so that - * security_bprm_free() is called even if do_execve() failed at - * search_binary_handler() without allocating memory at - * security_bprm_alloc(). This trick assumes that active LSM module - * does not access bprm->security if that module did not allocate - * memory at security_bprm_alloc(). - */ - bprm->security = (void *) &ccs_bprm_security; - return 0; -} - -/** - * ccs_bprm_free_security - Release memory for "struct linux_binprm". - * - * @bprm: Pointer to "struct linux_binprm". - * - * Returns nothing. - */ -static void ccs_bprm_free_security(struct linux_binprm *bprm) -{ - /* - * If do_execve() succeeded, bprm->security will be updated to NULL at - * security_bprm_compute_creds()/security_bprm_apply_creds() if - * bprm->security was set to &ccs_bprm_security at - * security_bprm_alloc(). - * - * If do_execve() failed, bprm->security remains at &ccs_bprm_security - * if bprm->security was set to &ccs_bprm_security at - * security_bprm_alloc(). - * - * And do_execve() does not call security_bprm_free() if do_execve() - * failed and bprm->security == NULL. Therefore, do not call - * original_security_ops.bprm_free_security() if bprm->security remains - * at &ccs_bprm_security . - */ - if (bprm->security != &ccs_bprm_security) { - while (!original_security_ops.bprm_free_security); - original_security_ops.bprm_free_security(bprm); - } - /* - * If do_execve() succeeded, - * ccs_clear_execve(0, ccs_current_security()); - * is called before calling below one. - * Thus, below call becomes no-op if do_execve() succeeded. - */ - ccs_clear_execve(-1, ccs_current_security()); -} - -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 6) - -/** - * ccs_bprm_compute_creds - A hook which is called when do_execve() succeeded. - * - * @bprm: Pointer to "struct linux_binprm". - * - * Returns nothing. - */ -static void ccs_bprm_compute_creds(struct linux_binprm *bprm) -{ - if (bprm->security == &ccs_bprm_security) - bprm->security = NULL; - while (!original_security_ops.bprm_compute_creds); - original_security_ops.bprm_compute_creds(bprm); - ccs_clear_execve(0, ccs_current_security()); -} - -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) - -/** - * ccs_bprm_apply_creds - A hook which is called when do_execve() succeeded. - * - * @bprm: Pointer to "struct linux_binprm". - * @unsafe: Unsafe flag. - * - * Returns nothing. - */ -static void ccs_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) -{ - if (bprm->security == &ccs_bprm_security) - bprm->security = NULL; - while (!original_security_ops.bprm_apply_creds); - original_security_ops.bprm_apply_creds(bprm, unsafe); - ccs_clear_execve(0, ccs_current_security()); -} - -#else - -/** - * ccs_bprm_committing_creds - A hook which is called when do_execve() succeeded. - * - * @bprm: Pointer to "struct linux_binprm". - * - * Returns nothing. - */ -static void ccs_bprm_committing_creds(struct linux_binprm *bprm) -{ - struct ccs_security *old_security; - struct ccs_security *new_security; - while (!original_security_ops.bprm_committing_creds); - original_security_ops.bprm_committing_creds(bprm); - old_security = ccs_current_security(); - if (old_security == &ccs_default_security || - old_security == &ccs_oom_security) - return; - ccs_clear_execve(0, old_security); - /* Update current task's cred's domain for future fork(). */ - new_security = ccs_find_cred_security(bprm->cred); - new_security->ccs_flags = old_security->ccs_flags; - new_security->ccs_domain_info = old_security->ccs_domain_info; -} - -#endif - -/** - * ccs_bprm_check_security - Check permission for execve(). - * - * @bprm: Pointer to "struct linux_binprm". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_bprm_check_security(struct linux_binprm *bprm) -{ - struct ccs_security *security = ccs_current_security(); - if (security == &ccs_default_security || security == &ccs_oom_security) - return -ENOMEM; - if (!security->ee) { - int rc; -#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER - if (!ccs_policy_loaded) - ccs_load_policy(bprm->filename); -#endif - rc = ccs_start_execve(bprm, &security->ee); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) - if (security->ee) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) - /* - * Get refcount on "struct cred" in - * "struct linux_binprm" and remember it. - */ - get_cred(bprm->cred); - security->cred = bprm->cred; -#endif - atomic_inc(&ccs_in_execve_tasks); - } -#endif - if (rc) - return rc; - } - while (!original_security_ops.bprm_check_security); - return original_security_ops.bprm_check_security(bprm); -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) - -/** - * ccs_open - Check permission for open(). - * - * @f: Pointer to "struct file". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_open(struct file *f) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) - return ccs_open_permission(f); -#elif defined(RHEL_MAJOR) && RHEL_MAJOR == 6 - return ccs_open_permission(f->f_path.dentry, f->f_path.mnt, - f->f_flags); -#else - return ccs_open_permission(f->f_path.dentry, f->f_path.mnt, - f->f_flags + 1); -#endif -} - -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) - -/** - * ccs_file_open - Check permission for open(). - * - * @f: Pointer to "struct file". - * @cred: Pointer to "struct cred". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_file_open(struct file *f, const struct cred *cred) -{ - int rc = ccs_open(f); - if (rc) - return rc; - while (!original_security_ops.file_open); - return original_security_ops.file_open(f, cred); -} - -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) - -/** - * ccs_dentry_open - Check permission for open(). - * - * @f: Pointer to "struct file". - * @cred: Pointer to "struct cred". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_dentry_open(struct file *f, const struct cred *cred) -{ - int rc = ccs_open(f); - if (rc) - return rc; - while (!original_security_ops.dentry_open); - return original_security_ops.dentry_open(f, cred); -} - -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) - -/** - * ccs_dentry_open - Check permission for open(). - * - * @f: Pointer to "struct file". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_dentry_open(struct file *f) -{ - int rc = ccs_open(f); - if (rc) - return rc; - while (!original_security_ops.dentry_open); - return original_security_ops.dentry_open(f); -} - -#else - -/** - * ccs_open - Check permission for open(). - * - * @inode: Pointer to "struct inode". - * @mask: Open mode. - * @nd: Pointer to "struct nameidata". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_open(struct inode *inode, int mask, struct nameidata *nd) -{ - int flags; - if (!nd || !nd->dentry) - return 0; - /* open_exec() passes MAY_EXEC . */ - if (mask == MAY_EXEC && inode && S_ISREG(inode->i_mode) && - (ccs_current_flags() & CCS_TASK_IS_IN_EXECVE)) - mask = MAY_READ; - /* - * This flags value is passed to ACC_MODE(). - * ccs_open_permission() for older versions uses old ACC_MODE(). - */ - switch (mask & (MAY_READ | MAY_WRITE)) { - case MAY_READ: - flags = 01; - break; - case MAY_WRITE: - flags = 02; - break; - case MAY_READ | MAY_WRITE: - flags = 03; - break; - default: - return 0; - } - return ccs_open_permission(nd->dentry, nd->mnt, flags); -} - -/** - * ccs_inode_permission - Check permission for open(). - * - * @inode: Pointer to "struct inode". - * @mask: Open mode. - * @nd: Pointer to "struct nameidata". - * - * Returns 0 on success, negative value otherwise. - * - * Note that this hook is called from permission(), and may not be called for - * open(). Maybe it is better to use security_file_permission(). - */ -static int ccs_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) -{ - int rc = ccs_open(inode, mask, nd); - if (rc) - return rc; - while (!original_security_ops.inode_permission); - return original_security_ops.inode_permission(inode, mask, nd); -} - -#endif - -#if defined(CONFIG_SECURITY_PATH) - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) - -/** - * ccs_path_chown - Check permission for chown()/chgrp(). - * - * @path: Pointer to "struct path". - * @user: User ID. - * @group: Group ID. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_chown(struct path *path, kuid_t user, kgid_t group) -{ - int rc = ccs_chown_permission(path->dentry, path->mnt, user, group); - if (rc) - return rc; - while (!original_security_ops.path_chown); - return original_security_ops.path_chown(path, user, group); -} - -/** - * ccs_path_chmod - Check permission for chmod(). - * - * @path: Pointer to "struct path". - * @mode: Mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_chmod(struct path *path, umode_t mode) -{ - int rc = ccs_chmod_permission(path->dentry, path->mnt, mode); - if (rc) - return rc; - while (!original_security_ops.path_chmod); - return original_security_ops.path_chmod(path, mode); -} - -/** - * ccs_path_chroot - Check permission for chroot(). - * - * @path: Pointer to "struct path". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_chroot(struct path *path) -{ - int rc = ccs_chroot_permission(path); - if (rc) - return rc; - while (!original_security_ops.path_chroot); - return original_security_ops.path_chroot(path); -} - -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) - -/** - * ccs_path_chown - Check permission for chown()/chgrp(). - * - * @path: Pointer to "struct path". - * @user: User ID. - * @group: Group ID. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_chown(struct path *path, uid_t user, gid_t group) -{ - int rc = ccs_chown_permission(path->dentry, path->mnt, user, group); - if (rc) - return rc; - while (!original_security_ops.path_chown); - return original_security_ops.path_chown(path, user, group); -} - -#if defined(USE_UMODE_T) - -/** - * ccs_path_chmod - Check permission for chmod(). - * - * @path: Pointer to "struct path". - * @mode: Mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_chmod(struct path *path, umode_t mode) -{ - int rc = ccs_chmod_permission(path->dentry, path->mnt, mode); - if (rc) - return rc; - while (!original_security_ops.path_chmod); - return original_security_ops.path_chmod(path, mode); -} - -#else - -/** - * ccs_path_chmod - Check permission for chmod(). - * - * @dentry: Pointer to "struct dentry". - * @vfsmnt: Pointer to "struct vfsmount". - * @mode: Mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_chmod(struct dentry *dentry, struct vfsmount *vfsmnt, - mode_t mode) -{ - int rc = ccs_chmod_permission(dentry, vfsmnt, mode); - if (rc) - return rc; - while (!original_security_ops.path_chmod); - return original_security_ops.path_chmod(dentry, vfsmnt, mode); -} - -#endif - -/** - * ccs_path_chroot - Check permission for chroot(). - * - * @path: Pointer to "struct path". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_chroot(struct path *path) -{ - int rc = ccs_chroot_permission(path); - if (rc) - return rc; - while (!original_security_ops.path_chroot); - return original_security_ops.path_chroot(path); -} - -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) - -/** - * ccs_path_truncate - Check permission for truncate(). - * - * @path: Pointer to "struct path". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_truncate(struct path *path) -{ - int rc = ccs_truncate_permission(path->dentry, path->mnt); - if (rc) - return rc; - while (!original_security_ops.path_truncate); - return original_security_ops.path_truncate(path); -} - -#else - -/** - * ccs_path_truncate - Check permission for truncate(). - * - * @path: Pointer to "struct path". - * @length: New length. - * @time_attrs: New time attributes. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_truncate(struct path *path, loff_t length, - unsigned int time_attrs) -{ - int rc = ccs_truncate_permission(path->dentry, path->mnt); - if (rc) - return rc; - while (!original_security_ops.path_truncate); - return original_security_ops.path_truncate(path, length, time_attrs); -} - -#endif - -#endif - -#ifdef CCS_INODE_HOOK_HAS_MNT - -/** - * ccs_inode_setattr - Check permission for chown()/chgrp()/chmod()/truncate(). - * - * @dentry: Pointer to "struct dentry". - * @mnt: Pointer to "struct vfsmount". Maybe NULL. - * @attr: Pointer to "struct iattr". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_setattr(struct dentry *dentry, struct vfsmount *mnt, - struct iattr *attr) -{ - int rc = 0; -#if !defined(CONFIG_SECURITY_PATH) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) - if (attr->ia_valid & ATTR_UID) - rc = ccs_chown_permission(dentry, mnt, attr->ia_uid, -1); - if (!rc && (attr->ia_valid & ATTR_GID)) - rc = ccs_chown_permission(dentry, mnt, -1, attr->ia_gid); - if (!rc && (attr->ia_valid & ATTR_MODE)) - rc = ccs_chmod_permission(dentry, mnt, attr->ia_mode); -#endif -#if !defined(CONFIG_SECURITY_PATH) - if (!rc && (attr->ia_valid & ATTR_SIZE)) - rc = ccs_truncate_permission(dentry, mnt); -#endif - if (rc) - return rc; - while (!original_security_ops.inode_setattr); - return original_security_ops.inode_setattr(dentry, mnt, attr); -} - -#else - -/** - * ccs_inode_setattr - Check permission for chown()/chgrp()/chmod()/truncate(). - * - * @dentry: Pointer to "struct dentry". - * @attr: Pointer to "struct iattr". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_setattr(struct dentry *dentry, struct iattr *attr) -{ - int rc = 0; -#if !defined(CONFIG_SECURITY_PATH) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) - if (attr->ia_valid & ATTR_UID) - rc = ccs_chown_permission(dentry, NULL, attr->ia_uid, - INVALID_GID); - if (!rc && (attr->ia_valid & ATTR_GID)) - rc = ccs_chown_permission(dentry, NULL, INVALID_UID, - attr->ia_gid); -#else - if (attr->ia_valid & ATTR_UID) - rc = ccs_chown_permission(dentry, NULL, attr->ia_uid, -1); - if (!rc && (attr->ia_valid & ATTR_GID)) - rc = ccs_chown_permission(dentry, NULL, -1, attr->ia_gid); -#endif - if (!rc && (attr->ia_valid & ATTR_MODE)) - rc = ccs_chmod_permission(dentry, NULL, attr->ia_mode); -#endif -#if !defined(CONFIG_SECURITY_PATH) - if (!rc && (attr->ia_valid & ATTR_SIZE)) - rc = ccs_truncate_permission(dentry, NULL); -#endif - if (rc) - return rc; - while (!original_security_ops.inode_setattr); - return original_security_ops.inode_setattr(dentry, attr); -} - -#endif - -/** - * ccs_inode_getattr - Check permission for stat(). - * - * @mnt: Pointer to "struct vfsmount". - * @dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) -{ - int rc = ccs_getattr_permission(mnt, dentry); - if (rc) - return rc; - while (!original_security_ops.inode_getattr); - return original_security_ops.inode_getattr(mnt, dentry); -} - -#if defined(CONFIG_SECURITY_PATH) - -#if defined(USE_UMODE_T) - -/** - * ccs_path_mknod - Check permission for mknod(). - * - * @dir: Pointer to "struct path". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * @dev: Device major/minor number. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_mknod(struct path *dir, struct dentry *dentry, - umode_t mode, unsigned int dev) -{ - int rc = ccs_mknod_permission(dentry, dir->mnt, mode, dev); - if (rc) - return rc; - while (!original_security_ops.path_mknod); - return original_security_ops.path_mknod(dir, dentry, mode, dev); -} - -/** - * ccs_path_mkdir - Check permission for mkdir(). - * - * @dir: Pointer to "struct path". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_mkdir(struct path *dir, struct dentry *dentry, - umode_t mode) -{ - int rc = ccs_mkdir_permission(dentry, dir->mnt, mode); - if (rc) - return rc; - while (!original_security_ops.path_mkdir); - return original_security_ops.path_mkdir(dir, dentry, mode); -} - -#else - -/** - * ccs_path_mknod - Check permission for mknod(). - * - * @dir: Pointer to "struct path". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * @dev: Device major/minor number. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_mknod(struct path *dir, struct dentry *dentry, int mode, - unsigned int dev) -{ - int rc = ccs_mknod_permission(dentry, dir->mnt, mode, dev); - if (rc) - return rc; - while (!original_security_ops.path_mknod); - return original_security_ops.path_mknod(dir, dentry, mode, dev); -} - -/** - * ccs_path_mkdir - Check permission for mkdir(). - * - * @dir: Pointer to "struct path". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_mkdir(struct path *dir, struct dentry *dentry, int mode) -{ - int rc = ccs_mkdir_permission(dentry, dir->mnt, mode); - if (rc) - return rc; - while (!original_security_ops.path_mkdir); - return original_security_ops.path_mkdir(dir, dentry, mode); -} - -#endif - -/** - * ccs_path_rmdir - Check permission for rmdir(). - * - * @dir: Pointer to "struct path". - * @dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_rmdir(struct path *dir, struct dentry *dentry) -{ - int rc = ccs_rmdir_permission(dentry, dir->mnt); - if (rc) - return rc; - while (!original_security_ops.path_rmdir); - return original_security_ops.path_rmdir(dir, dentry); -} - -/** - * ccs_path_unlink - Check permission for unlink(). - * - * @dir: Pointer to "struct path". - * @dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_unlink(struct path *dir, struct dentry *dentry) -{ - int rc = ccs_unlink_permission(dentry, dir->mnt); - if (rc) - return rc; - while (!original_security_ops.path_unlink); - return original_security_ops.path_unlink(dir, dentry); -} - -/** - * ccs_path_symlink - Check permission for symlink(). - * - * @dir: Pointer to "struct path". - * @dentry: Pointer to "struct dentry". - * @old_name: Content of symbolic link. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_symlink(struct path *dir, struct dentry *dentry, - const char *old_name) -{ - int rc = ccs_symlink_permission(dentry, dir->mnt, old_name); - if (rc) - return rc; - while (!original_security_ops.path_symlink); - return original_security_ops.path_symlink(dir, dentry, old_name); -} - -/** - * ccs_path_rename - Check permission for rename(). - * - * @old_dir: Pointer to "struct path". - * @old_dentry: Pointer to "struct dentry". - * @new_dir: Pointer to "struct path". - * @new_dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_rename(struct path *old_dir, struct dentry *old_dentry, - struct path *new_dir, struct dentry *new_dentry) -{ - int rc = ccs_rename_permission(old_dentry, new_dentry, old_dir->mnt); - if (rc) - return rc; - while (!original_security_ops.path_rename); - return original_security_ops.path_rename(old_dir, old_dentry, new_dir, - new_dentry); -} - -/** - * ccs_path_link - Check permission for link(). - * - * @old_dentry: Pointer to "struct dentry". - * @new_dir: Pointer to "struct path". - * @new_dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_link(struct dentry *old_dentry, struct path *new_dir, - struct dentry *new_dentry) -{ - int rc = ccs_link_permission(old_dentry, new_dentry, new_dir->mnt); - if (rc) - return rc; - while (!original_security_ops.path_link); - return original_security_ops.path_link(old_dentry, new_dir, - new_dentry); -} - -#elif defined(CCS_INODE_HOOK_HAS_MNT) - -/** - * ccs_inode_mknod - Check permission for mknod(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @mnt: Pointer to "struct vfsmount". Maybe NULL. - * @mode: Create mode. - * @dev: Device major/minor number. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_mknod(struct inode *dir, struct dentry *dentry, - struct vfsmount *mnt, int mode, dev_t dev) -{ - int rc = ccs_mknod_permission(dentry, mnt, mode, dev); - if (rc) - return rc; - while (!original_security_ops.inode_mknod); - return original_security_ops.inode_mknod(dir, dentry, mnt, mode, dev); -} - -/** - * ccs_inode_mkdir - Check permission for mkdir(). - * - * @dir: Pointer to "struct inode". - * @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_inode_mkdir(struct inode *dir, struct dentry *dentry, - struct vfsmount *mnt, int mode) -{ - int rc = ccs_mkdir_permission(dentry, mnt, mode); - if (rc) - return rc; - while (!original_security_ops.inode_mkdir); - return original_security_ops.inode_mkdir(dir, dentry, mnt, mode); -} - -/** - * ccs_inode_rmdir - Check permission for rmdir(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @mnt: Pointer to "struct vfsmount". Maybe NULL. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_rmdir(struct inode *dir, struct dentry *dentry, - struct vfsmount *mnt) -{ - int rc = ccs_rmdir_permission(dentry, mnt); - if (rc) - return rc; - while (!original_security_ops.inode_rmdir); - return original_security_ops.inode_rmdir(dir, dentry, mnt); -} - -/** - * ccs_inode_unlink - Check permission for unlink(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @mnt: Pointer to "struct vfsmount". Maybe NULL. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_unlink(struct inode *dir, struct dentry *dentry, - struct vfsmount *mnt) -{ - int rc = ccs_unlink_permission(dentry, mnt); - if (rc) - return rc; - while (!original_security_ops.inode_unlink); - return original_security_ops.inode_unlink(dir, dentry, mnt); -} - -/** - * ccs_inode_symlink - Check permission for symlink(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @mnt: Pointer to "struct vfsmount". Maybe NULL. - * @old_name: Content of symbolic link. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_symlink(struct inode *dir, struct dentry *dentry, - struct vfsmount *mnt, const char *old_name) -{ - int rc = ccs_symlink_permission(dentry, mnt, old_name); - if (rc) - return rc; - while (!original_security_ops.inode_symlink); - return original_security_ops.inode_symlink(dir, dentry, mnt, old_name); -} - -/** - * ccs_inode_rename - Check permission for rename(). - * - * @old_dir: Pointer to "struct inode". - * @old_dentry: Pointer to "struct dentry". - * @old_mnt: Pointer to "struct vfsmount". Maybe NULL. - * @new_dir: Pointer to "struct inode". - * @new_dentry: Pointer to "struct dentry". - * @new_mnt: Pointer to "struct vfsmount". Maybe NULL. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_rename(struct inode *old_dir, struct dentry *old_dentry, - struct vfsmount *old_mnt, struct inode *new_dir, - struct dentry *new_dentry, - struct vfsmount *new_mnt) -{ - int rc = ccs_rename_permission(old_dentry, new_dentry, new_mnt); - if (rc) - return rc; - while (!original_security_ops.inode_rename); - return original_security_ops.inode_rename(old_dir, old_dentry, old_mnt, - new_dir, new_dentry, - new_mnt); -} - -/** - * ccs_inode_link - Check permission for link(). - * - * @old_dentry: Pointer to "struct dentry". - * @old_mnt: Pointer to "struct vfsmount". Maybe NULL. - * @dir: Pointer to "struct inode". - * @new_dentry: Pointer to "struct dentry". - * @new_mnt: Pointer to "struct vfsmount". Maybe NULL. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_link(struct dentry *old_dentry, struct vfsmount *old_mnt, - struct inode *dir, struct dentry *new_dentry, - struct vfsmount *new_mnt) -{ - int rc = ccs_link_permission(old_dentry, new_dentry, new_mnt); - if (rc) - return rc; - while (!original_security_ops.inode_link); - return original_security_ops.inode_link(old_dentry, old_mnt, dir, - new_dentry, new_mnt); -} - -/** - * ccs_inode_create - Check permission for creat(). - * - * @dir: Pointer to "struct inode". - * @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_inode_create(struct inode *dir, struct dentry *dentry, - struct vfsmount *mnt, int mode) -{ - int rc = ccs_mknod_permission(dentry, mnt, mode, 0); - if (rc) - return rc; - while (!original_security_ops.inode_create); - return original_security_ops.inode_create(dir, dentry, mnt, mode); -} - -#else - -#if defined(USE_UMODE_T) - -/** - * ccs_inode_mknod - Check permission for mknod(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * @dev: Device major/minor number. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_mknod(struct inode *dir, struct dentry *dentry, - umode_t mode, dev_t dev) -{ - int rc = ccs_mknod_permission(dentry, NULL, mode, dev); - if (rc) - return rc; - while (!original_security_ops.inode_mknod); - return original_security_ops.inode_mknod(dir, dentry, mode, dev); -} - -/** - * ccs_inode_mkdir - Check permission for mkdir(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_mkdir(struct inode *dir, struct dentry *dentry, - umode_t mode) -{ - int rc = ccs_mkdir_permission(dentry, NULL, mode); - if (rc) - return rc; - while (!original_security_ops.inode_mkdir); - return original_security_ops.inode_mkdir(dir, dentry, mode); -} - -#else - -/** - * ccs_inode_mknod - Check permission for mknod(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * @dev: Device major/minor number. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, - dev_t dev) -{ - int rc = ccs_mknod_permission(dentry, NULL, mode, dev); - if (rc) - return rc; - while (!original_security_ops.inode_mknod); - return original_security_ops.inode_mknod(dir, dentry, mode, dev); -} - -/** - * ccs_inode_mkdir - Check permission for mkdir(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode) -{ - int rc = ccs_mkdir_permission(dentry, NULL, mode); - if (rc) - return rc; - while (!original_security_ops.inode_mkdir); - return original_security_ops.inode_mkdir(dir, dentry, mode); -} - -#endif - -/** - * ccs_inode_rmdir - Check permission for rmdir(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_rmdir(struct inode *dir, struct dentry *dentry) -{ - int rc = ccs_rmdir_permission(dentry, NULL); - if (rc) - return rc; - while (!original_security_ops.inode_rmdir); - return original_security_ops.inode_rmdir(dir, dentry); -} - -/** - * ccs_inode_unlink - Check permission for unlink(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_unlink(struct inode *dir, struct dentry *dentry) -{ - int rc = ccs_unlink_permission(dentry, NULL); - if (rc) - return rc; - while (!original_security_ops.inode_unlink); - return original_security_ops.inode_unlink(dir, dentry); -} - -/** - * ccs_inode_symlink - Check permission for symlink(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @old_name: Content of symbolic link. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_symlink(struct inode *dir, struct dentry *dentry, - const char *old_name) -{ - int rc = ccs_symlink_permission(dentry, NULL, old_name); - if (rc) - return rc; - while (!original_security_ops.inode_symlink); - return original_security_ops.inode_symlink(dir, dentry, old_name); -} - -/** - * ccs_inode_rename - Check permission for rename(). - * - * @old_dir: Pointer to "struct inode". - * @old_dentry: Pointer to "struct dentry". - * @new_dir: Pointer to "struct inode". - * @new_dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - int rc = ccs_rename_permission(old_dentry, new_dentry, NULL); - if (rc) - return rc; - while (!original_security_ops.inode_rename); - return original_security_ops.inode_rename(old_dir, old_dentry, new_dir, - new_dentry); -} - -/** - * ccs_inode_link - Check permission for link(). - * - * @old_dentry: Pointer to "struct dentry". - * @dir: Pointer to "struct inode". - * @new_dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *new_dentry) -{ - int rc = ccs_link_permission(old_dentry, new_dentry, NULL); - if (rc) - return rc; - while (!original_security_ops.inode_link); - return original_security_ops.inode_link(old_dentry, dir, new_dentry); -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) - -/** - * ccs_inode_create - Check permission for creat(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_create(struct inode *dir, struct dentry *dentry, - umode_t mode) -{ - int rc = ccs_mknod_permission(dentry, NULL, mode, 0); - if (rc) - return rc; - while (!original_security_ops.inode_create); - return original_security_ops.inode_create(dir, dentry, mode); -} - -#else - -/** - * ccs_inode_create - Check permission for creat(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_create(struct inode *dir, struct dentry *dentry, - int mode) -{ - int rc = ccs_mknod_permission(dentry, NULL, mode, 0); - if (rc) - return rc; - while (!original_security_ops.inode_create); - return original_security_ops.inode_create(dir, dentry, mode); -} - -#endif - -#endif - -#ifdef CONFIG_SECURITY_NETWORK - -#include - -/* Structure for remembering an accept()ed socket's status. */ -struct ccs_socket_tag { - struct list_head list; - struct inode *inode; - int status; - struct rcu_head rcu; -}; - -/* - * List for managing accept()ed sockets. - * Since we don't need to keep an accept()ed socket into this list after - * once the permission was granted, the number of entries in this list is - * likely small. Therefore, we don't use hash tables. - */ -static LIST_HEAD(ccs_accepted_socket_list); -/* Lock for protecting ccs_accepted_socket_list . */ -static DEFINE_SPINLOCK(ccs_accepted_socket_list_lock); - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) - -/** - * ccs_socket_rcu_free - RCU callback for releasing "struct ccs_socket_tag". - * - * @rcu: Pointer to "struct rcu_head". - * - * Returns nothing. - */ -static void ccs_socket_rcu_free(struct rcu_head *rcu) -{ - struct ccs_socket_tag *ptr = container_of(rcu, typeof(*ptr), rcu); - kfree(ptr); -} - -#else - -/** - * ccs_socket_rcu_free - RCU callback for releasing "struct ccs_socket_tag". - * - * @arg: Pointer to "void". - * - * Returns nothing. - */ -static void ccs_socket_rcu_free(void *arg) -{ - struct ccs_socket_tag *ptr = arg; - kfree(ptr); -} - -#endif - -/** - * ccs_update_socket_tag - Update tag associated with accept()ed sockets. - * - * @inode: Pointer to "struct inode". - * @status: New status. - * - * Returns nothing. - * - * If @status == 0, memory for that socket will be released after RCU grace - * period. - */ -static void ccs_update_socket_tag(struct inode *inode, int status) -{ - struct ccs_socket_tag *ptr; - /* - * Protect whole section because multiple threads may call this - * function with same "sock" via ccs_validate_socket(). - */ - spin_lock(&ccs_accepted_socket_list_lock); - rcu_read_lock(); - list_for_each_entry_rcu(ptr, &ccs_accepted_socket_list, list) { - if (ptr->inode != inode) - continue; - ptr->status = status; - if (status) - break; - list_del_rcu(&ptr->list); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) - call_rcu(&ptr->rcu, ccs_socket_rcu_free); -#else - call_rcu(&ptr->rcu, ccs_socket_rcu_free, ptr); -#endif - break; - } - rcu_read_unlock(); - spin_unlock(&ccs_accepted_socket_list_lock); -} - -/** - * ccs_validate_socket - Check post accept() permission if needed. - * - * @sock: Pointer to "struct socket". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_validate_socket(struct socket *sock) -{ - struct inode *inode = SOCK_INODE(sock); - struct ccs_socket_tag *ptr; - int ret = 0; - rcu_read_lock(); - list_for_each_entry_rcu(ptr, &ccs_accepted_socket_list, list) { - if (ptr->inode != inode) - continue; - ret = ptr->status; - break; - } - rcu_read_unlock(); - if (ret <= 0) - /* - * This socket is not an accept()ed socket or this socket is - * an accept()ed socket and post accept() permission is done. - */ - return ret; - /* - * Check post accept() permission now. - * - * Strictly speaking, we need to pass both listen()ing socket and - * accept()ed socket to __ccs_socket_post_accept_permission(). - * But since socket's family and type are same for both sockets, - * passing the accept()ed socket in place for the listen()ing socket - * will work. - */ - ret = ccs_socket_post_accept_permission(sock, sock); - /* - * If permission was granted, we forget that this is an accept()ed - * socket. Otherwise, we remember that this socket needs to return - * error for subsequent socketcalls. - */ - ccs_update_socket_tag(inode, ret); - return ret; -} - -/** - * ccs_socket_accept - Check permission for accept(). - * - * @sock: Pointer to "struct socket". - * @newsock: Pointer to "struct socket". - * - * Returns 0 on success, negative value otherwise. - * - * This hook is used for setting up environment for doing post accept() - * permission check. If dereferencing sock->ops->something() were ordered by - * rcu_dereference(), we could replace sock->ops with "a copy of original - * sock->ops with modified sock->ops->accept()" using rcu_assign_pointer() - * in order to do post accept() permission check before returning to userspace. - * If we make the copy in security_socket_post_create(), it would be possible - * to safely replace sock->ops here, but we don't do so because we don't want - * to allocate memory for sockets which do not call sock->ops->accept(). - * Therefore, we do post accept() permission check upon next socket syscalls - * rather than between sock->ops->accept() and returning to userspace. - * This means that if a socket was close()d before calling some socket - * syscalls, post accept() permission check will not be done. - */ -static int ccs_socket_accept(struct socket *sock, struct socket *newsock) -{ - struct ccs_socket_tag *ptr; - int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - while (!original_security_ops.socket_accept); - rc = original_security_ops.socket_accept(sock, newsock); - if (rc) { - kfree(ptr); - return rc; - } - /* - * Subsequent LSM hooks will receive "newsock". Therefore, I mark - * "newsock" as "an accept()ed socket but post accept() permission - * check is not done yet" by allocating memory using inode of the - * "newsock" as a search key. - */ - ptr->inode = SOCK_INODE(newsock); - ptr->status = 1; /* Check post accept() permission later. */ - spin_lock(&ccs_accepted_socket_list_lock); - list_add_tail_rcu(&ptr->list, &ccs_accepted_socket_list); - spin_unlock(&ccs_accepted_socket_list_lock); - return 0; -} - -/** - * ccs_socket_listen - Check permission for listen(). - * - * @sock: Pointer to "struct socket". - * @backlog: Backlog parameter. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_listen(struct socket *sock, int backlog) -{ - int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - rc = ccs_socket_listen_permission(sock); - if (rc) - return rc; - while (!original_security_ops.socket_listen); - return original_security_ops.socket_listen(sock, backlog); -} - -/** - * ccs_socket_connect - Check permission for connect(). - * - * @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(struct socket *sock, struct sockaddr *addr, - int addr_len) -{ - int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - rc = ccs_socket_connect_permission(sock, addr, addr_len); - if (rc) - return rc; - while (!original_security_ops.socket_connect); - return original_security_ops.socket_connect(sock, addr, addr_len); -} - -/** - * ccs_socket_bind - Check permission for bind(). - * - * @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(struct socket *sock, struct sockaddr *addr, - int addr_len) -{ - int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - rc = ccs_socket_bind_permission(sock, addr, addr_len); - if (rc) - return rc; - while (!original_security_ops.socket_bind); - return original_security_ops.socket_bind(sock, addr, addr_len); -} - -/** - * ccs_socket_sendmsg - Check permission for sendmsg(). - * - * @sock: Pointer to "struct socket". - * @msg: Pointer to "struct msghdr". - * @size: Size of message. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_sendmsg(struct socket *sock, struct msghdr *msg, - int size) -{ - int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - rc = ccs_socket_sendmsg_permission(sock, msg, size); - if (rc) - return rc; - while (!original_security_ops.socket_sendmsg); - return original_security_ops.socket_sendmsg(sock, msg, size); -} - -/** - * ccs_socket_recvmsg - Check permission for recvmsg(). - * - * @sock: Pointer to "struct socket". - * @msg: Pointer to "struct msghdr". - * @size: Size of message. - * @flags: Flags. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_recvmsg(struct socket *sock, struct msghdr *msg, - int size, int flags) -{ - int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - while (!original_security_ops.socket_recvmsg); - return original_security_ops.socket_recvmsg(sock, msg, size, flags); -} - -/** - * ccs_socket_getsockname - Check permission for getsockname(). - * - * @sock: Pointer to "struct socket". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_getsockname(struct socket *sock) -{ - int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - while (!original_security_ops.socket_getsockname); - return original_security_ops.socket_getsockname(sock); -} - -/** - * ccs_socket_getpeername - Check permission for getpeername(). - * - * @sock: Pointer to "struct socket". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_getpeername(struct socket *sock) -{ - int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - while (!original_security_ops.socket_getpeername); - return original_security_ops.socket_getpeername(sock); -} - -/** - * ccs_socket_getsockopt - Check permission for getsockopt(). - * - * @sock: Pointer to "struct socket". - * @level: Level. - * @optname: Option's name, - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_getsockopt(struct socket *sock, int level, int optname) -{ - int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - while (!original_security_ops.socket_getsockopt); - return original_security_ops.socket_getsockopt(sock, level, optname); -} - -/** - * ccs_socket_setsockopt - Check permission for setsockopt(). - * - * @sock: Pointer to "struct socket". - * @level: Level. - * @optname: Option's name, - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_setsockopt(struct socket *sock, int level, int optname) -{ - int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - while (!original_security_ops.socket_setsockopt); - return original_security_ops.socket_setsockopt(sock, level, optname); -} - -/** - * ccs_socket_shutdown - Check permission for shutdown(). - * - * @sock: Pointer to "struct socket". - * @how: Shutdown mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_shutdown(struct socket *sock, int how) -{ - int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - while (!original_security_ops.socket_shutdown); - return original_security_ops.socket_shutdown(sock, how); -} - -#define SOCKFS_MAGIC 0x534F434B - -/** - * ccs_inode_free_security - Release memory associated with an inode. - * - * @inode: Pointer to "struct inode". - * - * Returns nothing. - * - * We use this hook for releasing memory associated with an accept()ed socket. - */ -static void ccs_inode_free_security(struct inode *inode) -{ - while (!original_security_ops.inode_free_security); - original_security_ops.inode_free_security(inode); - if (inode->i_sb && inode->i_sb->s_magic == SOCKFS_MAGIC) - ccs_update_socket_tag(inode, 0); -} - -#endif - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24) - -/** - * ccs_sb_pivotroot - 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_sb_pivotroot(struct nameidata *old_nd, struct nameidata *new_nd) -{ - int rc = ccs_pivot_root_permission(old_nd, new_nd); - if (rc) - return rc; - while (!original_security_ops.sb_pivotroot); - return original_security_ops.sb_pivotroot(old_nd, new_nd); -} - -/** - * ccs_sb_mount - Check permission for mount(). - * - * @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_sb_mount(char *dev_name, struct nameidata *nd, char *type, - unsigned long flags, void *data_page) -{ - int rc = ccs_mount_permission(dev_name, nd, type, flags, data_page); - if (rc) - return rc; - while (!original_security_ops.sb_mount); - return original_security_ops.sb_mount(dev_name, nd, type, flags, - data_page); -} - -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) - -/** - * ccs_sb_pivotroot - 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_sb_pivotroot(struct nameidata *old_nd, struct nameidata *new_nd) -{ - int rc = ccs_pivot_root_permission(&old_nd->path, &new_nd->path); - if (rc) - return rc; - while (!original_security_ops.sb_pivotroot); - return original_security_ops.sb_pivotroot(old_nd, new_nd); -} - -/** - * ccs_sb_mount - Check permission for mount(). - * - * @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_sb_mount(char *dev_name, struct nameidata *nd, char *type, - unsigned long flags, void *data_page) -{ - int rc = ccs_mount_permission(dev_name, &nd->path, type, flags, - data_page); - if (rc) - return rc; - while (!original_security_ops.sb_mount); - return original_security_ops.sb_mount(dev_name, nd, type, flags, - data_page); -} - -#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) - -/** - * ccs_sb_pivotroot - 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_sb_pivotroot(struct path *old_path, struct path *new_path) -{ - int rc = ccs_pivot_root_permission(old_path, new_path); - if (rc) - return rc; - while (!original_security_ops.sb_pivotroot); - return original_security_ops.sb_pivotroot(old_path, new_path); -} - -/** - * ccs_sb_mount - Check permission for mount(). - * - * @dev_name: Name of device file. - * @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_sb_mount(char *dev_name, struct path *path, char *type, - unsigned long flags, void *data_page) -{ - int rc = ccs_mount_permission(dev_name, path, type, flags, data_page); - if (rc) - return rc; - while (!original_security_ops.sb_mount); - return original_security_ops.sb_mount(dev_name, path, type, flags, - data_page); -} - -#else - -/** - * ccs_sb_pivotroot - 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_sb_pivotroot(struct path *old_path, struct path *new_path) -{ - int rc = ccs_pivot_root_permission(old_path, new_path); - if (rc) - return rc; - while (!original_security_ops.sb_pivotroot); - return original_security_ops.sb_pivotroot(old_path, new_path); -} - -/** - * ccs_sb_mount - Check permission for mount(). - * - * @dev_name: Name of device file. - * @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_sb_mount(const char *dev_name, struct path *path, - const char *type, unsigned long flags, void *data_page) -{ - int rc = ccs_mount_permission(dev_name, path, type, flags, data_page); - if (rc) - return rc; - while (!original_security_ops.sb_mount); - return original_security_ops.sb_mount(dev_name, path, type, flags, - data_page); -} - -#endif - -/** - * ccs_sb_umount - Check permission for umount(). - * - * @mnt: Pointer to "struct vfsmount". - * @flags: Unmount flags. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_sb_umount(struct vfsmount *mnt, int flags) -{ - int rc = ccs_umount_permission(mnt, flags); - if (rc) - return rc; - while (!original_security_ops.sb_umount); - return original_security_ops.sb_umount(mnt, flags); -} - -/** - * ccs_file_fcntl - Check permission for fcntl(). - * - * @file: Pointer to "struct file". - * @cmd: Command number. - * @arg: Value for @cmd. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_file_fcntl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - int rc = ccs_fcntl_permission(file, cmd, arg); - if (rc) - return rc; - while (!original_security_ops.file_fcntl); - return original_security_ops.file_fcntl(file, cmd, arg); -} - -/** - * ccs_file_ioctl - Check permission for ioctl(). - * - * @filp: Pointer to "struct file". - * @cmd: Command number. - * @arg: Value for @cmd. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_file_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) -{ - int rc = ccs_ioctl_permission(filp, cmd, arg); - if (rc) - return rc; - while (!original_security_ops.file_ioctl); - return original_security_ops.file_ioctl(filp, cmd, arg); -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL) -int ccs_path_permission(struct ccs_request_info *r, u8 operation, - const struct ccs_path_info *filename); - -/** - * ccs_prepend - Copy of prepend() in fs/dcache.c. - * - * @buffer: Pointer to "struct char *". - * @buflen: Pointer to int which holds size of @buffer. - * @str: String to copy. - * - * Returns 0 on success, negative value otherwise. - * - * @buffer and @buflen are updated upon success. - */ -static int ccs_prepend(char **buffer, int *buflen, const char *str) -{ - int namelen = strlen(str); - if (*buflen < namelen) - return -ENOMEM; - *buflen -= namelen; - *buffer -= namelen; - memcpy(*buffer, str, namelen); - return 0; -} - -/** - * ccs_sysctl_permission - Check permission for sysctl(). - * - * @table: Pointer to "struct ctl_table". - * @op: Operation. (MAY_READ and/or MAY_WRITE) - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_sysctl(struct ctl_table *table, int op) -{ - int error; - struct ccs_path_info buf; - struct ccs_request_info r; - int buflen; - char *buffer; - int idx; - while (!original_security_ops.sysctl); - error = original_security_ops.sysctl(table, op); - if (error) - return error; - op &= MAY_READ | MAY_WRITE; - if (!op) - return 0; - buffer = NULL; - buf.name = NULL; - idx = ccs_read_lock(); - if (ccs_init_request_info(&r, CCS_MAC_FILE_OPEN) - == CCS_CONFIG_DISABLED) - goto out; - error = -ENOMEM; - buflen = 4096; - buffer = kmalloc(buflen, CCS_GFP_FLAGS); - if (buffer) { - char *end = buffer + buflen; - *--end = '\0'; - buflen--; - while (table) { - char num[32]; - const char *sp = table->procname; - if (!sp) { - memset(num, 0, sizeof(num)); - snprintf(num, sizeof(num) - 1, "=%d=", - table->ctl_name); - sp = num; - } - if (ccs_prepend(&end, &buflen, sp) || - ccs_prepend(&end, &buflen, "/")) - goto out; - table = table->parent; - } - if (ccs_prepend(&end, &buflen, "proc:/sys")) - goto out; - buf.name = ccs_encode(end); - } - if (buf.name) { - 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); - } -out: - ccs_read_unlock(idx); - kfree(buf.name); - kfree(buffer); - return error; -} - -#endif - -/* - * Why not to copy all operations by "original_security_ops = *ops" ? - * Because copying byte array is not atomic. Reader checks - * original_security_ops.op != NULL before doing original_security_ops.op(). - * Thus, modifying original_security_ops.op has to be atomic. - */ -#define swap_security_ops(op) \ - original_security_ops.op = ops->op; smp_wmb(); ops->op = ccs_##op; - -/** - * ccs_update_security_ops - Overwrite original "struct security_operations". - * - * @ops: Pointer to "struct security_operations". - * - * Returns nothing. - */ -static void __init ccs_update_security_ops(struct security_operations *ops) -{ - /* Security context allocator. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) - swap_security_ops(task_create); - swap_security_ops(cred_prepare); - swap_security_ops(cred_free); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) - swap_security_ops(cred_alloc_blank); - swap_security_ops(cred_transfer); -#endif -#else - swap_security_ops(task_alloc_security); - swap_security_ops(task_free_security); - swap_security_ops(bprm_alloc_security); - swap_security_ops(bprm_free_security); -#endif - /* Security context updater for successful execve(). */ - swap_security_ops(bprm_check_security); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 6) - swap_security_ops(bprm_compute_creds); -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) - swap_security_ops(bprm_apply_creds); -#else - swap_security_ops(bprm_committing_creds); -#endif - /* Various permission checker. */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) - swap_security_ops(file_open); -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) - swap_security_ops(dentry_open); -#else - swap_security_ops(inode_permission); -#endif - swap_security_ops(file_fcntl); - swap_security_ops(file_ioctl); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL) - swap_security_ops(sysctl); -#endif - swap_security_ops(sb_pivotroot); - swap_security_ops(sb_mount); - swap_security_ops(sb_umount); -#if defined(CONFIG_SECURITY_PATH) - swap_security_ops(path_mknod); - swap_security_ops(path_mkdir); - swap_security_ops(path_rmdir); - swap_security_ops(path_unlink); - swap_security_ops(path_symlink); - swap_security_ops(path_rename); - swap_security_ops(path_link); - swap_security_ops(path_truncate); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) - swap_security_ops(path_chmod); - swap_security_ops(path_chown); - swap_security_ops(path_chroot); -#endif -#else - swap_security_ops(inode_mknod); - swap_security_ops(inode_mkdir); - swap_security_ops(inode_rmdir); - swap_security_ops(inode_unlink); - swap_security_ops(inode_symlink); - swap_security_ops(inode_rename); - swap_security_ops(inode_link); - swap_security_ops(inode_create); -#endif - swap_security_ops(inode_setattr); - swap_security_ops(inode_getattr); -#ifdef CONFIG_SECURITY_NETWORK - swap_security_ops(socket_bind); - swap_security_ops(socket_connect); - swap_security_ops(socket_listen); - swap_security_ops(socket_sendmsg); - swap_security_ops(socket_recvmsg); - swap_security_ops(socket_getsockname); - swap_security_ops(socket_getpeername); - swap_security_ops(socket_getsockopt); - swap_security_ops(socket_setsockopt); - swap_security_ops(socket_shutdown); - swap_security_ops(socket_accept); - swap_security_ops(inode_free_security); -#endif -} - -#undef swap_security_ops - -/** - * ccs_init - Initialize this module. - * - * Returns 0 on success, negative value otherwise. - */ -static int __init ccs_init(void) -{ - struct security_operations *ops = probe_security_ops(); - if (!ops) - goto out; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) - ccsecurity_exports.find_task_by_vpid = probe_find_task_by_vpid(); - if (!ccsecurity_exports.find_task_by_vpid) - goto out; - ccsecurity_exports.find_task_by_pid_ns = probe_find_task_by_pid_ns(); - if (!ccsecurity_exports.find_task_by_pid_ns) - goto out; -#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) - ccsecurity_exports.vfsmount_lock = probe_vfsmount_lock(); - if (!ccsecurity_exports.vfsmount_lock) - goto out; -#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) - ccsecurity_exports.__d_path = probe___d_path(); - if (!ccsecurity_exports.__d_path) - goto out; -#else - ccsecurity_exports.d_absolute_path = probe_d_absolute_path(); - if (!ccsecurity_exports.d_absolute_path) - goto out; -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) - { - int idx; - for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) { - INIT_LIST_HEAD(&ccs_cred_security_list[idx]); - INIT_LIST_HEAD(&ccs_task_security_list[idx]); - } - } -#endif - ccs_main_init(); - ccs_update_security_ops(ops); - printk(KERN_INFO "AKARI: 1.0.30 2013/02/14\n"); - printk(KERN_INFO - "Access Keeping And Regulating Instrument registered.\n"); - return 0; -out: - return -EINVAL; -} - -module_init(ccs_init); -MODULE_LICENSE("GPL"); - -/** - * ccs_used_by_cred - Check whether the given domain is in use or not. - * - * @domain: Pointer to "struct ccs_domain_info". - * - * Returns true if @domain is in use, false otherwise. - * - * Caller holds rcu_read_lock(). - */ -bool ccs_used_by_cred(const struct ccs_domain_info *domain) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) - int idx; - struct ccs_security *ptr; - for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) { - struct list_head *list = &ccs_cred_security_list[idx]; - list_for_each_entry_rcu(ptr, list, list) { - struct ccs_execve *ee = ptr->ee; - if (ptr->ccs_domain_info == domain || - (ee && ee->previous_domain == domain)) { - return true; - } - } - } -#endif - return false; -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) - -/** - * 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_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->pid != task->pids[PIDTYPE_PID].pid) - continue; - rcu_read_unlock(); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) - /* - * Current thread needs to transit from old domain to new - * domain before do_execve() succeeds in order to check - * permission for interpreters and environment variables using - * new domain's ACL rules. The domain transition has to be - * visible from other CPU in order to allow interactive - * enforcing mode. Also, the domain transition has to be - * reverted if do_execve() failed. However, an LSM hook for - * reverting domain transition is missing. - * - * security_prepare_creds() is called from prepare_creds() from - * prepare_bprm_creds() from do_execve() before setting - * current->in_execve flag, and current->in_execve flag is - * cleared by the time next do_execve() request starts. - * This means that we can emulate the missing LSM hook for - * reverting domain transition, by calling this function from - * security_prepare_creds(). - * - * If current->in_execve is not set but ptr->ccs_flags has - * CCS_TASK_IS_IN_EXECVE set, it indicates that do_execve() - * has failed and reverting domain transition is needed. - */ - if (task == current && - (ptr->ccs_flags & CCS_TASK_IS_IN_EXECVE) && - !current->in_execve) { - ccs_debug_trace("4"); - ccs_clear_execve(-1, ptr); - } -#else - /* - * Current thread needs to transit from old domain to new - * domain before do_execve() succeeds in order to check - * permission for interpreters and environment variables using - * new domain's ACL rules. The domain transition has to be - * visible from other CPU in order to allow interactive - * enforcing mode. Also, the domain transition has to be - * reverted if do_execve() failed. However, an LSM hook for - * reverting domain transition is missing. - * - * When do_execve() failed, "struct cred" in - * "struct linux_binprm" is scheduled for destruction. - * But current thread returns to userspace without waiting for - * destruction. The security_cred_free() LSM hook is called - * after an RCU grace period has elapsed. Since some CPU may be - * doing long long RCU read side critical section, there is - * no guarantee that security_cred_free() is called before - * current thread again calls do_execve(). - * - * To be able to revert domain transition before processing - * next do_execve() request, current thread gets a refcount on - * "struct cred" in "struct linux_binprm" and memorizes it. - * Current thread drops the refcount and forgets it when - * do_execve() succeeded. - * - * Therefore, if current thread hasn't forgotten it and - * current thread is the last one using that "struct cred", - * it indicates that do_execve() has failed and reverting - * domain transition is needed. - */ - if (task == current && ptr->cred && - atomic_read(&ptr->cred->usage) == 1) { - ccs_debug_trace("4"); - ccs_clear_execve(-1, ptr); - } -#endif - return ptr; - } - rcu_read_unlock(); - if (task != current) { - /* - * If a thread does nothing after fork(), caller will reach - * here because "struct ccs_security" for that thread is not - * yet allocated. But that thread is keeping a snapshot of - * "struct ccs_security" taken as of ccs_task_create() - * associated with that thread's "struct cred". - * - * Since that snapshot will be used as initial data when that - * thread allocates "struct ccs_security" for that thread, we - * can return that snapshot rather than &ccs_default_security. - * - * Since this function is called by only ccs_select_one() and - * ccs_read_pid() (via ccs_task_domain() and ccs_task_flags()), - * it is guaranteed that caller has called rcu_read_lock() - * (via ccs_tasklist_lock()) before finding this thread and - * this thread is valid. Therefore, we can do __task_cred(task) - * like get_robust_list() does. - */ - return ccs_find_cred_security(__task_cred(task)); - } - /* 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_find_cred_security(task->cred); - /* We can shortcut because task == current. */ - ptr->pid = get_pid(((struct task_struct *) task)-> - pids[PIDTYPE_PID].pid); - ptr->cred = NULL; - ccs_add_task_security(ptr, list); - return ptr; -} - -/** - * ccs_copy_cred_security - Allocate memory for new credentials. - * - * @new: Pointer to "struct cred". - * @old: Pointer to "struct cred". - * @gfp: Memory allocation flags. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_copy_cred_security(const struct cred *new, - const struct cred *old, gfp_t gfp) -{ - struct ccs_security *old_security = ccs_find_cred_security(old); - struct ccs_security *new_security = - kzalloc(sizeof(*new_security), gfp); - if (!new_security) - return -ENOMEM; - *new_security = *old_security; - new_security->cred = new; - ccs_add_cred_security(new_security); - return 0; -} - -/** - * ccs_find_cred_security - Find "struct ccs_security" for given credential. - * - * @cred: Pointer to "struct cred". - * - * Returns pointer to "struct ccs_security" on success, &ccs_default_security - * otherwise. - */ -static struct ccs_security *ccs_find_cred_security(const struct cred *cred) -{ - struct ccs_security *ptr; - struct list_head *list = &ccs_cred_security_list - [hash_ptr((void *) cred, CCS_TASK_SECURITY_HASH_BITS)]; - rcu_read_lock(); - list_for_each_entry_rcu(ptr, list, list) { - if (ptr->cred != cred) - continue; - rcu_read_unlock(); - return ptr; - } - rcu_read_unlock(); - return &ccs_default_security; -} - -/** - * ccs_task_security_gc - Do garbage collection for "struct task_struct". - * - * Returns nothing. - * - * Since security_task_free() is missing, I can't release memory associated - * with "struct task_struct" when a task dies. Therefore, I hold a reference on - * "struct pid" and runs garbage collection when associated - * "struct task_struct" has gone. - */ -static void ccs_task_security_gc(void) -{ - static DEFINE_SPINLOCK(lock); - static atomic_t gc_counter = ATOMIC_INIT(0); - unsigned int idx; - /* - * If some process is doing execve(), try to garbage collection now. - * We should kfree() memory associated with "struct ccs_security"->ee - * as soon as execve() has completed in order to compensate for lack of - * security_bprm_free() and security_task_free() hooks. - * - * Otherwise, reduce frequency for performance reason. - */ - if (!atomic_read(&ccs_in_execve_tasks) && - atomic_inc_return(&gc_counter) < 1024) - return; - if (!spin_trylock(&lock)) - return; - atomic_set(&gc_counter, 0); - 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 (pid_task(ptr->pid, PIDTYPE_PID)) - continue; - ccs_del_security(ptr); - } - } - rcu_read_unlock(); - spin_unlock(&lock); -} - -#endif diff --git a/akari/mclsm.c b/akari/mclsm.c deleted file mode 100644 index 2a2a78a..0000000 --- a/akari/mclsm.c +++ /dev/null @@ -1,1545 +0,0 @@ -/* - * mclsm.c - * - * Copyright (C) 2010-2013 Tetsuo Handa - * - * Version: 1.0.30 2013/02/14 - */ - -#include "internal.h" -#include "probe.h" - -/* Prototype definition. */ -static void ccs_task_security_gc(void); -static int ccs_copy_cred_security(const struct cred *new, - const struct cred *old, gfp_t gfp); -static struct ccs_security *ccs_find_cred_security(const struct cred *cred); -static DEFINE_SPINLOCK(ccs_task_security_list_lock); -static atomic_t ccs_in_execve_tasks = ATOMIC_INIT(0); -/* - * List of "struct ccs_security" for "struct pid". - * - * All instances on this list is guaranteed that "struct ccs_security"->pid != - * NULL. Also, instances on this list that are in execve() are guaranteed that - * "struct ccs_security"->cred remembers "struct linux_binprm"->cred with a - * refcount on "struct linux_binprm"->cred. - */ -struct list_head ccs_task_security_list[CCS_MAX_TASK_SECURITY_HASH]; -/* - * List of "struct ccs_security" for "struct cred". - * - * Since the number of "struct cred" is nearly equals to the number of - * "struct pid", we allocate hash tables like ccs_task_security_list. - * - * All instances on this list are guaranteed that "struct ccs_security"->pid == - * NULL and "struct ccs_security"->cred != NULL. - */ -static struct list_head ccs_cred_security_list[CCS_MAX_TASK_SECURITY_HASH]; - -/* 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 -}; - -/* For exporting variables and functions. */ -struct ccsecurity_exports ccsecurity_exports; -/* Members are updated by loadable kernel module. */ -struct ccsecurity_operations ccsecurity_ops; - -/* Original hooks. */ -static struct security_operations original_security_ops; - -#ifdef CONFIG_AKARI_TRACE_EXECVE_COUNT - -/** - * ccs_update_ee_counter - Update "struct ccs_execve" counter. - * - * @count: Count to increment or decrement. - * - * Returns updated counter. - */ -static unsigned int ccs_update_ee_counter(int count) -{ - /* Debug counter for detecting "struct ccs_execve" memory leak. */ - static atomic_t ccs_ee_counter = ATOMIC_INIT(0); - return atomic_add_return(count, &ccs_ee_counter); -} - -/** - * ccs_audit_alloc_execve - Audit allocation of "struct ccs_execve". - * - * @ee: Pointer to "struct ccs_execve". - * - * Returns nothing. - */ -void ccs_audit_alloc_execve(const struct ccs_execve * const ee) -{ - printk(KERN_INFO "AKARI: Allocated %p by pid=%u (count=%u)\n", ee, - current->pid, ccs_update_ee_counter(1) - 1); -} - -/** - * ccs_audit_free_execve - Audit release of "struct ccs_execve". - * - * @ee: Pointer to "struct ccs_execve". - * @task: True if released by current task, false otherwise. - * - * Returns nothing. - */ -void ccs_audit_free_execve(const struct ccs_execve * const ee, - const bool is_current) -{ - const unsigned int tmp = ccs_update_ee_counter(-1); - if (is_current) - printk(KERN_INFO "AKARI: Releasing %p by pid=%u (count=%u)\n", - ee, current->pid, tmp); - else - printk(KERN_INFO "AKARI: Releasing %p by kernel (count=%u)\n", - ee, tmp); -} - -#endif - -#if !defined(CONFIG_AKARI_DEBUG) -#define ccs_debug_trace(pos) do { } while (0) -#else -#define ccs_debug_trace(pos) \ - do { \ - static bool done; \ - if (!done) { \ - printk(KERN_INFO \ - "AKARI: Debug trace: " pos " of 4\n"); \ - done = true; \ - } \ - } while (0) -#endif - -/** - * ccs_clear_execve - Release memory used by do_execve(). - * - * @ret: 0 if do_execve() succeeded, negative value otherwise. - * @security: Pointer to "struct ccs_security". - * - * Returns nothing. - */ -static void ccs_clear_execve(int ret, struct ccs_security *security) -{ - struct ccs_execve *ee; - if (security == &ccs_default_security || security == &ccs_oom_security) - return; - ee = security->ee; - security->ee = NULL; - if (!ee) - return; - atomic_dec(&ccs_in_execve_tasks); - ccs_finish_execve(ret, ee); -} - -/** - * 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); - struct ccs_execve *ee = ptr->ee; - /* - * If this security context was associated with "struct pid" and - * ptr->ccs_flags has CCS_TASK_IS_IN_EXECVE set, it indicates that a - * "struct task_struct" associated with this security context exited - * immediately after do_execve() has failed. - */ - if (ptr->pid && (ptr->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { - ccs_debug_trace("1"); - atomic_dec(&ccs_in_execve_tasks); - } - /* - * If this security context was associated with "struct pid", - * drop refcount obtained by get_pid() in ccs_find_task_security(). - */ - if (ptr->pid) { - ccs_debug_trace("2"); - put_pid(ptr->pid); - } - if (ee) { - ccs_debug_trace("3"); - ccs_audit_free_execve(ee, false); - kfree(ee->handler_path); - kfree(ee); - } - kfree(ptr); -} - -/** - * ccs_del_security - Release "struct ccs_security". - * - * @ptr: Pointer to "struct ccs_security". - * - * Returns nothing. - */ -static void ccs_del_security(struct ccs_security *ptr) -{ - unsigned long flags; - 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); - call_rcu(&ptr->rcu, ccs_rcu_free); -} - -/** - * ccs_add_cred_security - Add "struct ccs_security" to list. - * - * @ptr: Pointer to "struct ccs_security". - * - * Returns nothing. - */ -static void ccs_add_cred_security(struct ccs_security *ptr) -{ - unsigned long flags; - struct list_head *list = &ccs_cred_security_list - [hash_ptr((void *) ptr->cred, CCS_TASK_SECURITY_HASH_BITS)]; -#ifdef CONFIG_AKARI_DEBUG - if (ptr->pid) - printk(KERN_INFO "AKARI: \"struct ccs_security\"->pid != NULL" - "\n"); -#endif - ptr->pid = NULL; - 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_task_create - Make snapshot of security context for new task. - * - * @clone_flags: Flags passed to clone(). - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_task_create(unsigned long clone_flags) -{ - struct ccs_security *old_security; - struct ccs_security *new_security; - struct cred *cred = prepare_creds(); - if (!cred) - return -ENOMEM; - old_security = ccs_find_task_security(current); - new_security = ccs_find_cred_security(cred); - new_security->ccs_domain_info = old_security->ccs_domain_info; - new_security->ccs_flags = old_security->ccs_flags; - return commit_creds(cred); -} - -/** - * ccs_cred_prepare - Allocate memory for new credentials. - * - * @new: Pointer to "struct cred". - * @old: Pointer to "struct cred". - * @gfp: Memory allocation flags. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_cred_prepare(struct cred *new, const struct cred *old, - gfp_t gfp) -{ - int rc1; - /* - * For checking whether reverting domain transition is needed or not. - * - * See ccs_find_task_security() for reason. - */ - if (gfp == GFP_KERNEL) - ccs_find_task_security(current); - rc1 = ccs_copy_cred_security(new, old, gfp); - if (gfp == GFP_KERNEL) - ccs_task_security_gc(); - if (original_security_ops.cred_prepare) { - const int rc2 = original_security_ops.cred_prepare(new, old, - gfp); - if (rc2) { - ccs_del_security(ccs_find_cred_security(new)); - return rc2; - } - } - return rc1; -} - -/** - * ccs_cred_free - Release memory used by credentials. - * - * @cred: Pointer to "struct cred". - * - * Returns nothing. - */ -static void ccs_cred_free(struct cred *cred) -{ - if (original_security_ops.cred_free) - original_security_ops.cred_free(cred); - ccs_del_security(ccs_find_cred_security(cred)); -} - -/** - * ccs_alloc_cred_security - Allocate memory for new credentials. - * - * @cred: Pointer to "struct cred". - * @gfp: Memory allocation flags. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_alloc_cred_security(const struct cred *cred, gfp_t gfp) -{ - struct ccs_security *new_security = kzalloc(sizeof(*new_security), - gfp); - if (!new_security) - return -ENOMEM; - new_security->cred = cred; - ccs_add_cred_security(new_security); - return 0; -} - -/** - * ccs_cred_alloc_blank - Allocate memory for new credentials. - * - * @new: Pointer to "struct cred". - * @gfp: Memory allocation flags. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_cred_alloc_blank(struct cred *new, gfp_t gfp) -{ - const int rc1 = ccs_alloc_cred_security(new, gfp); - if (original_security_ops.cred_alloc_blank) { - const int rc2 = original_security_ops.cred_alloc_blank(new, - gfp); - if (rc2) { - ccs_del_security(ccs_find_cred_security(new)); - return rc2; - } - } - return rc1; -} - -/** - * ccs_cred_transfer - Transfer "struct ccs_security" between credentials. - * - * @new: Pointer to "struct cred". - * @old: Pointer to "struct cred". - * - * Returns nothing. - */ -static void ccs_cred_transfer(struct cred *new, const struct cred *old) -{ - struct ccs_security *new_security = ccs_find_cred_security(new); - struct ccs_security *old_security = ccs_find_cred_security(old); - if (new_security == &ccs_default_security || - new_security == &ccs_oom_security || - old_security == &ccs_default_security || - old_security == &ccs_oom_security) - return; - new_security->ccs_flags = old_security->ccs_flags; - new_security->ccs_domain_info = old_security->ccs_domain_info; -} - -/** - * ccs_bprm_committing_creds - A hook which is called when do_execve() succeeded. - * - * @bprm: Pointer to "struct linux_binprm". - * - * Returns nothing. - */ -static void ccs_bprm_committing_creds(struct linux_binprm *bprm) -{ - struct ccs_security *old_security = ccs_current_security(); - struct ccs_security *new_security; - if (old_security == &ccs_default_security || - old_security == &ccs_oom_security) - return; - ccs_clear_execve(0, old_security); - /* Update current task's cred's domain for future fork(). */ - new_security = ccs_find_cred_security(bprm->cred); - new_security->ccs_flags = old_security->ccs_flags; - new_security->ccs_domain_info = old_security->ccs_domain_info; -} - -/** - * ccs_bprm_check_security - Check permission for execve(). - * - * @bprm: Pointer to "struct linux_binprm". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_bprm_check_security(struct linux_binprm *bprm) -{ - struct ccs_security *security = ccs_current_security(); - int rc; - if (security == &ccs_default_security || security == &ccs_oom_security) - return -ENOMEM; - if (security->ee) - return 0; -#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER - if (!ccs_policy_loaded) - ccs_load_policy(bprm->filename); -#endif - rc = ccs_start_execve(bprm, &security->ee); - if (security->ee) - atomic_inc(&ccs_in_execve_tasks); - return rc; -} - -/** - * ccs_file_open - Check permission for open(). - * - * @f: Pointer to "struct file". - * @cred: Pointer to "struct cred". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_file_open(struct file *f, const struct cred *cred) -{ - return ccs_open_permission(f); -} - -#ifdef CONFIG_SECURITY_PATH - -/** - * ccs_path_chown - Check permission for chown()/chgrp(). - * - * @path: Pointer to "struct path". - * @user: User ID. - * @group: Group ID. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_chown(struct path *path, kuid_t user, kgid_t group) -{ - return ccs_chown_permission(path->dentry, path->mnt, user, group); -} - -/** - * ccs_path_chmod - Check permission for chmod(). - * - * @path: Pointer to "struct path". - * @mode: Mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_chmod(struct path *path, umode_t mode) -{ - return ccs_chmod_permission(path->dentry, path->mnt, mode); -} - -/** - * ccs_path_chroot - Check permission for chroot(). - * - * @path: Pointer to "struct path". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_chroot(struct path *path) -{ - return ccs_chroot_permission(path); -} - -/** - * ccs_path_truncate - Check permission for truncate(). - * - * @path: Pointer to "struct path". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_truncate(struct path *path) -{ - return ccs_truncate_permission(path->dentry, path->mnt); -} - -#else - -/** - * ccs_inode_setattr - Check permission for chown()/chgrp()/chmod()/truncate(). - * - * @dentry: Pointer to "struct dentry". - * @attr: Pointer to "struct iattr". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_setattr(struct dentry *dentry, struct iattr *attr) -{ - const int rc1 = (attr->ia_valid & ATTR_UID) ? - ccs_chown_permission(dentry, NULL, attr->ia_uid, INVALID_GID) : - 0; - const int rc2 = (attr->ia_valid & ATTR_GID) ? - ccs_chown_permission(dentry, NULL, INVALID_UID, attr->ia_gid) : - 0; - const int rc3 = (attr->ia_valid & ATTR_MODE) ? - ccs_chmod_permission(dentry, NULL, attr->ia_mode) : 0; - const int rc4 = (attr->ia_valid & ATTR_SIZE) ? - ccs_truncate_permission(dentry, NULL) : 0; - if (rc4) - return rc4; - if (rc3) - return rc3; - if (rc2) - return rc2; - return rc1; -} - -#endif - -/** - * ccs_inode_getattr - Check permission for stat(). - * - * @mnt: Pointer to "struct vfsmount". - * @dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) -{ - return ccs_getattr_permission(mnt, dentry); -} - -#ifdef CONFIG_SECURITY_PATH - -/** - * ccs_path_mknod - Check permission for mknod(). - * - * @dir: Pointer to "struct path". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * @dev: Device major/minor number. - * - * Returns 0 on success, negative value otherwise. - */ -static 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); -} - -/** - * ccs_path_mkdir - Check permission for mkdir(). - * - * @dir: Pointer to "struct path". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_mkdir(struct path *dir, struct dentry *dentry, - umode_t mode) -{ - return ccs_mkdir_permission(dentry, dir->mnt, mode); -} - -/** - * ccs_path_rmdir - Check permission for rmdir(). - * - * @dir: Pointer to "struct path". - * @dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_rmdir(struct path *dir, struct dentry *dentry) -{ - return ccs_rmdir_permission(dentry, dir->mnt); -} - -/** - * ccs_path_unlink - Check permission for unlink(). - * - * @dir: Pointer to "struct path". - * @dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_unlink(struct path *dir, struct dentry *dentry) -{ - return ccs_unlink_permission(dentry, dir->mnt); -} - -/** - * ccs_path_symlink - Check permission for symlink(). - * - * @dir: Pointer to "struct path". - * @dentry: Pointer to "struct dentry". - * @old_name: Content of symbolic link. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_path_symlink(struct path *dir, struct dentry *dentry, - const char *old_name) -{ - return ccs_symlink_permission(dentry, dir->mnt, old_name); -} - -/** - * ccs_path_rename - Check permission for rename(). - * - * @old_dir: Pointer to "struct path". - * @old_dentry: Pointer to "struct dentry". - * @new_dir: Pointer to "struct path". - * @new_dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static 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, old_dir->mnt); -} - -/** - * ccs_path_link - Check permission for link(). - * - * @old_dentry: Pointer to "struct dentry". - * @new_dir: Pointer to "struct path". - * @new_dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static 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); -} - -#else - -/** - * ccs_inode_mknod - Check permission for mknod(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * @dev: Device major/minor number. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_mknod(struct inode *dir, struct dentry *dentry, - umode_t mode, dev_t dev) -{ - return ccs_mknod_permission(dentry, NULL, mode, dev); -} - -/** - * ccs_inode_mkdir - Check permission for mkdir(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_mkdir(struct inode *dir, struct dentry *dentry, - umode_t mode) -{ - return ccs_mkdir_permission(dentry, NULL, mode); -} - -/** - * ccs_inode_rmdir - Check permission for rmdir(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_rmdir(struct inode *dir, struct dentry *dentry) -{ - return ccs_rmdir_permission(dentry, NULL); -} - -/** - * ccs_inode_unlink - Check permission for unlink(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_unlink(struct inode *dir, struct dentry *dentry) -{ - return ccs_unlink_permission(dentry, NULL); -} - -/** - * ccs_inode_symlink - Check permission for symlink(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @old_name: Content of symbolic link. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_symlink(struct inode *dir, struct dentry *dentry, - const char *old_name) -{ - return ccs_symlink_permission(dentry, NULL, old_name); -} - -/** - * ccs_inode_rename - Check permission for rename(). - * - * @old_dir: Pointer to "struct inode". - * @old_dentry: Pointer to "struct dentry". - * @new_dir: Pointer to "struct inode". - * @new_dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - return ccs_rename_permission(old_dentry, new_dentry, NULL); -} - -/** - * ccs_inode_link - Check permission for link(). - * - * @old_dentry: Pointer to "struct dentry". - * @dir: Pointer to "struct inode". - * @new_dentry: Pointer to "struct dentry". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *new_dentry) -{ - return ccs_link_permission(old_dentry, new_dentry, NULL); -} - -/** - * ccs_inode_create - Check permission for creat(). - * - * @dir: Pointer to "struct inode". - * @dentry: Pointer to "struct dentry". - * @mode: Create mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_inode_create(struct inode *dir, struct dentry *dentry, - umode_t mode) -{ - return ccs_mknod_permission(dentry, NULL, mode, 0); -} - -#endif - -#ifdef CONFIG_SECURITY_NETWORK - -#include - -/* Structure for remembering an accept()ed socket's status. */ -struct ccs_socket_tag { - struct list_head list; - struct inode *inode; - int status; - struct rcu_head rcu; -}; - -/* - * List for managing accept()ed sockets. - * Since we don't need to keep an accept()ed socket into this list after - * once the permission was granted, the number of entries in this list is - * likely small. Therefore, we don't use hash tables. - */ -static LIST_HEAD(ccs_accepted_socket_list); -/* Lock for protecting ccs_accepted_socket_list . */ -static DEFINE_SPINLOCK(ccs_accepted_socket_list_lock); - -/** - * ccs_socket_rcu_free - RCU callback for releasing "struct ccs_socket_tag". - * - * @rcu: Pointer to "struct rcu_head". - * - * Returns nothing. - */ -static void ccs_socket_rcu_free(struct rcu_head *rcu) -{ - struct ccs_socket_tag *ptr = container_of(rcu, typeof(*ptr), rcu); - kfree(ptr); -} - -/** - * ccs_update_socket_tag - Update tag associated with accept()ed sockets. - * - * @inode: Pointer to "struct inode". - * @status: New status. - * - * Returns nothing. - * - * If @status == 0, memory for that socket will be released after RCU grace - * period. - */ -static void ccs_update_socket_tag(struct inode *inode, int status) -{ - struct ccs_socket_tag *ptr; - /* - * Protect whole section because multiple threads may call this - * function with same "sock" via ccs_validate_socket(). - */ - spin_lock(&ccs_accepted_socket_list_lock); - rcu_read_lock(); - list_for_each_entry_rcu(ptr, &ccs_accepted_socket_list, list) { - if (ptr->inode != inode) - continue; - ptr->status = status; - if (status) - break; - list_del_rcu(&ptr->list); - call_rcu(&ptr->rcu, ccs_socket_rcu_free); - break; - } - rcu_read_unlock(); - spin_unlock(&ccs_accepted_socket_list_lock); -} - -/** - * ccs_validate_socket - Check post accept() permission if needed. - * - * @sock: Pointer to "struct socket". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_validate_socket(struct socket *sock) -{ - struct inode *inode = SOCK_INODE(sock); - struct ccs_socket_tag *ptr; - int ret = 0; - rcu_read_lock(); - list_for_each_entry_rcu(ptr, &ccs_accepted_socket_list, list) { - if (ptr->inode != inode) - continue; - ret = ptr->status; - break; - } - rcu_read_unlock(); - if (ret <= 0) - /* - * This socket is not an accept()ed socket or this socket is - * an accept()ed socket and post accept() permission is done. - */ - return ret; - /* - * Check post accept() permission now. - * - * Strictly speaking, we need to pass both listen()ing socket and - * accept()ed socket to __ccs_socket_post_accept_permission(). - * But since socket's family and type are same for both sockets, - * passing the accept()ed socket in place for the listen()ing socket - * will work. - */ - ret = ccs_socket_post_accept_permission(sock, sock); - /* - * If permission was granted, we forget that this is an accept()ed - * socket. Otherwise, we remember that this socket needs to return - * error for subsequent socketcalls. - */ - ccs_update_socket_tag(inode, ret); - return ret; -} - -/** - * ccs_socket_accept - Check permission for accept(). - * - * @sock: Pointer to "struct socket". - * @newsock: Pointer to "struct socket". - * - * Returns 0 on success, negative value otherwise. - * - * This hook is used for setting up environment for doing post accept() - * permission check. If dereferencing sock->ops->something() were ordered by - * rcu_dereference(), we could replace sock->ops with "a copy of original - * sock->ops with modified sock->ops->accept()" using rcu_assign_pointer() - * in order to do post accept() permission check before returning to userspace. - * If we make the copy in security_socket_post_create(), it would be possible - * to safely replace sock->ops here, but we don't do so because we don't want - * to allocate memory for sockets which do not call sock->ops->accept(). - * Therefore, we do post accept() permission check upon next socket syscalls - * rather than between sock->ops->accept() and returning to userspace. - * This means that if a socket was close()d before calling some socket - * syscalls, post accept() permission check will not be done. - */ -static int ccs_socket_accept(struct socket *sock, struct socket *newsock) -{ - struct ccs_socket_tag *ptr; - const int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - /* - * Subsequent LSM hooks will receive "newsock". Therefore, I mark - * "newsock" as "an accept()ed socket but post accept() permission - * check is not done yet" by allocating memory using inode of the - * "newsock" as a search key. - */ - ptr->inode = SOCK_INODE(newsock); - ptr->status = 1; /* Check post accept() permission later. */ - spin_lock(&ccs_accepted_socket_list_lock); - list_add_tail_rcu(&ptr->list, &ccs_accepted_socket_list); - spin_unlock(&ccs_accepted_socket_list_lock); - return 0; -} - -/** - * ccs_socket_listen - Check permission for listen(). - * - * @sock: Pointer to "struct socket". - * @backlog: Backlog parameter. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_listen(struct socket *sock, int backlog) -{ - const int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - return ccs_socket_listen_permission(sock); -} - -/** - * ccs_socket_connect - Check permission for connect(). - * - * @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(struct socket *sock, struct sockaddr *addr, - int addr_len) -{ - const int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - return ccs_socket_connect_permission(sock, addr, addr_len); -} - -/** - * ccs_socket_bind - Check permission for bind(). - * - * @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(struct socket *sock, struct sockaddr *addr, - int addr_len) -{ - const int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - return ccs_socket_bind_permission(sock, addr, addr_len); -} - -/** - * ccs_socket_sendmsg - Check permission for sendmsg(). - * - * @sock: Pointer to "struct socket". - * @msg: Pointer to "struct msghdr". - * @size: Size of message. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_sendmsg(struct socket *sock, struct msghdr *msg, - int size) -{ - const int rc = ccs_validate_socket(sock); - if (rc < 0) - return rc; - return ccs_socket_sendmsg_permission(sock, msg, size); -} - -/** - * ccs_socket_recvmsg - Check permission for recvmsg(). - * - * @sock: Pointer to "struct socket". - * @msg: Pointer to "struct msghdr". - * @size: Size of message. - * @flags: Flags. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_recvmsg(struct socket *sock, struct msghdr *msg, - int size, int flags) -{ - return ccs_validate_socket(sock); -} - -/** - * ccs_socket_getsockname - Check permission for getsockname(). - * - * @sock: Pointer to "struct socket". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_getsockname(struct socket *sock) -{ - return ccs_validate_socket(sock); -} - -/** - * ccs_socket_getpeername - Check permission for getpeername(). - * - * @sock: Pointer to "struct socket". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_getpeername(struct socket *sock) -{ - return ccs_validate_socket(sock); -} - -/** - * ccs_socket_getsockopt - Check permission for getsockopt(). - * - * @sock: Pointer to "struct socket". - * @level: Level. - * @optname: Option's name, - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_getsockopt(struct socket *sock, int level, int optname) -{ - return ccs_validate_socket(sock); -} - -/** - * ccs_socket_setsockopt - Check permission for setsockopt(). - * - * @sock: Pointer to "struct socket". - * @level: Level. - * @optname: Option's name, - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_setsockopt(struct socket *sock, int level, int optname) -{ - return ccs_validate_socket(sock); -} - -/** - * ccs_socket_shutdown - Check permission for shutdown(). - * - * @sock: Pointer to "struct socket". - * @how: Shutdown mode. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_socket_shutdown(struct socket *sock, int how) -{ - return ccs_validate_socket(sock); -} - -#define SOCKFS_MAGIC 0x534F434B - -/** - * ccs_inode_free_security - Release memory associated with an inode. - * - * @inode: Pointer to "struct inode". - * - * Returns nothing. - * - * We use this hook for releasing memory associated with an accept()ed socket. - */ -static void ccs_inode_free_security(struct inode *inode) -{ - if (inode->i_sb && inode->i_sb->s_magic == SOCKFS_MAGIC) - ccs_update_socket_tag(inode, 0); -} - -#endif - -/** - * ccs_sb_pivotroot - 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_sb_pivotroot(struct path *old_path, struct path *new_path) -{ - return ccs_pivot_root_permission(old_path, new_path); -} - -/** - * ccs_sb_mount - Check permission for mount(). - * - * @dev_name: Name of device file. - * @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_sb_mount(const char *dev_name, struct path *path, - const char *type, unsigned long flags, void *data_page) -{ - return ccs_mount_permission(dev_name, path, type, flags, data_page); -} - -/** - * ccs_sb_umount - Check permission for umount(). - * - * @mnt: Pointer to "struct vfsmount". - * @flags: Unmount flags. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_sb_umount(struct vfsmount *mnt, int flags) -{ - return ccs_umount_permission(mnt, flags); -} - -/** - * ccs_file_fcntl - Check permission for fcntl(). - * - * @file: Pointer to "struct file". - * @cmd: Command number. - * @arg: Value for @cmd. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_file_fcntl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - return ccs_fcntl_permission(file, cmd, arg); -} - -/** - * ccs_file_ioctl - Check permission for ioctl(). - * - * @filp: Pointer to "struct file". - * @cmd: Command number. - * @arg: Value for @cmd. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_file_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) -{ - return ccs_ioctl_permission(filp, cmd, arg); -} - -static struct security_operations akari_security_ops = { - .name = "akari", - .task_create = ccs_task_create, - .cred_prepare = ccs_cred_prepare, - .cred_free = ccs_cred_free, - .cred_alloc_blank = ccs_cred_alloc_blank, - .cred_transfer = ccs_cred_transfer, - .bprm_check_security = ccs_bprm_check_security, - .bprm_committing_creds = ccs_bprm_committing_creds, - .file_open = ccs_file_open, - .file_fcntl = ccs_file_fcntl, - .file_ioctl = ccs_file_ioctl, - .sb_pivotroot = ccs_sb_pivotroot, - .sb_mount = ccs_sb_mount, - .sb_umount = ccs_sb_umount, -#ifdef CONFIG_SECURITY_PATH - .path_mknod = ccs_path_mknod, - .path_mkdir = ccs_path_mkdir, - .path_rmdir = ccs_path_rmdir, - .path_unlink = ccs_path_unlink, - .path_symlink = ccs_path_symlink, - .path_rename = ccs_path_rename, - .path_link = ccs_path_link, - .path_truncate = ccs_path_truncate, - .path_chmod = ccs_path_chmod, - .path_chown = ccs_path_chown, - .path_chroot = ccs_path_chroot, -#else - .inode_mknod = ccs_inode_mknod, - .inode_mkdir = ccs_inode_mkdir, - .inode_rmdir = ccs_inode_rmdir, - .inode_unlink = ccs_inode_unlink, - .inode_symlink = ccs_inode_symlink, - .inode_rename = ccs_inode_rename, - .inode_link = ccs_inode_link, - .inode_create = ccs_inode_create, - .inode_setattr = ccs_inode_setattr, -#endif - .inode_getattr = ccs_inode_getattr, -#ifdef CONFIG_SECURITY_NETWORK - .socket_bind = ccs_socket_bind, - .socket_connect = ccs_socket_connect, - .socket_listen = ccs_socket_listen, - .socket_sendmsg = ccs_socket_sendmsg, - .socket_recvmsg = ccs_socket_recvmsg, - .socket_getsockname = ccs_socket_getsockname, - .socket_getpeername = ccs_socket_getpeername, - .socket_getsockopt = ccs_socket_getsockopt, - .socket_setsockopt = ccs_socket_setsockopt, - .socket_shutdown = ccs_socket_shutdown, - .socket_accept = ccs_socket_accept, - .inode_free_security = ccs_inode_free_security, -#endif -}; - -#define swap_security_ops(op, lsm_list) \ - do { \ - if (list_empty(&lsm_list[lsm_##op])) \ - add_security_ops(op, lsm_list); \ - else { \ - struct security_operations *ops = \ - list_first_entry(&lsm_list[lsm_##op], \ - typeof(*ops), \ - list[lsm_##op]); \ - original_security_ops.op = ops->op; \ - smp_wmb(); \ - ops->op = ccs_##op; \ - } \ - } while (0) - -#define add_security_ops(op, lsm_list) \ - do { \ - list_add_tail_rcu(&akari_security_ops.list[lsm_##op], \ - &lsm_list[lsm_##op]); \ - } while (0) - -/** - * ccs_update_security_ops - Overwrite original "struct security_operations". - * - * @lsm_list: Pointer to "struct list_head lsm_hooks[LSM_MAX_HOOKS]". - * - * Returns nothing. - */ -static void __init ccs_update_security_ops(struct list_head *lsm_list) -{ - /* Security context allocator. */ - swap_security_ops(cred_free, lsm_list); - swap_security_ops(cred_prepare, lsm_list); - swap_security_ops(cred_alloc_blank, lsm_list); - add_security_ops(cred_transfer, lsm_list); - add_security_ops(task_create, lsm_list); - /* Security context updater for successful execve(). */ - add_security_ops(bprm_check_security, lsm_list); - add_security_ops(bprm_committing_creds, lsm_list); - /* Various permission checker. */ - add_security_ops(file_open, lsm_list); - add_security_ops(file_fcntl, lsm_list); - add_security_ops(file_ioctl, lsm_list); - add_security_ops(sb_pivotroot, lsm_list); - add_security_ops(sb_mount, lsm_list); - add_security_ops(sb_umount, lsm_list); -#ifdef CONFIG_SECURITY_PATH - add_security_ops(path_mknod, lsm_list); - add_security_ops(path_mkdir, lsm_list); - add_security_ops(path_rmdir, lsm_list); - add_security_ops(path_unlink, lsm_list); - add_security_ops(path_symlink, lsm_list); - add_security_ops(path_rename, lsm_list); - add_security_ops(path_link, lsm_list); - add_security_ops(path_truncate, lsm_list); - add_security_ops(path_chmod, lsm_list); - add_security_ops(path_chown, lsm_list); - add_security_ops(path_chroot, lsm_list); -#else - add_security_ops(inode_mknod, lsm_list); - add_security_ops(inode_mkdir, lsm_list); - add_security_ops(inode_rmdir, lsm_list); - add_security_ops(inode_unlink, lsm_list); - add_security_ops(inode_symlink, lsm_list); - add_security_ops(inode_rename, lsm_list); - add_security_ops(inode_link, lsm_list); - add_security_ops(inode_create, lsm_list); - add_security_ops(inode_setattr, lsm_list); -#endif - add_security_ops(inode_getattr, lsm_list); -#ifdef CONFIG_SECURITY_NETWORK - add_security_ops(inode_free_security, lsm_list); - add_security_ops(socket_bind, lsm_list); - add_security_ops(socket_connect, lsm_list); - add_security_ops(socket_listen, lsm_list); - add_security_ops(socket_sendmsg, lsm_list); - add_security_ops(socket_recvmsg, lsm_list); - add_security_ops(socket_getsockname, lsm_list); - add_security_ops(socket_getpeername, lsm_list); - add_security_ops(socket_getsockopt, lsm_list); - add_security_ops(socket_setsockopt, lsm_list); - add_security_ops(socket_shutdown, lsm_list); - add_security_ops(socket_accept, lsm_list); -#endif -} - -#undef swap_security_ops -#undef add_security_ops - -/** - * ccs_init - Initialize this module. - * - * Returns 0 on success, negative value otherwise. - */ -static int __init ccs_init(void) -{ - int idx; - struct list_head *hooks = probe_lsm_hooks_list(); - if (!hooks) - goto out; - ccsecurity_exports.find_task_by_vpid = probe_find_task_by_vpid(); - if (!ccsecurity_exports.find_task_by_vpid) - goto out; - ccsecurity_exports.find_task_by_pid_ns = probe_find_task_by_pid_ns(); - if (!ccsecurity_exports.find_task_by_pid_ns) - goto out; - ccsecurity_exports.d_absolute_path = probe_d_absolute_path(); - if (!ccsecurity_exports.d_absolute_path) - goto out; - for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) { - INIT_LIST_HEAD(&ccs_cred_security_list[idx]); - INIT_LIST_HEAD(&ccs_task_security_list[idx]); - } - ccs_main_init(); - ccs_update_security_ops(hooks); - printk(KERN_INFO "AKARI: 1.0.30 2013/02/14\n"); - printk(KERN_INFO - "Access Keeping And Regulating Instrument registered.\n"); - return 0; -out: - return -EINVAL; -} - -module_init(ccs_init); -MODULE_LICENSE("GPL"); - -/** - * ccs_used_by_cred - Check whether the given domain is in use or not. - * - * @domain: Pointer to "struct ccs_domain_info". - * - * Returns true if @domain is in use, false otherwise. - * - * Caller holds rcu_read_lock(). - */ -bool ccs_used_by_cred(const struct ccs_domain_info *domain) -{ - int idx; - struct ccs_security *ptr; - for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) { - struct list_head *list = &ccs_cred_security_list[idx]; - list_for_each_entry_rcu(ptr, list, list) { - struct ccs_execve *ee = ptr->ee; - if (ptr->ccs_domain_info == domain || - (ee && ee->previous_domain == domain)) { - return true; - } - } - } - return false; -} - -/** - * 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_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->pid != task->pids[PIDTYPE_PID].pid) - continue; - rcu_read_unlock(); - /* - * Current thread needs to transit from old domain to new - * domain before do_execve() succeeds in order to check - * permission for interpreters and environment variables using - * new domain's ACL rules. The domain transition has to be - * visible from other CPU in order to allow interactive - * enforcing mode. Also, the domain transition has to be - * reverted if do_execve() failed. However, an LSM hook for - * reverting domain transition is missing. - * - * security_prepare_creds() is called from prepare_creds() from - * prepare_bprm_creds() from do_execve() before setting - * current->in_execve flag, and current->in_execve flag is - * cleared by the time next do_execve() request starts. - * This means that we can emulate the missing LSM hook for - * reverting domain transition, by calling this function from - * security_prepare_creds(). - * - * If current->in_execve is not set but ptr->ccs_flags has - * CCS_TASK_IS_IN_EXECVE set, it indicates that do_execve() - * has failed and reverting domain transition is needed. - */ - if (task == current && - (ptr->ccs_flags & CCS_TASK_IS_IN_EXECVE) && - !current->in_execve) { - ccs_debug_trace("4"); - ccs_clear_execve(-1, ptr); - } - return ptr; - } - rcu_read_unlock(); - if (task != current) { - /* - * If a thread does nothing after fork(), caller will reach - * here because "struct ccs_security" for that thread is not - * yet allocated. But that thread is keeping a snapshot of - * "struct ccs_security" taken as of ccs_task_create() - * associated with that thread's "struct cred". - * - * Since that snapshot will be used as initial data when that - * thread allocates "struct ccs_security" for that thread, we - * can return that snapshot rather than &ccs_default_security. - * - * Since this function is called by only ccs_select_one() and - * ccs_read_pid() (via ccs_task_domain() and ccs_task_flags()), - * it is guaranteed that caller has called rcu_read_lock() - * (via ccs_tasklist_lock()) before finding this thread and - * this thread is valid. Therefore, we can do __task_cred(task) - * like get_robust_list() does. - */ - return ccs_find_cred_security(__task_cred(task)); - } - /* 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_find_cred_security(task->cred); - /* We can shortcut because task == current. */ - ptr->pid = get_pid(((struct task_struct *) task)-> - pids[PIDTYPE_PID].pid); - ptr->cred = NULL; - ccs_add_task_security(ptr, list); - return ptr; -} - -/** - * ccs_copy_cred_security - Allocate memory for new credentials. - * - * @new: Pointer to "struct cred". - * @old: Pointer to "struct cred". - * @gfp: Memory allocation flags. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_copy_cred_security(const struct cred *new, - const struct cred *old, gfp_t gfp) -{ - struct ccs_security *old_security = ccs_find_cred_security(old); - struct ccs_security *new_security = - kzalloc(sizeof(*new_security), gfp); - if (!new_security) - return -ENOMEM; - *new_security = *old_security; - new_security->cred = new; - ccs_add_cred_security(new_security); - return 0; -} - -/** - * ccs_find_cred_security - Find "struct ccs_security" for given credential. - * - * @cred: Pointer to "struct cred". - * - * Returns pointer to "struct ccs_security" on success, &ccs_default_security - * otherwise. - */ -static struct ccs_security *ccs_find_cred_security(const struct cred *cred) -{ - struct ccs_security *ptr; - struct list_head *list = &ccs_cred_security_list - [hash_ptr((void *) cred, CCS_TASK_SECURITY_HASH_BITS)]; - rcu_read_lock(); - list_for_each_entry_rcu(ptr, list, list) { - if (ptr->cred != cred) - continue; - rcu_read_unlock(); - return ptr; - } - rcu_read_unlock(); - return &ccs_default_security; -} - -/** - * ccs_task_security_gc - Do garbage collection for "struct task_struct". - * - * Returns nothing. - * - * Since security_task_free() is missing, I can't release memory associated - * with "struct task_struct" when a task dies. Therefore, I hold a reference on - * "struct pid" and runs garbage collection when associated - * "struct task_struct" has gone. - */ -static void ccs_task_security_gc(void) -{ - static DEFINE_SPINLOCK(lock); - static atomic_t gc_counter = ATOMIC_INIT(0); - unsigned int idx; - /* - * If some process is doing execve(), try to garbage collection now. - * We should kfree() memory associated with "struct ccs_security"->ee - * as soon as execve() has completed in order to compensate for lack of - * security_bprm_free() and security_task_free() hooks. - * - * Otherwise, reduce frequency for performance reason. - */ - if (!atomic_read(&ccs_in_execve_tasks) && - atomic_inc_return(&gc_counter) < 1024) - return; - if (!spin_trylock(&lock)) - return; - atomic_set(&gc_counter, 0); - 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 (pid_task(ptr->pid, PIDTYPE_PID)) - continue; - ccs_del_security(ptr); - } - } - rcu_read_unlock(); - spin_unlock(&lock); -} diff --git a/akari/memory.c b/akari/memory.c deleted file mode 100644 index 1ce3630..0000000 --- a/akari/memory.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * security/ccsecurity/memory.c - * - * Copyright (C) 2005-2012 NTT DATA CORPORATION - * - * Version: 1.8.3+ 2012/05/05 - */ - -#include "internal.h" - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) -/* Use functions in lsm.c */ -#undef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY -#endif - -/***** 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. */ -struct ccs_security ccs_oom_security = { - .ccs_domain_info = &ccs_kernel_domain -}; - -/* Dummy security context for avoiding NULL pointer dereference. */ -struct ccs_security ccs_default_security = { - .ccs_domain_info = &ccs_kernel_domain -}; - -/* List of "struct ccs_security". */ -struct list_head ccs_task_security_list[CCS_MAX_TASK_SECURITY_HASH]; -/* Lock for protecting ccs_task_security_list[]. */ -static DEFINE_SPINLOCK(ccs_task_security_list_lock); - -#endif - -/***** SECTION6: Dependent functions section *****/ - -/** - * ccs_warn_oom - Print out of memory warning message. - * - * @function: Function's name. - * - * Returns nothing. - */ -void ccs_warn_oom(const char *function) -{ - /* Reduce error messages. */ - static pid_t ccs_last_pid; - const pid_t pid = current->pid; - if (ccs_last_pid != pid) { - printk(KERN_WARNING "ERROR: Out of memory at %s.\n", - function); - ccs_last_pid = pid; - } - if (!ccs_policy_loaded) - panic("MAC Initialization failed.\n"); -} - -/** - * ccs_memory_ok - Check memory quota. - * - * @ptr: Pointer to allocated memory. Maybe NULL. - * @size: Size in byte. Not used if @ptr is NULL. - * - * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. - * - * Caller holds ccs_policy_lock mutex. - */ -bool ccs_memory_ok(const void *ptr, const unsigned int size) -{ - if (ptr) { - const size_t s = ccs_round2(size); - ccs_memory_used[CCS_MEMORY_POLICY] += s; - if (!ccs_memory_quota[CCS_MEMORY_POLICY] || - ccs_memory_used[CCS_MEMORY_POLICY] <= - ccs_memory_quota[CCS_MEMORY_POLICY]) - return true; - ccs_memory_used[CCS_MEMORY_POLICY] -= s; - } - ccs_warn_oom(__func__); - return false; -} - -/** - * ccs_commit_ok - Allocate memory and check memory quota. - * - * @data: Data to copy from. - * @size: Size in byte. - * - * Returns pointer to allocated memory on success, NULL otherwise. - * @data is zero-cleared on success. - * - * Caller holds ccs_policy_lock mutex. - */ -void *ccs_commit_ok(void *data, const unsigned int size) -{ - void *ptr = kmalloc(size, CCS_GFP_FLAGS); - if (ccs_memory_ok(ptr, size)) { - memmove(ptr, data, size); - memset(data, 0, size); - return ptr; - } - kfree(ptr); - return NULL; -} - -/** - * ccs_get_name - Allocate memory for string data. - * - * @name: The string to store into the permernent memory. - * - * Returns pointer to "struct ccs_path_info" on success, NULL otherwise. - */ -const struct ccs_path_info *ccs_get_name(const char *name) -{ - struct ccs_name *ptr; - unsigned int hash; - int len; - int allocated_len; - struct list_head *head; - - if (!name) - return NULL; - len = strlen(name) + 1; - hash = full_name_hash((const unsigned char *) name, len - 1); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) || defined(RHEL_MAJOR) - head = &ccs_name_list[hash_long(hash, CCS_HASH_BITS)]; -#else - head = &ccs_name_list[hash % CCS_MAX_HASH]; -#endif - if (mutex_lock_interruptible(&ccs_policy_lock)) - return NULL; - list_for_each_entry(ptr, head, head.list) { - if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) || - atomic_read(&ptr->head.users) == CCS_GC_IN_PROGRESS) - continue; - atomic_inc(&ptr->head.users); - goto out; - } - allocated_len = sizeof(*ptr) + len; - ptr = kzalloc(allocated_len, CCS_GFP_FLAGS); - if (ccs_memory_ok(ptr, allocated_len)) { - ptr->entry.name = ((char *) ptr) + sizeof(*ptr); - memmove((char *) ptr->entry.name, name, len); - atomic_set(&ptr->head.users, 1); - ccs_fill_path_info(&ptr->entry); - ptr->size = allocated_len; - list_add_tail(&ptr->head.list, head); - } else { - kfree(ptr); - ptr = NULL; - } -out: - mutex_unlock(&ccs_policy_lock); - return ptr ? &ptr->entry : NULL; -} - -#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY - -/** - * ccs_add_task_security - Add "struct ccs_security" to list. - * - * @ptr: Pointer to "struct ccs_security". - * @list: Pointer to "struct list_head". - * - * Returns nothing. - */ -static void ccs_add_task_security(struct ccs_security *ptr, - struct list_head *list) -{ - unsigned long flags; - spin_lock_irqsave(&ccs_task_security_list_lock, flags); - list_add_rcu(&ptr->list, list); - spin_unlock_irqrestore(&ccs_task_security_list_lock, flags); -} - -/** - * __ccs_alloc_task_security - Allocate memory for new tasks. - * - * @task: Pointer to "struct task_struct". - * - * Returns 0 on success, negative value otherwise. - */ -static int __ccs_alloc_task_security(const struct task_struct *task) -{ - struct ccs_security *old_security = ccs_current_security(); - struct ccs_security *new_security = kzalloc(sizeof(*new_security), - GFP_KERNEL); - struct list_head *list = &ccs_task_security_list - [hash_ptr((void *) task, CCS_TASK_SECURITY_HASH_BITS)]; - if (!new_security) - return -ENOMEM; - new_security->task = task; - new_security->ccs_domain_info = old_security->ccs_domain_info; - new_security->ccs_flags = old_security->ccs_flags; - ccs_add_task_security(new_security, list); - return 0; -} - -/** - * ccs_find_task_security - Find "struct ccs_security" for given task. - * - * @task: Pointer to "struct task_struct". - * - * Returns pointer to "struct ccs_security" on success, &ccs_oom_security on - * out of memory, &ccs_default_security otherwise. - * - * If @task is current thread and "struct ccs_security" for current thread was - * not found, I try to allocate it. But if allocation failed, current thread - * will be killed by SIGKILL. Note that if current->pid == 1, sending SIGKILL - * won't work. - */ -struct ccs_security *ccs_find_task_security(const struct task_struct *task) -{ - struct ccs_security *ptr; - struct list_head *list = &ccs_task_security_list - [hash_ptr((void *) task, CCS_TASK_SECURITY_HASH_BITS)]; - /* Make sure INIT_LIST_HEAD() in ccs_mm_init() takes effect. */ - while (!list->next); - rcu_read_lock(); - list_for_each_entry_rcu(ptr, list, list) { - if (ptr->task != task) - continue; - rcu_read_unlock(); - return ptr; - } - rcu_read_unlock(); - if (task != current) - return &ccs_default_security; - /* Use GFP_ATOMIC because caller may have called rcu_read_lock(). */ - ptr = kzalloc(sizeof(*ptr), GFP_ATOMIC); - if (!ptr) { - printk(KERN_WARNING "Unable to allocate memory for pid=%u\n", - task->pid); - send_sig(SIGKILL, current, 0); - return &ccs_oom_security; - } - *ptr = ccs_default_security; - ptr->task = task; - ccs_add_task_security(ptr, list); - return ptr; -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) - -/** - * ccs_rcu_free - RCU callback for releasing "struct ccs_security". - * - * @rcu: Pointer to "struct rcu_head". - * - * Returns nothing. - */ -static void ccs_rcu_free(struct rcu_head *rcu) -{ - struct ccs_security *ptr = container_of(rcu, typeof(*ptr), rcu); - kfree(ptr); -} - -#else - -/** - * ccs_rcu_free - RCU callback for releasing "struct ccs_security". - * - * @arg: Pointer to "void". - * - * Returns nothing. - */ -static void ccs_rcu_free(void *arg) -{ - struct ccs_security *ptr = arg; - kfree(ptr); -} - -#endif - -/** - * __ccs_free_task_security - Release memory associated with "struct task_struct". - * - * @task: Pointer to "struct task_struct". - * - * Returns nothing. - */ -static void __ccs_free_task_security(const struct task_struct *task) -{ - unsigned long flags; - struct ccs_security *ptr = ccs_find_task_security(task); - if (ptr == &ccs_default_security || ptr == &ccs_oom_security) - return; - spin_lock_irqsave(&ccs_task_security_list_lock, flags); - list_del_rcu(&ptr->list); - spin_unlock_irqrestore(&ccs_task_security_list_lock, flags); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) - call_rcu(&ptr->rcu, ccs_rcu_free); -#else - call_rcu(&ptr->rcu, ccs_rcu_free, ptr); -#endif -} - -#endif - -/** - * ccs_mm_init - Initialize mm related code. - * - * Returns nothing. - */ -void __init ccs_mm_init(void) -{ - int idx; - for (idx = 0; idx < CCS_MAX_HASH; idx++) - INIT_LIST_HEAD(&ccs_name_list[idx]); -#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY - for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) - INIT_LIST_HEAD(&ccs_task_security_list[idx]); -#endif - smp_wmb(); /* Avoid out of order execution. */ -#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY - ccsecurity_ops.alloc_task_security = __ccs_alloc_task_security; - ccsecurity_ops.free_task_security = __ccs_free_task_security; -#endif - ccs_kernel_domain.domainname = ccs_get_name(""); - list_add_tail_rcu(&ccs_kernel_domain.list, &ccs_domain_list); -} diff --git a/akari/permission.c b/akari/permission.c deleted file mode 100644 index 9b6f70f..0000000 --- a/akari/permission.c +++ /dev/null @@ -1,5009 +0,0 @@ -/* - * security/ccsecurity/permission.c - * - * Copyright (C) 2005-2012 NTT DATA CORPORATION - * - * Version: 1.8.3+ 2012/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); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21) || LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) || !defined(CONFIG_SYSCTL_SYSCALL) -static -#endif -int ccs_path_permission(struct ccs_request_info *r, u8 operation, - const struct ccs_path_info *filename); -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); - -#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; - bool retried = false; - do { - struct ccs_acl_info *ptr; - const struct list_head *list = &domain->acl_info_list; -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; - } - if (!retried) { - retried = true; - list = &domain->ns->acl_group[domain->group]; - goto retry; - } - r->granted = false; - if (r->mode != CCS_CONFIG_DISABLED || - r->type != CCS_MAC_FILE_EXECUTE) - error = ccs_audit_log(r); - else - error = 0; - } 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.validate_done = false; - allow_write_access(bprm->file); - fput(bprm->file); - bprm->file = NULL; - - /* Invalidate page dump cache. */ - ee->dump.page = NULL; - - /* Move envp[] to argv[] */ - bprm->argc += bprm->envc; - bprm->envc = 0; - - /* Set argv[6] */ - { - snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_envc); - retval = ccs_copy_argv(ee->tmp, bprm); - if (retval < 0) - goto out; - } - - /* Set argv[5] */ - { - snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_argc); - retval = ccs_copy_argv(ee->tmp, bprm); - if (retval < 0) - goto out; - } - - /* Set argv[4] */ - { - retval = ccs_copy_argv(bprm->filename, bprm); - if (retval < 0) - goto out; - } - - /* Set argv[3] */ - { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) - /* - * Pass uid/gid seen from current user namespace, for these - * values are used by programs in current user namespace in - * order to decide whether to execve() or not (rather than by - * auditing daemon in init's user namespace). - */ - snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, - "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d " - "sgid=%d fsuid=%d fsgid=%d", ccs_sys_getpid(), - __kuid_val(current_uid()), __kgid_val(current_gid()), - __kuid_val(current_euid()), - __kgid_val(current_egid()), - __kuid_val(current_suid()), - __kgid_val(current_sgid()), - __kuid_val(current_fsuid()), - __kgid_val(current_fsgid())); -#else - snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, - "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d " - "sgid=%d fsuid=%d fsgid=%d", ccs_sys_getpid(), - current_uid(), current_gid(), current_euid(), - current_egid(), current_suid(), current_sgid(), - current_fsuid(), current_fsgid()); -#endif - retval = ccs_copy_argv(ee->tmp, bprm); - if (retval < 0) - goto out; - } - - /* Set argv[2] */ - { - char *exe = (char *) ccs_get_exe(); - if (exe) { - retval = ccs_copy_argv(exe, bprm); - kfree(exe); - } else { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) - retval = ccs_copy_argv("", bprm); -#else - snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, ""); - retval = ccs_copy_argv(ee->tmp, bprm); -#endif - } - if (retval < 0) - goto out; - } - - /* Set argv[1] */ - { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) - retval = ccs_copy_argv(ccs_current_domain()->domainname->name, - bprm); -#else - snprintf(ee->tmp, CCS_EXEC_TMPSIZE - 1, "%s", - ccs_current_domain()->domainname->name); - retval = ccs_copy_argv(ee->tmp, bprm); -#endif - if (retval < 0) - goto out; - } - - /* Set argv[0] */ - { - struct path root; - char *cp; - int root_len; - int handler_len; - get_fs_root(current->fs, &root); - cp = ccs_realpath(&root); - path_put(&root); - if (!cp) { - retval = -ENOMEM; - goto out; - } - root_len = strlen(cp); - retval = strncmp(ee->handler->name, cp, root_len); - root_len--; - kfree(cp); - if (retval) { - retval = -ENOENT; - goto out; - } - handler_len = ee->handler->total_len + 1; - cp = kmalloc(handler_len, CCS_GFP_FLAGS); - if (!cp) { - retval = -ENOMEM; - goto out; - } - /* ee->handler_path is released by ccs_finish_execve(). */ - ee->handler_path = cp; - /* Adjust root directory for open_exec(). */ - memmove(cp, ee->handler->name + root_len, - handler_len - root_len); - ccs_unescape(cp); - retval = -ENOENT; - if (*cp != '/') - goto out; - retval = ccs_copy_argv(cp, bprm); - if (retval < 0) - goto out; - } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24) - bprm->argv_len = bprm->exec - bprm->p; -#endif -#endif - - /* - * OK, now restart the process with execute handler program's dentry. - */ - filp = open_exec(ee->handler_path); - if (IS_ERR(filp)) { - retval = PTR_ERR(filp); - goto out; - } - ee->obj.path1.dentry = filp->f_dentry; - ee->obj.path1.mnt = filp->f_vfsmnt; - 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. - */ -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; - } - ccs_audit_alloc_execve(ee); - 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; - ee->obj.path1.dentry = bprm->file->f_dentry; - ee->obj.path1.mnt = bprm->file->f_vfsmnt; -#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. - */ -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; - ccs_audit_free_execve(ee, true); - kfree(ee->handler_path); - kfree(ee); -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) - -/** - * __ccs_search_binary_handler - Main routine for do_execve(). - * - * @bprm: Pointer to "struct linux_binprm". - * - * Returns 0 on success, negative value otherwise. - * - * Performs permission checks for do_execve() and domain transition. - * Domain transition by "struct ccs_domain_transition_control" and - * "auto_domain_transition=" parameter of "struct ccs_condition" are reverted - * if do_execve() failed. - * Garbage collector does not remove "struct ccs_domain_info" from - * ccs_domain_list nor kfree("struct ccs_domain_info") if the current thread is - * marked as CCS_TASK_IS_IN_EXECVE. - */ -static int __ccs_search_binary_handler(struct linux_binprm *bprm) -{ - struct ccs_execve *ee; - int retval; -#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER - if (!ccs_policy_loaded) - ccsecurity_exports.load_policy(bprm->filename); -#endif - retval = ccs_start_execve(bprm, &ee); - if (!retval) - retval = search_binary_handler(bprm); - ccs_finish_execve(retval, ee); - return retval; -} - -#else - -/** - * __ccs_search_binary_handler - Main routine for do_execve(). - * - * @bprm: Pointer to "struct linux_binprm". - * @regs: Pointer to "struct pt_regs". - * - * Returns 0 on success, negative value otherwise. - * - * Performs permission checks for do_execve() and domain transition. - * Domain transition by "struct ccs_domain_transition_control" and - * "auto_domain_transition=" parameter of "struct ccs_condition" are reverted - * if do_execve() failed. - * Garbage collector does not remove "struct ccs_domain_info" from - * ccs_domain_list nor kfree("struct ccs_domain_info") if the current thread is - * marked as CCS_TASK_IS_IN_EXECVE. - */ -static int __ccs_search_binary_handler(struct linux_binprm *bprm, - struct pt_regs *regs) -{ - struct ccs_execve *ee; - int retval; -#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER - if (!ccs_policy_loaded) - ccsecurity_exports.load_policy(bprm->filename); -#endif - retval = ccs_start_execve(bprm, &ee); - if (!retval) - retval = search_binary_handler(bprm, regs); - ccs_finish_execve(retval, ee); - return retval; -} - -#endif - -/** - * ccs_permission_init - Register permission check hooks. - * - * Returns nothing. - */ -void __init ccs_permission_init(void) -{ -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) - ccsecurity_ops.save_open_mode = __ccs_save_open_mode; - ccsecurity_ops.clear_open_mode = __ccs_clear_open_mode; - ccsecurity_ops.open_permission = __ccs_open_permission; -#else - ccsecurity_ops.open_permission = ccs_new_open_permission; -#endif - ccsecurity_ops.fcntl_permission = __ccs_fcntl_permission; - ccsecurity_ops.ioctl_permission = __ccs_ioctl_permission; - ccsecurity_ops.chmod_permission = __ccs_chmod_permission; - ccsecurity_ops.chown_permission = __ccs_chown_permission; -#ifdef CONFIG_CCSECURITY_FILE_GETATTR - ccsecurity_ops.getattr_permission = __ccs_getattr_permission; -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) - ccsecurity_ops.pivot_root_permission = __ccs_pivot_root_permission; - ccsecurity_ops.chroot_permission = __ccs_chroot_permission; -#else - ccsecurity_ops.pivot_root_permission = ccs_old_pivot_root_permission; - ccsecurity_ops.chroot_permission = ccs_old_chroot_permission; -#endif - ccsecurity_ops.umount_permission = __ccs_umount_permission; - ccsecurity_ops.mknod_permission = __ccs_mknod_permission; - ccsecurity_ops.mkdir_permission = __ccs_mkdir_permission; - ccsecurity_ops.rmdir_permission = __ccs_rmdir_permission; - ccsecurity_ops.unlink_permission = __ccs_unlink_permission; - ccsecurity_ops.symlink_permission = __ccs_symlink_permission; - ccsecurity_ops.truncate_permission = __ccs_truncate_permission; - ccsecurity_ops.rename_permission = __ccs_rename_permission; - ccsecurity_ops.link_permission = __ccs_link_permission; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) - ccsecurity_ops.open_exec_permission = __ccs_open_exec_permission; - ccsecurity_ops.uselib_permission = __ccs_uselib_permission; -#endif -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) || (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL)) - ccsecurity_ops.parse_table = __ccs_parse_table; -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) - ccsecurity_ops.mount_permission = __ccs_mount_permission; -#else - ccsecurity_ops.mount_permission = ccs_old_mount_permission; -#endif -#ifdef CONFIG_CCSECURITY_CAPABILITY - ccsecurity_ops.socket_create_permission = - __ccs_socket_create_permission; -#endif -#ifdef CONFIG_CCSECURITY_NETWORK - ccsecurity_ops.socket_listen_permission = - __ccs_socket_listen_permission; - ccsecurity_ops.socket_connect_permission = - __ccs_socket_connect_permission; - ccsecurity_ops.socket_bind_permission = __ccs_socket_bind_permission; - ccsecurity_ops.socket_post_accept_permission = - __ccs_socket_post_accept_permission; - ccsecurity_ops.socket_sendmsg_permission = - __ccs_socket_sendmsg_permission; -#endif -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG - ccsecurity_ops.socket_post_recvmsg_permission = - __ccs_socket_post_recvmsg_permission; -#endif -#ifdef CONFIG_CCSECURITY_IPC - ccsecurity_ops.kill_permission = ccs_signal_acl; - ccsecurity_ops.tgkill_permission = ccs_signal_acl0; - ccsecurity_ops.tkill_permission = ccs_signal_acl; - ccsecurity_ops.sigqueue_permission = ccs_signal_acl; - ccsecurity_ops.tgsigqueue_permission = ccs_signal_acl0; -#endif -#ifdef CONFIG_CCSECURITY_CAPABILITY - ccsecurity_ops.capable = __ccs_capable; - ccsecurity_ops.ptrace_permission = __ccs_ptrace_permission; -#endif - ccsecurity_ops.search_binary_handler = __ccs_search_binary_handler; -} - -/** - * ccs_kern_path - Wrapper for kern_path(). - * - * @pathname: Pathname to resolve. Maybe NULL. - * @flags: Lookup flags. - * @path: Pointer to "struct path". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_kern_path(const char *pathname, int flags, struct path *path) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) - if (!pathname || kern_path(pathname, flags, path)) - return -ENOENT; -#else - struct nameidata nd; - if (!pathname || path_lookup(pathname, flags, &nd)) - return -ENOENT; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) - *path = nd.path; -#else - path->dentry = nd.dentry; - path->mnt = nd.mnt; -#endif -#endif - return 0; -} - -/** - * ccs_get_path - Get dentry/vfsmmount of a pathname. - * - * @pathname: The pathname to solve. Maybe NULL. - * @path: Pointer to "struct path". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_get_path(const char *pathname, struct path *path) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) - return ccs_kern_path(pathname, LOOKUP_FOLLOW, path); -#else - return ccs_kern_path(pathname, LOOKUP_FOLLOW | LOOKUP_POSITIVE, path); -#endif -} - -/** - * ccs_symlink_path - Get symlink's pathname. - * - * @pathname: The pathname to solve. Maybe NULL. - * @name: Pointer to "struct ccs_path_info". - * - * Returns 0 on success, negative value otherwise. - * - * This function uses kzalloc(), so caller must kfree() if this function - * didn't return NULL. - */ -static int ccs_symlink_path(const char *pathname, struct ccs_path_info *name) -{ - char *buf; - struct path path; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) - if (ccs_kern_path(pathname, 0, &path)) - return -ENOENT; -#else - if (ccs_kern_path(pathname, LOOKUP_POSITIVE, &path)) - return -ENOENT; -#endif - buf = ccs_realpath(&path); - path_put(&path); - if (buf) { - name->name = buf; - ccs_fill_path_info(name); - return 0; - } - return -ENOMEM; -} - -/** - * ccs_check_mount_acl - Check permission for path path path number operation. - * - * @r: Pointer to "struct ccs_request_info". - * @ptr: Pointer to "struct ccs_acl_info". - * - * Returns true if granted, false otherwise. - */ -static bool ccs_check_mount_acl(struct ccs_request_info *r, - const struct ccs_acl_info *ptr) -{ - const struct ccs_mount_acl *acl = - container_of(ptr, typeof(*acl), head); - return ccs_compare_number_union(r->param.mount.flags, &acl->flags) && - ccs_compare_name_union(r->param.mount.type, &acl->fs_type) && - ccs_compare_name_union(r->param.mount.dir, &acl->dir_name) && - (!r->param.mount.need_dev || - ccs_compare_name_union(r->param.mount.dev, &acl->dev_name)); -} - -/** - * ccs_mount_acl - Check permission for mount() operation. - * - * @r: Pointer to "struct ccs_request_info". - * @dev_name: Name of device file. Maybe NULL. - * @dir: Pointer to "struct path". - * @type: Name of filesystem type. - * @flags: Mount options. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds ccs_read_lock(). - */ -static int ccs_mount_acl(struct ccs_request_info *r, const char *dev_name, - struct path *dir, const char *type, - unsigned long flags) -{ - struct ccs_obj_info obj = { }; - struct file_system_type *fstype = NULL; - const char *requested_type = NULL; - const char *requested_dir_name = NULL; - const char *requested_dev_name = NULL; - struct ccs_path_info rtype; - struct ccs_path_info rdev; - struct ccs_path_info rdir; - int need_dev = 0; - int error = -ENOMEM; - r->obj = &obj; - - /* Get fstype. */ - requested_type = ccs_encode(type); - if (!requested_type) - goto out; - rtype.name = requested_type; - ccs_fill_path_info(&rtype); - - /* Get mount point. */ - obj.path2 = *dir; - requested_dir_name = ccs_realpath(dir); - if (!requested_dir_name) { - error = -ENOMEM; - goto out; - } - rdir.name = requested_dir_name; - ccs_fill_path_info(&rdir); - - /* Compare fs name. */ - if (type == ccs_mounts[CCS_MOUNT_REMOUNT]) { - /* dev_name is ignored. */ - } else if (type == ccs_mounts[CCS_MOUNT_MAKE_UNBINDABLE] || - type == ccs_mounts[CCS_MOUNT_MAKE_PRIVATE] || - type == ccs_mounts[CCS_MOUNT_MAKE_SLAVE] || - type == ccs_mounts[CCS_MOUNT_MAKE_SHARED]) { - /* dev_name is ignored. */ - } else if (type == ccs_mounts[CCS_MOUNT_BIND] || - type == ccs_mounts[CCS_MOUNT_MOVE]) { - need_dev = -1; /* dev_name is a directory */ - } else { - fstype = get_fs_type(type); - if (!fstype) { - error = -ENODEV; - goto out; - } - if (fstype->fs_flags & FS_REQUIRES_DEV) - /* dev_name is a block device file. */ - need_dev = 1; - } - if (need_dev) { - /* Get mount point or device file. */ - if (ccs_get_path(dev_name, &obj.path1)) { - error = -ENOENT; - goto out; - } - requested_dev_name = ccs_realpath(&obj.path1); - if (!requested_dev_name) { - error = -ENOENT; - goto out; - } - } else { - /* Map dev_name to "" if no dev_name given. */ - if (!dev_name) - dev_name = ""; - requested_dev_name = ccs_encode(dev_name); - if (!requested_dev_name) { - error = -ENOMEM; - goto out; - } - } - rdev.name = requested_dev_name; - ccs_fill_path_info(&rdev); - r->param_type = CCS_TYPE_MOUNT_ACL; - r->param.mount.need_dev = need_dev; - r->param.mount.dev = &rdev; - r->param.mount.dir = &rdir; - r->param.mount.type = &rtype; - r->param.mount.flags = flags; - error = ccs_check_acl(r); -out: - kfree(requested_dev_name); - kfree(requested_dir_name); - if (fstype) - ccs_put_filesystem(fstype); - kfree(requested_type); - /* Drop refcount obtained by ccs_get_path(). */ - if (obj.path1.dentry) - path_put(&obj.path1); - return error; -} - -/** - * __ccs_mount_permission - Check permission for mount() operation. - * - * @dev_name: Name of device file. Maybe NULL. - * @path: Pointer to "struct path". - * @type: Name of filesystem type. Maybe NULL. - * @flags: Mount options. - * @data_page: Optional data. Maybe NULL. - * - * Returns 0 on success, negative value otherwise. - */ -static int __ccs_mount_permission(const char *dev_name, struct path *path, - const char *type, unsigned long flags, - void *data_page) -{ - struct ccs_request_info r; - int error = 0; - int idx; - if ((flags & MS_MGC_MSK) == MS_MGC_VAL) - flags &= ~MS_MGC_MSK; - if (flags & MS_REMOUNT) { - type = ccs_mounts[CCS_MOUNT_REMOUNT]; - flags &= ~MS_REMOUNT; - } else if (flags & MS_BIND) { - type = ccs_mounts[CCS_MOUNT_BIND]; - flags &= ~MS_BIND; - } else if (flags & MS_SHARED) { - if (flags & (MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) - return -EINVAL; - type = ccs_mounts[CCS_MOUNT_MAKE_SHARED]; - flags &= ~MS_SHARED; - } else if (flags & MS_PRIVATE) { - if (flags & (MS_SHARED | MS_SLAVE | MS_UNBINDABLE)) - return -EINVAL; - type = ccs_mounts[CCS_MOUNT_MAKE_PRIVATE]; - flags &= ~MS_PRIVATE; - } else if (flags & MS_SLAVE) { - if (flags & (MS_SHARED | MS_PRIVATE | MS_UNBINDABLE)) - return -EINVAL; - type = ccs_mounts[CCS_MOUNT_MAKE_SLAVE]; - flags &= ~MS_SLAVE; - } else if (flags & MS_UNBINDABLE) { - if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE)) - return -EINVAL; - type = ccs_mounts[CCS_MOUNT_MAKE_UNBINDABLE]; - flags &= ~MS_UNBINDABLE; - } else if (flags & MS_MOVE) { - type = ccs_mounts[CCS_MOUNT_MOVE]; - flags &= ~MS_MOVE; - } - if (!type) - type = ""; - idx = ccs_read_lock(); - if (ccs_init_request_info(&r, CCS_MAC_FILE_MOUNT) - != CCS_CONFIG_DISABLED) - error = ccs_mount_acl(&r, dev_name, path, type, flags); - ccs_read_unlock(idx); - return error; -} - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24) - -/** - * ccs_old_mount_permission - Check permission for mount() operation. - * - * @dev_name: Name of device file. - * @nd: Pointer to "struct nameidata". - * @type: Name of filesystem type. Maybe NULL. - * @flags: Mount options. - * @data_page: Optional data. Maybe NULL. - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_old_mount_permission(const char *dev_name, struct nameidata *nd, - const char *type, unsigned long flags, - void *data_page) -{ - struct path path = { nd->mnt, nd->dentry }; - return __ccs_mount_permission(dev_name, &path, type, flags, data_page); -} - -#endif - -/** - * ccs_compare_number_union - Check whether a value matches "struct ccs_number_union" or not. - * - * @value: Number to check. - * @ptr: Pointer to "struct ccs_number_union". - * - * Returns true if @value matches @ptr, false otherwise. - */ -static bool ccs_compare_number_union(const unsigned long value, - const struct ccs_number_union *ptr) -{ - if (ptr->group) - return ccs_number_matches_group(value, value, ptr->group); - return value >= ptr->values[0] && value <= ptr->values[1]; -} - -/** - * ccs_compare_name_union - Check whether a name matches "struct ccs_name_union" or not. - * - * @name: Pointer to "struct ccs_path_info". - * @ptr: Pointer to "struct ccs_name_union". - * - * Returns "struct ccs_path_info" if @name matches @ptr, NULL otherwise. - */ -static const struct ccs_path_info *ccs_compare_name_union -(const struct ccs_path_info *name, const struct ccs_name_union *ptr) -{ - if (ptr->group) - return ccs_path_matches_group(name, ptr->group); - if (ccs_path_matches_pattern(name, ptr->filename)) - return ptr->filename; - return NULL; -} - -/** - * ccs_add_slash - Add trailing '/' if needed. - * - * @buf: Pointer to "struct ccs_path_info". - * - * Returns nothing. - * - * @buf must be generated by ccs_encode() because this function does not - * allocate memory for adding '/'. - */ -static void ccs_add_slash(struct ccs_path_info *buf) -{ - if (buf->is_dir) - return; - /* This is OK because ccs_encode() reserves space for appending "/". */ - strcat((char *) buf->name, "/"); - ccs_fill_path_info(buf); -} - -/** - * ccs_get_realpath - Get realpath. - * - * @buf: Pointer to "struct ccs_path_info". - * @path: Pointer to "struct path". @path->mnt may be NULL. - * - * Returns true on success, false otherwise. - */ -static bool ccs_get_realpath(struct ccs_path_info *buf, struct path *path) -{ - buf->name = ccs_realpath(path); - if (buf->name) { - ccs_fill_path_info(buf); - return true; - } - return false; -} - -/** - * ccs_check_path_acl - Check permission for path operation. - * - * @r: Pointer to "struct ccs_request_info". - * @ptr: Pointer to "struct ccs_acl_info". - * - * Returns true if granted, false otherwise. - * - * To be able to use wildcard for domain transition, this function sets - * matching entry on success. Since the caller holds ccs_read_lock(), - * it is safe to set matching entry. - */ -static bool ccs_check_path_acl(struct ccs_request_info *r, - const struct ccs_acl_info *ptr) -{ - const struct ccs_path_acl *acl = container_of(ptr, typeof(*acl), head); - if (ptr->perm & (1 << r->param.path.operation)) { - r->param.path.matched_path = - ccs_compare_name_union(r->param.path.filename, - &acl->name); - return r->param.path.matched_path != NULL; - } - return false; -} - -/** - * ccs_check_path_number_acl - Check permission for path number operation. - * - * @r: Pointer to "struct ccs_request_info". - * @ptr: Pointer to "struct ccs_acl_info". - * - * Returns true if granted, false otherwise. - */ -static bool ccs_check_path_number_acl(struct ccs_request_info *r, - const struct ccs_acl_info *ptr) -{ - const struct ccs_path_number_acl *acl = - container_of(ptr, typeof(*acl), head); - return (ptr->perm & (1 << r->param.path_number.operation)) && - ccs_compare_number_union(r->param.path_number.number, - &acl->number) && - ccs_compare_name_union(r->param.path_number.filename, - &acl->name); -} - -/** - * ccs_check_path2_acl - Check permission for path path operation. - * - * @r: Pointer to "struct ccs_request_info". - * @ptr: Pointer to "struct ccs_acl_info". - * - * Returns true if granted, false otherwise. - */ -static bool ccs_check_path2_acl(struct ccs_request_info *r, - const struct ccs_acl_info *ptr) -{ - const struct ccs_path2_acl *acl = - container_of(ptr, typeof(*acl), head); - return (ptr->perm & (1 << r->param.path2.operation)) && - ccs_compare_name_union(r->param.path2.filename1, &acl->name1) - && ccs_compare_name_union(r->param.path2.filename2, - &acl->name2); -} - -/** - * ccs_check_mkdev_acl - Check permission for path number number number operation. - * - * @r: Pointer to "struct ccs_request_info". - * @ptr: Pointer to "struct ccs_acl_info". - * - * Returns true if granted, false otherwise. - */ -static bool ccs_check_mkdev_acl(struct ccs_request_info *r, - const struct ccs_acl_info *ptr) -{ - const struct ccs_mkdev_acl *acl = - container_of(ptr, typeof(*acl), head); - return (ptr->perm & (1 << r->param.mkdev.operation)) && - ccs_compare_number_union(r->param.mkdev.mode, &acl->mode) && - ccs_compare_number_union(r->param.mkdev.major, &acl->major) && - ccs_compare_number_union(r->param.mkdev.minor, &acl->minor) && - ccs_compare_name_union(r->param.mkdev.filename, &acl->name); -} - -/** - * ccs_path_permission - Check permission for path operation. - * - * @r: Pointer to "struct ccs_request_info". - * @operation: Type of operation. - * @filename: Filename to check. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds ccs_read_lock(). - */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21) || LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) || !defined(CONFIG_SYSCTL_SYSCALL) -static -#endif -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_GETATTR - if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) - return 0; -#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; -#ifdef CONFIG_SECURITY_PATH - 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 (!dentry1->d_inode || !S_ISDIR(dentry1->d_inode->i_mode)) - break; - /* 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) -{ - return ccs_path_number_perm(CCS_TYPE_IOCTL, filp->f_dentry, - filp->f_vfsmnt, cmd); -} - -/** - * __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) -{ - if (mode == (mode_t) -1) - return 0; - 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_dentry, file->f_vfsmnt, - 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(). - */ -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/akari/policy_io.c b/akari/policy_io.c deleted file mode 100644 index 6f2d01d..0000000 --- a/akari/policy_io.c +++ /dev/null @@ -1,6451 +0,0 @@ -/* - * security/ccsecurity/policy_io.c - * - * Copyright (C) 2005-2012 NTT DATA CORPORATION - * - * Version: 1.8.3+ 2012/05/05 - */ - -#include "internal.h" - -/***** SECTION1: Constants definition *****/ - -/* Define this to enable debug mode. */ -/* #define DEBUG_CONDITION */ - -#ifdef DEBUG_CONDITION -#define dprintk printk -#else -#define dprintk(...) do { } while (0) -#endif - -/* Mapping table from "enum ccs_mac_index" to "enum ccs_mac_category_index". */ -static const u8 ccs_index2category[CCS_MAX_MAC_INDEX] = { - /* CONFIG::file group */ - [CCS_MAC_FILE_EXECUTE] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_OPEN] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_CREATE] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_UNLINK] = CCS_MAC_CATEGORY_FILE, -#ifdef CONFIG_CCSECURITY_FILE_GETATTR - [CCS_MAC_FILE_GETATTR] = CCS_MAC_CATEGORY_FILE, -#endif - [CCS_MAC_FILE_MKDIR] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_RMDIR] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_MKFIFO] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_MKSOCK] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_TRUNCATE] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_SYMLINK] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_MKBLOCK] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_MKCHAR] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_LINK] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_RENAME] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_CHMOD] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_CHOWN] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_CHGRP] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_IOCTL] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_CHROOT] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_MOUNT] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_UMOUNT] = CCS_MAC_CATEGORY_FILE, - [CCS_MAC_FILE_PIVOT_ROOT] = CCS_MAC_CATEGORY_FILE, -#ifdef CONFIG_CCSECURITY_MISC - /* CONFIG::misc group */ - [CCS_MAC_ENVIRON] = CCS_MAC_CATEGORY_MISC, -#endif -#ifdef CONFIG_CCSECURITY_NETWORK - /* CONFIG::network group */ - [CCS_MAC_NETWORK_INET_STREAM_BIND] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_INET_STREAM_LISTEN] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_INET_STREAM_CONNECT] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_INET_STREAM_ACCEPT] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_INET_DGRAM_BIND] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_INET_DGRAM_SEND] = CCS_MAC_CATEGORY_NETWORK, -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG - [CCS_MAC_NETWORK_INET_DGRAM_RECV] = CCS_MAC_CATEGORY_NETWORK, -#endif - [CCS_MAC_NETWORK_INET_RAW_BIND] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_INET_RAW_SEND] = CCS_MAC_CATEGORY_NETWORK, -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG - [CCS_MAC_NETWORK_INET_RAW_RECV] = CCS_MAC_CATEGORY_NETWORK, -#endif - [CCS_MAC_NETWORK_UNIX_STREAM_BIND] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_UNIX_STREAM_LISTEN] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_UNIX_STREAM_CONNECT] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_UNIX_DGRAM_BIND] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_UNIX_DGRAM_SEND] = CCS_MAC_CATEGORY_NETWORK, -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG - [CCS_MAC_NETWORK_UNIX_DGRAM_RECV] = CCS_MAC_CATEGORY_NETWORK, -#endif - [CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = CCS_MAC_CATEGORY_NETWORK, - [CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT] = CCS_MAC_CATEGORY_NETWORK, -#endif -#ifdef CONFIG_CCSECURITY_IPC - /* CONFIG::ipc group */ - [CCS_MAC_SIGNAL] = CCS_MAC_CATEGORY_IPC, -#endif -#ifdef CONFIG_CCSECURITY_CAPABILITY - /* CONFIG::capability group */ - [CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET] = CCS_MAC_CATEGORY_CAPABILITY, - [CCS_MAC_CAPABILITY_USE_PACKET_SOCKET] = CCS_MAC_CATEGORY_CAPABILITY, - [CCS_MAC_CAPABILITY_SYS_REBOOT] = CCS_MAC_CATEGORY_CAPABILITY, - [CCS_MAC_CAPABILITY_SYS_VHANGUP] = CCS_MAC_CATEGORY_CAPABILITY, - [CCS_MAC_CAPABILITY_SYS_SETTIME] = CCS_MAC_CATEGORY_CAPABILITY, - [CCS_MAC_CAPABILITY_SYS_NICE] = CCS_MAC_CATEGORY_CAPABILITY, - [CCS_MAC_CAPABILITY_SYS_SETHOSTNAME] = CCS_MAC_CATEGORY_CAPABILITY, - [CCS_MAC_CAPABILITY_USE_KERNEL_MODULE] = CCS_MAC_CATEGORY_CAPABILITY, - [CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD] = CCS_MAC_CATEGORY_CAPABILITY, - [CCS_MAC_CAPABILITY_SYS_PTRACE] = CCS_MAC_CATEGORY_CAPABILITY, -#endif -}; - -/* String table for operation mode. */ -static const char * const ccs_mode[CCS_CONFIG_MAX_MODE] = { - [CCS_CONFIG_DISABLED] = "disabled", - [CCS_CONFIG_LEARNING] = "learning", - [CCS_CONFIG_PERMISSIVE] = "permissive", - [CCS_CONFIG_ENFORCING] = "enforcing" -}; - -/* String table for /proc/ccs/profile interface. */ -static const char * const ccs_mac_keywords[CCS_MAX_MAC_INDEX - + CCS_MAX_MAC_CATEGORY_INDEX] = { - /* CONFIG::file group */ - [CCS_MAC_FILE_EXECUTE] = "execute", - [CCS_MAC_FILE_OPEN] = "open", - [CCS_MAC_FILE_CREATE] = "create", - [CCS_MAC_FILE_UNLINK] = "unlink", -#ifdef CONFIG_CCSECURITY_FILE_GETATTR - [CCS_MAC_FILE_GETATTR] = "getattr", -#endif - [CCS_MAC_FILE_MKDIR] = "mkdir", - [CCS_MAC_FILE_RMDIR] = "rmdir", - [CCS_MAC_FILE_MKFIFO] = "mkfifo", - [CCS_MAC_FILE_MKSOCK] = "mksock", - [CCS_MAC_FILE_TRUNCATE] = "truncate", - [CCS_MAC_FILE_SYMLINK] = "symlink", - [CCS_MAC_FILE_MKBLOCK] = "mkblock", - [CCS_MAC_FILE_MKCHAR] = "mkchar", - [CCS_MAC_FILE_LINK] = "link", - [CCS_MAC_FILE_RENAME] = "rename", - [CCS_MAC_FILE_CHMOD] = "chmod", - [CCS_MAC_FILE_CHOWN] = "chown", - [CCS_MAC_FILE_CHGRP] = "chgrp", - [CCS_MAC_FILE_IOCTL] = "ioctl", - [CCS_MAC_FILE_CHROOT] = "chroot", - [CCS_MAC_FILE_MOUNT] = "mount", - [CCS_MAC_FILE_UMOUNT] = "unmount", - [CCS_MAC_FILE_PIVOT_ROOT] = "pivot_root", -#ifdef CONFIG_CCSECURITY_MISC - /* CONFIG::misc group */ - [CCS_MAC_ENVIRON] = "env", -#endif -#ifdef CONFIG_CCSECURITY_NETWORK - /* CONFIG::network group */ - [CCS_MAC_NETWORK_INET_STREAM_BIND] = "inet_stream_bind", - [CCS_MAC_NETWORK_INET_STREAM_LISTEN] = "inet_stream_listen", - [CCS_MAC_NETWORK_INET_STREAM_CONNECT] = "inet_stream_connect", - [CCS_MAC_NETWORK_INET_STREAM_ACCEPT] = "inet_stream_accept", - [CCS_MAC_NETWORK_INET_DGRAM_BIND] = "inet_dgram_bind", - [CCS_MAC_NETWORK_INET_DGRAM_SEND] = "inet_dgram_send", -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG - [CCS_MAC_NETWORK_INET_DGRAM_RECV] = "inet_dgram_recv", -#endif - [CCS_MAC_NETWORK_INET_RAW_BIND] = "inet_raw_bind", - [CCS_MAC_NETWORK_INET_RAW_SEND] = "inet_raw_send", -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG - [CCS_MAC_NETWORK_INET_RAW_RECV] = "inet_raw_recv", -#endif - [CCS_MAC_NETWORK_UNIX_STREAM_BIND] = "unix_stream_bind", - [CCS_MAC_NETWORK_UNIX_STREAM_LISTEN] = "unix_stream_listen", - [CCS_MAC_NETWORK_UNIX_STREAM_CONNECT] = "unix_stream_connect", - [CCS_MAC_NETWORK_UNIX_STREAM_ACCEPT] = "unix_stream_accept", - [CCS_MAC_NETWORK_UNIX_DGRAM_BIND] = "unix_dgram_bind", - [CCS_MAC_NETWORK_UNIX_DGRAM_SEND] = "unix_dgram_send", -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG - [CCS_MAC_NETWORK_UNIX_DGRAM_RECV] = "unix_dgram_recv", -#endif - [CCS_MAC_NETWORK_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind", - [CCS_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen", - [CCS_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect", - [CCS_MAC_NETWORK_UNIX_SEQPACKET_ACCEPT] = "unix_seqpacket_accept", -#endif -#ifdef CONFIG_CCSECURITY_IPC - /* CONFIG::ipc group */ - [CCS_MAC_SIGNAL] = "signal", -#endif -#ifdef CONFIG_CCSECURITY_CAPABILITY - /* CONFIG::capability group */ - [CCS_MAC_CAPABILITY_USE_ROUTE_SOCKET] = "use_route", - [CCS_MAC_CAPABILITY_USE_PACKET_SOCKET] = "use_packet", - [CCS_MAC_CAPABILITY_SYS_REBOOT] = "SYS_REBOOT", - [CCS_MAC_CAPABILITY_SYS_VHANGUP] = "SYS_VHANGUP", - [CCS_MAC_CAPABILITY_SYS_SETTIME] = "SYS_TIME", - [CCS_MAC_CAPABILITY_SYS_NICE] = "SYS_NICE", - [CCS_MAC_CAPABILITY_SYS_SETHOSTNAME] = "SYS_SETHOSTNAME", - [CCS_MAC_CAPABILITY_USE_KERNEL_MODULE] = "use_kernel_module", - [CCS_MAC_CAPABILITY_SYS_KEXEC_LOAD] = "SYS_KEXEC_LOAD", - [CCS_MAC_CAPABILITY_SYS_PTRACE] = "SYS_PTRACE", -#endif - /* CONFIG group */ - [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_FILE] = "file", -#ifdef CONFIG_CCSECURITY_NETWORK - [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_NETWORK] = "network", -#endif -#ifdef CONFIG_CCSECURITY_MISC - [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_MISC] = "misc", -#endif -#ifdef CONFIG_CCSECURITY_IPC - [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_IPC] = "ipc", -#endif -#ifdef CONFIG_CCSECURITY_CAPABILITY - [CCS_MAX_MAC_INDEX + CCS_MAC_CATEGORY_CAPABILITY] = "capability", -#endif -}; - -/* String table for path operation. */ -static const char * const ccs_path_keyword[CCS_MAX_PATH_OPERATION] = { - [CCS_TYPE_EXECUTE] = "execute", - [CCS_TYPE_READ] = "read", - [CCS_TYPE_WRITE] = "write", - [CCS_TYPE_APPEND] = "append", - [CCS_TYPE_UNLINK] = "unlink", -#ifdef CONFIG_CCSECURITY_FILE_GETATTR - [CCS_TYPE_GETATTR] = "getattr", -#endif - [CCS_TYPE_RMDIR] = "rmdir", - [CCS_TYPE_TRUNCATE] = "truncate", - [CCS_TYPE_SYMLINK] = "symlink", - [CCS_TYPE_CHROOT] = "chroot", - [CCS_TYPE_UMOUNT] = "unmount", -}; - -#ifdef CONFIG_CCSECURITY_NETWORK - -/* String table for socket's operation. */ -static const char * const ccs_socket_keyword[CCS_MAX_NETWORK_OPERATION] = { - [CCS_NETWORK_BIND] = "bind", - [CCS_NETWORK_LISTEN] = "listen", - [CCS_NETWORK_CONNECT] = "connect", - [CCS_NETWORK_ACCEPT] = "accept", - [CCS_NETWORK_SEND] = "send", -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG - [CCS_NETWORK_RECV] = "recv", -#endif -}; - -/* String table for socket's protocols. */ -static const char * const ccs_proto_keyword[CCS_SOCK_MAX] = { - [SOCK_STREAM] = "stream", - [SOCK_DGRAM] = "dgram", - [SOCK_RAW] = "raw", - [SOCK_SEQPACKET] = "seqpacket", - [0] = " ", /* Dummy for avoiding NULL pointer dereference. */ - [4] = " ", /* Dummy for avoiding NULL pointer dereference. */ -}; - -#endif - -/* String table for categories. */ -static const char * const ccs_category_keywords[CCS_MAX_MAC_CATEGORY_INDEX] = { - [CCS_MAC_CATEGORY_FILE] = "file", -#ifdef CONFIG_CCSECURITY_NETWORK - [CCS_MAC_CATEGORY_NETWORK] = "network", -#endif -#ifdef CONFIG_CCSECURITY_MISC - [CCS_MAC_CATEGORY_MISC] = "misc", -#endif -#ifdef CONFIG_CCSECURITY_IPC - [CCS_MAC_CATEGORY_IPC] = "ipc", -#endif -#ifdef CONFIG_CCSECURITY_CAPABILITY - [CCS_MAC_CATEGORY_CAPABILITY] = "capability", -#endif -}; - -/* String table for conditions. */ -static const char * const ccs_condition_keyword[CCS_MAX_CONDITION_KEYWORD] = { - [CCS_TASK_UID] = "task.uid", - [CCS_TASK_EUID] = "task.euid", - [CCS_TASK_SUID] = "task.suid", - [CCS_TASK_FSUID] = "task.fsuid", - [CCS_TASK_GID] = "task.gid", - [CCS_TASK_EGID] = "task.egid", - [CCS_TASK_SGID] = "task.sgid", - [CCS_TASK_FSGID] = "task.fsgid", - [CCS_TASK_PID] = "task.pid", - [CCS_TASK_PPID] = "task.ppid", - [CCS_EXEC_ARGC] = "exec.argc", - [CCS_EXEC_ENVC] = "exec.envc", - [CCS_TYPE_IS_SOCKET] = "socket", - [CCS_TYPE_IS_SYMLINK] = "symlink", - [CCS_TYPE_IS_FILE] = "file", - [CCS_TYPE_IS_BLOCK_DEV] = "block", - [CCS_TYPE_IS_DIRECTORY] = "directory", - [CCS_TYPE_IS_CHAR_DEV] = "char", - [CCS_TYPE_IS_FIFO] = "fifo", - [CCS_MODE_SETUID] = "setuid", - [CCS_MODE_SETGID] = "setgid", - [CCS_MODE_STICKY] = "sticky", - [CCS_MODE_OWNER_READ] = "owner_read", - [CCS_MODE_OWNER_WRITE] = "owner_write", - [CCS_MODE_OWNER_EXECUTE] = "owner_execute", - [CCS_MODE_GROUP_READ] = "group_read", - [CCS_MODE_GROUP_WRITE] = "group_write", - [CCS_MODE_GROUP_EXECUTE] = "group_execute", - [CCS_MODE_OTHERS_READ] = "others_read", - [CCS_MODE_OTHERS_WRITE] = "others_write", - [CCS_MODE_OTHERS_EXECUTE] = "others_execute", - [CCS_TASK_TYPE] = "task.type", - [CCS_TASK_EXECUTE_HANDLER] = "execute_handler", - [CCS_EXEC_REALPATH] = "exec.realpath", - [CCS_SYMLINK_TARGET] = "symlink.target", - [CCS_PATH1_UID] = "path1.uid", - [CCS_PATH1_GID] = "path1.gid", - [CCS_PATH1_INO] = "path1.ino", - [CCS_PATH1_MAJOR] = "path1.major", - [CCS_PATH1_MINOR] = "path1.minor", - [CCS_PATH1_PERM] = "path1.perm", - [CCS_PATH1_TYPE] = "path1.type", - [CCS_PATH1_DEV_MAJOR] = "path1.dev_major", - [CCS_PATH1_DEV_MINOR] = "path1.dev_minor", - [CCS_PATH2_UID] = "path2.uid", - [CCS_PATH2_GID] = "path2.gid", - [CCS_PATH2_INO] = "path2.ino", - [CCS_PATH2_MAJOR] = "path2.major", - [CCS_PATH2_MINOR] = "path2.minor", - [CCS_PATH2_PERM] = "path2.perm", - [CCS_PATH2_TYPE] = "path2.type", - [CCS_PATH2_DEV_MAJOR] = "path2.dev_major", - [CCS_PATH2_DEV_MINOR] = "path2.dev_minor", - [CCS_PATH1_PARENT_UID] = "path1.parent.uid", - [CCS_PATH1_PARENT_GID] = "path1.parent.gid", - [CCS_PATH1_PARENT_INO] = "path1.parent.ino", - [CCS_PATH1_PARENT_PERM] = "path1.parent.perm", - [CCS_PATH2_PARENT_UID] = "path2.parent.uid", - [CCS_PATH2_PARENT_GID] = "path2.parent.gid", - [CCS_PATH2_PARENT_INO] = "path2.parent.ino", - [CCS_PATH2_PARENT_PERM] = "path2.parent.perm", -}; - -/* String table for PREFERENCE keyword. */ -static const char * const ccs_pref_keywords[CCS_MAX_PREF] = { - [CCS_PREF_MAX_AUDIT_LOG] = "max_audit_log", - [CCS_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry", - [CCS_PREF_ENFORCING_PENALTY] = "enforcing_penalty", -}; - -/* String table for domain flags. */ -const char * const ccs_dif[CCS_MAX_DOMAIN_INFO_FLAGS] = { - [CCS_DIF_QUOTA_WARNED] = "quota_exceeded\n", - [CCS_DIF_TRANSITION_FAILED] = "transition_failed\n", -}; - -/* String table for domain transition control keywords. */ -static const char * const ccs_transition_type[CCS_MAX_TRANSITION_TYPE] = { - [CCS_TRANSITION_CONTROL_NO_RESET] = "no_reset_domain ", - [CCS_TRANSITION_CONTROL_RESET] = "reset_domain ", - [CCS_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ", - [CCS_TRANSITION_CONTROL_INITIALIZE] = "initialize_domain ", - [CCS_TRANSITION_CONTROL_NO_KEEP] = "no_keep_domain ", - [CCS_TRANSITION_CONTROL_KEEP] = "keep_domain ", -}; - -/* String table for grouping keywords. */ -static const char * const ccs_group_name[CCS_MAX_GROUP] = { - [CCS_PATH_GROUP] = "path_group ", - [CCS_NUMBER_GROUP] = "number_group ", -#ifdef CONFIG_CCSECURITY_NETWORK - [CCS_ADDRESS_GROUP] = "address_group ", -#endif -}; - -/* String table for /proc/ccs/stat interface. */ -static const char * const ccs_policy_headers[CCS_MAX_POLICY_STAT] = { - [CCS_STAT_POLICY_UPDATES] = "update:", - [CCS_STAT_POLICY_LEARNING] = "violation in learning mode:", - [CCS_STAT_POLICY_PERMISSIVE] = "violation in permissive mode:", - [CCS_STAT_POLICY_ENFORCING] = "violation in enforcing mode:", -}; - -/* String table for /proc/ccs/stat interface. */ -static const char * const ccs_memory_headers[CCS_MAX_MEMORY_STAT] = { - [CCS_MEMORY_POLICY] = "policy:", - [CCS_MEMORY_AUDIT] = "audit log:", - [CCS_MEMORY_QUERY] = "query message:", -}; - -/***** SECTION2: Structure definition *****/ - -struct iattr; - -/* Structure for query. */ -struct ccs_query { - struct list_head list; - struct ccs_domain_info *domain; - char *query; - size_t query_len; - unsigned int serial; - u8 timer; - u8 answer; - u8 retry; -}; - -/* Structure for audit log. */ -struct ccs_log { - struct list_head list; - char *log; - int size; -}; - -/***** SECTION3: Prototype definition section *****/ - -int ccs_audit_log(struct ccs_request_info *r); -struct ccs_domain_info *ccs_assign_domain(const char *domainname, - const bool transit); -u8 ccs_get_config(const u8 profile, const u8 index); -void ccs_transition_failed(const char *domainname); -void ccs_write_log(struct ccs_request_info *r, const char *fmt, ...); - -static bool ccs_correct_domain(const unsigned char *domainname); -static bool ccs_correct_path(const char *filename); -static bool ccs_correct_word(const char *string); -static bool ccs_correct_word2(const char *string, size_t len); -static bool ccs_domain_def(const unsigned char *buffer); -static bool ccs_domain_quota_ok(struct ccs_request_info *r); -static bool ccs_flush(struct ccs_io_buffer *head); -static bool ccs_get_audit(const struct ccs_request_info *r); -static bool ccs_has_more_namespace(struct ccs_io_buffer *head); -static bool ccs_manager(void); -static bool ccs_namespace_jump(const char *domainname); -static bool ccs_parse_argv(char *left, char *right, struct ccs_argv *argv); -static bool ccs_parse_envp(char *left, char *right, struct ccs_envp *envp); -static bool ccs_parse_name_union(struct ccs_acl_param *param, - struct ccs_name_union *ptr); -static bool ccs_parse_name_union_quoted(struct ccs_acl_param *param, - struct ccs_name_union *ptr); -static bool ccs_parse_number_union(struct ccs_acl_param *param, - struct ccs_number_union *ptr); -static bool ccs_permstr(const char *string, const char *keyword); -static bool ccs_print_condition(struct ccs_io_buffer *head, - const struct ccs_condition *cond); -static bool ccs_print_entry(struct ccs_io_buffer *head, - const struct ccs_acl_info *acl); -static bool ccs_print_group(struct ccs_io_buffer *head, - const struct ccs_group *group); -static bool ccs_read_acl(struct ccs_io_buffer *head, struct list_head *list); -static bool ccs_read_group(struct ccs_io_buffer *head, const int idx); -static bool ccs_read_policy(struct ccs_io_buffer *head, const int idx); -static bool ccs_same_condition(const struct ccs_condition *a, - const struct ccs_condition *b); -static bool ccs_select_domain(struct ccs_io_buffer *head, const char *data); -static bool ccs_set_lf(struct ccs_io_buffer *head); -static bool ccs_str_starts(char **src, const char *find); -static char *ccs_get_transit_preference(struct ccs_acl_param *param, - struct ccs_condition *e); -static char *ccs_init_log(struct ccs_request_info *r, int len, const char *fmt, - va_list args); -static char *ccs_print_bprm(struct linux_binprm *bprm, - struct ccs_page_dump *dump); -static char *ccs_print_header(struct ccs_request_info *r); -static char *ccs_read_token(struct ccs_acl_param *param); -static const char *ccs_yesno(const unsigned int value); -static const struct ccs_path_info *ccs_get_domainname -(struct ccs_acl_param *param); -static const struct ccs_path_info *ccs_get_dqword(char *start); -static int __init ccs_init_module(void); -static int ccs_delete_domain(char *domainname); -static int ccs_open(struct inode *inode, struct file *file); -static int ccs_parse_policy(struct ccs_io_buffer *head, char *line); -static int ccs_release(struct inode *inode, struct file *file); -static int ccs_set_mode(char *name, const char *value, - struct ccs_profile *profile); -static int ccs_supervisor(struct ccs_request_info *r, const char *fmt, ...) - __printf(2, 3); -static int ccs_truncate(char *str); -static int ccs_update_acl(const int size, struct ccs_acl_param *param); -static int ccs_update_manager_entry(const char *manager, const bool is_delete); -static int ccs_update_policy(const int size, struct ccs_acl_param *param); -static int ccs_write_acl(struct ccs_policy_namespace *ns, - struct list_head *list, char *data, - const bool is_delete); -static int ccs_write_aggregator(struct ccs_acl_param *param); -static int ccs_write_answer(struct ccs_io_buffer *head); -static int ccs_write_domain(struct ccs_io_buffer *head); -static int ccs_write_exception(struct ccs_io_buffer *head); -static int ccs_write_file(struct ccs_acl_param *param); -static int ccs_write_group(struct ccs_acl_param *param, const u8 type); -static int ccs_write_manager(struct ccs_io_buffer *head); -static int ccs_write_pid(struct ccs_io_buffer *head); -static int ccs_write_profile(struct ccs_io_buffer *head); -static int ccs_write_stat(struct ccs_io_buffer *head); -static int ccs_write_task(struct ccs_acl_param *param); -static int ccs_write_transition_control(struct ccs_acl_param *param, - const u8 type); -static s8 ccs_find_yesno(const char *string, const char *find); -static ssize_t ccs_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos); -static ssize_t ccs_read_self(struct file *file, char __user *buf, size_t count, - loff_t *ppos); -static ssize_t ccs_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos); -static struct ccs_condition *ccs_commit_condition(struct ccs_condition *entry); -static struct ccs_condition *ccs_get_condition(struct ccs_acl_param *param); -static struct ccs_domain_info *ccs_find_domain(const char *domainname); -static struct ccs_domain_info *ccs_find_domain_by_qid(unsigned int serial); -static struct ccs_group *ccs_get_group(struct ccs_acl_param *param, - const u8 idx); -static struct ccs_policy_namespace *ccs_assign_namespace -(const char *domainname); -static struct ccs_policy_namespace *ccs_find_namespace(const char *name, - const unsigned int len); -static struct ccs_profile *ccs_assign_profile(struct ccs_policy_namespace *ns, - const unsigned int profile); -static struct ccs_profile *ccs_profile(const u8 profile); -static u8 ccs_condition_type(const char *word); -static u8 ccs_make_byte(const u8 c1, const u8 c2, const u8 c3); -static u8 ccs_parse_ulong(unsigned long *result, char **str); -static unsigned int ccs_poll(struct file *file, poll_table *wait); -static void __init ccs_create_entry(const char *name, const umode_t mode, - struct proc_dir_entry *parent, - const u8 key); -static void __init ccs_load_builtin_policy(void); -static void __init ccs_policy_io_init(void); -static void __init ccs_proc_init(void); -static void ccs_add_entry(char *header); -static void ccs_addprintf(char *buffer, int len, const char *fmt, ...) - __printf(3, 4); -static void ccs_addprintf(char *buffer, int len, const char *fmt, ...); -static void ccs_check_profile(void); -static void ccs_convert_time(time_t time, struct ccs_time *stamp); -static void ccs_init_policy_namespace(struct ccs_policy_namespace *ns); -static void ccs_io_printf(struct ccs_io_buffer *head, const char *fmt, ...) - __printf(2, 3); -static void ccs_normalize_line(unsigned char *buffer); -static void ccs_print_config(struct ccs_io_buffer *head, const u8 config); -static void ccs_print_name_union(struct ccs_io_buffer *head, - const struct ccs_name_union *ptr); -static void ccs_print_name_union_quoted(struct ccs_io_buffer *head, - const struct ccs_name_union *ptr); -static void ccs_print_namespace(struct ccs_io_buffer *head); -static void ccs_print_number_union(struct ccs_io_buffer *head, - const struct ccs_number_union *ptr); -static void ccs_print_number_union_nospace(struct ccs_io_buffer *head, - const struct ccs_number_union *ptr); -static void ccs_read_domain(struct ccs_io_buffer *head); -static void ccs_read_exception(struct ccs_io_buffer *head); -static void ccs_read_log(struct ccs_io_buffer *head); -static void ccs_read_manager(struct ccs_io_buffer *head); -static void ccs_read_pid(struct ccs_io_buffer *head); -static void ccs_read_profile(struct ccs_io_buffer *head); -static void ccs_read_query(struct ccs_io_buffer *head); -static void ccs_read_stat(struct ccs_io_buffer *head); -static void ccs_read_version(struct ccs_io_buffer *head); -static void ccs_set_group(struct ccs_io_buffer *head, const char *category); -static void ccs_set_namespace_cursor(struct ccs_io_buffer *head); -static void ccs_set_slash(struct ccs_io_buffer *head); -static void ccs_set_space(struct ccs_io_buffer *head); -static void ccs_set_string(struct ccs_io_buffer *head, const char *string); -static void ccs_set_uint(unsigned int *i, const char *string, - const char *find); -static void ccs_update_stat(const u8 index); -static void ccs_update_task_domain(struct ccs_request_info *r); -static void ccs_write_log2(struct ccs_request_info *r, int len, - const char *fmt, va_list args); - -#ifdef CONFIG_CCSECURITY_PORTRESERVE -static bool __ccs_lport_reserved(const u16 port); -static int ccs_write_reserved_port(struct ccs_acl_param *param); -#endif - -#ifdef CONFIG_CCSECURITY_NETWORK -static bool ccs_parse_ipaddr_union(struct ccs_acl_param *param, - struct ccs_ipaddr_union *ptr); -static int ccs_print_ipv4(char *buffer, const unsigned int buffer_len, - const u32 *ip); -static int ccs_print_ipv6(char *buffer, const unsigned int buffer_len, - const struct in6_addr *ip); -static int ccs_write_inet_network(struct ccs_acl_param *param); -static int ccs_write_unix_network(struct ccs_acl_param *param); -static void ccs_print_ip(char *buf, const unsigned int size, - const struct ccs_ipaddr_union *ptr); -#endif - -#ifdef CONFIG_CCSECURITY_CAPABILITY -static int ccs_write_capability(struct ccs_acl_param *param); -#endif - -#ifdef CONFIG_CCSECURITY_MISC -static int ccs_write_misc(struct ccs_acl_param *param); -#endif - -#ifdef CONFIG_CCSECURITY_IPC -static int ccs_write_ipc(struct ccs_acl_param *param); -#endif - -#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION -static ssize_t ccs_write_self(struct file *file, const char __user *buf, - size_t count, loff_t *ppos); -#endif - -/***** SECTION4: Standalone functions section *****/ - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) - -/** - * fatal_signal_pending - Check whether SIGKILL is pending or not. - * - * @p: Pointer to "struct task_struct". - * - * Returns true if SIGKILL is pending on @p, false otherwise. - * - * This is for compatibility with older kernels. - */ -#define fatal_signal_pending(p) (signal_pending(p) && \ - sigismember(&p->pending.signal, SIGKILL)) - -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) - -/** - * __wait_event_interruptible_timeout - Sleep until a condition gets true or a timeout elapses. - * - * @wq: The waitqueue to wait on. - * @condition: A C expression for the event to wait for. - * @ret: Timeout, in jiffies. - * - * Returns 0 if the @timeout elapsed, -ERESTARTSYS if it was interrupted by a - * signal, and the remaining jiffies otherwise if the condition evaluated to - * true before the timeout elapsed. - * - * This is for compatibility with older kernels. - */ -#define __wait_event_interruptible_timeout(wq, condition, ret) \ -do { \ - wait_queue_t __wait; \ - init_waitqueue_entry(&__wait, current); \ - \ - add_wait_queue(&wq, &__wait); \ - for (;;) { \ - set_current_state(TASK_INTERRUPTIBLE); \ - if (condition) \ - break; \ - if (!signal_pending(current)) { \ - ret = schedule_timeout(ret); \ - if (!ret) \ - break; \ - continue; \ - } \ - ret = -ERESTARTSYS; \ - break; \ - } \ - current->state = TASK_RUNNING; \ - remove_wait_queue(&wq, &__wait); \ -} while (0) - -/** - * wait_event_interruptible_timeout - Sleep until a condition gets true or a timeout elapses. - * - * @wq: The waitqueue to wait on. - * @condition: A C expression for the event to wait for. - * @timeout: Timeout, in jiffies. - * - * Returns 0 if the @timeout elapsed, -ERESTARTSYS if it was interrupted by a - * signal, and the remaining jiffies otherwise if the condition evaluated to - * true before the timeout elapsed. - * - * This is for compatibility with older kernels. - */ -#define wait_event_interruptible_timeout(wq, condition, timeout) \ -({ \ - long __ret = timeout; \ - if (!(condition)) \ - __wait_event_interruptible_timeout(wq, condition, __ret); \ - __ret; \ -}) - -#endif - -/** - * ccs_convert_time - Convert time_t to YYYY/MM/DD hh/mm/ss. - * - * @time: Seconds since 1970/01/01 00:00:00. - * @stamp: Pointer to "struct ccs_time". - * - * Returns nothing. - * - * This function does not handle Y2038 problem. - */ -static void ccs_convert_time(time_t time, struct ccs_time *stamp) -{ - static const u16 ccs_eom[2][12] = { - { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, - { 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } - }; - u16 y; - u8 m; - bool r; - stamp->sec = time % 60; - time /= 60; - stamp->min = time % 60; - time /= 60; - stamp->hour = time % 24; - time /= 24; - for (y = 1970; ; y++) { - const unsigned short days = (y & 3) ? 365 : 366; - if (time < days) - break; - time -= days; - } - r = (y & 3) == 0; - for (m = 0; m < 11 && time >= ccs_eom[r][m]; m++); - if (m) - time -= ccs_eom[r][m - 1]; - stamp->year = y; - stamp->month = ++m; - stamp->day = ++time; -} - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 23) -#if !defined(RHEL_VERSION) || RHEL_VERSION != 3 - -/** - * PDE - Get "struct proc_dir_entry". - * - * @inode: Pointer to "struct inode". - * - * Returns pointer to "struct proc_dir_entry". - * - * This is for compatibility with older kernels. - */ -static inline struct proc_dir_entry *PDE(const struct inode *inode) -{ - return (struct proc_dir_entry *) inode->u.generic_ip; -} - -#endif -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) - -/** - * proc_notify_change - Update inode's attributes and reflect to the dentry. - * - * @dentry: Pointer to "struct dentry". - * @iattr: Pointer to "struct iattr". - * - * Returns 0 on success, negative value otherwise. - * - * The 2.4 kernels don't allow chmod()/chown() for files in /proc, - * while the 2.6 kernels allow. - * To permit management of /proc/ccs/ interface by non-root user, - * I modified to allow chmod()/chown() of /proc/ccs/ interface like 2.6 kernels - * by adding "struct inode_operations"->setattr hook. - */ -static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) -{ - struct inode *inode = dentry->d_inode; - struct proc_dir_entry *de = PDE(inode); - int error; - - error = inode_change_ok(inode, iattr); - if (error) - goto out; - - error = inode_setattr(inode, iattr); - if (error) - goto out; - - de->uid = inode->i_uid; - de->gid = inode->i_gid; - de->mode = inode->i_mode; -out: - return error; -} - -#endif - -#ifdef CONFIG_CCSECURITY_NETWORK - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) && defined(CONFIG_NET) -#define ccs_in4_pton in4_pton -#define ccs_in6_pton in6_pton -#else -/* - * Routines for parsing IPv4 or IPv6 address. - * These are copied from lib/hexdump.c net/core/utils.c . - */ -#include - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) -static int hex_to_bin(char ch) -{ - if ((ch >= '0') && (ch <= '9')) - return ch - '0'; - ch = tolower(ch); - if ((ch >= 'a') && (ch <= 'f')) - return ch - 'a' + 10; - return -1; -} -#endif - -#define IN6PTON_XDIGIT 0x00010000 -#define IN6PTON_DIGIT 0x00020000 -#define IN6PTON_COLON_MASK 0x00700000 -#define IN6PTON_COLON_1 0x00100000 /* single : requested */ -#define IN6PTON_COLON_2 0x00200000 /* second : requested */ -#define IN6PTON_COLON_1_2 0x00400000 /* :: requested */ -#define IN6PTON_DOT 0x00800000 /* . */ -#define IN6PTON_DELIM 0x10000000 -#define IN6PTON_NULL 0x20000000 /* first/tail */ -#define IN6PTON_UNKNOWN 0x40000000 - -static inline int xdigit2bin(char c, int delim) -{ - int val; - - if (c == delim || c == '\0') - return IN6PTON_DELIM; - if (c == ':') - return IN6PTON_COLON_MASK; - if (c == '.') - return IN6PTON_DOT; - - val = hex_to_bin(c); - if (val >= 0) - return val | IN6PTON_XDIGIT | (val < 10 ? IN6PTON_DIGIT : 0); - - if (delim == -1) - return IN6PTON_DELIM; - return IN6PTON_UNKNOWN; -} - -static int ccs_in4_pton(const char *src, int srclen, u8 *dst, int delim, - const char **end) -{ - const char *s; - u8 *d; - u8 dbuf[4]; - int ret = 0; - int i; - int w = 0; - - if (srclen < 0) - srclen = strlen(src); - s = src; - d = dbuf; - i = 0; - while (1) { - int c; - c = xdigit2bin(srclen > 0 ? *s : '\0', delim); - if (!(c & (IN6PTON_DIGIT | IN6PTON_DOT | IN6PTON_DELIM | - IN6PTON_COLON_MASK))) - goto out; - if (c & (IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK)) { - if (w == 0) - goto out; - *d++ = w & 0xff; - w = 0; - i++; - if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { - if (i != 4) - goto out; - break; - } - goto cont; - } - w = (w * 10) + c; - if ((w & 0xffff) > 255) - goto out; -cont: - if (i >= 4) - goto out; - s++; - srclen--; - } - ret = 1; - memcpy(dst, dbuf, sizeof(dbuf)); -out: - if (end) - *end = s; - return ret; -} - -static int ccs_in6_pton(const char *src, int srclen, u8 *dst, int delim, - const char **end) -{ - const char *s, *tok = NULL; - u8 *d, *dc = NULL; - u8 dbuf[16]; - int ret = 0; - int i; - int state = IN6PTON_COLON_1_2 | IN6PTON_XDIGIT | IN6PTON_NULL; - int w = 0; - - memset(dbuf, 0, sizeof(dbuf)); - - s = src; - d = dbuf; - if (srclen < 0) - srclen = strlen(src); - - while (1) { - int c; - - c = xdigit2bin(srclen > 0 ? *s : '\0', delim); - if (!(c & state)) - goto out; - if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { - /* process one 16-bit word */ - if (!(state & IN6PTON_NULL)) { - *d++ = (w >> 8) & 0xff; - *d++ = w & 0xff; - } - w = 0; - if (c & IN6PTON_DELIM) { - /* We've processed last word */ - break; - } - /* - * COLON_1 => XDIGIT - * COLON_2 => XDIGIT|DELIM - * COLON_1_2 => COLON_2 - */ - switch (state & IN6PTON_COLON_MASK) { - case IN6PTON_COLON_2: - dc = d; - state = IN6PTON_XDIGIT | IN6PTON_DELIM; - if (dc - dbuf >= sizeof(dbuf)) - state |= IN6PTON_NULL; - break; - case IN6PTON_COLON_1|IN6PTON_COLON_1_2: - state = IN6PTON_XDIGIT | IN6PTON_COLON_2; - break; - case IN6PTON_COLON_1: - state = IN6PTON_XDIGIT; - break; - case IN6PTON_COLON_1_2: - state = IN6PTON_COLON_2; - break; - default: - state = 0; - } - tok = s + 1; - goto cont; - } - - if (c & IN6PTON_DOT) { - ret = ccs_in4_pton(tok ? tok : s, srclen + - (int)(s - tok), d, delim, &s); - if (ret > 0) { - d += 4; - break; - } - goto out; - } - - w = (w << 4) | (0xff & c); - state = IN6PTON_COLON_1 | IN6PTON_DELIM; - if (!(w & 0xf000)) - state |= IN6PTON_XDIGIT; - if (!dc && d + 2 < dbuf + sizeof(dbuf)) { - state |= IN6PTON_COLON_1_2; - state &= ~IN6PTON_DELIM; - } - if (d + 2 >= dbuf + sizeof(dbuf)) - state &= ~(IN6PTON_COLON_1|IN6PTON_COLON_1_2); -cont: - if ((dc && d + 4 < dbuf + sizeof(dbuf)) || - d + 4 == dbuf + sizeof(dbuf)) - state |= IN6PTON_DOT; - if (d >= dbuf + sizeof(dbuf)) - state &= ~(IN6PTON_XDIGIT|IN6PTON_COLON_MASK); - s++; - srclen--; - } - - i = 15; d--; - - if (dc) { - while (d >= dc) - dst[i--] = *d--; - while (i >= dc - dbuf) - dst[i--] = 0; - while (i >= 0) - dst[i--] = *d--; - } else - memcpy(dst, dbuf, sizeof(dbuf)); - - ret = 1; -out: - if (end) - *end = s; - return ret; -} -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) - -/* - * Routines for printing IPv4 or IPv6 address. - * These are copied from include/linux/kernel.h include/net/ipv6.h - * include/net/addrconf.h lib/hexdump.c lib/vsprintf.c and simplified. - */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) -#if !defined(RHEL_MAJOR) || RHEL_MAJOR != 5 || !defined(RHEL_MINOR) || RHEL_MINOR < 9 -static const char hex_asc[] = "0123456789abcdef"; -#define hex_asc_lo(x) hex_asc[((x) & 0x0f)] -#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4] - -static inline char *pack_hex_byte(char *buf, u8 byte) -{ - *buf++ = hex_asc_hi(byte); - *buf++ = hex_asc_lo(byte); - return buf; -} -#endif -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) -static inline int ipv6_addr_v4mapped(const struct in6_addr *a) -{ - return (a->s6_addr32[0] | a->s6_addr32[1] | - (a->s6_addr32[2] ^ htonl(0x0000ffff))) == 0; -} -#endif - -static inline int ipv6_addr_is_isatap(const struct in6_addr *addr) -{ - return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE); -} - -static char *ip4_string(char *p, const u8 *addr) -{ - /* - * Since this function is called outside vsnprintf(), I can use - * sprintf() here. - */ - return p + - sprintf(p, "%u.%u.%u.%u", addr[0], addr[1], addr[2], addr[3]); -} - -static char *ip6_compressed_string(char *p, const char *addr) -{ - int i, j, range; - unsigned char zerolength[8]; - int longest = 1; - int colonpos = -1; - u16 word; - u8 hi, lo; - bool needcolon = false; - bool useIPv4; - struct in6_addr in6; - - memcpy(&in6, addr, sizeof(struct in6_addr)); - - useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6); - - memset(zerolength, 0, sizeof(zerolength)); - - if (useIPv4) - range = 6; - else - range = 8; - - /* find position of longest 0 run */ - for (i = 0; i < range; i++) { - for (j = i; j < range; j++) { - if (in6.s6_addr16[j] != 0) - break; - zerolength[i]++; - } - } - for (i = 0; i < range; i++) { - if (zerolength[i] > longest) { - longest = zerolength[i]; - colonpos = i; - } - } - if (longest == 1) /* don't compress a single 0 */ - colonpos = -1; - - /* emit address */ - for (i = 0; i < range; i++) { - if (i == colonpos) { - if (needcolon || i == 0) - *p++ = ':'; - *p++ = ':'; - needcolon = false; - i += longest - 1; - continue; - } - if (needcolon) { - *p++ = ':'; - needcolon = false; - } - /* hex u16 without leading 0s */ - word = ntohs(in6.s6_addr16[i]); - hi = word >> 8; - lo = word & 0xff; - if (hi) { - if (hi > 0x0f) - p = pack_hex_byte(p, hi); - else - *p++ = hex_asc_lo(hi); - p = pack_hex_byte(p, lo); - } else if (lo > 0x0f) - p = pack_hex_byte(p, lo); - else - *p++ = hex_asc_lo(lo); - needcolon = true; - } - - if (useIPv4) { - if (needcolon) - *p++ = ':'; - p = ip4_string(p, &in6.s6_addr[12]); - } - *p = '\0'; - - return p; -} -#endif - -/** - * ccs_print_ipv4 - Print an IPv4 address. - * - * @buffer: Buffer to write to. - * @buffer_len: Size of @buffer. - * @ip: Pointer to "u32 in network byte order". - * - * Returns written length. - */ -static int ccs_print_ipv4(char *buffer, const unsigned int buffer_len, - const u32 *ip) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) - return snprintf(buffer, buffer_len, "%pI4", ip); -#else - char addr[sizeof("255.255.255.255")]; - ip4_string(addr, (const u8 *) ip); - return snprintf(buffer, buffer_len, "%s", addr); -#endif -} - -/** - * ccs_print_ipv6 - Print an IPv6 address. - * - * @buffer: Buffer to write to. - * @buffer_len: Size of @buffer. - * @ip: Pointer to "struct in6_addr". - * - * Returns written length. - */ -static int ccs_print_ipv6(char *buffer, const unsigned int buffer_len, - const struct in6_addr *ip) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) - return snprintf(buffer, buffer_len, "%pI6c", ip); -#else - char addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")]; - ip6_compressed_string(addr, (const u8 *) ip); - return snprintf(buffer, buffer_len, "%s", addr); -#endif -} - -/** - * ccs_print_ip - Print an IP address. - * - * @buf: Buffer to write to. - * @size: Size of @buf. - * @ptr: Pointer to "struct ipaddr_union". - * - * Returns nothing. - */ -static void ccs_print_ip(char *buf, const unsigned int size, - const struct ccs_ipaddr_union *ptr) -{ - int len; - if (ptr->is_ipv6) - len = ccs_print_ipv6(buf, size, &ptr->ip[0]); - else - len = ccs_print_ipv4(buf, size, &ptr->ip[0].s6_addr32[0]); - if (!memcmp(&ptr->ip[0], &ptr->ip[1], 16) || len >= size / 2) - return; - buf[len++] = '-'; - if (ptr->is_ipv6) - ccs_print_ipv6(buf + len, size - len, &ptr->ip[1]); - else - ccs_print_ipv4(buf + len, size - len, - &ptr->ip[1].s6_addr32[0]); -} - -#endif - -/***** SECTION5: Variables definition section *****/ - -/* Permit policy management by non-root user? */ -static bool ccs_manage_by_non_root; - -/* Lock for protecting policy. */ -DEFINE_MUTEX(ccs_policy_lock); - -/* Has /sbin/init started? */ -bool ccs_policy_loaded; - -/* List of namespaces. */ -LIST_HEAD(ccs_namespace_list); -/* True if namespace other than ccs_kernel_namespace is defined. */ -static bool ccs_namespace_enabled; - -/* Initial namespace.*/ -static struct ccs_policy_namespace ccs_kernel_namespace; - -/* List of "struct ccs_condition". */ -LIST_HEAD(ccs_condition_list); - -#ifdef CONFIG_CCSECURITY_PORTRESERVE -/* Bitmap for reserved local port numbers.*/ -static u8 ccs_reserved_port_map[8192]; -#endif - -/* Wait queue for kernel -> userspace notification. */ -static DECLARE_WAIT_QUEUE_HEAD(ccs_query_wait); -/* Wait queue for userspace -> kernel notification. */ -static DECLARE_WAIT_QUEUE_HEAD(ccs_answer_wait); - -/* The list for "struct ccs_query". */ -static LIST_HEAD(ccs_query_list); - -/* Lock for manipulating ccs_query_list. */ -static DEFINE_SPINLOCK(ccs_query_list_lock); - -/* Number of "struct file" referring /proc/ccs/query interface. */ -static atomic_t ccs_query_observers = ATOMIC_INIT(0); - -/* Wait queue for /proc/ccs/audit. */ -static DECLARE_WAIT_QUEUE_HEAD(ccs_log_wait); - -/* The list for "struct ccs_log". */ -static LIST_HEAD(ccs_log); - -/* Lock for "struct list_head ccs_log". */ -static DEFINE_SPINLOCK(ccs_log_lock); - -/* Length of "stuct list_head ccs_log". */ -static unsigned int ccs_log_count; - -/* Timestamp counter for last updated. */ -static unsigned int ccs_stat_updated[CCS_MAX_POLICY_STAT]; - -/* Counter for number of updates. */ -static unsigned int ccs_stat_modified[CCS_MAX_POLICY_STAT]; - -/* Operations for /proc/ccs/self_domain interface. */ -static -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) -const -#endif -struct file_operations ccs_self_operations = { -#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION - .write = ccs_write_self, -#endif - .read = ccs_read_self, -}; - -/* Operations for /proc/ccs/ interface. */ -static -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) -const -#endif -struct file_operations ccs_operations = { - .open = ccs_open, - .release = ccs_release, - .poll = ccs_poll, - .read = ccs_read, - .write = ccs_write, -}; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) - -/* The inode operations for /proc/ccs/ directory. */ -static struct inode_operations ccs_dir_inode_operations; - -/* The inode operations for files under /proc/ccs/ directory. */ -static struct inode_operations ccs_file_inode_operations; - -#endif - -/***** SECTION6: Dependent functions section *****/ - -/** - * list_for_each_cookie - iterate over a list with cookie. - * - * @pos: Pointer to "struct list_head". - * @head: Pointer to "struct list_head". - */ -#define list_for_each_cookie(pos, head) \ - for (pos = pos ? pos : srcu_dereference((head)->next, &ccs_ss); \ - pos != (head); pos = srcu_dereference(pos->next, &ccs_ss)) - -/** - * ccs_read_token - Read a word from a line. - * - * @param: Pointer to "struct ccs_acl_param". - * - * Returns a word on success, "" otherwise. - * - * To allow the caller to skip NULL check, this function returns "" rather than - * NULL if there is no more words to read. - */ -static char *ccs_read_token(struct ccs_acl_param *param) -{ - char *pos = param->data; - char *del = strchr(pos, ' '); - if (del) - *del++ = '\0'; - else - del = pos + strlen(pos); - param->data = del; - return pos; -} - -/** - * ccs_make_byte - Make byte value from three octal characters. - * - * @c1: The first character. - * @c2: The second character. - * @c3: The third character. - * - * Returns byte value. - */ -static u8 ccs_make_byte(const u8 c1, const u8 c2, const u8 c3) -{ - return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); -} - -/** - * ccs_correct_word2 - Check whether the given string follows the naming rules. - * - * @string: The byte sequence to check. Not '\0'-terminated. - * @len: Length of @string. - * - * Returns true if @string follows the naming rules, false otherwise. - */ -static bool ccs_correct_word2(const char *string, size_t len) -{ - const char *const start = string; - bool in_repetition = false; - unsigned char c; - unsigned char d; - unsigned char e; - if (!len) - goto out; - while (len--) { - c = *string++; - if (c == '\\') { - if (!len--) - goto out; - c = *string++; - switch (c) { - case '\\': /* "\\" */ - continue; - case '$': /* "\$" */ - case '+': /* "\+" */ - case '?': /* "\?" */ - case '*': /* "\*" */ - case '@': /* "\@" */ - case 'x': /* "\x" */ - case 'X': /* "\X" */ - case 'a': /* "\a" */ - case 'A': /* "\A" */ - case '-': /* "\-" */ - continue; - case '{': /* "/\{" */ - if (string - 3 < start || *(string - 3) != '/') - break; - in_repetition = true; - continue; - case '}': /* "\}/" */ - if (*string != '/') - break; - if (!in_repetition) - break; - in_repetition = false; - continue; - case '0': /* "\ooo" */ - case '1': - case '2': - case '3': - if (!len-- || !len--) - break; - d = *string++; - e = *string++; - if (d < '0' || d > '7' || e < '0' || e > '7') - break; - c = ccs_make_byte(c, d, e); - if (c <= ' ' || c >= 127) - continue; - } - goto out; - } else if (in_repetition && c == '/') { - goto out; - } else if (c <= ' ' || c >= 127) { - goto out; - } - } - if (in_repetition) - goto out; - return true; -out: - return false; -} - -/** - * ccs_correct_word - Check whether the given string follows the naming rules. - * - * @string: The string to check. - * - * Returns true if @string follows the naming rules, false otherwise. - */ -static bool ccs_correct_word(const char *string) -{ - return ccs_correct_word2(string, strlen(string)); -} - -/** - * ccs_get_group - Allocate memory for "struct ccs_path_group"/"struct ccs_number_group"/"struct ccs_address_group". - * - * @param: Pointer to "struct ccs_acl_param". - * @idx: Index number. - * - * Returns pointer to "struct ccs_group" on success, NULL otherwise. - */ -static struct ccs_group *ccs_get_group(struct ccs_acl_param *param, - const u8 idx) -{ - struct ccs_group e = { }; - struct ccs_group *group = NULL; - struct list_head *list; - const char *group_name = ccs_read_token(param); - bool found = false; - if (!ccs_correct_word(group_name) || idx >= CCS_MAX_GROUP) - return NULL; - e.group_name = ccs_get_name(group_name); - if (!e.group_name) - return NULL; - if (mutex_lock_interruptible(&ccs_policy_lock)) - goto out; - list = ¶m->ns->group_list[idx]; - list_for_each_entry(group, list, head.list) { - if (e.group_name != group->group_name || - atomic_read(&group->head.users) == CCS_GC_IN_PROGRESS) - continue; - atomic_inc(&group->head.users); - found = true; - break; - } - if (!found) { - struct ccs_group *entry = ccs_commit_ok(&e, sizeof(e)); - if (entry) { - INIT_LIST_HEAD(&entry->member_list); - atomic_set(&entry->head.users, 1); - list_add_tail_rcu(&entry->head.list, list); - group = entry; - found = true; - } - } - mutex_unlock(&ccs_policy_lock); -out: - ccs_put_name(e.group_name); - return found ? group : NULL; -} - -/** - * ccs_parse_name_union - Parse a ccs_name_union. - * - * @param: Pointer to "struct ccs_acl_param". - * @ptr: Pointer to "struct ccs_name_union". - * - * Returns true on success, false otherwise. - */ -static bool ccs_parse_name_union(struct ccs_acl_param *param, - struct ccs_name_union *ptr) -{ - char *filename; - if (param->data[0] == '@') { - param->data++; - ptr->group = ccs_get_group(param, CCS_PATH_GROUP); - return ptr->group != NULL; - } - filename = ccs_read_token(param); - if (!ccs_correct_word(filename)) - return false; - ptr->filename = ccs_get_name(filename); - return ptr->filename != NULL; -} - -/** - * ccs_parse_ulong - Parse an "unsigned long" value. - * - * @result: Pointer to "unsigned long". - * @str: Pointer to string to parse. - * - * Returns one of values in "enum ccs_value_type". - * - * The @src is updated to point the first character after the value - * on success. - */ -static u8 ccs_parse_ulong(unsigned long *result, char **str) -{ - const char *cp = *str; - char *ep; - int base = 10; - if (*cp == '0') { - char c = *(cp + 1); - if (c == 'x' || c == 'X') { - base = 16; - cp += 2; - } else if (c >= '0' && c <= '7') { - base = 8; - cp++; - } - } - *result = simple_strtoul(cp, &ep, base); - if (cp == ep) - return CCS_VALUE_TYPE_INVALID; - *str = ep; - switch (base) { - case 16: - return CCS_VALUE_TYPE_HEXADECIMAL; - case 8: - return CCS_VALUE_TYPE_OCTAL; - default: - return CCS_VALUE_TYPE_DECIMAL; - } -} - -/** - * ccs_parse_number_union - Parse a ccs_number_union. - * - * @param: Pointer to "struct ccs_acl_param". - * @ptr: Pointer to "struct ccs_number_union". - * - * Returns true on success, false otherwise. - */ -static bool ccs_parse_number_union(struct ccs_acl_param *param, - struct ccs_number_union *ptr) -{ - char *data; - u8 type; - unsigned long v; - memset(ptr, 0, sizeof(*ptr)); - if (param->data[0] == '@') { - param->data++; - ptr->group = ccs_get_group(param, CCS_NUMBER_GROUP); - return ptr->group != NULL; - } - data = ccs_read_token(param); - type = ccs_parse_ulong(&v, &data); - if (type == CCS_VALUE_TYPE_INVALID) - return false; - ptr->values[0] = v; - ptr->value_type[0] = type; - if (!*data) { - ptr->values[1] = v; - ptr->value_type[1] = type; - return true; - } - if (*data++ != '-') - return false; - type = ccs_parse_ulong(&v, &data); - if (type == CCS_VALUE_TYPE_INVALID || *data || ptr->values[0] > v) - return false; - ptr->values[1] = v; - ptr->value_type[1] = type; - return true; -} - -#ifdef CONFIG_CCSECURITY_NETWORK - -/** - * ccs_parse_ipaddr_union - Parse an IP address. - * - * @param: Pointer to "struct ccs_acl_param". - * @ptr: Pointer to "struct ccs_ipaddr_union". - * - * Returns true on success, false otherwise. - */ -static bool ccs_parse_ipaddr_union(struct ccs_acl_param *param, - struct ccs_ipaddr_union *ptr) -{ - u8 * const min = ptr->ip[0].in6_u.u6_addr8; - u8 * const max = ptr->ip[1].in6_u.u6_addr8; - char *address = ccs_read_token(param); - const char *end; - if (!strchr(address, ':') && - ccs_in4_pton(address, -1, min, '-', &end) > 0) { - ptr->is_ipv6 = false; - if (!*end) - ptr->ip[1].s6_addr32[0] = ptr->ip[0].s6_addr32[0]; - else if (*end++ != '-' || - ccs_in4_pton(end, -1, max, '\0', &end) <= 0 || *end) - return false; - return true; - } - if (ccs_in6_pton(address, -1, min, '-', &end) > 0) { - ptr->is_ipv6 = true; - if (!*end) - memmove(max, min, sizeof(u16) * 8); - else if (*end++ != '-' || - ccs_in6_pton(end, -1, max, '\0', &end) <= 0 || *end) - return false; - return true; - } - return false; -} - -#endif - -/** - * ccs_get_dqword - ccs_get_name() for a quoted string. - * - * @start: String to save. - * - * Returns pointer to "struct ccs_path_info" on success, NULL otherwise. - */ -static const struct ccs_path_info *ccs_get_dqword(char *start) -{ - char *cp = start + strlen(start) - 1; - if (cp == start || *start++ != '"' || *cp != '"') - return NULL; - *cp = '\0'; - if (*start && !ccs_correct_word(start)) - return NULL; - return ccs_get_name(start); -} - -/** - * ccs_parse_name_union_quoted - Parse a quoted word. - * - * @param: Pointer to "struct ccs_acl_param". - * @ptr: Pointer to "struct ccs_name_union". - * - * Returns true on success, false otherwise. - */ -static bool ccs_parse_name_union_quoted(struct ccs_acl_param *param, - struct ccs_name_union *ptr) -{ - char *filename = param->data; - if (*filename == '@') - return ccs_parse_name_union(param, ptr); - ptr->filename = ccs_get_dqword(filename); - return ptr->filename != NULL; -} - -/** - * ccs_parse_argv - Parse an argv[] condition part. - * - * @left: Lefthand value. - * @right: Righthand value. - * @argv: Pointer to "struct ccs_argv". - * - * Returns true on success, false otherwise. - */ -static bool ccs_parse_argv(char *left, char *right, struct ccs_argv *argv) -{ - if (ccs_parse_ulong(&argv->index, &left) != CCS_VALUE_TYPE_DECIMAL || - *left++ != ']' || *left) - return false; - argv->value = ccs_get_dqword(right); - return argv->value != NULL; -} - -/** - * ccs_parse_envp - Parse an envp[] condition part. - * - * @left: Lefthand value. - * @right: Righthand value. - * @envp: Pointer to "struct ccs_envp". - * - * Returns true on success, false otherwise. - */ -static bool ccs_parse_envp(char *left, char *right, struct ccs_envp *envp) -{ - const struct ccs_path_info *name; - const struct ccs_path_info *value; - char *cp = left + strlen(left) - 1; - if (*cp-- != ']' || *cp != '"') - goto out; - *cp = '\0'; - if (!ccs_correct_word(left)) - goto out; - name = ccs_get_name(left); - if (!name) - goto out; - if (!strcmp(right, "NULL")) { - value = NULL; - } else { - value = ccs_get_dqword(right); - if (!value) { - ccs_put_name(name); - goto out; - } - } - envp->name = name; - envp->value = value; - return true; -out: - return false; -} - -/** - * ccs_same_condition - Check for duplicated "struct ccs_condition" entry. - * - * @a: Pointer to "struct ccs_condition". - * @b: Pointer to "struct ccs_condition". - * - * Returns true if @a == @b, false otherwise. - */ -static bool ccs_same_condition(const struct ccs_condition *a, - const struct ccs_condition *b) -{ - return a->size == b->size && a->condc == b->condc && - a->numbers_count == b->numbers_count && - a->names_count == b->names_count && - a->argc == b->argc && a->envc == b->envc && - a->grant_log == b->grant_log && - a->exec_transit == b->exec_transit && a->transit == b->transit - && !memcmp(a + 1, b + 1, a->size - sizeof(*a)); -} - -/** - * ccs_condition_type - Get condition type. - * - * @word: Keyword string. - * - * Returns one of values in "enum ccs_conditions_index" on success, - * CCS_MAX_CONDITION_KEYWORD otherwise. - */ -static u8 ccs_condition_type(const char *word) -{ - u8 i; - for (i = 0; i < CCS_MAX_CONDITION_KEYWORD; i++) { - if (!strcmp(word, ccs_condition_keyword[i])) - break; - } - return i; -} - -/** - * ccs_commit_condition - Commit "struct ccs_condition". - * - * @entry: Pointer to "struct ccs_condition". - * - * Returns pointer to "struct ccs_condition" on success, NULL otherwise. - * - * This function merges duplicated entries. This function returns NULL if - * @entry is not duplicated but memory quota for policy has exceeded. - */ -static struct ccs_condition *ccs_commit_condition(struct ccs_condition *entry) -{ - struct ccs_condition *ptr; - bool found = false; - if (mutex_lock_interruptible(&ccs_policy_lock)) { - dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__); - ptr = NULL; - found = true; - goto out; - } - list_for_each_entry(ptr, &ccs_condition_list, head.list) { - if (!ccs_same_condition(ptr, entry) || - atomic_read(&ptr->head.users) == CCS_GC_IN_PROGRESS) - continue; - /* Same entry found. Share this entry. */ - atomic_inc(&ptr->head.users); - found = true; - break; - } - if (!found) { - if (ccs_memory_ok(entry, entry->size)) { - atomic_set(&entry->head.users, 1); - list_add(&entry->head.list, &ccs_condition_list); - } else { - found = true; - ptr = NULL; - } - } - mutex_unlock(&ccs_policy_lock); -out: - if (found) { - ccs_del_condition(&entry->head.list); - kfree(entry); - entry = ptr; - } - return entry; -} - -/** - * ccs_correct_path - Check whether the given pathname follows the naming rules. - * - * @filename: The pathname to check. - * - * Returns true if @filename follows the naming rules, false otherwise. - */ -static bool ccs_correct_path(const char *filename) -{ - return *filename == '/' && ccs_correct_word(filename); -} - -/** - * ccs_domain_def - Check whether the given token can be a domainname. - * - * @buffer: The token to check. - * - * Returns true if @buffer possibly be a domainname, false otherwise. - */ -static bool ccs_domain_def(const unsigned char *buffer) -{ - const unsigned char *cp; - int len; - if (*buffer != '<') - return false; - cp = strchr(buffer, ' '); - if (!cp) - len = strlen(buffer); - else - len = cp - buffer; - if (buffer[len - 1] != '>' || !ccs_correct_word2(buffer + 1, len - 2)) - return false; - return true; -} - -/** - * ccs_correct_domain - Check whether the given domainname follows the naming rules. - * - * @domainname: The domainname to check. - * - * Returns true if @domainname follows the naming rules, false otherwise. - */ -static bool ccs_correct_domain(const unsigned char *domainname) -{ - if (!domainname || !ccs_domain_def(domainname)) - return false; - domainname = strchr(domainname, ' '); - if (!domainname++) - return true; - while (1) { - const unsigned char *cp = strchr(domainname, ' '); - if (!cp) - break; - if (*domainname != '/' || - !ccs_correct_word2(domainname, cp - domainname)) - return false; - domainname = cp + 1; - } - return ccs_correct_path(domainname); -} - -/** - * ccs_normalize_line - Format string. - * - * @buffer: The line to normalize. - * - * Returns nothing. - * - * Leading and trailing whitespaces are removed. - * Multiple whitespaces are packed into single space. - */ -static void ccs_normalize_line(unsigned char *buffer) -{ - unsigned char *sp = buffer; - unsigned char *dp = buffer; - bool first = true; - while (*sp && (*sp <= ' ' || *sp >= 127)) - sp++; - while (*sp) { - if (!first) - *dp++ = ' '; - first = false; - while (*sp > ' ' && *sp < 127) - *dp++ = *sp++; - while (*sp && (*sp <= ' ' || *sp >= 127)) - sp++; - } - *dp = '\0'; -} - -/** - * ccs_get_domainname - Read a domainname from a line. - * - * @param: Pointer to "struct ccs_acl_param". - * - * Returns a domainname on success, NULL otherwise. - */ -static const struct ccs_path_info *ccs_get_domainname -(struct ccs_acl_param *param) -{ - char *start = param->data; - char *pos = start; - while (*pos) { - if (*pos++ != ' ' || *pos++ == '/') - continue; - pos -= 2; - *pos++ = '\0'; - break; - } - param->data = pos; - if (ccs_correct_domain(start)) - return ccs_get_name(start); - return NULL; -} - -/** - * ccs_get_transit_preference - Parse domain transition preference for execve(). - * - * @param: Pointer to "struct ccs_acl_param". - * @e: Pointer to "struct ccs_condition". - * - * Returns the condition string part. - */ -static char *ccs_get_transit_preference(struct ccs_acl_param *param, - struct ccs_condition *e) -{ - char * const pos = param->data; - bool flag; - if (*pos == '<') { - e->transit = ccs_get_domainname(param); - goto done; - } - { - char *cp = strchr(pos, ' '); - if (cp) - *cp = '\0'; - flag = ccs_correct_path(pos) || !strcmp(pos, "keep") || - !strcmp(pos, "initialize") || !strcmp(pos, "reset") || - !strcmp(pos, "child") || !strcmp(pos, "parent"); - if (cp) - *cp = ' '; - } - if (!flag) - return pos; - e->transit = ccs_get_name(ccs_read_token(param)); -done: - if (e->transit) { - e->exec_transit = true; - return param->data; - } - /* - * Return a bad read-only condition string that will let - * ccs_get_condition() return NULL. - */ - return "/"; -} - -/** - * ccs_get_condition - Parse condition part. - * - * @param: Pointer to "struct ccs_acl_param". - * - * Returns pointer to "struct ccs_condition" on success, NULL otherwise. - */ -struct ccs_condition *ccs_get_condition(struct ccs_acl_param *param) -{ - struct ccs_condition *entry = NULL; - struct ccs_condition_element *condp = NULL; - struct ccs_number_union *numbers_p = NULL; - struct ccs_name_union *names_p = NULL; - struct ccs_argv *argv = NULL; - struct ccs_envp *envp = NULL; - struct ccs_condition e = { }; - char * const start_of_string = ccs_get_transit_preference(param, &e); - char * const end_of_string = start_of_string + strlen(start_of_string); - char *pos; -rerun: - pos = start_of_string; - while (1) { - u8 left = -1; - u8 right = -1; - char *left_word = pos; - char *cp; - char *right_word; - bool is_not; - if (!*left_word) - break; - /* - * Since left-hand condition does not allow use of "path_group" - * or "number_group" and environment variable's names do not - * accept '=', it is guaranteed that the original line consists - * of one or more repetition of $left$operator$right blocks - * where "$left is free from '=' and ' '" and "$operator is - * either '=' or '!='" and "$right is free from ' '". - * Therefore, we can reconstruct the original line at the end - * of dry run even if we overwrite $operator with '\0'. - */ - cp = strchr(pos, ' '); - if (cp) { - *cp = '\0'; /* Will restore later. */ - pos = cp + 1; - } else { - pos = ""; - } - right_word = strchr(left_word, '='); - if (!right_word || right_word == left_word) - goto out; - is_not = *(right_word - 1) == '!'; - if (is_not) - *(right_word++ - 1) = '\0'; /* Will restore later. */ - else if (*(right_word + 1) != '=') - *right_word++ = '\0'; /* Will restore later. */ - else - goto out; - dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word, - is_not ? "!" : "", right_word); - if (!strcmp(left_word, "grant_log")) { - if (entry) { - if (is_not || - entry->grant_log != CCS_GRANTLOG_AUTO) - goto out; - else if (!strcmp(right_word, "yes")) - entry->grant_log = CCS_GRANTLOG_YES; - else if (!strcmp(right_word, "no")) - entry->grant_log = CCS_GRANTLOG_NO; - else - goto out; - } - continue; - } - if (!strcmp(left_word, "auto_domain_transition")) { - if (entry) { - if (is_not || entry->transit) - goto out; - entry->transit = ccs_get_dqword(right_word); - if (!entry->transit || - (entry->transit->name[0] != '/' && - !ccs_domain_def(entry->transit->name))) - goto out; - } - continue; - } - if (!strncmp(left_word, "exec.argv[", 10)) { - if (!argv) { - e.argc++; - e.condc++; - } else { - e.argc--; - e.condc--; - left = CCS_ARGV_ENTRY; - argv->is_not = is_not; - if (!ccs_parse_argv(left_word + 10, - right_word, argv++)) - goto out; - } - goto store_value; - } - if (!strncmp(left_word, "exec.envp[\"", 11)) { - if (!envp) { - e.envc++; - e.condc++; - } else { - e.envc--; - e.condc--; - left = CCS_ENVP_ENTRY; - envp->is_not = is_not; - if (!ccs_parse_envp(left_word + 11, - right_word, envp++)) - goto out; - } - goto store_value; - } - left = ccs_condition_type(left_word); - dprintk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__, left_word, - left); - if (left == CCS_MAX_CONDITION_KEYWORD) { - if (!numbers_p) { - e.numbers_count++; - } else { - e.numbers_count--; - left = CCS_NUMBER_UNION; - param->data = left_word; - if (*left_word == '@' || - !ccs_parse_number_union(param, - numbers_p++)) - goto out; - } - } - if (!condp) - e.condc++; - else - e.condc--; - if (left == CCS_EXEC_REALPATH || left == CCS_SYMLINK_TARGET) { - if (!names_p) { - e.names_count++; - } else { - e.names_count--; - right = CCS_NAME_UNION; - param->data = right_word; - if (!ccs_parse_name_union_quoted(param, - names_p++)) - goto out; - } - goto store_value; - } - right = ccs_condition_type(right_word); - if (right == CCS_MAX_CONDITION_KEYWORD) { - if (!numbers_p) { - e.numbers_count++; - } else { - e.numbers_count--; - right = CCS_NUMBER_UNION; - param->data = right_word; - if (!ccs_parse_number_union(param, - numbers_p++)) - goto out; - } - } -store_value: - if (!condp) { - dprintk(KERN_WARNING "%u: dry_run left=%u right=%u " - "match=%u\n", __LINE__, left, right, !is_not); - continue; - } - condp->left = left; - condp->right = right; - condp->equals = !is_not; - dprintk(KERN_WARNING "%u: left=%u right=%u match=%u\n", - __LINE__, condp->left, condp->right, - condp->equals); - condp++; - } - dprintk(KERN_INFO "%u: cond=%u numbers=%u names=%u ac=%u ec=%u\n", - __LINE__, e.condc, e.numbers_count, e.names_count, e.argc, - e.envc); - if (entry) { - BUG_ON(e.names_count | e.numbers_count | e.argc | e.envc | - e.condc); - return ccs_commit_condition(entry); - } - e.size = sizeof(*entry) - + e.condc * sizeof(struct ccs_condition_element) - + e.numbers_count * sizeof(struct ccs_number_union) - + e.names_count * sizeof(struct ccs_name_union) - + e.argc * sizeof(struct ccs_argv) - + e.envc * sizeof(struct ccs_envp); - entry = kzalloc(e.size, CCS_GFP_FLAGS); - if (!entry) - goto out2; - *entry = e; - e.transit = NULL; - condp = (struct ccs_condition_element *) (entry + 1); - numbers_p = (struct ccs_number_union *) (condp + e.condc); - names_p = (struct ccs_name_union *) (numbers_p + e.numbers_count); - argv = (struct ccs_argv *) (names_p + e.names_count); - envp = (struct ccs_envp *) (argv + e.argc); - { - bool flag = false; - for (pos = start_of_string; pos < end_of_string; pos++) { - if (*pos) - continue; - if (flag) /* Restore " ". */ - *pos = ' '; - else if (*(pos + 1) == '=') /* Restore "!=". */ - *pos = '!'; - else /* Restore "=". */ - *pos = '='; - flag = !flag; - } - } - goto rerun; -out: - dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__); - if (entry) { - ccs_del_condition(&entry->head.list); - kfree(entry); - } -out2: - ccs_put_name(e.transit); - return NULL; -} - -/** - * ccs_yesno - Return "yes" or "no". - * - * @value: Bool value. - * - * Returns "yes" if @value is not 0, "no" otherwise. - */ -static const char *ccs_yesno(const unsigned int value) -{ - return value ? "yes" : "no"; -} - -/** - * ccs_addprintf - strncat()-like-snprintf(). - * - * @buffer: Buffer to write to. Must be '\0'-terminated. - * @len: Size of @buffer. - * @fmt: The printf()'s format string, followed by parameters. - * - * Returns nothing. - */ -static void ccs_addprintf(char *buffer, int len, const char *fmt, ...) -{ - va_list args; - const int pos = strlen(buffer); - va_start(args, fmt); - vsnprintf(buffer + pos, len - pos - 1, fmt, args); - va_end(args); -} - -/** - * ccs_flush - Flush queued string to userspace's buffer. - * - * @head: Pointer to "struct ccs_io_buffer". - * - * Returns true if all data was flushed, false otherwise. - */ -static bool ccs_flush(struct ccs_io_buffer *head) -{ - while (head->r.w_pos) { - const char *w = head->r.w[0]; - size_t len = strlen(w); - if (len) { - if (len > head->read_user_buf_avail) - len = head->read_user_buf_avail; - if (!len) - return false; - if (copy_to_user(head->read_user_buf, w, len)) - return false; - head->read_user_buf_avail -= len; - head->read_user_buf += len; - w += len; - } - head->r.w[0] = w; - if (*w) - return false; - /* Add '\0' for audit logs and query. */ - if (head->type == CCS_AUDIT || head->type == CCS_QUERY) { - if (!head->read_user_buf_avail || - copy_to_user(head->read_user_buf, "", 1)) - return false; - head->read_user_buf_avail--; - head->read_user_buf++; - } - head->r.w_pos--; - for (len = 0; len < head->r.w_pos; len++) - head->r.w[len] = head->r.w[len + 1]; - } - head->r.avail = 0; - return true; -} - -/** - * ccs_set_string - Queue string to "struct ccs_io_buffer" structure. - * - * @head: Pointer to "struct ccs_io_buffer". - * @string: String to print. - * - * Returns nothing. - * - * Note that @string has to be kept valid until @head is kfree()d. - * This means that char[] allocated on stack memory cannot be passed to - * this function. Use ccs_io_printf() for char[] allocated on stack memory. - */ -static void ccs_set_string(struct ccs_io_buffer *head, const char *string) -{ - if (head->r.w_pos < CCS_MAX_IO_READ_QUEUE) { - head->r.w[head->r.w_pos++] = string; - ccs_flush(head); - } else - printk(KERN_WARNING "Too many words in a line.\n"); -} - -/** - * ccs_io_printf - printf() to "struct ccs_io_buffer" structure. - * - * @head: Pointer to "struct ccs_io_buffer". - * @fmt: The printf()'s format string, followed by parameters. - * - * Returns nothing. - */ -static void ccs_io_printf(struct ccs_io_buffer *head, const char *fmt, ...) -{ - va_list args; - size_t len; - size_t pos = head->r.avail; - int size = head->readbuf_size - pos; - if (size <= 0) - return; - va_start(args, fmt); - len = vsnprintf(head->read_buf + pos, size, fmt, args) + 1; - va_end(args); - if (pos + len >= head->readbuf_size) { - printk(KERN_WARNING "Too many words in a line.\n"); - return; - } - head->r.avail += len; - ccs_set_string(head, head->read_buf + pos); -} - -/** - * ccs_set_space - Put a space to "struct ccs_io_buffer" structure. - * - * @head: Pointer to "struct ccs_io_buffer". - * - * Returns nothing. - */ -static void ccs_set_space(struct ccs_io_buffer *head) -{ - ccs_set_string(head, " "); -} - -/** - * ccs_set_lf - Put a line feed to "struct ccs_io_buffer" structure. - * - * @head: Pointer to "struct ccs_io_buffer". - * - * Returns true if all data was flushed, false otherwise. - */ -static bool ccs_set_lf(struct ccs_io_buffer *head) -{ - ccs_set_string(head, "\n"); - return !head->r.w_pos; -} - -/** - * ccs_set_slash - Put a shash to "struct ccs_io_buffer" structure. - * - * @head: Pointer to "struct ccs_io_buffer". - * - * Returns nothing. - */ -static void ccs_set_slash(struct ccs_io_buffer *head) -{ - ccs_set_string(head, "/"); -} - -/** - * ccs_init_policy_namespace - Initialize namespace. - * - * @ns: Pointer to "struct ccs_policy_namespace". - * - * Returns nothing. - */ -static void ccs_init_policy_namespace(struct ccs_policy_namespace *ns) -{ - unsigned int idx; - for (idx = 0; idx < CCS_MAX_ACL_GROUPS; idx++) - INIT_LIST_HEAD(&ns->acl_group[idx]); - for (idx = 0; idx < CCS_MAX_GROUP; idx++) - INIT_LIST_HEAD(&ns->group_list[idx]); - for (idx = 0; idx < CCS_MAX_POLICY; idx++) - INIT_LIST_HEAD(&ns->policy_list[idx]); - ns->profile_version = 20100903; - 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.3+ 2012/05/05\n"); - list_for_each_entry_srcu(domain, &ccs_domain_list, list, &ccs_ss) { - const u8 profile = domain->profile; - const struct ccs_policy_namespace *ns = domain->ns; - if (ns->profile_version != 20100903) - 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.sourceforge.jp/1.8/ " - "for more information.\n"); - panic("STOP!"); - } - ccs_read_unlock(idx); - 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 profile; - 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", &profile) == 1 - && profile < CCS_MAX_PROFILES) { - if (!ccs_policy_loaded || ns->profile_ptr[(u8) profile]) - if (!is_delete) - domain->profile = (u8) profile; - return 0; - } - if (sscanf(data, "use_group %u\n", &profile) == 1 - && profile < CCS_MAX_ACL_GROUPS) { - if (!is_delete) - domain->group = (u8) profile; - return 0; - } - for (profile = 0; profile < CCS_MAX_DOMAIN_INFO_FLAGS; profile++) { - const char *cp = ccs_dif[profile]; - if (strncmp(data, cp, strlen(cp) - 1)) - continue; - domain->flags[profile] = !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); - ccs_io_printf(head, "use_group %u\n", domain->group); - for (i = 0; i < CCS_MAX_DOMAIN_INFO_FLAGS; i++) - if (domain->flags[i]) - ccs_set_string(head, ccs_dif[i]); - head->r.step++; - ccs_set_lf(head); - /* fall through */ - case 1: - if (!ccs_read_acl(head, &domain->acl_info_list)) - return; - head->r.step++; - if (!ccs_set_lf(head)) - return; - /* fall through */ - case 2: - 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.3"); - 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) -{ - 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; -} - -/** - * 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; - { - struct timeval tv; - do_gettimeofday(&tv); - ccs_convert_time(tv.tv_sec, &stamp); - } - 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; - e.group = domain->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; - ccs_init_request_info(&r, CCS_MAC_FILE_EXECUTE); - r.granted = false; - ccs_write_log(&r, "use_profile %u\n", entry->profile); - ccs_write_log(&r, "use_group %u\n", entry->group); - ccs_update_stat(CCS_STAT_POLICY_UPDATES); - } - } - return entry; -} - -/** - * ccs_parse_policy - Parse a policy line. - * - * @head: Poiter to "struct ccs_io_buffer". - * @line: Line to parse. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds ccs_read_lock(). - */ -static int ccs_parse_policy(struct ccs_io_buffer *head, char *line) -{ - /* Delete request? */ - head->w.is_delete = !strncmp(line, "delete ", 7); - if (head->w.is_delete) - memmove(line, line + 7, strlen(line + 7) + 1); - /* Selecting namespace to update. */ - if (head->type == CCS_EXCEPTION_POLICY || head->type == CCS_PROFILE) { - if (*line == '<') { - char *cp = strchr(line, ' '); - if (cp) { - *cp++ = '\0'; - head->w.ns = ccs_assign_namespace(line); - memmove(line, cp, strlen(cp) + 1); - } else - head->w.ns = NULL; - } else - head->w.ns = &ccs_kernel_namespace; - /* Don't allow updating if namespace is invalid. */ - if (!head->w.ns) - return -ENOENT; - } - /* Do the update. */ - switch (head->type) { - case CCS_DOMAIN_POLICY: - return ccs_write_domain(head); - case CCS_EXCEPTION_POLICY: - return ccs_write_exception(head); -#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER - case CCS_EXECUTE_HANDLER: -#endif - case CCS_PROCESS_STATUS: - return ccs_write_pid(head); - case CCS_STAT: - return ccs_write_stat(head); - case CCS_PROFILE: - return ccs_write_profile(head); - case CCS_QUERY: - return ccs_write_answer(head); - case CCS_MANAGER: - return ccs_write_manager(head); - default: - return -ENOSYS; - } -} - -/** - * ccs_policy_io_init - Register hooks for policy I/O. - * - * Returns nothing. - */ -static void __init ccs_policy_io_init(void) -{ - ccsecurity_ops.check_profile = ccs_check_profile; -} - -/** - * ccs_load_builtin_policy - Load built-in policy. - * - * Returns nothing. - */ -static void __init ccs_load_builtin_policy(void) -{ - /* - * This include file is manually created and contains built-in policy - * named "ccs_builtin_profile", "ccs_builtin_exception_policy", - * "ccs_builtin_domain_policy", "ccs_builtin_manager", - * "ccs_builtin_stat" in the form of "static char [] __initdata". - */ -#include "builtin-policy.h" - u8 i; - const int idx = ccs_read_lock(); - for (i = 0; i < 5; i++) { - struct ccs_io_buffer head = { }; - char *start = ""; - switch (i) { - case 0: - start = ccs_builtin_profile; - head.type = CCS_PROFILE; - break; - case 1: - start = ccs_builtin_exception_policy; - head.type = CCS_EXCEPTION_POLICY; - break; - case 2: - start = ccs_builtin_domain_policy; - head.type = CCS_DOMAIN_POLICY; - break; - case 3: - start = ccs_builtin_manager; - head.type = CCS_MANAGER; - break; - case 4: - start = ccs_builtin_stat; - head.type = CCS_STAT; - break; - } - while (1) { - char *end = strchr(start, '\n'); - if (!end) - break; - *end = '\0'; - ccs_normalize_line(start); - head.write_buf = start; - ccs_parse_policy(&head, start); - start = end + 1; - } - } - ccs_read_unlock(idx); -#ifdef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER - ccs_check_profile(); -#endif -} - -/** - * ccs_read_self - read() for /proc/ccs/self_domain interface. - * - * @file: Pointer to "struct file". - * @buf: Domainname which current thread belongs to. - * @count: Size of @buf. - * @ppos: Bytes read by now. - * - * Returns read size on success, negative value otherwise. - */ -static ssize_t ccs_read_self(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - const char *domain = ccs_current_domain()->domainname->name; - loff_t len = strlen(domain); - loff_t pos = *ppos; - if (pos >= len || !count) - return 0; - len -= pos; - if (count < len) - len = count; - if (copy_to_user(buf, domain + pos, len)) - return -EFAULT; - *ppos += len; - return len; -} - -/** - * ccs_open - open() for /proc/ccs/ interface. - * - * @inode: Pointer to "struct inode". - * @file: Pointer to "struct file". - * - * Returns 0 on success, negative value otherwise. - */ -static int ccs_open(struct inode *inode, struct file *file) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) - const u8 type = (unsigned long) PDE_DATA(inode); -#else - const u8 type = (unsigned long) PDE(inode)->data; -#endif - struct ccs_io_buffer *head = kzalloc(sizeof(*head), CCS_GFP_FLAGS); - if (!head) - return -ENOMEM; - mutex_init(&head->io_sem); - head->type = type; -#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER - if (type == CCS_EXECUTE_HANDLER) { - /* Allow execute_handler to read process's status. */ - if (!(ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER)) { - kfree(head); - return -EPERM; - } - } -#endif - if ((file->f_mode & FMODE_READ) && type != CCS_AUDIT && - type != CCS_QUERY) { - /* Don't allocate read_buf for poll() access. */ - head->readbuf_size = 4096; - head->read_buf = kzalloc(head->readbuf_size, CCS_GFP_FLAGS); - if (!head->read_buf) { - kfree(head); - return -ENOMEM; - } - } - if (file->f_mode & FMODE_WRITE) { - head->writebuf_size = 4096; - head->write_buf = kzalloc(head->writebuf_size, CCS_GFP_FLAGS); - if (!head->write_buf) { - kfree(head->read_buf); - kfree(head); - return -ENOMEM; - } - } - /* - * If the file is /proc/ccs/query, increment the observer counter. - * The obserber counter is used by ccs_supervisor() to see if - * there is some process monitoring /proc/ccs/query. - */ - if (type == CCS_QUERY) - atomic_inc(&ccs_query_observers); - file->private_data = head; - ccs_notify_gc(head, true); - return 0; -} - -/** - * ccs_release - close() for /proc/ccs/ interface. - * - * @inode: Pointer to "struct inode". - * @file: Pointer to "struct file". - * - * Returns 0. - */ -static int ccs_release(struct inode *inode, struct file *file) -{ - struct ccs_io_buffer *head = file->private_data; - /* - * If the file is /proc/ccs/query, decrement the observer counter. - */ - if (head->type == CCS_QUERY && - atomic_dec_and_test(&ccs_query_observers)) - wake_up_all(&ccs_answer_wait); - ccs_notify_gc(head, false); - return 0; -} - -/** - * ccs_poll - poll() for /proc/ccs/ interface. - * - * @file: Pointer to "struct file". - * @wait: Pointer to "poll_table". Maybe NULL. - * - * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, - * POLLOUT | POLLWRNORM otherwise. - */ -static unsigned int ccs_poll(struct file *file, poll_table *wait) -{ - struct ccs_io_buffer *head = file->private_data; - if (head->type == CCS_AUDIT) { - if (!ccs_memory_used[CCS_MEMORY_AUDIT]) { - poll_wait(file, &ccs_log_wait, wait); - if (!ccs_memory_used[CCS_MEMORY_AUDIT]) - return POLLOUT | POLLWRNORM; - } - } else if (head->type == CCS_QUERY) { - if (list_empty(&ccs_query_list)) { - poll_wait(file, &ccs_query_wait, wait); - if (list_empty(&ccs_query_list)) - return POLLOUT | POLLWRNORM; - } - } - return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; -} - -/** - * ccs_read - read() for /proc/ccs/ interface. - * - * @file: Pointer to "struct file". - * @buf: Pointer to buffer. - * @count: Size of @buf. - * @ppos: Unused. - * - * Returns bytes read on success, negative value otherwise. - */ -static ssize_t ccs_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - struct ccs_io_buffer *head = file->private_data; - int len; - int idx; - if (mutex_lock_interruptible(&head->io_sem)) - return -EINTR; - head->read_user_buf = buf; - head->read_user_buf_avail = count; - idx = ccs_read_lock(); - if (ccs_flush(head)) - /* Call the policy handler. */ - do { - ccs_set_namespace_cursor(head); - switch (head->type) { - case CCS_DOMAIN_POLICY: - ccs_read_domain(head); - break; - case CCS_EXCEPTION_POLICY: - ccs_read_exception(head); - break; - case CCS_AUDIT: - ccs_read_log(head); - break; -#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER - case CCS_EXECUTE_HANDLER: -#endif - case CCS_PROCESS_STATUS: - ccs_read_pid(head); - break; - case CCS_VERSION: - ccs_read_version(head); - break; - case CCS_STAT: - ccs_read_stat(head); - break; - case CCS_PROFILE: - ccs_read_profile(head); - break; - case CCS_QUERY: - ccs_read_query(head); - break; - case CCS_MANAGER: - ccs_read_manager(head); - break; - } - } while (ccs_flush(head) && ccs_has_more_namespace(head)); - ccs_read_unlock(idx); - len = head->read_user_buf - buf; - mutex_unlock(&head->io_sem); - return len; -} - -#ifdef CONFIG_CCSECURITY_TASK_DOMAIN_TRANSITION - -/** - * ccs_write_self - write() for /proc/ccs/self_domain interface. - * - * @file: Pointer to "struct file". - * @buf: Domainname to transit to. - * @count: Size of @buf. - * @ppos: Unused. - * - * Returns @count on success, negative value otherwise. - * - * If domain transition was permitted but the domain transition failed, this - * function returns error rather than terminating current thread with SIGKILL. - */ -static ssize_t ccs_write_self(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - char *data; - int error; - if (!count || count >= CCS_EXEC_TMPSIZE - 10) - return -ENOMEM; - data = kzalloc(count + 1, CCS_GFP_FLAGS); - if (!data) - return -ENOMEM; - if (copy_from_user(data, buf, count)) { - error = -EFAULT; - goto out; - } - ccs_normalize_line(data); - if (ccs_correct_domain(data)) { - const int idx = ccs_read_lock(); - struct ccs_path_info name; - struct ccs_request_info r; - name.name = data; - ccs_fill_path_info(&name); - /* Check "task manual_domain_transition" permission. */ - ccs_init_request_info(&r, CCS_MAC_FILE_EXECUTE); - r.param_type = CCS_TYPE_MANUAL_TASK_ACL; - r.param.task.domainname = &name; - ccs_check_acl(&r); - if (!r.granted) - error = -EPERM; - else - error = ccs_assign_domain(data, true) ? 0 : -ENOENT; - ccs_read_unlock(idx); - } else - error = -EINVAL; -out: - kfree(data); - return error ? error : count; -} - -#endif - -/** - * ccs_write - write() for /proc/ccs/ interface. - * - * @file: Pointer to "struct file". - * @buf: Pointer to buffer. - * @count: Size of @buf. - * @ppos: Unused. - * - * Returns @count on success, negative value otherwise. - */ -static ssize_t ccs_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct ccs_io_buffer *head = file->private_data; - int error = count; - char *cp0 = head->write_buf; - int idx; - if (mutex_lock_interruptible(&head->io_sem)) - return -EINTR; - head->read_user_buf_avail = 0; - idx = ccs_read_lock(); - /* Read a line and dispatch it to the policy handler. */ - while (count) { - char c; - if (head->w.avail >= head->writebuf_size - 1) { - const int len = head->writebuf_size * 2; - char *cp = kzalloc(len, CCS_GFP_FLAGS); - if (!cp) { - error = -ENOMEM; - break; - } - memmove(cp, cp0, head->w.avail); - kfree(cp0); - head->write_buf = cp; - cp0 = cp; - head->writebuf_size = len; - } - if (get_user(c, buf)) { - error = -EFAULT; - break; - } - buf++; - count--; - cp0[head->w.avail++] = c; - if (c != '\n') - continue; - cp0[head->w.avail - 1] = '\0'; - head->w.avail = 0; - ccs_normalize_line(cp0); - if (!strcmp(cp0, "reset")) { - head->w.ns = &ccs_kernel_namespace; - head->w.domain = NULL; - memset(&head->r, 0, sizeof(head->r)); - continue; - } - /* Don't allow updating policies by non manager programs. */ - switch (head->type) { - case CCS_PROCESS_STATUS: - /* This does not write anything. */ - break; - case CCS_DOMAIN_POLICY: - if (ccs_select_domain(head, cp0)) - continue; - /* fall through */ - case CCS_EXCEPTION_POLICY: - if (!strcmp(cp0, "select transition_only")) { - head->r.print_transition_related_only = true; - continue; - } - /* fall through */ - default: - if (!ccs_manager()) { - error = -EPERM; - goto out; - } - } - switch (ccs_parse_policy(head, cp0)) { - case -EPERM: - error = -EPERM; - goto out; - case 0: - /* Update statistics. */ - switch (head->type) { - case CCS_DOMAIN_POLICY: - case CCS_EXCEPTION_POLICY: - case CCS_STAT: - case CCS_PROFILE: - case CCS_MANAGER: - ccs_update_stat(CCS_STAT_POLICY_UPDATES); - break; - default: - break; - } - break; - } - } -out: - ccs_read_unlock(idx); - mutex_unlock(&head->io_sem); - return error; -} - -/** - * ccs_create_entry - Create interface files under /proc/ccs/ directory. - * - * @name: The name of the interface file. - * @mode: The permission of the interface file. - * @parent: The parent directory. - * @key: Type of interface. - * - * Returns nothing. - */ -static void __init ccs_create_entry(const char *name, const umode_t mode, - struct proc_dir_entry *parent, - const u8 key) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) - proc_create_data(name, mode, parent, &ccs_operations, - ((u8 *) NULL) + key); -#else - struct proc_dir_entry *entry = create_proc_entry(name, mode, parent); - if (entry) { - entry->proc_fops = &ccs_operations; - entry->data = ((u8 *) NULL) + key; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) - if (entry->proc_iops) - ccs_file_inode_operations = *entry->proc_iops; - if (!ccs_file_inode_operations.setattr) - ccs_file_inode_operations.setattr = proc_notify_change; - entry->proc_iops = &ccs_file_inode_operations; -#endif - } -#endif -} - -/** - * ccs_proc_init - Initialize /proc/ccs/ interface. - * - * Returns nothing. - */ -static void __init ccs_proc_init(void) -{ - struct proc_dir_entry *ccs_dir = proc_mkdir("ccs", NULL); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) - if (ccs_dir->proc_iops) - ccs_dir_inode_operations = *ccs_dir->proc_iops; - if (!ccs_dir_inode_operations.setattr) - ccs_dir_inode_operations.setattr = proc_notify_change; - ccs_dir->proc_iops = &ccs_dir_inode_operations; -#endif - ccs_create_entry("query", 0600, ccs_dir, CCS_QUERY); - ccs_create_entry("domain_policy", 0600, ccs_dir, CCS_DOMAIN_POLICY); - ccs_create_entry("exception_policy", 0600, ccs_dir, - CCS_EXCEPTION_POLICY); - ccs_create_entry("audit", 0400, ccs_dir, CCS_AUDIT); - ccs_create_entry(".process_status", 0600, ccs_dir, - CCS_PROCESS_STATUS); - ccs_create_entry("stat", 0644, ccs_dir, CCS_STAT); - ccs_create_entry("profile", 0600, ccs_dir, CCS_PROFILE); - ccs_create_entry("manager", 0600, ccs_dir, CCS_MANAGER); - ccs_create_entry("version", 0400, ccs_dir, CCS_VERSION); -#ifdef CONFIG_CCSECURITY_TASK_EXECUTE_HANDLER - ccs_create_entry(".execute_handler", 0666, ccs_dir, - CCS_EXECUTE_HANDLER); -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) - proc_create("self_domain", 0666, ccs_dir, &ccs_self_operations); -#else - { - struct proc_dir_entry *e = create_proc_entry("self_domain", - 0666, ccs_dir); - if (e) - e->proc_fops = &ccs_self_operations; - } -#endif -} - -/** - * ccs_init_module - Initialize this module. - * - * Returns 0 on success, negative value otherwise. - */ -static int __init ccs_init_module(void) -{ - if (ccsecurity_ops.disabled) - return -EINVAL; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) - MOD_INC_USE_COUNT; -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) - if (init_srcu_struct(&ccs_ss)) - panic("Out of memory."); -#endif - ccs_kernel_namespace.name = ""; - ccs_init_policy_namespace(&ccs_kernel_namespace); - ccs_kernel_domain.ns = &ccs_kernel_namespace; - INIT_LIST_HEAD(&ccs_kernel_domain.acl_info_list); - ccs_mm_init(); - ccs_policy_io_init(); - ccs_permission_init(); - ccs_proc_init(); - ccs_load_builtin_policy(); - return 0; -} - -/** - * ccs_main_init - Initialize this module. - * - * Returns nothing. - */ -void __init ccs_main_init(void) -{ - ccs_init_module(); -} diff --git a/akari/probe.c b/akari/probe.c deleted file mode 100644 index 8a58d81..0000000 --- a/akari/probe.c +++ /dev/null @@ -1,735 +0,0 @@ -/* - * probe.c - * - * Copyright (C) 2010-2013 Tetsuo Handa - * - * Functions in this file are doing runtime address resolution based on byte - * code comparison in order to allow LKM-based LSM modules to access built-in - * functions and variables which are not exported to LKMs. - * Since functions in this file are assuming that using identical source code, - * identical kernel config and identical compiler generates identical byte code - * output, functions in this file may not work on some architectures and/or - * environments. - * - * This file is used by AKARI and CaitSith. This file will become unnecessary - * when LKM-based LSM module comes back and TOMOYO 2.x becomes a LKM-based LSM - * module. - */ - -#include "probe.h" - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) - -/** - * probe_kernel_read - Wrapper for kernel_read(). - * - * @file: Pointer to "struct file". - * @offset: Starting position. - * @addr: Buffer. - * @count: Size of @addr. - * - * Returns return value from kernel_read(). - */ -static int __init probe_kernel_read(struct file *file, unsigned long offset, - char *addr, unsigned long count) -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 8) - /* - * I can't use kernel_read() because seq_read() returns -EPIPE - * if &pos != &file->f_pos . - */ - mm_segment_t old_fs; - unsigned long pos = file->f_pos; - int result; - file->f_pos = offset; - old_fs = get_fs(); - set_fs(get_ds()); - result = vfs_read(file, (void __user *)addr, count, &file->f_pos); - set_fs(old_fs); - file->f_pos = pos; - return result; -#else - return kernel_read(file, offset, addr, count); -#endif -} - -/** - * probe_find_symbol - Find function's address from /proc/kallsyms . - * - * @keyline: Function to find. - * - * Returns address of specified function on success, NULL otherwise. - */ -static void *__init probe_find_symbol(const char *keyline) -{ - struct file *file = NULL; - char *buf; - unsigned long entry = 0; - { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) - struct file_system_type *fstype = get_fs_type("proc"); - struct vfsmount *mnt = vfs_kern_mount(fstype, 0, "proc", NULL); -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) - struct file_system_type *fstype = NULL; - struct vfsmount *mnt = do_kern_mount("proc", 0, "proc", NULL); -#else - struct file_system_type *fstype = get_fs_type("proc"); - struct vfsmount *mnt = kern_mount(fstype); -#endif - struct dentry *root; - struct dentry *dentry; - /* - * We embed put_filesystem() here because it is not exported. - */ - if (fstype) - module_put(fstype->owner); - if (IS_ERR(mnt)) - goto out; - root = dget(mnt->mnt_root); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) - mutex_lock(&root->d_inode->i_mutex); - dentry = lookup_one_len("kallsyms", root, 8); - mutex_unlock(&root->d_inode->i_mutex); -#else - down(&root->d_inode->i_sem); - dentry = lookup_one_len("kallsyms", root, 8); - up(&root->d_inode->i_sem); -#endif - dput(root); - if (IS_ERR(dentry)) - mntput(mnt); - else { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) - struct path path = { mnt, dentry }; - file = dentry_open(&path, O_RDONLY, current_cred()); -#else - file = dentry_open(dentry, mnt, O_RDONLY -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) - , current_cred() -#endif - ); -#endif - } - } - if (IS_ERR(file) || !file) - goto out; - buf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (buf) { - int len; - int offset = 0; - while ((len = probe_kernel_read(file, offset, buf, - PAGE_SIZE - 1)) > 0) { - char *cp; - buf[len] = '\0'; - cp = strrchr(buf, '\n'); - if (!cp) - break; - *(cp + 1) = '\0'; - offset += strlen(buf); - cp = strstr(buf, keyline); - if (!cp) - continue; - *cp = '\0'; - while (cp > buf && *(cp - 1) != '\n') - cp--; - entry = simple_strtoul(cp, NULL, 16); - break; - } - kfree(buf); - } - filp_close(file, NULL); -out: - return (void *) entry; -} - -#endif - -#if defined(CONFIG_SECURITY_COMPOSER_MAX) - -/* - * Dummy variable for finding location of - * "struct list_head lsm_hooks[LSM_MAX_HOOKS]". - */ -struct list_head probe_lsm_hooks[LSM_MAX_HOOKS]; - -/** - * probe_security_bprm_committed_creds - Dummy function which does identical to security_bprm_committed_creds() in security/security.c. - * - * @bprm: Pointer to "struct linux_binprm". - * - * Returns nothing. - */ -void probe_security_bprm_committed_creds(struct linux_binprm *bprm) -{ - do { - struct security_operations *sop; - - list_for_each_entry(sop, - &probe_lsm_hooks[lsm_bprm_committed_creds], - list[lsm_bprm_committed_creds]) - sop->bprm_committed_creds(bprm); - } while (0); -} - -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) - -/* - * Dummy variable for finding address of - * "struct security_operations *security_ops". - */ -static struct security_operations *probe_dummy_security_ops; - -/** - * probe_security_file_alloc - Dummy function which does identical to security_file_alloc() in security/security.c. - * - * @file: Pointer to "struct file". - * - * Returns return value from security_file_alloc(). - */ -static int probe_security_file_alloc(struct file *file) -{ - return probe_dummy_security_ops->file_alloc_security(file); -} - -#if defined(CONFIG_ARM) - -/** - * probe_security_ops_on_arm - Find security_ops on ARM. - * - * @base: Address of security_file_alloc(). - * - * Returns address of security_ops on success, NULL otherwise. - */ -static void * __init probe_security_ops_on_arm(unsigned int *base) -{ - static unsigned int *ip4ret; - int i; - const unsigned long addr = (unsigned long) &probe_dummy_security_ops; - unsigned int *ip = (unsigned int *) probe_security_file_alloc; - for (i = 0; i < 32; ip++, i++) { - if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) - continue; - ip = base + i; - ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); - return &ip4ret; - } - ip = (unsigned int *) probe_security_file_alloc; - for (i = 0; i < 32; ip++, i++) { - /* - * Find - * ldr r3, [pc, #offset1] - * ldr r3, [r3, #offset2] - * sequence. - */ - if ((*ip & 0xFFFFF000) != 0xE59F3000 || - (*(ip + 1) & 0xFFFFF000) != 0xE5933000) - continue; - ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); - ip4ret += (*(ip + 1) & 0xFFF) >> 2; - if ((unsigned long) ip4ret != addr) - continue; - ip = base + i; - ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); - ip4ret += (*(ip + 1) & 0xFFF) >> 2; - return &ip4ret; - } - return NULL; -} - -#endif - -#endif - -#if defined(CONFIG_ARM) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) -/** - * probe_find_vfsmount_lock_on_arm - Find vfsmount_lock spinlock on ARM. - * - * @ip: Address of dummy function's entry point. - * @addr: Address of the variable which is used within @function. - * @base: Address of function's entry point. - * - * Returns address of vfsmount_lock on success, NULL otherwise. - */ -static void * __init probe_find_vfsmount_lock_on_arm(unsigned int *ip, - unsigned long addr, - unsigned int *base) -{ - int i; - for (i = 0; i < 32; ip++, i++) { - static unsigned int *ip4ret; - if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) - continue; - ip = base + i; - ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); - return &ip4ret; - } - return NULL; -} -#endif - -/** - * probe_find_variable - Find variable's address using dummy. - * - * @function: Pointer to dummy function's entry point. - * @addr: Address of the variable which is used within @function. - * @symbol: Name of symbol to resolve. - * - * This trick depends on below assumptions. - * - * (1) @addr is found within 128 bytes from @function, even if additional - * code (e.g. debug symbols) is added. - * (2) It is safe to read 128 bytes from @function. - * (3) @addr != Byte code except @addr. - */ -static void * __init probe_find_variable(void *function, unsigned long addr, - const char *symbol) -{ - int i; - u8 *base; - u8 *cp = function; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) - if (*symbol == ' ') - base = probe_find_symbol(symbol); - else -#endif - base = __symbol_get(symbol); - if (!base) - return NULL; -#if defined(CONFIG_ARM) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) && !defined(CONFIG_SECURITY_COMPOSER_MAX) - if (function == probe_security_file_alloc) - return probe_security_ops_on_arm((unsigned int *) base); -#endif -#if defined(CONFIG_ARM) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) - return probe_find_vfsmount_lock_on_arm(function, addr, - (unsigned int *) base); -#endif - /* First, assume absolute adressing mode is used. */ - for (i = 0; i < 128; i++) { - if (*(unsigned long *) cp == addr) - return base + i; - cp++; - } - /* Next, assume PC-relative addressing mode is used. */ -#if defined(CONFIG_S390) - cp = function; - for (i = 0; i < 128; i++) { - if ((unsigned long) (cp + (*(int *) cp) * 2 - 2) == addr) { - static void *cp4ret; - cp = base + i; - cp += (*(int *) cp) * 2 - 2; - cp4ret = cp; - return &cp4ret; - } - cp++; - } -#endif - cp = function; - for (i = 0; i < 128; i++) { - if ((unsigned long) (cp + sizeof(int) + *(int *) cp) == addr) { - static void *cp4ret; - cp = base + i; - cp += sizeof(int) + *(int *) cp; - cp4ret = cp; - return &cp4ret; - } - cp++; - } - cp = function; - for (i = 0; i < 128; i++) { - if ((unsigned long) (long) (*(int *) cp) == addr) { - static void *cp4ret; - cp = base + i; - cp = (void *) (long) (*(int *) cp); - cp4ret = cp; - return &cp4ret; - } - cp++; - } - return NULL; -} - -#if defined(CONFIG_SECURITY_COMPOSER_MAX) - -/** - * probe_lsm_hooks_list - Find address of "struct list_head lsm_hooks[LSM_MAX_HOOKS]". - * - * Returns pointer to "struct security_operations" on success, NULL otherwise. - */ -struct list_head * __init probe_lsm_hooks_list(void) -{ - unsigned int offset = 0; - void *cp; - /* Guess "struct list_head lsm_hooks[LSM_MAX_HOOKS];". */ - /* Try without offset. GCC 4.x seems to use this one. */ - cp = probe_find_variable(probe_security_bprm_committed_creds, - (unsigned long) probe_lsm_hooks, - " security_bprm_committed_creds\n"); - if (!cp) { - /* Retry with offset. GCC 3.x seems to use this one. */ - offset = offsetof(struct security_operations, - list[lsm_bprm_committed_creds]); - cp = probe_find_variable(probe_security_bprm_committed_creds, - ((unsigned long) probe_lsm_hooks) - + offset, - " security_bprm_committed_creds\n"); - } - if (!cp) { - printk(KERN_ERR - "Can't resolve security_bprm_committed_creds().\n"); - goto out; - } - /* This should be "struct list_head lsm_hooks[LSM_MAX_HOOKS];". */ - cp = (struct list_head *) (*(unsigned long *) cp); - if (!cp) { - printk(KERN_ERR "Can't resolve lsm_hooks array.\n"); - goto out; - } - /* Adjust if offset is used. */ - cp -= offset; - printk(KERN_INFO "lsm_hooks=%p\n", cp); - return cp; -out: - return NULL; -} - -#else - -/** - * probe_security_ops - Find address of "struct security_operations *security_ops". - * - * Returns pointer to "struct security_operations" on success, NULL otherwise. - */ -struct security_operations * __init probe_security_ops(void) -{ - struct security_operations **ptr; - struct security_operations *ops; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) - void *cp; - /* Guess "struct security_operations *security_ops;". */ - cp = probe_find_variable(probe_security_file_alloc, (unsigned long) - &probe_dummy_security_ops, - " security_file_alloc\n"); - if (!cp) { - printk(KERN_ERR "Can't resolve security_file_alloc().\n"); - return NULL; - } - /* This should be "struct security_operations *security_ops;". */ - ptr = *(struct security_operations ***) cp; -#else - /* This is "struct security_operations *security_ops;". */ - ptr = (struct security_operations **) __symbol_get("security_ops"); -#endif - if (!ptr) { - printk(KERN_ERR "Can't resolve security_ops structure.\n"); - return NULL; - } - printk(KERN_INFO "security_ops=%p\n", ptr); - ops = *ptr; - if (!ops) { - printk(KERN_ERR "No security_operations registered.\n"); - return NULL; - } - return ops; -} - -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) - -/** - * probe_find_task_by_vpid - Find address of find_task_by_vpid(). - * - * Returns address of find_task_by_vpid() on success, NULL otherwise. - */ -void * __init probe_find_task_by_vpid(void) -{ - void *ptr; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) - ptr = probe_find_symbol(" find_task_by_vpid\n"); -#else - ptr = __symbol_get("find_task_by_vpid"); -#endif - if (!ptr) { - printk(KERN_ERR "Can't resolve find_task_by_vpid().\n"); - return NULL; - } - printk(KERN_INFO "find_task_by_vpid=%p\n", ptr); - return ptr; -} - -/** - * probe_find_task_by_pid_ns - Find address of find_task_by_pid(). - * - * Returns address of find_task_by_pid_ns() on success, NULL otherwise. - */ -void * __init probe_find_task_by_pid_ns(void) -{ - void *ptr; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) - ptr = probe_find_symbol(" find_task_by_pid_ns\n"); -#else - ptr = __symbol_get("find_task_by_pid_ns"); -#endif - if (!ptr) { - printk(KERN_ERR "Can't resolve find_task_by_pid_ns().\n"); - return NULL; - } - printk(KERN_INFO "find_task_by_pid_ns=%p\n", ptr); - return ptr; -} - -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) - -#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) - -/* Dummy variable for finding address of "spinlock_t vfsmount_lock". */ -static spinlock_t probe_dummy_vfsmount_lock __cacheline_aligned_in_smp = - SPIN_LOCK_UNLOCKED; - -static struct list_head *probe_mount_hashtable; -static int probe_hash_mask, probe_hash_bits; - -/** - * hash - Copy of hash() in fs/namespace.c. - * - * @mnt: Pointer to "struct vfsmount". - * @dentry: Pointer to "struct dentry". - * - * Returns hash value. - */ -static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) -{ - unsigned long tmp = ((unsigned long) mnt / L1_CACHE_BYTES); - tmp += ((unsigned long) dentry / L1_CACHE_BYTES); - tmp = tmp + (tmp >> probe_hash_bits); - return tmp & probe_hash_mask; -} - -/** - * probe_lookup_mnt - Dummy function which does identical to lookup_mnt() in fs/namespace.c. - * - * @mnt: Pointer to "struct vfsmount". - * @dentry: Pointer to "struct dentry". - * - * Returns pointer to "struct vfsmount". - */ -static struct vfsmount *probe_lookup_mnt(struct vfsmount *mnt, - struct dentry *dentry) -{ - struct list_head *head = probe_mount_hashtable + hash(mnt, dentry); - struct list_head *tmp = head; - struct vfsmount *p, *found = NULL; - - spin_lock(&probe_dummy_vfsmount_lock); - for (;;) { - tmp = tmp->next; - p = NULL; - if (tmp == head) - break; - p = list_entry(tmp, struct vfsmount, mnt_hash); - if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) { - found = mntget(p); - break; - } - } - spin_unlock(&probe_dummy_vfsmount_lock); - return found; -} - -/** - * probe_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". - * - * Returns address of vfsmount_lock on success, NULL otherwise. - */ -void * __init probe_vfsmount_lock(void) -{ - void *cp; - spinlock_t *ptr; - /* Guess "spinlock_t vfsmount_lock;". */ - cp = probe_find_variable(probe_lookup_mnt, (unsigned long) - &probe_dummy_vfsmount_lock, " lookup_mnt\n"); - if (!cp) { - printk(KERN_ERR "Can't resolve lookup_mnt().\n"); - return NULL; - } - /* This should be "spinlock_t *vfsmount_lock;". */ - ptr = *(spinlock_t **) cp; - if (!ptr) { - printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); - return NULL; - } - printk(KERN_INFO "vfsmount_lock=%p\n", ptr); - return ptr; -} - -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) - -/* Dummy variable for finding address of "spinlock_t vfsmount_lock". */ -static spinlock_t probe_dummy_vfsmount_lock; - -/** - * probe_follow_up - Dummy function which does identical to follow_up() in fs/namei.c. - * - * @mnt: Pointer to "struct vfsmount *". - * @dentry: Pointer to "struct dentry *". - * - * Returns 1 if followed up, 0 otehrwise. - */ -static int probe_follow_up(struct vfsmount **mnt, struct dentry **dentry) -{ - struct vfsmount *parent; - struct dentry *mountpoint; - spin_lock(&probe_dummy_vfsmount_lock); - parent = (*mnt)->mnt_parent; - if (parent == *mnt) { - spin_unlock(&probe_dummy_vfsmount_lock); - return 0; - } - mntget(parent); - mountpoint = dget((*mnt)->mnt_mountpoint); - spin_unlock(&probe_dummy_vfsmount_lock); - dput(*dentry); - *dentry = mountpoint; - mntput(*mnt); - *mnt = parent; - return 1; -} - -/** - * probe_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". - * - * Returns address of vfsmount_lock on success, NULL otherwise. - */ -void * __init probe_vfsmount_lock(void) -{ - void *cp; - spinlock_t *ptr; - /* Guess "spinlock_t vfsmount_lock;". */ - cp = probe_find_variable(probe_follow_up, (unsigned long) - &probe_dummy_vfsmount_lock, "follow_up"); - if (!cp) { - printk(KERN_ERR "Can't resolve follow_up().\n"); - return NULL; - } - /* This should be "spinlock_t *vfsmount_lock;". */ - ptr = *(spinlock_t **) cp; - if (!ptr) { - printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); - return NULL; - } - printk(KERN_INFO "vfsmount_lock=%p\n", ptr); - return ptr; -} - -#else - -/* Dummy variable for finding address of "spinlock_t vfsmount_lock". */ -static spinlock_t probe_dummy_vfsmount_lock; - -/** - * probe_mnt_pin - Dummy function which does identical to mnt_pin() in fs/namespace.c. - * - * @mnt: Pointer to "struct vfsmount". - * - * Returns nothing. - */ -static void probe_mnt_pin(struct vfsmount *mnt) -{ - spin_lock(&probe_dummy_vfsmount_lock); - mnt->mnt_pinned++; - spin_unlock(&probe_dummy_vfsmount_lock); -} - -/** - * probe_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". - * - * Returns address of vfsmount_lock on success, NULL otherwise. - */ -void * __init probe_vfsmount_lock(void) -{ - void *cp; - spinlock_t *ptr; - /* Guess "spinlock_t vfsmount_lock;". */ - cp = probe_find_variable(probe_mnt_pin, (unsigned long) - &probe_dummy_vfsmount_lock, "mnt_pin"); - if (!cp) { - printk(KERN_ERR "Can't resolve mnt_pin().\n"); - return NULL; - } - /* This should be "spinlock_t *vfsmount_lock;". */ - ptr = *(spinlock_t **) cp; - if (!ptr) { - printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); - return NULL; - } - printk(KERN_INFO "vfsmount_lock=%p\n", ptr); - return ptr; -} - -#endif - -#else - -/* - * Never mark this variable as __initdata , for this variable might be accessed - * by caller of probe_find_vfsmount_lock(). - */ -static spinlock_t probe_dummy_vfsmount_lock; - -/** - * probe_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". - * - * Returns address of vfsmount_lock. - */ -void * __init probe_vfsmount_lock(void) -{ - return &probe_dummy_vfsmount_lock; -} - -#endif - -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) - -/** - * probe___d_path - Find address of "__d_path()". - * - * Returns address of __d_path() on success, NULL otherwise. - */ -void * __init probe___d_path(void) -{ - void *ptr = probe_find_symbol(" __d_path\n"); - if (!ptr) { - printk(KERN_ERR "Can't resolve __d_path().\n"); - return NULL; - } - printk(KERN_INFO "__d_path=%p\n", ptr); - return ptr; -} - -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) - -/** - * probe_d_absolute_path - Find address of "d_absolute_path()". - * - * Returns address of d_absolute_path() on success, NULL otherwise. - */ -void * __init probe_d_absolute_path(void) -{ - void *ptr = probe_find_symbol(" d_absolute_path\n"); - if (!ptr) { - printk(KERN_ERR "Can't resolve d_absolute_path().\n"); - return NULL; - } - printk(KERN_INFO "d_absolute_path=%p\n", ptr); - return ptr; -} - -#endif diff --git a/akari/probe.h b/akari/probe.h deleted file mode 100644 index 0f685bf..0000000 --- a/akari/probe.h +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) -#error This module supports only 2.6.0 and later kernels. -#endif -#ifndef CONFIG_SECURITY -#error You must choose CONFIG_SECURITY=y for building this module. -#endif -#ifndef CONFIG_KALLSYMS -#error You must choose CONFIG_KALLSYMS=y for building this module. -#endif -#ifndef CONFIG_PROC_FS -#error You must choose CONFIG_PROC_FS=y for building this module. -#endif -#ifndef CONFIG_MODULES -#error You must choose CONFIG_MODULES=y for building this module. -#endif - -#if defined(CONFIG_SECURITY_COMPOSER_MAX) -struct list_head; -struct list_head * __init probe_lsm_hooks_list(void); -#else -struct security_operations; -struct security_operations * __init probe_security_ops(void); -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) -void * __init probe_find_task_by_vpid(void); -void * __init probe_find_task_by_pid_ns(void); -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) -void * __init probe_vfsmount_lock(void); -#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) -void * __init probe___d_path(void); -#else -void * __init probe_d_absolute_path(void); -#endif diff --git a/akari/realpath.c b/akari/realpath.c deleted file mode 100644 index 6490993..0000000 --- a/akari/realpath.c +++ /dev/null @@ -1,732 +0,0 @@ -/* - * security/ccsecurity/realpath.c - * - * Copyright (C) 2005-2012 NTT DATA CORPORATION - * - * Version: 1.8.3+ 2012/05/05 - */ - -#include "internal.h" - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) -#include -#include -#endif - -/***** SECTION1: Constants definition *****/ - -#define SOCKFS_MAGIC 0x534F434B - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) -#define s_fs_info u.generic_sbp -#endif - -/***** SECTION2: Structure definition *****/ - -/***** SECTION3: Prototype definition section *****/ - -char *ccs_encode(const char *str); -char *ccs_encode2(const char *str, int str_len); -char *ccs_realpath(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(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(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(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(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 (inode->i_op && !inode->i_op->rename) - goto prepend_filesystem_name; - } - /* 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(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(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 (!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); -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(3, 7, 0) - struct vm_area_struct *vma; -#endif - const char *cp = NULL; - if (!mm) - return NULL; - down_read(&mm->mmap_sem); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) - if (mm->exe_file) - cp = ccs_realpath(&mm->exe_file->f_path); -#else - for (vma = mm->mmap; vma; vma = vma->vm_next) { - if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - struct path path = { vma->vm_file->f_vfsmnt, - vma->vm_file->f_dentry }; - cp = ccs_realpath(&path); -#else - cp = ccs_realpath(&vma->vm_file->f_path); -#endif - break; - } - } -#endif - up_read(&mm->mmap_sem); - return cp; -} diff --git a/akari/test.c b/akari/test.c deleted file mode 100644 index e8a1d8c..0000000 --- a/akari/test.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * akari_test.c - * - * Copyright (C) 2010-2013 Tetsuo Handa - */ -#include "probe.h" - -/** - * ccs_init - Initialize this module. - * - * Returns 0 on success, -EINVAL otherwise. - */ -static int __init ccs_init(void) -{ -#ifdef CONFIG_SECURITY_COMPOSER_MAX - if (!probe_lsm_hooks_list()) - goto out; -#else - if (!probe_security_ops()) - goto out; -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) - if (!probe_find_task_by_vpid()) - goto out; - if (!probe_find_task_by_pid_ns()) - goto out; -#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) - if (!probe_vfsmount_lock()) - goto out; -#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) - if (!probe___d_path()) - goto out; -#else - if (!probe_d_absolute_path()) - goto out; -#endif - printk(KERN_INFO "All dependent symbols have been guessed.\n"); - printk(KERN_INFO "Please verify these addresses using System.map for " - "this kernel (e.g. /boot/System.map-`uname -r` ).\n"); - printk(KERN_INFO "If these addresses are correct, you can try loading " - "AKARI module on this kernel.\n"); - return 0; -out: - printk(KERN_INFO "Sorry, I couldn't guess dependent symbols.\n"); - printk(KERN_INFO "I need some changes for supporting your " - "environment.\n"); - printk(KERN_INFO "Please contact the author.\n"); - return -EINVAL; -} - -/** - * ccs_exit - Exit this module. - * - * Returns nothing. - */ -static void ccs_exit(void) -{ -} - -module_init(ccs_init); -module_exit(ccs_exit); -MODULE_LICENSE("GPL"); diff --git a/udplogger.c b/udplogger.c new file mode 100644 index 0000000..3ad7e7f --- /dev/null +++ b/udplogger.c @@ -0,0 +1,559 @@ +/* + * Simple UDP logger - A utility for receiving output from netconsole. + * + * Written by Tetsuo Handa + * + * Distributed under GPL v2. + */ +#define _FILE_OFFSET_BITS 64 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define round_up(size) ((((size) + 4095u) / 4096u) * 4096u) + +/* Structure for holding IPv4 or IPv6 address. */ +union ipaddr { + struct sockaddr_in ip4; + struct sockaddr_in6 ip6; +}; + +/* Structure for tracking partially received data. */ +static struct client { + union ipaddr addr; /* Sender's IPv4 or IPv6 address. */ + socklen_t size; /* Valid bytes in @addr . */ + char *buffer; /* Buffer for holding received data. */ + int avail; /* Valid bytes in @buffer . */ + char addr_str[50]; /* String representation of @addr . */ + time_t stamp; /* Timestamp of receiving the first byte in @buffer . */ +} *clients = NULL; + +/* Current clients. */ +static int num_clients = 0; +/* Max clients. */ +static int max_clients = 1024; +/* Max write buffer per a client. */ +static int wbuf_size = 65536; +/* Max seconds to wait for new line. */ +static int wait_timeout = 10; +/* Try to release unused memory? */ +static _Bool try_drop_memory_usage = 0; +/* Name of today's log file. */ +static char filename[16] = { }; +/* Handle for today's log file. */ +static FILE *log_fp = NULL; +/* The mode of log file. */ +static mode_t log_perm = 0600; +/* Previous time. */ +static struct tm last_tm = { .tm_year = 70, .tm_mday = 1 }; + +/** + * die - Print error and exit. + * + * @fmt: Format string, followed by arguments. + * + * This function does not return. + */ +static void die(const char *fmt, ...) __attribute__((format(printf, 1, 2))); +static void die(const char *fmt, ...) +{ + const int err = errno; + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, " : %s(%d)\n", strerror(err), err); + exit(1); +} + +/** + * switch_logfile - Close yesterday's log file and open today's log file. + * + * @tm: Pointer to "struct tm" holding current time. + * + * Returns nothing. + */ +static void switch_logfile(struct tm *tm) +{ + FILE *fp; + int fd; + struct stat buf; + snprintf(filename, sizeof(filename) - 1, "%04u-%02u-%02u.log", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); + /* + * The log file must be a regular file without any executable bits. + * + * Print error message and exit if the first open() failed. + * Otherwise, continue using current log file if open() failed. + */ + fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, log_perm); + if (fd == EOF) + goto out1; + if (fstat(fd, &buf)) + goto out1; + if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0111)) + goto out2; + fp = fdopen(fd, "a"); + if (!fp) + goto out1; + if (log_fp) + fclose(log_fp); + log_fp = fp; + try_drop_memory_usage = 1; + return; + out2: + if (!log_fp) { + fprintf(stderr, "Log file %s is not a regular file without " + "executable bits.\n", filename); + exit(1); + } + out1: + if (!log_fp) + die("Can't create log file %s", filename); + if (fd != EOF) + close(fd); +} + +/** + * write_logfile - Write to today's log file. + * + * @ptr: Pointer to "struct client". + * @forced: True if the partial line should be written. + * + * Returns nothing. + */ +static void write_logfile(struct client *ptr, const _Bool forced) +{ + static time_t last_time = 0; + static char stamp[24] = { }; + char *buffer = ptr->buffer; + int avail = ptr->avail; + const time_t now_time = ptr->stamp; + if (last_time != now_time) { + struct tm *tm = localtime(&now_time); + if (!tm) + tm = &last_tm; + snprintf(stamp, sizeof(stamp) - 1, "%04u-%02u-%02u " + "%02u:%02u:%02u ", tm->tm_year + 1900, tm->tm_mon + 1, + tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + /* + * Switch log file if the day has changed. We can't use + * (last_time / 86400 != now_time / 86400) in order to allow + * switching at 00:00:00 of the local time. + */ + if (tm->tm_mday != last_tm.tm_mday || + tm->tm_mon != last_tm.tm_mon || + tm->tm_year != last_tm.tm_year) { + last_tm = *tm; + switch_logfile(tm); + } + last_time = now_time; + } + /* Write the completed lines. */ + while (1) { + char *cp = memchr(buffer, '\n', avail); + const int len = cp - buffer + 1; + if (!cp) + break; + fprintf(log_fp, "%s%s", stamp, ptr->addr_str); + fwrite(buffer, 1, len, log_fp); + avail -= len; + buffer += len; + } + /* Write the incomplete line if forced. */ + if (forced && avail) { + fprintf(log_fp, "%s%s", stamp, ptr->addr_str); + fwrite(buffer, 1, avail, log_fp); + fprintf(log_fp, "\n"); + avail = 0; + } + /* Discard the written data. */ + if (ptr->buffer != buffer) + memmove(ptr->buffer, buffer, avail); + ptr->avail = avail; +} + +/** + * drop_memory_usage - Try to reduce memory usage. + * + * Returns nothing. + */ +static void drop_memory_usage(void) +{ + struct client *ptr; + int i = 0; + if (!try_drop_memory_usage) + return; + try_drop_memory_usage = 0; + while (i < num_clients) { + ptr = &clients[i]; + if (ptr->avail) { + char *tmp = realloc(ptr->buffer, round_up(ptr->avail)); + if (tmp) + ptr->buffer = tmp; + i++; + continue; + } + free(ptr->buffer); + num_clients--; + memmove(ptr, ptr + 1, (num_clients - i) * sizeof(*ptr)); + } + if (num_clients) { + ptr = realloc(clients, round_up(sizeof(*ptr) * num_clients)); + if (ptr) + clients = ptr; + } else { + free(clients); + clients = NULL; + } +} + +/** + * flush_all_and_abort - Clean up upon out of memory. + * + * This function does not return. + */ +static void flush_all_and_abort(void) +{ + int i; + for (i = 0; i < num_clients; i++) + if (clients[i].avail) { + write_logfile(&clients[i], 1); + free(clients[i].buffer); + } + fprintf(log_fp, "[aborted due to memory allocation failure]\n"); + fflush(log_fp); + exit(1); +} + +/** + * get_addr - Get string representation of IP address. + * + * @ipv6: True if @addr contains an IPv6 address, false otheriwse. + * @addr: Pointer to "union ipaddr". + * + * Returns static buffer to IP string. + */ +static const char *get_addr(_Bool ipv6, const union ipaddr *addr) +{ + static char buf[40]; + memset(buf, 0, sizeof(buf)); + if (ipv6) + inet_ntop(AF_INET6, &addr->ip6.sin6_addr, buf, sizeof(buf)); + else + inet_ntop(AF_INET, &addr->ip4.sin_addr, buf, sizeof(buf)); + return buf; +} + +/** + * find_client - Find the structure for given address. + * + * @addr: Pointer to "union ipaddr". + * @size: Valid bytes in @addr . + * + * Returns "struct client" for @addr on success, NULL otherwise. + */ +static struct client *find_client(union ipaddr *addr, socklen_t size) +{ + struct client *ptr; + const char *ip; + _Bool ipv6 = 0; + int i; + if (size == sizeof(struct sockaddr_in)) { + if (addr->ip4.sin_family != AF_INET) + return NULL; + } else if (size == sizeof(struct sockaddr_in6)) { + if (addr->ip6.sin6_family != AF_INET6) + return NULL; + /* Use IPv4 format for v4-mapped-v6 address. */ + if ((addr->ip6.sin6_addr.s6_addr32[0] | + addr->ip6.sin6_addr.s6_addr32[1] | + (addr->ip6.sin6_addr.s6_addr32[2] ^ + htonl(0x0000ffff))) == 0) { + size = sizeof(struct sockaddr_in); + addr->ip4.sin_family = AF_INET; + addr->ip4.sin_port = addr->ip6.sin6_port; + memmove(&addr->ip4.sin_addr.s_addr, + &addr->ip6.sin6_addr.s6_addr[12], 4); + } else + ipv6 = 1; + } else { + return NULL; + } + for (i = 0; i < num_clients; i++) + if (clients[i].size == size && + !memcmp(&clients[i].addr, addr, size)) + return &clients[i]; + if (i >= max_clients) { + try_drop_memory_usage = 1; + drop_memory_usage(); + if (i >= max_clients) + return NULL; + } + ptr = realloc(clients, round_up(sizeof(*ptr) * (num_clients + 1))); + if (!ptr) + return NULL; + clients = ptr; + ptr = &clients[num_clients++]; + memset(ptr, 0, sizeof(*ptr)); + ptr->size = size; + memmove(&ptr->addr, addr, size); + ip = get_addr(ipv6, addr); + if (ipv6) + snprintf(ptr->addr_str, sizeof(ptr->addr_str) - 1, + "[%s]:%u ", ip, ntohs(addr->ip6.sin6_port)); + else + snprintf(ptr->addr_str, sizeof(ptr->addr_str) - 1, "%s:%u ", + ip, ntohs(addr->ip4.sin_port)); + return ptr; +} + +/** + * do_main - The main loop. + * + * @fd: Receiver socket's file descriptor. + * + * Returns nothing. + */ +static void do_main(const int fd) +{ + static char buf[65536]; + while (1) { + struct pollfd pfd = { fd, POLLIN, 0 }; + int i; + time_t now; + /* Don't wait forever if checking for timeout. */ + for (i = 0; i < num_clients; i++) + if (clients[i].avail) + break; + /* Flush log file and wait for data. */ + fflush(log_fp); + poll(&pfd, 1, i < num_clients ? 1000 : -1); + now = time(NULL); + /* Check for timeout. */ + for (i = 0; i < num_clients; i++) + if (clients[i].avail && + now - clients[i].stamp >= wait_timeout) + write_logfile(&clients[i], 1); + /* Don't receive forever in order to check for timeout. */ + while (now == time(NULL)) { + struct client *ptr; + char *tmp; + union ipaddr addr; + socklen_t size = sizeof(addr); + int len = recvfrom(fd, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &addr, &size); + if (len <= 0) + break; + ptr = find_client(&addr, size); + if (!ptr) + continue; + /* Save current time if receiving the first byte. */ + if (!ptr->avail) + ptr->stamp = now; + /* Append data to the line. */ + tmp = realloc(ptr->buffer, round_up(ptr->avail + len)); + if (!tmp) + flush_all_and_abort(); + memmove(tmp + ptr->avail, buf, len); + ptr->avail += len; + ptr->buffer = tmp; + /* Write if at least one line completed. */ + if (memchr(buf, '\n', len)) + write_logfile(ptr, 0); + /* Write if the line is too long. */ + if (ptr->avail >= wbuf_size) + write_logfile(ptr, 1); + } + drop_memory_usage(); + } +} + +/** + * usage - Print usage and exit. + * + * @name: Program's name. + * + * This function does not return. + */ +static void usage(const char *name) +{ + fprintf(stderr, "Simple UDP logger\n\n" + "Usage:\n %s [ip=$listen_ip] [port=$listen_port] " + "[dir=$log_dir] [timeout=$seconds_waiting_for_newline] " + "[clients=$max_clients] [wbuf=$write_buffer_size] " + "[rbuf=$receive_buffer_size] [uid=$user_id] [gid=$group_id] " + "[perm=$log_perm]\n\nPlease see the README file.\n", name); + exit (1); +} + +/** + * do_init - Initialization function. + * + * @argc: Number of arguments. + * @argv: Arguments. + * + * Returns the listener socket's file descriptor. + */ +static int do_init(int argc, char *argv[]) +{ + union ipaddr addr = { }; + char pwd[4096]; + /* Max receive buffer size. */ + int rbuf_size = 8 * 1048576; + socklen_t size; + int fd = EOF; + int i; + /* Directory to save logs. */ + const char *log_dir = "."; + /* Address to listen. */ + _Bool ipv6 = 1; + const char *ip = NULL; + unsigned short port = 6666; + uid_t user_id = getuid(); + gid_t group_id = getgid(); + struct tm *tm = NULL; + for (i = 1; i < argc; i++) { + char *arg = argv[i]; + if (!strncmp(arg, "ip=", 3)) + ip = arg + 3; + else if (!strncmp(arg, "port=", 5)) + port = atoi(arg + 5); + else if (!strncmp(arg, "dir=", 4)) + log_dir = arg + 4; + else if (!strncmp(arg, "timeout=", 8)) + wait_timeout = atoi(arg + 8); + else if (!strncmp(arg, "clients=", 8)) + max_clients = atoi(arg + 8); + else if (!strncmp(arg, "wbuf=", 5)) + wbuf_size = atoi(arg + 5); + else if (!strncmp(arg, "rbuf=", 5)) + rbuf_size = atoi(arg + 5); + else if (!strncmp(arg, "uid=", 4)) + user_id = atoi(arg + 4); + else if (!strncmp(arg, "gid=", 4)) + group_id = atoi(arg + 4); + else if (!strncmp(arg, "perm=", 5)) + log_perm = strtol(arg + 5, NULL, 8) & 0666; + else + usage(argv[0]); + } + /* Sanity check. */ + if (max_clients < 10) + max_clients = 10; + if (max_clients > 65536) + max_clients = 65536; + if (wait_timeout < 5) + wait_timeout = 5; + if (wait_timeout > 600) + wait_timeout = 600; + if (wbuf_size < 1024) + wbuf_size = 1024; + if (wbuf_size > 1048576) + wbuf_size = 1048576; + if (rbuf_size < 65536) + rbuf_size = 65536; + if (rbuf_size > 1024 * 1048576) + rbuf_size = 1024 * 1048576; + /* Create the listener socket and configure it. */ + if (!ip || inet_pton(AF_INET, ip, &addr.ip4.sin_addr) != 1) { + fd = socket(PF_INET6, SOCK_DGRAM, 0); + /* + * Give up if IPv6 address was requested but IPv6 socket is not + * available. + */ + if (fd == EOF && ip) + die("Can't create IPv6 socket"); + } + if (fd == EOF) { + fd = socket(PF_INET, SOCK_DGRAM, 0); + /* Give up if IPv4 socket is not available. */ + if (fd == EOF) + die("Can't create IPv4 socket"); + ipv6 = 0; + } + if (ipv6) { + addr.ip6.sin6_family = AF_INET6; + addr.ip6.sin6_port = htons(port); + if (!ip) + addr.ip6.sin6_addr = in6addr_any; + else if (inet_pton(AF_INET6, ip, &addr.ip6.sin6_addr) != 1) { + fprintf(stderr, "%s is an Invalid IPv6 address\n", ip); + exit(1); + } + } else { + addr.ip4.sin_family = AF_INET; + addr.ip4.sin_port = htons(port); + if (!ip) + addr.ip4.sin_addr.s_addr = htonl(INADDR_ANY); + } +#ifdef SO_RCVBUFFORCE + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &rbuf_size, + sizeof(rbuf_size))) +#endif + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rbuf_size, + sizeof(rbuf_size))) + die("Can't set receive buffer size"); + size = sizeof(rbuf_size); + if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rbuf_size, &size)) + die("Can't get receive buffer size"); + ip = get_addr(ipv6, &addr); + if (ipv6) + port = ntohs(addr.ip6.sin6_port); + else + port = ntohs(addr.ip4.sin_port); + size = ipv6 ? sizeof(addr.ip6) : sizeof(addr.ip4); + if (bind(fd, (struct sockaddr *) &addr, size)) + die("Can't bind to ip=%s port=%u", ip, port); + else if (getsockname(fd, (struct sockaddr *) &addr, &size)) + die("Can't get bound address"); + ip = get_addr(ipv6, &addr); + if (ipv6) + port = ntohs(addr.ip6.sin6_port); + else + port = ntohs(addr.ip4.sin_port); + /* Open the initial log file. */ + memset(pwd, 0, sizeof(pwd)); + if (chdir(log_dir) || !getcwd(pwd, sizeof(pwd) - 1)) + die("Can't change directory to %s", log_dir); + setgid(group_id); /* We can ignore errors here. */ + if (getgid() != group_id) + die("Can't change group ID to %d", group_id); + setuid(user_id); /* We can ignore errors here. */ + if (getuid() != user_id) + die("Can't change user ID to %d", user_id); + { + const time_t now = time(NULL); + tm = localtime(&now); + if (!tm) + tm = &last_tm; + umask(0); + switch_logfile(tm); + } + /* Successfully initialized. */ + printf("Started at %04u-%02u-%02u %02u:%02u:%02u from %s/%s ", + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, + tm->tm_min, tm->tm_sec, pwd, filename); + printf("using options ip=%s port=%u dir=%s timeout=%u clients=%u " + "wbuf=%u rbuf=%u uid=%u gid=%u perm=0%03o\n", ip, port, pwd, + wait_timeout, max_clients, wbuf_size, rbuf_size, user_id, + group_id, log_perm); + return fd; +} + +int main(int argc, char *argv[]) +{ + const int fd = do_init(argc, argv); + do_main(fd); + return 0; +} diff --git a/udplogger.spec b/udplogger.spec new file mode 100644 index 0000000..71aa096 --- /dev/null +++ b/udplogger.spec @@ -0,0 +1,45 @@ +Summary: A utility for receiving netconsole messages. +Name: udplogger +Version: 1.1 +Release: 1 +License: GPLv2 +Group: Applications/System +ExclusiveOS: Linux +Autoreqprov: no +Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot +Conflicts: udplogger < 1.1-1 + +Source0: udplogger.tar.gz + +%description +This package contains a utility for receiving netconsole messages. + +%prep + +%setup -q -n udplogger + +%build + +make CFLAGS="-Wall $RPM_OPT_FLAGS" + +%install + +rm -rf $RPM_BUILD_ROOT +make INSTALLDIR=$RPM_BUILD_ROOT install + +%clean + +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root) +/usr/bin/udplogger +%doc /usr/share/doc/udplogger/README +%doc /usr/share/doc/udplogger/COPYING + +%changelog +* Sun Apr 20 2014 1.1-1 +- Typo fix. + +* Mon Mar 10 2014 1.0-1 +- Initial packaging.