summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorTimothy Pearson <tpearson@raptorengineering.com>2017-08-23 14:45:25 -0500
committerTimothy Pearson <tpearson@raptorengineering.com>2017-08-23 14:45:25 -0500
commitfcbb27b0ec6dcbc5a5108cb8fb19eae64593d204 (patch)
tree22962a4387943edc841c72a4e636a068c66d58fd /security
downloadast2050-linux-kernel-fcbb27b0ec6dcbc5a5108cb8fb19eae64593d204.zip
ast2050-linux-kernel-fcbb27b0ec6dcbc5a5108cb8fb19eae64593d204.tar.gz
Initial import of modified Linux 2.6.28 tree
Original upstream URL: git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git | branch linux-2.6.28.y
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig130
-rw-r--r--security/Makefile19
-rw-r--r--security/capability.c997
-rw-r--r--security/commoncap.c712
-rw-r--r--security/device_cgroup.c544
-rw-r--r--security/inode.c334
-rw-r--r--security/keys/Makefile17
-rw-r--r--security/keys/compat.c89
-rw-r--r--security/keys/internal.h183
-rw-r--r--security/keys/key.c1006
-rw-r--r--security/keys/keyctl.c1241
-rw-r--r--security/keys/keyring.c998
-rw-r--r--security/keys/permission.c107
-rw-r--r--security/keys/proc.c262
-rw-r--r--security/keys/process_keys.c799
-rw-r--r--security/keys/request_key.c525
-rw-r--r--security/keys/request_key_auth.c280
-rw-r--r--security/keys/sysctl.c50
-rw-r--r--security/keys/user_defined.c218
-rw-r--r--security/root_plug.c105
-rw-r--r--security/security.c1170
-rw-r--r--security/selinux/Kconfig160
-rw-r--r--security/selinux/Makefile22
-rw-r--r--security/selinux/avc.c949
-rw-r--r--security/selinux/exports.c61
-rw-r--r--security/selinux/hooks.c5867
-rw-r--r--security/selinux/include/audit.h65
-rw-r--r--security/selinux/include/av_inherit.h33
-rw-r--r--security/selinux/include/av_perm_to_string.h178
-rw-r--r--security/selinux/include/av_permissions.h843
-rw-r--r--security/selinux/include/avc.h142
-rw-r--r--security/selinux/include/avc_ss.h35
-rw-r--r--security/selinux/include/class_to_string.h74
-rw-r--r--security/selinux/include/common_perm_to_string.h58
-rw-r--r--security/selinux/include/conditional.h22
-rw-r--r--security/selinux/include/flask.h89
-rw-r--r--security/selinux/include/initial_sid_to_string.h33
-rw-r--r--security/selinux/include/netif.h23
-rw-r--r--security/selinux/include/netlabel.h150
-rw-r--r--security/selinux/include/netnode.h32
-rw-r--r--security/selinux/include/netport.h31
-rw-r--r--security/selinux/include/objsec.h133
-rw-r--r--security/selinux/include/security.h181
-rw-r--r--security/selinux/include/xfrm.h88
-rw-r--r--security/selinux/netif.c319
-rw-r--r--security/selinux/netlabel.c549
-rw-r--r--security/selinux/netlink.c116
-rw-r--r--security/selinux/netnode.c347
-rw-r--r--security/selinux/netport.c281
-rw-r--r--security/selinux/nlmsgtab.c176
-rw-r--r--security/selinux/selinuxfs.c1809
-rw-r--r--security/selinux/ss/Makefile9
-rw-r--r--security/selinux/ss/avtab.c515
-rw-r--r--security/selinux/ss/avtab.h91
-rw-r--r--security/selinux/ss/conditional.c506
-rw-r--r--security/selinux/ss/conditional.h77
-rw-r--r--security/selinux/ss/constraint.h61
-rw-r--r--security/selinux/ss/context.h155
-rw-r--r--security/selinux/ss/ebitmap.c448
-rw-r--r--security/selinux/ss/ebitmap.h145
-rw-r--r--security/selinux/ss/hashtab.c165
-rw-r--r--security/selinux/ss/hashtab.h87
-rw-r--r--security/selinux/ss/mls.c654
-rw-r--r--security/selinux/ss/mls.h88
-rw-r--r--security/selinux/ss/mls_types.h56
-rw-r--r--security/selinux/ss/policydb.c2151
-rw-r--r--security/selinux/ss/policydb.h299
-rw-r--r--security/selinux/ss/services.c2990
-rw-r--r--security/selinux/ss/services.h15
-rw-r--r--security/selinux/ss/sidtab.c278
-rw-r--r--security/selinux/ss/sidtab.h54
-rw-r--r--security/selinux/ss/symtab.c44
-rw-r--r--security/selinux/ss/symtab.h23
-rw-r--r--security/selinux/xfrm.c489
-rw-r--r--security/smack/Kconfig10
-rw-r--r--security/smack/Makefile7
-rw-r--r--security/smack/smack.h215
-rw-r--r--security/smack/smack_access.c364
-rw-r--r--security/smack/smack_lsm.c2754
-rw-r--r--security/smack/smackfs.c1095
80 files changed, 36497 insertions, 0 deletions
diff --git a/security/Kconfig b/security/Kconfig
new file mode 100644
index 0000000..d9f47ce
--- /dev/null
+++ b/security/Kconfig
@@ -0,0 +1,130 @@
+#
+# Security configuration
+#
+
+menu "Security options"
+
+config KEYS
+ bool "Enable access key retention support"
+ help
+ This option provides support for retaining authentication tokens and
+ access keys in the kernel.
+
+ It also includes provision of methods by which such keys might be
+ associated with a process so that network filesystems, encryption
+ support and the like can find them.
+
+ Furthermore, a special type of key is available that acts as keyring:
+ a searchable sequence of keys. Each process is equipped with access
+ to five standard keyrings: UID-specific, GID-specific, session,
+ process and thread.
+
+ If you are unsure as to whether this is required, answer N.
+
+config KEYS_DEBUG_PROC_KEYS
+ bool "Enable the /proc/keys file by which keys may be viewed"
+ depends on KEYS
+ help
+ This option turns on support for the /proc/keys file - through which
+ can be listed all the keys on the system that are viewable by the
+ reading process.
+
+ The only keys included in the list are those that grant View
+ permission to the reading process whether or not it possesses them.
+ Note that LSM security checks are still performed, and may further
+ filter out keys that the current process is not authorised to view.
+
+ Only key attributes are listed here; key payloads are not included in
+ the resulting table.
+
+ If you are unsure as to whether this is required, answer N.
+
+config SECURITY
+ bool "Enable different security models"
+ depends on SYSFS
+ help
+ This allows you to choose different security modules to be
+ configured into your kernel.
+
+ If this option is not selected, the default Linux security
+ model will be used.
+
+ If you are unsure how to answer this question, answer N.
+
+config SECURITYFS
+ bool "Enable the securityfs filesystem"
+ help
+ This will build the securityfs filesystem. It is currently used by
+ the TPM bios character driver. It is not used by SELinux or SMACK.
+
+ If you are unsure how to answer this question, answer N.
+
+config SECURITY_NETWORK
+ bool "Socket and Networking Security Hooks"
+ depends on SECURITY
+ help
+ This enables the socket and networking security hooks.
+ If enabled, a security module can use these hooks to
+ implement socket and networking access controls.
+ If you are unsure how to answer this question, answer N.
+
+config SECURITY_NETWORK_XFRM
+ bool "XFRM (IPSec) Networking Security Hooks"
+ depends on XFRM && SECURITY_NETWORK
+ help
+ This enables the XFRM (IPSec) networking security hooks.
+ If enabled, a security module can use these hooks to
+ implement per-packet access controls based on labels
+ derived from IPSec policy. Non-IPSec communications are
+ designated as unlabelled, and only sockets authorized
+ to communicate unlabelled data can send without using
+ IPSec.
+ If you are unsure how to answer this question, answer N.
+
+config SECURITY_FILE_CAPABILITIES
+ bool "File POSIX Capabilities"
+ default n
+ help
+ This enables filesystem capabilities, allowing you to give
+ binaries a subset of root's powers without using setuid 0.
+
+ If in doubt, answer N.
+
+config SECURITY_ROOTPLUG
+ bool "Root Plug Support"
+ depends on USB=y && SECURITY
+ help
+ This is a sample LSM module that should only be used as such.
+ It prevents any programs running with egid == 0 if a specific
+ USB device is not present in the system.
+
+ See <http://www.linuxjournal.com/article.php?sid=6279> for
+ more information about this module.
+
+ If you are unsure how to answer this question, answer N.
+
+config SECURITY_DEFAULT_MMAP_MIN_ADDR
+ int "Low address space to protect from user allocation"
+ depends on SECURITY
+ default 0
+ help
+ This is the portion of low virtual memory which should be protected
+ from userspace allocation. Keeping a user from writing to low pages
+ can help reduce the impact of kernel NULL pointer bugs.
+
+ For most ia64, ppc64 and x86 users with lots of address space
+ a value of 65536 is reasonable and should cause no problems.
+ On arm and other archs it should not be higher than 32768.
+ Programs which use vm86 functionality would either need additional
+ permissions from either the LSM or the capabilities module or have
+ this protection disabled.
+
+ This value can be changed after boot using the
+ /proc/sys/vm/mmap_min_addr tunable.
+
+
+source security/selinux/Kconfig
+source security/smack/Kconfig
+
+endmenu
+
diff --git a/security/Makefile b/security/Makefile
new file mode 100644
index 0000000..c05c127
--- /dev/null
+++ b/security/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for the kernel security code
+#
+
+obj-$(CONFIG_KEYS) += keys/
+subdir-$(CONFIG_SECURITY_SELINUX) += selinux
+subdir-$(CONFIG_SECURITY_SMACK) += smack
+
+# always enable default capabilities
+obj-y += commoncap.o
+
+# Object file lists
+obj-$(CONFIG_SECURITY) += security.o capability.o
+obj-$(CONFIG_SECURITYFS) += inode.o
+# Must precede capability.o in order to stack properly.
+obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
+obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
+obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o
+obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
diff --git a/security/capability.c b/security/capability.c
new file mode 100644
index 0000000..2458748
--- /dev/null
+++ b/security/capability.c
@@ -0,0 +1,997 @@
+/*
+ * Capabilities Linux Security Module
+ *
+ * This is the default security module in case no other module is loaded.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/security.h>
+
+static int cap_acct(struct file *file)
+{
+ return 0;
+}
+
+static int cap_sysctl(ctl_table *table, int op)
+{
+ return 0;
+}
+
+static int cap_quotactl(int cmds, int type, int id, struct super_block *sb)
+{
+ return 0;
+}
+
+static int cap_quota_on(struct dentry *dentry)
+{
+ return 0;
+}
+
+static int cap_bprm_alloc_security(struct linux_binprm *bprm)
+{
+ return 0;
+}
+
+static void cap_bprm_free_security(struct linux_binprm *bprm)
+{
+}
+
+static void cap_bprm_post_apply_creds(struct linux_binprm *bprm)
+{
+}
+
+static int cap_bprm_check_security(struct linux_binprm *bprm)
+{
+ return 0;
+}
+
+static int cap_sb_alloc_security(struct super_block *sb)
+{
+ return 0;
+}
+
+static void cap_sb_free_security(struct super_block *sb)
+{
+}
+
+static int cap_sb_copy_data(char *orig, char *copy)
+{
+ return 0;
+}
+
+static int cap_sb_kern_mount(struct super_block *sb, void *data)
+{
+ return 0;
+}
+
+static int cap_sb_show_options(struct seq_file *m, struct super_block *sb)
+{
+ return 0;
+}
+
+static int cap_sb_statfs(struct dentry *dentry)
+{
+ return 0;
+}
+
+static int cap_sb_mount(char *dev_name, struct path *path, char *type,
+ unsigned long flags, void *data)
+{
+ return 0;
+}
+
+static int cap_sb_check_sb(struct vfsmount *mnt, struct path *path)
+{
+ return 0;
+}
+
+static int cap_sb_umount(struct vfsmount *mnt, int flags)
+{
+ return 0;
+}
+
+static void cap_sb_umount_close(struct vfsmount *mnt)
+{
+}
+
+static void cap_sb_umount_busy(struct vfsmount *mnt)
+{
+}
+
+static void cap_sb_post_remount(struct vfsmount *mnt, unsigned long flags,
+ void *data)
+{
+}
+
+static void cap_sb_post_addmount(struct vfsmount *mnt, struct path *path)
+{
+}
+
+static int cap_sb_pivotroot(struct path *old_path, struct path *new_path)
+{
+ return 0;
+}
+
+static void cap_sb_post_pivotroot(struct path *old_path, struct path *new_path)
+{
+}
+
+static int cap_sb_set_mnt_opts(struct super_block *sb,
+ struct security_mnt_opts *opts)
+{
+ if (unlikely(opts->num_mnt_opts))
+ return -EOPNOTSUPP;
+ return 0;
+}
+
+static void cap_sb_clone_mnt_opts(const struct super_block *oldsb,
+ struct super_block *newsb)
+{
+}
+
+static int cap_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
+{
+ return 0;
+}
+
+static int cap_inode_alloc_security(struct inode *inode)
+{
+ return 0;
+}
+
+static void cap_inode_free_security(struct inode *inode)
+{
+}
+
+static int cap_inode_init_security(struct inode *inode, struct inode *dir,
+ char **name, void **value, size_t *len)
+{
+ return -EOPNOTSUPP;
+}
+
+static int cap_inode_create(struct inode *inode, struct dentry *dentry,
+ int mask)
+{
+ return 0;
+}
+
+static int cap_inode_link(struct dentry *old_dentry, struct inode *inode,
+ struct dentry *new_dentry)
+{
+ return 0;
+}
+
+static int cap_inode_unlink(struct inode *inode, struct dentry *dentry)
+{
+ return 0;
+}
+
+static int cap_inode_symlink(struct inode *inode, struct dentry *dentry,
+ const char *name)
+{
+ return 0;
+}
+
+static int cap_inode_mkdir(struct inode *inode, struct dentry *dentry,
+ int mask)
+{
+ return 0;
+}
+
+static int cap_inode_rmdir(struct inode *inode, struct dentry *dentry)
+{
+ return 0;
+}
+
+static int cap_inode_mknod(struct inode *inode, struct dentry *dentry,
+ int mode, dev_t dev)
+{
+ return 0;
+}
+
+static int cap_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
+ struct inode *new_inode, struct dentry *new_dentry)
+{
+ return 0;
+}
+
+static int cap_inode_readlink(struct dentry *dentry)
+{
+ return 0;
+}
+
+static int cap_inode_follow_link(struct dentry *dentry,
+ struct nameidata *nameidata)
+{
+ return 0;
+}
+
+static int cap_inode_permission(struct inode *inode, int mask)
+{
+ return 0;
+}
+
+static int cap_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ return 0;
+}
+
+static int cap_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
+{
+ return 0;
+}
+
+static void cap_inode_delete(struct inode *ino)
+{
+}
+
+static void cap_inode_post_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+}
+
+static int cap_inode_getxattr(struct dentry *dentry, const char *name)
+{
+ return 0;
+}
+
+static int cap_inode_listxattr(struct dentry *dentry)
+{
+ return 0;
+}
+
+static int cap_inode_getsecurity(const struct inode *inode, const char *name,
+ void **buffer, bool alloc)
+{
+ return -EOPNOTSUPP;
+}
+
+static int cap_inode_setsecurity(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ return -EOPNOTSUPP;
+}
+
+static int cap_inode_listsecurity(struct inode *inode, char *buffer,
+ size_t buffer_size)
+{
+ return 0;
+}
+
+static void cap_inode_getsecid(const struct inode *inode, u32 *secid)
+{
+ *secid = 0;
+}
+
+static int cap_file_permission(struct file *file, int mask)
+{
+ return 0;
+}
+
+static int cap_file_alloc_security(struct file *file)
+{
+ return 0;
+}
+
+static void cap_file_free_security(struct file *file)
+{
+}
+
+static int cap_file_ioctl(struct file *file, unsigned int command,
+ unsigned long arg)
+{
+ return 0;
+}
+
+static int cap_file_mmap(struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags,
+ unsigned long addr, unsigned long addr_only)
+{
+ if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO))
+ return -EACCES;
+ return 0;
+}
+
+static int cap_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+ unsigned long prot)
+{
+ return 0;
+}
+
+static int cap_file_lock(struct file *file, unsigned int cmd)
+{
+ return 0;
+}
+
+static int cap_file_fcntl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return 0;
+}
+
+static int cap_file_set_fowner(struct file *file)
+{
+ return 0;
+}
+
+static int cap_file_send_sigiotask(struct task_struct *tsk,
+ struct fown_struct *fown, int sig)
+{
+ return 0;
+}
+
+static int cap_file_receive(struct file *file)
+{
+ return 0;
+}
+
+static int cap_dentry_open(struct file *file)
+{
+ return 0;
+}
+
+static int cap_task_create(unsigned long clone_flags)
+{
+ return 0;
+}
+
+static int cap_task_alloc_security(struct task_struct *p)
+{
+ return 0;
+}
+
+static void cap_task_free_security(struct task_struct *p)
+{
+}
+
+static int cap_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
+{
+ return 0;
+}
+
+static int cap_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
+{
+ return 0;
+}
+
+static int cap_task_setpgid(struct task_struct *p, pid_t pgid)
+{
+ return 0;
+}
+
+static int cap_task_getpgid(struct task_struct *p)
+{
+ return 0;
+}
+
+static int cap_task_getsid(struct task_struct *p)
+{
+ return 0;
+}
+
+static void cap_task_getsecid(struct task_struct *p, u32 *secid)
+{
+ *secid = 0;
+}
+
+static int cap_task_setgroups(struct group_info *group_info)
+{
+ return 0;
+}
+
+static int cap_task_getioprio(struct task_struct *p)
+{
+ return 0;
+}
+
+static int cap_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
+{
+ return 0;
+}
+
+static int cap_task_getscheduler(struct task_struct *p)
+{
+ return 0;
+}
+
+static int cap_task_movememory(struct task_struct *p)
+{
+ return 0;
+}
+
+static int cap_task_wait(struct task_struct *p)
+{
+ return 0;
+}
+
+static int cap_task_kill(struct task_struct *p, struct siginfo *info,
+ int sig, u32 secid)
+{
+ return 0;
+}
+
+static void cap_task_to_inode(struct task_struct *p, struct inode *inode)
+{
+}
+
+static int cap_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
+{
+ return 0;
+}
+
+static void cap_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
+{
+ *secid = 0;
+}
+
+static int cap_msg_msg_alloc_security(struct msg_msg *msg)
+{
+ return 0;
+}
+
+static void cap_msg_msg_free_security(struct msg_msg *msg)
+{
+}
+
+static int cap_msg_queue_alloc_security(struct msg_queue *msq)
+{
+ return 0;
+}
+
+static void cap_msg_queue_free_security(struct msg_queue *msq)
+{
+}
+
+static int cap_msg_queue_associate(struct msg_queue *msq, int msqflg)
+{
+ return 0;
+}
+
+static int cap_msg_queue_msgctl(struct msg_queue *msq, int cmd)
+{
+ return 0;
+}
+
+static int cap_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
+ int msgflg)
+{
+ return 0;
+}
+
+static int cap_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
+ struct task_struct *target, long type, int mode)
+{
+ return 0;
+}
+
+static int cap_shm_alloc_security(struct shmid_kernel *shp)
+{
+ return 0;
+}
+
+static void cap_shm_free_security(struct shmid_kernel *shp)
+{
+}
+
+static int cap_shm_associate(struct shmid_kernel *shp, int shmflg)
+{
+ return 0;
+}
+
+static int cap_shm_shmctl(struct shmid_kernel *shp, int cmd)
+{
+ return 0;
+}
+
+static int cap_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr,
+ int shmflg)
+{
+ return 0;
+}
+
+static int cap_sem_alloc_security(struct sem_array *sma)
+{
+ return 0;
+}
+
+static void cap_sem_free_security(struct sem_array *sma)
+{
+}
+
+static int cap_sem_associate(struct sem_array *sma, int semflg)
+{
+ return 0;
+}
+
+static int cap_sem_semctl(struct sem_array *sma, int cmd)
+{
+ return 0;
+}
+
+static int cap_sem_semop(struct sem_array *sma, struct sembuf *sops,
+ unsigned nsops, int alter)
+{
+ return 0;
+}
+
+#ifdef CONFIG_SECURITY_NETWORK
+static int cap_unix_stream_connect(struct socket *sock, struct socket *other,
+ struct sock *newsk)
+{
+ return 0;
+}
+
+static int cap_unix_may_send(struct socket *sock, struct socket *other)
+{
+ return 0;
+}
+
+static int cap_socket_create(int family, int type, int protocol, int kern)
+{
+ return 0;
+}
+
+static int cap_socket_post_create(struct socket *sock, int family, int type,
+ int protocol, int kern)
+{
+ return 0;
+}
+
+static int cap_socket_bind(struct socket *sock, struct sockaddr *address,
+ int addrlen)
+{
+ return 0;
+}
+
+static int cap_socket_connect(struct socket *sock, struct sockaddr *address,
+ int addrlen)
+{
+ return 0;
+}
+
+static int cap_socket_listen(struct socket *sock, int backlog)
+{
+ return 0;
+}
+
+static int cap_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ return 0;
+}
+
+static void cap_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+}
+
+static int cap_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
+{
+ return 0;
+}
+
+static int cap_socket_recvmsg(struct socket *sock, struct msghdr *msg,
+ int size, int flags)
+{
+ return 0;
+}
+
+static int cap_socket_getsockname(struct socket *sock)
+{
+ return 0;
+}
+
+static int cap_socket_getpeername(struct socket *sock)
+{
+ return 0;
+}
+
+static int cap_socket_setsockopt(struct socket *sock, int level, int optname)
+{
+ return 0;
+}
+
+static int cap_socket_getsockopt(struct socket *sock, int level, int optname)
+{
+ return 0;
+}
+
+static int cap_socket_shutdown(struct socket *sock, int how)
+{
+ return 0;
+}
+
+static int cap_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ return 0;
+}
+
+static int cap_socket_getpeersec_stream(struct socket *sock,
+ char __user *optval,
+ int __user *optlen, unsigned len)
+{
+ return -ENOPROTOOPT;
+}
+
+static int cap_socket_getpeersec_dgram(struct socket *sock,
+ struct sk_buff *skb, u32 *secid)
+{
+ return -ENOPROTOOPT;
+}
+
+static int cap_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
+{
+ return 0;
+}
+
+static void cap_sk_free_security(struct sock *sk)
+{
+}
+
+static void cap_sk_clone_security(const struct sock *sk, struct sock *newsk)
+{
+}
+
+static void cap_sk_getsecid(struct sock *sk, u32 *secid)
+{
+}
+
+static void cap_sock_graft(struct sock *sk, struct socket *parent)
+{
+}
+
+static int cap_inet_conn_request(struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req)
+{
+ return 0;
+}
+
+static void cap_inet_csk_clone(struct sock *newsk,
+ const struct request_sock *req)
+{
+}
+
+static void cap_inet_conn_established(struct sock *sk, struct sk_buff *skb)
+{
+}
+
+static void cap_req_classify_flow(const struct request_sock *req,
+ struct flowi *fl)
+{
+}
+#endif /* CONFIG_SECURITY_NETWORK */
+
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+static int cap_xfrm_policy_alloc_security(struct xfrm_sec_ctx **ctxp,
+ struct xfrm_user_sec_ctx *sec_ctx)
+{
+ return 0;
+}
+
+static int cap_xfrm_policy_clone_security(struct xfrm_sec_ctx *old_ctx,
+ struct xfrm_sec_ctx **new_ctxp)
+{
+ return 0;
+}
+
+static void cap_xfrm_policy_free_security(struct xfrm_sec_ctx *ctx)
+{
+}
+
+static int cap_xfrm_policy_delete_security(struct xfrm_sec_ctx *ctx)
+{
+ return 0;
+}
+
+static int cap_xfrm_state_alloc_security(struct xfrm_state *x,
+ struct xfrm_user_sec_ctx *sec_ctx,
+ u32 secid)
+{
+ return 0;
+}
+
+static void cap_xfrm_state_free_security(struct xfrm_state *x)
+{
+}
+
+static int cap_xfrm_state_delete_security(struct xfrm_state *x)
+{
+ return 0;
+}
+
+static int cap_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 sk_sid, u8 dir)
+{
+ return 0;
+}
+
+static int cap_xfrm_state_pol_flow_match(struct xfrm_state *x,
+ struct xfrm_policy *xp,
+ struct flowi *fl)
+{
+ return 1;
+}
+
+static int cap_xfrm_decode_session(struct sk_buff *skb, u32 *fl, int ckall)
+{
+ return 0;
+}
+
+#endif /* CONFIG_SECURITY_NETWORK_XFRM */
+static void cap_d_instantiate(struct dentry *dentry, struct inode *inode)
+{
+}
+
+static int cap_getprocattr(struct task_struct *p, char *name, char **value)
+{
+ return -EINVAL;
+}
+
+static int cap_setprocattr(struct task_struct *p, char *name, void *value,
+ size_t size)
+{
+ return -EINVAL;
+}
+
+static int cap_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+{
+ return -EOPNOTSUPP;
+}
+
+static int cap_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
+{
+ return -EOPNOTSUPP;
+}
+
+static void cap_release_secctx(char *secdata, u32 seclen)
+{
+}
+
+#ifdef CONFIG_KEYS
+static int cap_key_alloc(struct key *key, struct task_struct *ctx,
+ unsigned long flags)
+{
+ return 0;
+}
+
+static void cap_key_free(struct key *key)
+{
+}
+
+static int cap_key_permission(key_ref_t key_ref, struct task_struct *context,
+ key_perm_t perm)
+{
+ return 0;
+}
+
+static int cap_key_getsecurity(struct key *key, char **_buffer)
+{
+ *_buffer = NULL;
+ return 0;
+}
+
+#endif /* CONFIG_KEYS */
+
+#ifdef CONFIG_AUDIT
+static int cap_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule)
+{
+ return 0;
+}
+
+static int cap_audit_rule_known(struct audit_krule *krule)
+{
+ return 0;
+}
+
+static int cap_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
+ struct audit_context *actx)
+{
+ return 0;
+}
+
+static void cap_audit_rule_free(void *lsmrule)
+{
+}
+#endif /* CONFIG_AUDIT */
+
+struct security_operations default_security_ops = {
+ .name = "default",
+};
+
+#define set_to_cap_if_null(ops, function) \
+ do { \
+ if (!ops->function) { \
+ ops->function = cap_##function; \
+ pr_debug("Had to override the " #function \
+ " security operation with the default.\n");\
+ } \
+ } while (0)
+
+void security_fixup_ops(struct security_operations *ops)
+{
+ set_to_cap_if_null(ops, ptrace_may_access);
+ set_to_cap_if_null(ops, ptrace_traceme);
+ set_to_cap_if_null(ops, capget);
+ set_to_cap_if_null(ops, capset_check);
+ set_to_cap_if_null(ops, capset_set);
+ set_to_cap_if_null(ops, acct);
+ set_to_cap_if_null(ops, capable);
+ set_to_cap_if_null(ops, quotactl);
+ set_to_cap_if_null(ops, quota_on);
+ set_to_cap_if_null(ops, sysctl);
+ set_to_cap_if_null(ops, syslog);
+ set_to_cap_if_null(ops, settime);
+ set_to_cap_if_null(ops, vm_enough_memory);
+ set_to_cap_if_null(ops, bprm_alloc_security);
+ set_to_cap_if_null(ops, bprm_free_security);
+ set_to_cap_if_null(ops, bprm_apply_creds);
+ set_to_cap_if_null(ops, bprm_post_apply_creds);
+ set_to_cap_if_null(ops, bprm_set_security);
+ set_to_cap_if_null(ops, bprm_check_security);
+ set_to_cap_if_null(ops, bprm_secureexec);
+ set_to_cap_if_null(ops, sb_alloc_security);
+ set_to_cap_if_null(ops, sb_free_security);
+ set_to_cap_if_null(ops, sb_copy_data);
+ set_to_cap_if_null(ops, sb_kern_mount);
+ set_to_cap_if_null(ops, sb_show_options);
+ set_to_cap_if_null(ops, sb_statfs);
+ set_to_cap_if_null(ops, sb_mount);
+ set_to_cap_if_null(ops, sb_check_sb);
+ set_to_cap_if_null(ops, sb_umount);
+ set_to_cap_if_null(ops, sb_umount_close);
+ set_to_cap_if_null(ops, sb_umount_busy);
+ set_to_cap_if_null(ops, sb_post_remount);
+ set_to_cap_if_null(ops, sb_post_addmount);
+ set_to_cap_if_null(ops, sb_pivotroot);
+ set_to_cap_if_null(ops, sb_post_pivotroot);
+ set_to_cap_if_null(ops, sb_set_mnt_opts);
+ set_to_cap_if_null(ops, sb_clone_mnt_opts);
+ set_to_cap_if_null(ops, sb_parse_opts_str);
+ set_to_cap_if_null(ops, inode_alloc_security);
+ set_to_cap_if_null(ops, inode_free_security);
+ set_to_cap_if_null(ops, inode_init_security);
+ set_to_cap_if_null(ops, inode_create);
+ set_to_cap_if_null(ops, inode_link);
+ set_to_cap_if_null(ops, inode_unlink);
+ set_to_cap_if_null(ops, inode_symlink);
+ set_to_cap_if_null(ops, inode_mkdir);
+ set_to_cap_if_null(ops, inode_rmdir);
+ set_to_cap_if_null(ops, inode_mknod);
+ set_to_cap_if_null(ops, inode_rename);
+ set_to_cap_if_null(ops, inode_readlink);
+ set_to_cap_if_null(ops, inode_follow_link);
+ set_to_cap_if_null(ops, inode_permission);
+ set_to_cap_if_null(ops, inode_setattr);
+ set_to_cap_if_null(ops, inode_getattr);
+ set_to_cap_if_null(ops, inode_delete);
+ set_to_cap_if_null(ops, inode_setxattr);
+ set_to_cap_if_null(ops, inode_post_setxattr);
+ set_to_cap_if_null(ops, inode_getxattr);
+ set_to_cap_if_null(ops, inode_listxattr);
+ set_to_cap_if_null(ops, inode_removexattr);
+ set_to_cap_if_null(ops, inode_need_killpriv);
+ set_to_cap_if_null(ops, inode_killpriv);
+ set_to_cap_if_null(ops, inode_getsecurity);
+ set_to_cap_if_null(ops, inode_setsecurity);
+ set_to_cap_if_null(ops, inode_listsecurity);
+ set_to_cap_if_null(ops, inode_getsecid);
+ set_to_cap_if_null(ops, file_permission);
+ set_to_cap_if_null(ops, file_alloc_security);
+ set_to_cap_if_null(ops, file_free_security);
+ set_to_cap_if_null(ops, file_ioctl);
+ set_to_cap_if_null(ops, file_mmap);
+ set_to_cap_if_null(ops, file_mprotect);
+ set_to_cap_if_null(ops, file_lock);
+ set_to_cap_if_null(ops, file_fcntl);
+ set_to_cap_if_null(ops, file_set_fowner);
+ set_to_cap_if_null(ops, file_send_sigiotask);
+ set_to_cap_if_null(ops, file_receive);
+ set_to_cap_if_null(ops, dentry_open);
+ set_to_cap_if_null(ops, task_create);
+ set_to_cap_if_null(ops, task_alloc_security);
+ set_to_cap_if_null(ops, task_free_security);
+ set_to_cap_if_null(ops, task_setuid);
+ set_to_cap_if_null(ops, task_post_setuid);
+ set_to_cap_if_null(ops, task_setgid);
+ set_to_cap_if_null(ops, task_setpgid);
+ set_to_cap_if_null(ops, task_getpgid);
+ set_to_cap_if_null(ops, task_getsid);
+ set_to_cap_if_null(ops, task_getsecid);
+ set_to_cap_if_null(ops, task_setgroups);
+ set_to_cap_if_null(ops, task_setnice);
+ set_to_cap_if_null(ops, task_setioprio);
+ set_to_cap_if_null(ops, task_getioprio);
+ set_to_cap_if_null(ops, task_setrlimit);
+ set_to_cap_if_null(ops, task_setscheduler);
+ set_to_cap_if_null(ops, task_getscheduler);
+ set_to_cap_if_null(ops, task_movememory);
+ set_to_cap_if_null(ops, task_wait);
+ set_to_cap_if_null(ops, task_kill);
+ set_to_cap_if_null(ops, task_prctl);
+ set_to_cap_if_null(ops, task_reparent_to_init);
+ set_to_cap_if_null(ops, task_to_inode);
+ set_to_cap_if_null(ops, ipc_permission);
+ set_to_cap_if_null(ops, ipc_getsecid);
+ set_to_cap_if_null(ops, msg_msg_alloc_security);
+ set_to_cap_if_null(ops, msg_msg_free_security);
+ set_to_cap_if_null(ops, msg_queue_alloc_security);
+ set_to_cap_if_null(ops, msg_queue_free_security);
+ set_to_cap_if_null(ops, msg_queue_associate);
+ set_to_cap_if_null(ops, msg_queue_msgctl);
+ set_to_cap_if_null(ops, msg_queue_msgsnd);
+ set_to_cap_if_null(ops, msg_queue_msgrcv);
+ set_to_cap_if_null(ops, shm_alloc_security);
+ set_to_cap_if_null(ops, shm_free_security);
+ set_to_cap_if_null(ops, shm_associate);
+ set_to_cap_if_null(ops, shm_shmctl);
+ set_to_cap_if_null(ops, shm_shmat);
+ set_to_cap_if_null(ops, sem_alloc_security);
+ set_to_cap_if_null(ops, sem_free_security);
+ set_to_cap_if_null(ops, sem_associate);
+ set_to_cap_if_null(ops, sem_semctl);
+ set_to_cap_if_null(ops, sem_semop);
+ set_to_cap_if_null(ops, netlink_send);
+ set_to_cap_if_null(ops, netlink_recv);
+ set_to_cap_if_null(ops, d_instantiate);
+ set_to_cap_if_null(ops, getprocattr);
+ set_to_cap_if_null(ops, setprocattr);
+ set_to_cap_if_null(ops, secid_to_secctx);
+ set_to_cap_if_null(ops, secctx_to_secid);
+ set_to_cap_if_null(ops, release_secctx);
+#ifdef CONFIG_SECURITY_NETWORK
+ set_to_cap_if_null(ops, unix_stream_connect);
+ set_to_cap_if_null(ops, unix_may_send);
+ set_to_cap_if_null(ops, socket_create);
+ set_to_cap_if_null(ops, socket_post_create);
+ set_to_cap_if_null(ops, socket_bind);
+ set_to_cap_if_null(ops, socket_connect);
+ set_to_cap_if_null(ops, socket_listen);
+ set_to_cap_if_null(ops, socket_accept);
+ set_to_cap_if_null(ops, socket_post_accept);
+ set_to_cap_if_null(ops, socket_sendmsg);
+ set_to_cap_if_null(ops, socket_recvmsg);
+ set_to_cap_if_null(ops, socket_getsockname);
+ set_to_cap_if_null(ops, socket_getpeername);
+ set_to_cap_if_null(ops, socket_setsockopt);
+ set_to_cap_if_null(ops, socket_getsockopt);
+ set_to_cap_if_null(ops, socket_shutdown);
+ set_to_cap_if_null(ops, socket_sock_rcv_skb);
+ set_to_cap_if_null(ops, socket_getpeersec_stream);
+ set_to_cap_if_null(ops, socket_getpeersec_dgram);
+ set_to_cap_if_null(ops, sk_alloc_security);
+ set_to_cap_if_null(ops, sk_free_security);
+ set_to_cap_if_null(ops, sk_clone_security);
+ set_to_cap_if_null(ops, sk_getsecid);
+ set_to_cap_if_null(ops, sock_graft);
+ set_to_cap_if_null(ops, inet_conn_request);
+ set_to_cap_if_null(ops, inet_csk_clone);
+ set_to_cap_if_null(ops, inet_conn_established);
+ set_to_cap_if_null(ops, req_classify_flow);
+#endif /* CONFIG_SECURITY_NETWORK */
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+ set_to_cap_if_null(ops, xfrm_policy_alloc_security);
+ set_to_cap_if_null(ops, xfrm_policy_clone_security);
+ set_to_cap_if_null(ops, xfrm_policy_free_security);
+ set_to_cap_if_null(ops, xfrm_policy_delete_security);
+ set_to_cap_if_null(ops, xfrm_state_alloc_security);
+ set_to_cap_if_null(ops, xfrm_state_free_security);
+ set_to_cap_if_null(ops, xfrm_state_delete_security);
+ set_to_cap_if_null(ops, xfrm_policy_lookup);
+ set_to_cap_if_null(ops, xfrm_state_pol_flow_match);
+ set_to_cap_if_null(ops, xfrm_decode_session);
+#endif /* CONFIG_SECURITY_NETWORK_XFRM */
+#ifdef CONFIG_KEYS
+ set_to_cap_if_null(ops, key_alloc);
+ set_to_cap_if_null(ops, key_free);
+ set_to_cap_if_null(ops, key_permission);
+ set_to_cap_if_null(ops, key_getsecurity);
+#endif /* CONFIG_KEYS */
+#ifdef CONFIG_AUDIT
+ set_to_cap_if_null(ops, audit_rule_init);
+ set_to_cap_if_null(ops, audit_rule_known);
+ set_to_cap_if_null(ops, audit_rule_match);
+ set_to_cap_if_null(ops, audit_rule_free);
+#endif
+}
diff --git a/security/commoncap.c b/security/commoncap.c
new file mode 100644
index 0000000..3976613
--- /dev/null
+++ b/security/commoncap.c
@@ -0,0 +1,712 @@
+/* Common capabilities, needed by capability.o and root_plug.o
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/capability.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/security.h>
+#include <linux/file.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/ptrace.h>
+#include <linux/xattr.h>
+#include <linux/hugetlb.h>
+#include <linux/mount.h>
+#include <linux/sched.h>
+#include <linux/prctl.h>
+#include <linux/securebits.h>
+
+int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
+{
+ NETLINK_CB(skb).eff_cap = current->cap_effective;
+ return 0;
+}
+
+int cap_netlink_recv(struct sk_buff *skb, int cap)
+{
+ if (!cap_raised(NETLINK_CB(skb).eff_cap, cap))
+ return -EPERM;
+ return 0;
+}
+
+EXPORT_SYMBOL(cap_netlink_recv);
+
+/*
+ * NOTE WELL: cap_capable() cannot be used like the kernel's capable()
+ * function. That is, it has the reverse semantics: cap_capable()
+ * returns 0 when a task has a capability, but the kernel's capable()
+ * returns 1 for this case.
+ */
+int cap_capable (struct task_struct *tsk, int cap)
+{
+ /* Derived from include/linux/sched.h:capable. */
+ if (cap_raised(tsk->cap_effective, cap))
+ return 0;
+ return -EPERM;
+}
+
+int cap_settime(struct timespec *ts, struct timezone *tz)
+{
+ if (!capable(CAP_SYS_TIME))
+ return -EPERM;
+ return 0;
+}
+
+int cap_ptrace_may_access(struct task_struct *child, unsigned int mode)
+{
+ /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */
+ if (cap_issubset(child->cap_permitted, current->cap_permitted))
+ return 0;
+ if (capable(CAP_SYS_PTRACE))
+ return 0;
+ return -EPERM;
+}
+
+int cap_ptrace_traceme(struct task_struct *parent)
+{
+ /* Derived from arch/i386/kernel/ptrace.c:sys_ptrace. */
+ if (cap_issubset(current->cap_permitted, parent->cap_permitted))
+ return 0;
+ if (has_capability(parent, CAP_SYS_PTRACE))
+ return 0;
+ return -EPERM;
+}
+
+int cap_capget (struct task_struct *target, kernel_cap_t *effective,
+ kernel_cap_t *inheritable, kernel_cap_t *permitted)
+{
+ /* Derived from kernel/capability.c:sys_capget. */
+ *effective = target->cap_effective;
+ *inheritable = target->cap_inheritable;
+ *permitted = target->cap_permitted;
+ return 0;
+}
+
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+
+static inline int cap_block_setpcap(struct task_struct *target)
+{
+ /*
+ * No support for remote process capability manipulation with
+ * filesystem capability support.
+ */
+ return (target != current);
+}
+
+static inline int cap_inh_is_capped(void)
+{
+ /*
+ * Return 1 if changes to the inheritable set are limited
+ * to the old permitted set. That is, if the current task
+ * does *not* possess the CAP_SETPCAP capability.
+ */
+ return (cap_capable(current, CAP_SETPCAP) != 0);
+}
+
+static inline int cap_limit_ptraced_target(void) { return 1; }
+
+#else /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */
+
+static inline int cap_block_setpcap(struct task_struct *t) { return 0; }
+static inline int cap_inh_is_capped(void) { return 1; }
+static inline int cap_limit_ptraced_target(void)
+{
+ return !capable(CAP_SETPCAP);
+}
+
+#endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */
+
+int cap_capset_check (struct task_struct *target, kernel_cap_t *effective,
+ kernel_cap_t *inheritable, kernel_cap_t *permitted)
+{
+ if (cap_block_setpcap(target)) {
+ return -EPERM;
+ }
+ if (cap_inh_is_capped()
+ && !cap_issubset(*inheritable,
+ cap_combine(target->cap_inheritable,
+ current->cap_permitted))) {
+ /* incapable of using this inheritable set */
+ return -EPERM;
+ }
+ if (!cap_issubset(*inheritable,
+ cap_combine(target->cap_inheritable,
+ current->cap_bset))) {
+ /* no new pI capabilities outside bounding set */
+ return -EPERM;
+ }
+
+ /* verify restrictions on target's new Permitted set */
+ if (!cap_issubset (*permitted,
+ cap_combine (target->cap_permitted,
+ current->cap_permitted))) {
+ return -EPERM;
+ }
+
+ /* verify the _new_Effective_ is a subset of the _new_Permitted_ */
+ if (!cap_issubset (*effective, *permitted)) {
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+void cap_capset_set (struct task_struct *target, kernel_cap_t *effective,
+ kernel_cap_t *inheritable, kernel_cap_t *permitted)
+{
+ target->cap_effective = *effective;
+ target->cap_inheritable = *inheritable;
+ target->cap_permitted = *permitted;
+}
+
+static inline void bprm_clear_caps(struct linux_binprm *bprm)
+{
+ cap_clear(bprm->cap_post_exec_permitted);
+ bprm->cap_effective = false;
+}
+
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+
+int cap_inode_need_killpriv(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+
+ if (!inode->i_op || !inode->i_op->getxattr)
+ return 0;
+
+ error = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, NULL, 0);
+ if (error <= 0)
+ return 0;
+ return 1;
+}
+
+int cap_inode_killpriv(struct dentry *dentry)
+{
+ struct inode *inode = dentry->d_inode;
+
+ if (!inode->i_op || !inode->i_op->removexattr)
+ return 0;
+
+ return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS);
+}
+
+static inline int cap_from_disk(struct vfs_cap_data *caps,
+ struct linux_binprm *bprm, unsigned size)
+{
+ __u32 magic_etc;
+ unsigned tocopy, i;
+ int ret;
+
+ if (size < sizeof(magic_etc))
+ return -EINVAL;
+
+ magic_etc = le32_to_cpu(caps->magic_etc);
+
+ switch ((magic_etc & VFS_CAP_REVISION_MASK)) {
+ case VFS_CAP_REVISION_1:
+ if (size != XATTR_CAPS_SZ_1)
+ return -EINVAL;
+ tocopy = VFS_CAP_U32_1;
+ break;
+ case VFS_CAP_REVISION_2:
+ if (size != XATTR_CAPS_SZ_2)
+ return -EINVAL;
+ tocopy = VFS_CAP_U32_2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE) {
+ bprm->cap_effective = true;
+ } else {
+ bprm->cap_effective = false;
+ }
+
+ ret = 0;
+
+ CAP_FOR_EACH_U32(i) {
+ __u32 value_cpu;
+
+ if (i >= tocopy) {
+ /*
+ * Legacy capability sets have no upper bits
+ */
+ bprm->cap_post_exec_permitted.cap[i] = 0;
+ continue;
+ }
+ /*
+ * pP' = (X & fP) | (pI & fI)
+ */
+ value_cpu = le32_to_cpu(caps->data[i].permitted);
+ bprm->cap_post_exec_permitted.cap[i] =
+ (current->cap_bset.cap[i] & value_cpu) |
+ (current->cap_inheritable.cap[i] &
+ le32_to_cpu(caps->data[i].inheritable));
+ if (value_cpu & ~bprm->cap_post_exec_permitted.cap[i]) {
+ /*
+ * insufficient to execute correctly
+ */
+ ret = -EPERM;
+ }
+ }
+
+ /*
+ * For legacy apps, with no internal support for recognizing they
+ * do not have enough capabilities, we return an error if they are
+ * missing some "forced" (aka file-permitted) capabilities.
+ */
+ return bprm->cap_effective ? ret : 0;
+}
+
+/* Locate any VFS capabilities: */
+static int get_file_caps(struct linux_binprm *bprm)
+{
+ struct dentry *dentry;
+ int rc = 0;
+ struct vfs_cap_data vcaps;
+ struct inode *inode;
+
+ bprm_clear_caps(bprm);
+
+ if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID)
+ return 0;
+
+ dentry = dget(bprm->file->f_dentry);
+ inode = dentry->d_inode;
+ if (!inode->i_op || !inode->i_op->getxattr)
+ goto out;
+
+ rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &vcaps,
+ XATTR_CAPS_SZ);
+ if (rc == -ENODATA || rc == -EOPNOTSUPP) {
+ /* no data, that's ok */
+ rc = 0;
+ goto out;
+ }
+ if (rc < 0)
+ goto out;
+
+ rc = cap_from_disk(&vcaps, bprm, rc);
+ if (rc == -EINVAL)
+ printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
+ __func__, rc, bprm->filename);
+
+out:
+ dput(dentry);
+ if (rc)
+ bprm_clear_caps(bprm);
+
+ return rc;
+}
+
+#else
+int cap_inode_need_killpriv(struct dentry *dentry)
+{
+ return 0;
+}
+
+int cap_inode_killpriv(struct dentry *dentry)
+{
+ return 0;
+}
+
+static inline int get_file_caps(struct linux_binprm *bprm)
+{
+ bprm_clear_caps(bprm);
+ return 0;
+}
+#endif
+
+int cap_bprm_set_security (struct linux_binprm *bprm)
+{
+ int ret;
+
+ ret = get_file_caps(bprm);
+
+ if (!issecure(SECURE_NOROOT)) {
+ /*
+ * To support inheritance of root-permissions and suid-root
+ * executables under compatibility mode, we override the
+ * capability sets for the file.
+ *
+ * If only the real uid is 0, we do not set the effective
+ * bit.
+ */
+ if (bprm->e_uid == 0 || current->uid == 0) {
+ /* pP' = (cap_bset & ~0) | (pI & ~0) */
+ bprm->cap_post_exec_permitted = cap_combine(
+ current->cap_bset, current->cap_inheritable
+ );
+ bprm->cap_effective = (bprm->e_uid == 0);
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
+{
+ if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
+ !cap_issubset(bprm->cap_post_exec_permitted,
+ current->cap_permitted)) {
+ set_dumpable(current->mm, suid_dumpable);
+ current->pdeath_signal = 0;
+
+ if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
+ if (!capable(CAP_SETUID)) {
+ bprm->e_uid = current->uid;
+ bprm->e_gid = current->gid;
+ }
+ if (cap_limit_ptraced_target()) {
+ bprm->cap_post_exec_permitted = cap_intersect(
+ bprm->cap_post_exec_permitted,
+ current->cap_permitted);
+ }
+ }
+ }
+
+ current->suid = current->euid = current->fsuid = bprm->e_uid;
+ current->sgid = current->egid = current->fsgid = bprm->e_gid;
+
+ /* For init, we want to retain the capabilities set
+ * in the init_task struct. Thus we skip the usual
+ * capability rules */
+ if (!is_global_init(current)) {
+ current->cap_permitted = bprm->cap_post_exec_permitted;
+ if (bprm->cap_effective)
+ current->cap_effective = bprm->cap_post_exec_permitted;
+ else
+ cap_clear(current->cap_effective);
+ }
+
+ /* AUD: Audit candidate if current->cap_effective is set */
+
+ current->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+}
+
+int cap_bprm_secureexec (struct linux_binprm *bprm)
+{
+ if (current->uid != 0) {
+ if (bprm->cap_effective)
+ return 1;
+ if (!cap_isclear(bprm->cap_post_exec_permitted))
+ return 1;
+ }
+
+ return (current->euid != current->uid ||
+ current->egid != current->gid);
+}
+
+int cap_inode_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ if (!strcmp(name, XATTR_NAME_CAPS)) {
+ if (!capable(CAP_SETFCAP))
+ return -EPERM;
+ return 0;
+ } else if (!strncmp(name, XATTR_SECURITY_PREFIX,
+ sizeof(XATTR_SECURITY_PREFIX) - 1) &&
+ !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return 0;
+}
+
+int cap_inode_removexattr(struct dentry *dentry, const char *name)
+{
+ if (!strcmp(name, XATTR_NAME_CAPS)) {
+ if (!capable(CAP_SETFCAP))
+ return -EPERM;
+ return 0;
+ } else if (!strncmp(name, XATTR_SECURITY_PREFIX,
+ sizeof(XATTR_SECURITY_PREFIX) - 1) &&
+ !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return 0;
+}
+
+/* moved from kernel/sys.c. */
+/*
+ * cap_emulate_setxuid() fixes the effective / permitted capabilities of
+ * a process after a call to setuid, setreuid, or setresuid.
+ *
+ * 1) When set*uiding _from_ one of {r,e,s}uid == 0 _to_ all of
+ * {r,e,s}uid != 0, the permitted and effective capabilities are
+ * cleared.
+ *
+ * 2) When set*uiding _from_ euid == 0 _to_ euid != 0, the effective
+ * capabilities of the process are cleared.
+ *
+ * 3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective
+ * capabilities are set to the permitted capabilities.
+ *
+ * fsuid is handled elsewhere. fsuid == 0 and {r,e,s}uid!= 0 should
+ * never happen.
+ *
+ * -astor
+ *
+ * cevans - New behaviour, Oct '99
+ * A process may, via prctl(), elect to keep its capabilities when it
+ * calls setuid() and switches away from uid==0. Both permitted and
+ * effective sets will be retained.
+ * Without this change, it was impossible for a daemon to drop only some
+ * of its privilege. The call to setuid(!=0) would drop all privileges!
+ * Keeping uid 0 is not an option because uid 0 owns too many vital
+ * files..
+ * Thanks to Olaf Kirch and Peter Benie for spotting this.
+ */
+static inline void cap_emulate_setxuid (int old_ruid, int old_euid,
+ int old_suid)
+{
+ if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) &&
+ (current->uid != 0 && current->euid != 0 && current->suid != 0) &&
+ !issecure(SECURE_KEEP_CAPS)) {
+ cap_clear (current->cap_permitted);
+ cap_clear (current->cap_effective);
+ }
+ if (old_euid == 0 && current->euid != 0) {
+ cap_clear (current->cap_effective);
+ }
+ if (old_euid != 0 && current->euid == 0) {
+ current->cap_effective = current->cap_permitted;
+ }
+}
+
+int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid,
+ int flags)
+{
+ switch (flags) {
+ case LSM_SETID_RE:
+ case LSM_SETID_ID:
+ case LSM_SETID_RES:
+ /* Copied from kernel/sys.c:setreuid/setuid/setresuid. */
+ if (!issecure (SECURE_NO_SETUID_FIXUP)) {
+ cap_emulate_setxuid (old_ruid, old_euid, old_suid);
+ }
+ break;
+ case LSM_SETID_FS:
+ {
+ uid_t old_fsuid = old_ruid;
+
+ /* Copied from kernel/sys.c:setfsuid. */
+
+ /*
+ * FIXME - is fsuser used for all CAP_FS_MASK capabilities?
+ * if not, we might be a bit too harsh here.
+ */
+
+ if (!issecure (SECURE_NO_SETUID_FIXUP)) {
+ if (old_fsuid == 0 && current->fsuid != 0) {
+ current->cap_effective =
+ cap_drop_fs_set(
+ current->cap_effective);
+ }
+ if (old_fsuid != 0 && current->fsuid == 0) {
+ current->cap_effective =
+ cap_raise_fs_set(
+ current->cap_effective,
+ current->cap_permitted);
+ }
+ }
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+/*
+ * Rationale: code calling task_setscheduler, task_setioprio, and
+ * task_setnice, assumes that
+ * . if capable(cap_sys_nice), then those actions should be allowed
+ * . if not capable(cap_sys_nice), but acting on your own processes,
+ * then those actions should be allowed
+ * This is insufficient now since you can call code without suid, but
+ * yet with increased caps.
+ * So we check for increased caps on the target process.
+ */
+static int cap_safe_nice(struct task_struct *p)
+{
+ if (!cap_issubset(p->cap_permitted, current->cap_permitted) &&
+ !capable(CAP_SYS_NICE))
+ return -EPERM;
+ return 0;
+}
+
+int cap_task_setscheduler (struct task_struct *p, int policy,
+ struct sched_param *lp)
+{
+ return cap_safe_nice(p);
+}
+
+int cap_task_setioprio (struct task_struct *p, int ioprio)
+{
+ return cap_safe_nice(p);
+}
+
+int cap_task_setnice (struct task_struct *p, int nice)
+{
+ return cap_safe_nice(p);
+}
+
+/*
+ * called from kernel/sys.c for prctl(PR_CABSET_DROP)
+ * done without task_capability_lock() because it introduces
+ * no new races - i.e. only another task doing capget() on
+ * this task could get inconsistent info. There can be no
+ * racing writer bc a task can only change its own caps.
+ */
+static long cap_prctl_drop(unsigned long cap)
+{
+ if (!capable(CAP_SETPCAP))
+ return -EPERM;
+ if (!cap_valid(cap))
+ return -EINVAL;
+ cap_lower(current->cap_bset, cap);
+ return 0;
+}
+
+#else
+int cap_task_setscheduler (struct task_struct *p, int policy,
+ struct sched_param *lp)
+{
+ return 0;
+}
+int cap_task_setioprio (struct task_struct *p, int ioprio)
+{
+ return 0;
+}
+int cap_task_setnice (struct task_struct *p, int nice)
+{
+ return 0;
+}
+#endif
+
+int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
+ unsigned long arg4, unsigned long arg5, long *rc_p)
+{
+ long error = 0;
+
+ switch (option) {
+ case PR_CAPBSET_READ:
+ if (!cap_valid(arg2))
+ error = -EINVAL;
+ else
+ error = !!cap_raised(current->cap_bset, arg2);
+ break;
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+ case PR_CAPBSET_DROP:
+ error = cap_prctl_drop(arg2);
+ break;
+
+ /*
+ * The next four prctl's remain to assist with transitioning a
+ * system from legacy UID=0 based privilege (when filesystem
+ * capabilities are not in use) to a system using filesystem
+ * capabilities only - as the POSIX.1e draft intended.
+ *
+ * Note:
+ *
+ * PR_SET_SECUREBITS =
+ * issecure_mask(SECURE_KEEP_CAPS_LOCKED)
+ * | issecure_mask(SECURE_NOROOT)
+ * | issecure_mask(SECURE_NOROOT_LOCKED)
+ * | issecure_mask(SECURE_NO_SETUID_FIXUP)
+ * | issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED)
+ *
+ * will ensure that the current process and all of its
+ * children will be locked into a pure
+ * capability-based-privilege environment.
+ */
+ case PR_SET_SECUREBITS:
+ if ((((current->securebits & SECURE_ALL_LOCKS) >> 1)
+ & (current->securebits ^ arg2)) /*[1]*/
+ || ((current->securebits & SECURE_ALL_LOCKS
+ & ~arg2)) /*[2]*/
+ || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
+ || (cap_capable(current, CAP_SETPCAP) != 0)) { /*[4]*/
+ /*
+ * [1] no changing of bits that are locked
+ * [2] no unlocking of locks
+ * [3] no setting of unsupported bits
+ * [4] doing anything requires privilege (go read about
+ * the "sendmail capabilities bug")
+ */
+ error = -EPERM; /* cannot change a locked bit */
+ } else {
+ current->securebits = arg2;
+ }
+ break;
+ case PR_GET_SECUREBITS:
+ error = current->securebits;
+ break;
+
+#endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */
+
+ case PR_GET_KEEPCAPS:
+ if (issecure(SECURE_KEEP_CAPS))
+ error = 1;
+ break;
+ case PR_SET_KEEPCAPS:
+ if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */
+ error = -EINVAL;
+ else if (issecure(SECURE_KEEP_CAPS_LOCKED))
+ error = -EPERM;
+ else if (arg2)
+ current->securebits |= issecure_mask(SECURE_KEEP_CAPS);
+ else
+ current->securebits &=
+ ~issecure_mask(SECURE_KEEP_CAPS);
+ break;
+
+ default:
+ /* No functionality available - continue with default */
+ return 0;
+ }
+
+ /* Functionality provided */
+ *rc_p = error;
+ return 1;
+}
+
+void cap_task_reparent_to_init (struct task_struct *p)
+{
+ cap_set_init_eff(p->cap_effective);
+ cap_clear(p->cap_inheritable);
+ cap_set_full(p->cap_permitted);
+ p->securebits = SECUREBITS_DEFAULT;
+ return;
+}
+
+int cap_syslog (int type)
+{
+ if ((type != 3 && type != 10) && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ return 0;
+}
+
+int cap_vm_enough_memory(struct mm_struct *mm, long pages)
+{
+ int cap_sys_admin = 0;
+
+ if (cap_capable(current, CAP_SYS_ADMIN) == 0)
+ cap_sys_admin = 1;
+ return __vm_enough_memory(mm, pages, cap_sys_admin);
+}
+
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
new file mode 100644
index 0000000..df9d491
--- /dev/null
+++ b/security/device_cgroup.c
@@ -0,0 +1,544 @@
+/*
+ * device_cgroup.c - device cgroup subsystem
+ *
+ * Copyright 2007 IBM Corp
+ */
+
+#include <linux/device_cgroup.h>
+#include <linux/cgroup.h>
+#include <linux/ctype.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/seq_file.h>
+#include <linux/rcupdate.h>
+
+#define ACC_MKNOD 1
+#define ACC_READ 2
+#define ACC_WRITE 4
+#define ACC_MASK (ACC_MKNOD | ACC_READ | ACC_WRITE)
+
+#define DEV_BLOCK 1
+#define DEV_CHAR 2
+#define DEV_ALL 4 /* this represents all devices */
+
+/*
+ * whitelist locking rules:
+ * hold cgroup_lock() for update/read.
+ * hold rcu_read_lock() for read.
+ */
+
+struct dev_whitelist_item {
+ u32 major, minor;
+ short type;
+ short access;
+ struct list_head list;
+ struct rcu_head rcu;
+};
+
+struct dev_cgroup {
+ struct cgroup_subsys_state css;
+ struct list_head whitelist;
+};
+
+static inline struct dev_cgroup *css_to_devcgroup(struct cgroup_subsys_state *s)
+{
+ return container_of(s, struct dev_cgroup, css);
+}
+
+static inline struct dev_cgroup *cgroup_to_devcgroup(struct cgroup *cgroup)
+{
+ return css_to_devcgroup(cgroup_subsys_state(cgroup, devices_subsys_id));
+}
+
+static inline struct dev_cgroup *task_devcgroup(struct task_struct *task)
+{
+ return css_to_devcgroup(task_subsys_state(task, devices_subsys_id));
+}
+
+struct cgroup_subsys devices_subsys;
+
+static int devcgroup_can_attach(struct cgroup_subsys *ss,
+ struct cgroup *new_cgroup, struct task_struct *task)
+{
+ if (current != task && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ return 0;
+}
+
+/*
+ * called under cgroup_lock()
+ */
+static int dev_whitelist_copy(struct list_head *dest, struct list_head *orig)
+{
+ struct dev_whitelist_item *wh, *tmp, *new;
+
+ list_for_each_entry(wh, orig, list) {
+ new = kmemdup(wh, sizeof(*wh), GFP_KERNEL);
+ if (!new)
+ goto free_and_exit;
+ list_add_tail(&new->list, dest);
+ }
+
+ return 0;
+
+free_and_exit:
+ list_for_each_entry_safe(wh, tmp, dest, list) {
+ list_del(&wh->list);
+ kfree(wh);
+ }
+ return -ENOMEM;
+}
+
+/* Stupid prototype - don't bother combining existing entries */
+/*
+ * called under cgroup_lock()
+ */
+static int dev_whitelist_add(struct dev_cgroup *dev_cgroup,
+ struct dev_whitelist_item *wh)
+{
+ struct dev_whitelist_item *whcopy, *walk;
+
+ whcopy = kmemdup(wh, sizeof(*wh), GFP_KERNEL);
+ if (!whcopy)
+ return -ENOMEM;
+
+ list_for_each_entry(walk, &dev_cgroup->whitelist, list) {
+ if (walk->type != wh->type)
+ continue;
+ if (walk->major != wh->major)
+ continue;
+ if (walk->minor != wh->minor)
+ continue;
+
+ walk->access |= wh->access;
+ kfree(whcopy);
+ whcopy = NULL;
+ }
+
+ if (whcopy != NULL)
+ list_add_tail_rcu(&whcopy->list, &dev_cgroup->whitelist);
+ return 0;
+}
+
+static void whitelist_item_free(struct rcu_head *rcu)
+{
+ struct dev_whitelist_item *item;
+
+ item = container_of(rcu, struct dev_whitelist_item, rcu);
+ kfree(item);
+}
+
+/*
+ * called under cgroup_lock()
+ */
+static void dev_whitelist_rm(struct dev_cgroup *dev_cgroup,
+ struct dev_whitelist_item *wh)
+{
+ struct dev_whitelist_item *walk, *tmp;
+
+ list_for_each_entry_safe(walk, tmp, &dev_cgroup->whitelist, list) {
+ if (walk->type == DEV_ALL)
+ goto remove;
+ if (walk->type != wh->type)
+ continue;
+ if (walk->major != ~0 && walk->major != wh->major)
+ continue;
+ if (walk->minor != ~0 && walk->minor != wh->minor)
+ continue;
+
+remove:
+ walk->access &= ~wh->access;
+ if (!walk->access) {
+ list_del_rcu(&walk->list);
+ call_rcu(&walk->rcu, whitelist_item_free);
+ }
+ }
+}
+
+/*
+ * called from kernel/cgroup.c with cgroup_lock() held.
+ */
+static struct cgroup_subsys_state *devcgroup_create(struct cgroup_subsys *ss,
+ struct cgroup *cgroup)
+{
+ struct dev_cgroup *dev_cgroup, *parent_dev_cgroup;
+ struct cgroup *parent_cgroup;
+ int ret;
+
+ dev_cgroup = kzalloc(sizeof(*dev_cgroup), GFP_KERNEL);
+ if (!dev_cgroup)
+ return ERR_PTR(-ENOMEM);
+ INIT_LIST_HEAD(&dev_cgroup->whitelist);
+ parent_cgroup = cgroup->parent;
+
+ if (parent_cgroup == NULL) {
+ struct dev_whitelist_item *wh;
+ wh = kmalloc(sizeof(*wh), GFP_KERNEL);
+ if (!wh) {
+ kfree(dev_cgroup);
+ return ERR_PTR(-ENOMEM);
+ }
+ wh->minor = wh->major = ~0;
+ wh->type = DEV_ALL;
+ wh->access = ACC_MASK;
+ list_add(&wh->list, &dev_cgroup->whitelist);
+ } else {
+ parent_dev_cgroup = cgroup_to_devcgroup(parent_cgroup);
+ ret = dev_whitelist_copy(&dev_cgroup->whitelist,
+ &parent_dev_cgroup->whitelist);
+ if (ret) {
+ kfree(dev_cgroup);
+ return ERR_PTR(ret);
+ }
+ }
+
+ return &dev_cgroup->css;
+}
+
+static void devcgroup_destroy(struct cgroup_subsys *ss,
+ struct cgroup *cgroup)
+{
+ struct dev_cgroup *dev_cgroup;
+ struct dev_whitelist_item *wh, *tmp;
+
+ dev_cgroup = cgroup_to_devcgroup(cgroup);
+ list_for_each_entry_safe(wh, tmp, &dev_cgroup->whitelist, list) {
+ list_del(&wh->list);
+ kfree(wh);
+ }
+ kfree(dev_cgroup);
+}
+
+#define DEVCG_ALLOW 1
+#define DEVCG_DENY 2
+#define DEVCG_LIST 3
+
+#define MAJMINLEN 13
+#define ACCLEN 4
+
+static void set_access(char *acc, short access)
+{
+ int idx = 0;
+ memset(acc, 0, ACCLEN);
+ if (access & ACC_READ)
+ acc[idx++] = 'r';
+ if (access & ACC_WRITE)
+ acc[idx++] = 'w';
+ if (access & ACC_MKNOD)
+ acc[idx++] = 'm';
+}
+
+static char type_to_char(short type)
+{
+ if (type == DEV_ALL)
+ return 'a';
+ if (type == DEV_CHAR)
+ return 'c';
+ if (type == DEV_BLOCK)
+ return 'b';
+ return 'X';
+}
+
+static void set_majmin(char *str, unsigned m)
+{
+ if (m == ~0)
+ strcpy(str, "*");
+ else
+ sprintf(str, "%u", m);
+}
+
+static int devcgroup_seq_read(struct cgroup *cgroup, struct cftype *cft,
+ struct seq_file *m)
+{
+ struct dev_cgroup *devcgroup = cgroup_to_devcgroup(cgroup);
+ struct dev_whitelist_item *wh;
+ char maj[MAJMINLEN], min[MAJMINLEN], acc[ACCLEN];
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(wh, &devcgroup->whitelist, list) {
+ set_access(acc, wh->access);
+ set_majmin(maj, wh->major);
+ set_majmin(min, wh->minor);
+ seq_printf(m, "%c %s:%s %s\n", type_to_char(wh->type),
+ maj, min, acc);
+ }
+ rcu_read_unlock();
+
+ return 0;
+}
+
+/*
+ * may_access_whitelist:
+ * does the access granted to dev_cgroup c contain the access
+ * requested in whitelist item refwh.
+ * return 1 if yes, 0 if no.
+ * call with c->lock held
+ */
+static int may_access_whitelist(struct dev_cgroup *c,
+ struct dev_whitelist_item *refwh)
+{
+ struct dev_whitelist_item *whitem;
+
+ list_for_each_entry(whitem, &c->whitelist, list) {
+ if (whitem->type & DEV_ALL)
+ return 1;
+ if ((refwh->type & DEV_BLOCK) && !(whitem->type & DEV_BLOCK))
+ continue;
+ if ((refwh->type & DEV_CHAR) && !(whitem->type & DEV_CHAR))
+ continue;
+ if (whitem->major != ~0 && whitem->major != refwh->major)
+ continue;
+ if (whitem->minor != ~0 && whitem->minor != refwh->minor)
+ continue;
+ if (refwh->access & (~whitem->access))
+ continue;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * parent_has_perm:
+ * when adding a new allow rule to a device whitelist, the rule
+ * must be allowed in the parent device
+ */
+static int parent_has_perm(struct dev_cgroup *childcg,
+ struct dev_whitelist_item *wh)
+{
+ struct cgroup *pcg = childcg->css.cgroup->parent;
+ struct dev_cgroup *parent;
+
+ if (!pcg)
+ return 1;
+ parent = cgroup_to_devcgroup(pcg);
+ return may_access_whitelist(parent, wh);
+}
+
+/*
+ * Modify the whitelist using allow/deny rules.
+ * CAP_SYS_ADMIN is needed for this. It's at least separate from CAP_MKNOD
+ * so we can give a container CAP_MKNOD to let it create devices but not
+ * modify the whitelist.
+ * It seems likely we'll want to add a CAP_CONTAINER capability to allow
+ * us to also grant CAP_SYS_ADMIN to containers without giving away the
+ * device whitelist controls, but for now we'll stick with CAP_SYS_ADMIN
+ *
+ * Taking rules away is always allowed (given CAP_SYS_ADMIN). Granting
+ * new access is only allowed if you're in the top-level cgroup, or your
+ * parent cgroup has the access you're asking for.
+ */
+static int devcgroup_update_access(struct dev_cgroup *devcgroup,
+ int filetype, const char *buffer)
+{
+ const char *b;
+ char *endp;
+ int count;
+ struct dev_whitelist_item wh;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ memset(&wh, 0, sizeof(wh));
+ b = buffer;
+
+ switch (*b) {
+ case 'a':
+ wh.type = DEV_ALL;
+ wh.access = ACC_MASK;
+ wh.major = ~0;
+ wh.minor = ~0;
+ goto handle;
+ case 'b':
+ wh.type = DEV_BLOCK;
+ break;
+ case 'c':
+ wh.type = DEV_CHAR;
+ break;
+ default:
+ return -EINVAL;
+ }
+ b++;
+ if (!isspace(*b))
+ return -EINVAL;
+ b++;
+ if (*b == '*') {
+ wh.major = ~0;
+ b++;
+ } else if (isdigit(*b)) {
+ wh.major = simple_strtoul(b, &endp, 10);
+ b = endp;
+ } else {
+ return -EINVAL;
+ }
+ if (*b != ':')
+ return -EINVAL;
+ b++;
+
+ /* read minor */
+ if (*b == '*') {
+ wh.minor = ~0;
+ b++;
+ } else if (isdigit(*b)) {
+ wh.minor = simple_strtoul(b, &endp, 10);
+ b = endp;
+ } else {
+ return -EINVAL;
+ }
+ if (!isspace(*b))
+ return -EINVAL;
+ for (b++, count = 0; count < 3; count++, b++) {
+ switch (*b) {
+ case 'r':
+ wh.access |= ACC_READ;
+ break;
+ case 'w':
+ wh.access |= ACC_WRITE;
+ break;
+ case 'm':
+ wh.access |= ACC_MKNOD;
+ break;
+ case '\n':
+ case '\0':
+ count = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+handle:
+ switch (filetype) {
+ case DEVCG_ALLOW:
+ if (!parent_has_perm(devcgroup, &wh))
+ return -EPERM;
+ return dev_whitelist_add(devcgroup, &wh);
+ case DEVCG_DENY:
+ dev_whitelist_rm(devcgroup, &wh);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int devcgroup_access_write(struct cgroup *cgrp, struct cftype *cft,
+ const char *buffer)
+{
+ int retval;
+ if (!cgroup_lock_live_group(cgrp))
+ return -ENODEV;
+ retval = devcgroup_update_access(cgroup_to_devcgroup(cgrp),
+ cft->private, buffer);
+ cgroup_unlock();
+ return retval;
+}
+
+static struct cftype dev_cgroup_files[] = {
+ {
+ .name = "allow",
+ .write_string = devcgroup_access_write,
+ .private = DEVCG_ALLOW,
+ },
+ {
+ .name = "deny",
+ .write_string = devcgroup_access_write,
+ .private = DEVCG_DENY,
+ },
+ {
+ .name = "list",
+ .read_seq_string = devcgroup_seq_read,
+ .private = DEVCG_LIST,
+ },
+};
+
+static int devcgroup_populate(struct cgroup_subsys *ss,
+ struct cgroup *cgroup)
+{
+ return cgroup_add_files(cgroup, ss, dev_cgroup_files,
+ ARRAY_SIZE(dev_cgroup_files));
+}
+
+struct cgroup_subsys devices_subsys = {
+ .name = "devices",
+ .can_attach = devcgroup_can_attach,
+ .create = devcgroup_create,
+ .destroy = devcgroup_destroy,
+ .populate = devcgroup_populate,
+ .subsys_id = devices_subsys_id,
+};
+
+int devcgroup_inode_permission(struct inode *inode, int mask)
+{
+ struct dev_cgroup *dev_cgroup;
+ struct dev_whitelist_item *wh;
+
+ dev_t device = inode->i_rdev;
+ if (!device)
+ return 0;
+ if (!S_ISBLK(inode->i_mode) && !S_ISCHR(inode->i_mode))
+ return 0;
+
+ rcu_read_lock();
+
+ dev_cgroup = task_devcgroup(current);
+
+ list_for_each_entry_rcu(wh, &dev_cgroup->whitelist, list) {
+ if (wh->type & DEV_ALL)
+ goto acc_check;
+ if ((wh->type & DEV_BLOCK) && !S_ISBLK(inode->i_mode))
+ continue;
+ if ((wh->type & DEV_CHAR) && !S_ISCHR(inode->i_mode))
+ continue;
+ if (wh->major != ~0 && wh->major != imajor(inode))
+ continue;
+ if (wh->minor != ~0 && wh->minor != iminor(inode))
+ continue;
+acc_check:
+ if ((mask & MAY_WRITE) && !(wh->access & ACC_WRITE))
+ continue;
+ if ((mask & MAY_READ) && !(wh->access & ACC_READ))
+ continue;
+ rcu_read_unlock();
+ return 0;
+ }
+
+ rcu_read_unlock();
+
+ return -EPERM;
+}
+
+int devcgroup_inode_mknod(int mode, dev_t dev)
+{
+ struct dev_cgroup *dev_cgroup;
+ struct dev_whitelist_item *wh;
+
+ if (!S_ISBLK(mode) && !S_ISCHR(mode))
+ return 0;
+
+ rcu_read_lock();
+
+ dev_cgroup = task_devcgroup(current);
+
+ list_for_each_entry(wh, &dev_cgroup->whitelist, list) {
+ if (wh->type & DEV_ALL)
+ goto acc_check;
+ if ((wh->type & DEV_BLOCK) && !S_ISBLK(mode))
+ continue;
+ if ((wh->type & DEV_CHAR) && !S_ISCHR(mode))
+ continue;
+ if (wh->major != ~0 && wh->major != MAJOR(dev))
+ continue;
+ if (wh->minor != ~0 && wh->minor != MINOR(dev))
+ continue;
+acc_check:
+ if (!(wh->access & ACC_MKNOD))
+ continue;
+ rcu_read_unlock();
+ return 0;
+ }
+
+ rcu_read_unlock();
+
+ return -EPERM;
+}
diff --git a/security/inode.c b/security/inode.c
new file mode 100644
index 0000000..efea5a6
--- /dev/null
+++ b/security/inode.c
@@ -0,0 +1,334 @@
+/*
+ * inode.c - securityfs
+ *
+ * Copyright (C) 2005 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * Based on fs/debugfs/inode.c which had the following copyright notice:
+ * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2004 IBM Inc.
+ */
+
+/* #define DEBUG */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/namei.h>
+#include <linux/security.h>
+#include <linux/magic.h>
+
+static struct vfsmount *mount;
+static int mount_count;
+
+/*
+ * TODO:
+ * I think I can get rid of these default_file_ops, but not quite sure...
+ */
+static ssize_t default_read_file(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return 0;
+}
+
+static ssize_t default_write_file(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return count;
+}
+
+static int default_open(struct inode *inode, struct file *file)
+{
+ if (inode->i_private)
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+static const struct file_operations default_file_ops = {
+ .read = default_read_file,
+ .write = default_write_file,
+ .open = default_open,
+};
+
+static struct inode *get_inode(struct super_block *sb, int mode, dev_t dev)
+{
+ struct inode *inode = new_inode(sb);
+
+ if (inode) {
+ inode->i_mode = mode;
+ inode->i_uid = 0;
+ inode->i_gid = 0;
+ inode->i_blocks = 0;
+ inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+ switch (mode & S_IFMT) {
+ default:
+ init_special_inode(inode, mode, dev);
+ break;
+ case S_IFREG:
+ inode->i_fop = &default_file_ops;
+ break;
+ case S_IFDIR:
+ inode->i_op = &simple_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inc_nlink(inode);
+ break;
+ }
+ }
+ return inode;
+}
+
+/* SMP-safe */
+static int mknod(struct inode *dir, struct dentry *dentry,
+ int mode, dev_t dev)
+{
+ struct inode *inode;
+ int error = -EPERM;
+
+ if (dentry->d_inode)
+ return -EEXIST;
+
+ inode = get_inode(dir->i_sb, mode, dev);
+ if (inode) {
+ d_instantiate(dentry, inode);
+ dget(dentry);
+ error = 0;
+ }
+ return error;
+}
+
+static int mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ int res;
+
+ mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
+ res = mknod(dir, dentry, mode, 0);
+ if (!res)
+ inc_nlink(dir);
+ return res;
+}
+
+static int create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ mode = (mode & S_IALLUGO) | S_IFREG;
+ return mknod(dir, dentry, mode, 0);
+}
+
+static inline int positive(struct dentry *dentry)
+{
+ return dentry->d_inode && !d_unhashed(dentry);
+}
+
+static int fill_super(struct super_block *sb, void *data, int silent)
+{
+ static struct tree_descr files[] = {{""}};
+
+ return simple_fill_super(sb, SECURITYFS_MAGIC, files);
+}
+
+static int get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *data, struct vfsmount *mnt)
+{
+ return get_sb_single(fs_type, flags, data, fill_super, mnt);
+}
+
+static struct file_system_type fs_type = {
+ .owner = THIS_MODULE,
+ .name = "securityfs",
+ .get_sb = get_sb,
+ .kill_sb = kill_litter_super,
+};
+
+static int create_by_name(const char *name, mode_t mode,
+ struct dentry *parent,
+ struct dentry **dentry)
+{
+ int error = 0;
+
+ *dentry = NULL;
+
+ /* If the parent is not specified, we create it in the root.
+ * We need the root dentry to do this, which is in the super
+ * block. A pointer to that is in the struct vfsmount that we
+ * have around.
+ */
+ if (!parent ) {
+ if (mount && mount->mnt_sb) {
+ parent = mount->mnt_sb->s_root;
+ }
+ }
+ if (!parent) {
+ pr_debug("securityfs: Ah! can not find a parent!\n");
+ return -EFAULT;
+ }
+
+ mutex_lock(&parent->d_inode->i_mutex);
+ *dentry = lookup_one_len(name, parent, strlen(name));
+ if (!IS_ERR(dentry)) {
+ if ((mode & S_IFMT) == S_IFDIR)
+ error = mkdir(parent->d_inode, *dentry, mode);
+ else
+ error = create(parent->d_inode, *dentry, mode);
+ } else
+ error = PTR_ERR(dentry);
+ mutex_unlock(&parent->d_inode->i_mutex);
+
+ return error;
+}
+
+/**
+ * securityfs_create_file - create a file in the securityfs filesystem
+ *
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file. This should be a
+ * directory dentry if set. If this parameter is %NULL, then the
+ * file will be created in the root of the securityfs filesystem.
+ * @data: a pointer to something that the caller will want to get to later
+ * on. The inode.i_private pointer will point to this value on
+ * the open() call.
+ * @fops: a pointer to a struct file_operations that should be used for
+ * this file.
+ *
+ * This is the basic "create a file" function for securityfs. It allows for a
+ * wide range of flexibility in creating a file, or a directory (if you
+ * want to create a directory, the securityfs_create_dir() function is
+ * recommended to be used instead).
+ *
+ * This function returns a pointer to a dentry if it succeeds. This
+ * pointer must be passed to the securityfs_remove() function when the file is
+ * to be removed (no automatic cleanup happens if your module is unloaded,
+ * you are responsible here). If an error occurs, %NULL is returned.
+ *
+ * If securityfs is not enabled in the kernel, the value %-ENODEV is
+ * returned. It is not wise to check for this value, but rather, check for
+ * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
+ * code.
+ */
+struct dentry *securityfs_create_file(const char *name, mode_t mode,
+ struct dentry *parent, void *data,
+ const struct file_operations *fops)
+{
+ struct dentry *dentry = NULL;
+ int error;
+
+ pr_debug("securityfs: creating file '%s'\n",name);
+
+ error = simple_pin_fs(&fs_type, &mount, &mount_count);
+ if (error) {
+ dentry = ERR_PTR(error);
+ goto exit;
+ }
+
+ error = create_by_name(name, mode, parent, &dentry);
+ if (error) {
+ dentry = ERR_PTR(error);
+ simple_release_fs(&mount, &mount_count);
+ goto exit;
+ }
+
+ if (dentry->d_inode) {
+ if (fops)
+ dentry->d_inode->i_fop = fops;
+ if (data)
+ dentry->d_inode->i_private = data;
+ }
+exit:
+ return dentry;
+}
+EXPORT_SYMBOL_GPL(securityfs_create_file);
+
+/**
+ * securityfs_create_dir - create a directory in the securityfs filesystem
+ *
+ * @name: a pointer to a string containing the name of the directory to
+ * create.
+ * @parent: a pointer to the parent dentry for this file. This should be a
+ * directory dentry if set. If this parameter is %NULL, then the
+ * directory will be created in the root of the securityfs filesystem.
+ *
+ * This function creates a directory in securityfs with the given @name.
+ *
+ * This function returns a pointer to a dentry if it succeeds. This
+ * pointer must be passed to the securityfs_remove() function when the file is
+ * to be removed (no automatic cleanup happens if your module is unloaded,
+ * you are responsible here). If an error occurs, %NULL will be returned.
+ *
+ * If securityfs is not enabled in the kernel, the value %-ENODEV is
+ * returned. It is not wise to check for this value, but rather, check for
+ * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
+ * code.
+ */
+struct dentry *securityfs_create_dir(const char *name, struct dentry *parent)
+{
+ return securityfs_create_file(name,
+ S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
+ parent, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(securityfs_create_dir);
+
+/**
+ * securityfs_remove - removes a file or directory from the securityfs filesystem
+ *
+ * @dentry: a pointer to a the dentry of the file or directory to be removed.
+ *
+ * This function removes a file or directory in securityfs that was previously
+ * created with a call to another securityfs function (like
+ * securityfs_create_file() or variants thereof.)
+ *
+ * This function is required to be called in order for the file to be
+ * removed. No automatic cleanup of files will happen when a module is
+ * removed; you are responsible here.
+ */
+void securityfs_remove(struct dentry *dentry)
+{
+ struct dentry *parent;
+
+ if (!dentry)
+ return;
+
+ parent = dentry->d_parent;
+ if (!parent || !parent->d_inode)
+ return;
+
+ mutex_lock(&parent->d_inode->i_mutex);
+ if (positive(dentry)) {
+ if (dentry->d_inode) {
+ if (S_ISDIR(dentry->d_inode->i_mode))
+ simple_rmdir(parent->d_inode, dentry);
+ else
+ simple_unlink(parent->d_inode, dentry);
+ dput(dentry);
+ }
+ }
+ mutex_unlock(&parent->d_inode->i_mutex);
+ simple_release_fs(&mount, &mount_count);
+}
+EXPORT_SYMBOL_GPL(securityfs_remove);
+
+static struct kobject *security_kobj;
+
+static int __init securityfs_init(void)
+{
+ int retval;
+
+ security_kobj = kobject_create_and_add("security", kernel_kobj);
+ if (!security_kobj)
+ return -EINVAL;
+
+ retval = register_filesystem(&fs_type);
+ if (retval)
+ kobject_put(security_kobj);
+ return retval;
+}
+
+core_initcall(securityfs_init);
+MODULE_LICENSE("GPL");
+
diff --git a/security/keys/Makefile b/security/keys/Makefile
new file mode 100644
index 0000000..747a464
--- /dev/null
+++ b/security/keys/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for key management
+#
+
+obj-y := \
+ key.o \
+ keyring.o \
+ keyctl.o \
+ permission.o \
+ process_keys.o \
+ request_key.o \
+ request_key_auth.o \
+ user_defined.o
+
+obj-$(CONFIG_KEYS_COMPAT) += compat.o
+obj-$(CONFIG_PROC_FS) += proc.o
+obj-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/security/keys/compat.c b/security/keys/compat.c
new file mode 100644
index 0000000..c766c68
--- /dev/null
+++ b/security/keys/compat.c
@@ -0,0 +1,89 @@
+/* compat.c: 32-bit compatibility syscall for 64-bit systems
+ *
+ * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/syscalls.h>
+#include <linux/keyctl.h>
+#include <linux/compat.h>
+#include "internal.h"
+
+/*****************************************************************************/
+/*
+ * the key control system call, 32-bit compatibility version for 64-bit archs
+ * - this should only be called if the 64-bit arch uses weird pointers in
+ * 32-bit mode or doesn't guarantee that the top 32-bits of the argument
+ * registers on taking a 32-bit syscall are zero
+ * - if you can, you should call sys_keyctl directly
+ */
+asmlinkage long compat_sys_keyctl(u32 option,
+ u32 arg2, u32 arg3, u32 arg4, u32 arg5)
+{
+ switch (option) {
+ case KEYCTL_GET_KEYRING_ID:
+ return keyctl_get_keyring_ID(arg2, arg3);
+
+ case KEYCTL_JOIN_SESSION_KEYRING:
+ return keyctl_join_session_keyring(compat_ptr(arg2));
+
+ case KEYCTL_UPDATE:
+ return keyctl_update_key(arg2, compat_ptr(arg3), arg4);
+
+ case KEYCTL_REVOKE:
+ return keyctl_revoke_key(arg2);
+
+ case KEYCTL_DESCRIBE:
+ return keyctl_describe_key(arg2, compat_ptr(arg3), arg4);
+
+ case KEYCTL_CLEAR:
+ return keyctl_keyring_clear(arg2);
+
+ case KEYCTL_LINK:
+ return keyctl_keyring_link(arg2, arg3);
+
+ case KEYCTL_UNLINK:
+ return keyctl_keyring_unlink(arg2, arg3);
+
+ case KEYCTL_SEARCH:
+ return keyctl_keyring_search(arg2, compat_ptr(arg3),
+ compat_ptr(arg4), arg5);
+
+ case KEYCTL_READ:
+ return keyctl_read_key(arg2, compat_ptr(arg3), arg4);
+
+ case KEYCTL_CHOWN:
+ return keyctl_chown_key(arg2, arg3, arg4);
+
+ case KEYCTL_SETPERM:
+ return keyctl_setperm_key(arg2, arg3);
+
+ case KEYCTL_INSTANTIATE:
+ return keyctl_instantiate_key(arg2, compat_ptr(arg3), arg4,
+ arg5);
+
+ case KEYCTL_NEGATE:
+ return keyctl_negate_key(arg2, arg3, arg4);
+
+ case KEYCTL_SET_REQKEY_KEYRING:
+ return keyctl_set_reqkey_keyring(arg2);
+
+ case KEYCTL_SET_TIMEOUT:
+ return keyctl_set_timeout(arg2, arg3);
+
+ case KEYCTL_ASSUME_AUTHORITY:
+ return keyctl_assume_authority(arg2);
+
+ case KEYCTL_GET_SECURITY:
+ return keyctl_get_security(arg2, compat_ptr(arg3), arg4);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+} /* end compat_sys_keyctl() */
diff --git a/security/keys/internal.h b/security/keys/internal.h
new file mode 100644
index 0000000..239098f
--- /dev/null
+++ b/security/keys/internal.h
@@ -0,0 +1,183 @@
+/* internal.h: authentication token and access key management internal defs
+ *
+ * Copyright (C) 2003-5, 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _INTERNAL_H
+#define _INTERNAL_H
+
+#include <linux/key-type.h>
+#include <linux/key-ui.h>
+
+static inline __attribute__((format(printf, 1, 2)))
+void no_printk(const char *fmt, ...)
+{
+}
+
+#ifdef __KDEBUG
+#define kenter(FMT, ...) \
+ printk(KERN_DEBUG "==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+ printk(KERN_DEBUG "<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+#define kdebug(FMT, ...) \
+ printk(KERN_DEBUG "xxx" FMT"yyy\n", ##__VA_ARGS__)
+#else
+#define kenter(FMT, ...) \
+ no_printk(KERN_DEBUG "==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+ no_printk(KERN_DEBUG "<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+#define kdebug(FMT, ...) \
+ no_printk(KERN_DEBUG FMT"\n", ##__VA_ARGS__)
+#endif
+
+extern struct key_type key_type_user;
+
+/*****************************************************************************/
+/*
+ * keep track of keys for a user
+ * - this needs to be separate to user_struct to avoid a refcount-loop
+ * (user_struct pins some keyrings which pin this struct)
+ * - this also keeps track of keys under request from userspace for this UID
+ */
+struct key_user {
+ struct rb_node node;
+ struct mutex cons_lock; /* construction initiation lock */
+ spinlock_t lock;
+ atomic_t usage; /* for accessing qnkeys & qnbytes */
+ atomic_t nkeys; /* number of keys */
+ atomic_t nikeys; /* number of instantiated keys */
+ uid_t uid;
+ int qnkeys; /* number of keys allocated to this user */
+ int qnbytes; /* number of bytes allocated to this user */
+};
+
+extern struct rb_root key_user_tree;
+extern spinlock_t key_user_lock;
+extern struct key_user root_key_user;
+
+extern struct key_user *key_user_lookup(uid_t uid);
+extern void key_user_put(struct key_user *user);
+
+/*
+ * key quota limits
+ * - root has its own separate limits to everyone else
+ */
+extern unsigned key_quota_root_maxkeys;
+extern unsigned key_quota_root_maxbytes;
+extern unsigned key_quota_maxkeys;
+extern unsigned key_quota_maxbytes;
+
+#define KEYQUOTA_LINK_BYTES 4 /* a link in a keyring is worth 4 bytes */
+
+
+extern struct rb_root key_serial_tree;
+extern spinlock_t key_serial_lock;
+extern struct mutex key_construction_mutex;
+extern wait_queue_head_t request_key_conswq;
+
+
+extern int __key_link(struct key *keyring, struct key *key);
+
+extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
+ const struct key_type *type,
+ const char *description,
+ key_perm_t perm);
+
+extern struct key *keyring_search_instkey(struct key *keyring,
+ key_serial_t target_id);
+
+typedef int (*key_match_func_t)(const struct key *, const void *);
+
+extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
+ struct task_struct *tsk,
+ struct key_type *type,
+ const void *description,
+ key_match_func_t match);
+
+extern key_ref_t search_process_keyrings(struct key_type *type,
+ const void *description,
+ key_match_func_t match,
+ struct task_struct *tsk);
+
+extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
+
+extern int install_user_keyrings(struct task_struct *tsk);
+extern int install_thread_keyring(struct task_struct *tsk);
+extern int install_process_keyring(struct task_struct *tsk);
+
+extern struct key *request_key_and_link(struct key_type *type,
+ const char *description,
+ const void *callout_info,
+ size_t callout_len,
+ void *aux,
+ struct key *dest_keyring,
+ unsigned long flags);
+
+/*
+ * request_key authorisation
+ */
+struct request_key_auth {
+ struct key *target_key;
+ struct task_struct *context;
+ void *callout_info;
+ size_t callout_len;
+ pid_t pid;
+};
+
+extern struct key_type key_type_request_key_auth;
+extern struct key *request_key_auth_new(struct key *target,
+ const void *callout_info,
+ size_t callout_len);
+
+extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
+
+/*
+ * keyctl functions
+ */
+extern long keyctl_get_keyring_ID(key_serial_t, int);
+extern long keyctl_join_session_keyring(const char __user *);
+extern long keyctl_update_key(key_serial_t, const void __user *, size_t);
+extern long keyctl_revoke_key(key_serial_t);
+extern long keyctl_keyring_clear(key_serial_t);
+extern long keyctl_keyring_link(key_serial_t, key_serial_t);
+extern long keyctl_keyring_unlink(key_serial_t, key_serial_t);
+extern long keyctl_describe_key(key_serial_t, char __user *, size_t);
+extern long keyctl_keyring_search(key_serial_t, const char __user *,
+ const char __user *, key_serial_t);
+extern long keyctl_read_key(key_serial_t, char __user *, size_t);
+extern long keyctl_chown_key(key_serial_t, uid_t, gid_t);
+extern long keyctl_setperm_key(key_serial_t, key_perm_t);
+extern long keyctl_instantiate_key(key_serial_t, const void __user *,
+ size_t, key_serial_t);
+extern long keyctl_negate_key(key_serial_t, unsigned, key_serial_t);
+extern long keyctl_set_reqkey_keyring(int);
+extern long keyctl_set_timeout(key_serial_t, unsigned);
+extern long keyctl_assume_authority(key_serial_t);
+extern long keyctl_get_security(key_serial_t keyid, char __user *buffer,
+ size_t buflen);
+
+/*
+ * debugging key validation
+ */
+#ifdef KEY_DEBUGGING
+extern void __key_check(const struct key *);
+
+static inline void key_check(const struct key *key)
+{
+ if (key && (IS_ERR(key) || key->magic != KEY_DEBUG_MAGIC))
+ __key_check(key);
+}
+
+#else
+
+#define key_check(key) do {} while(0)
+
+#endif
+
+#endif /* _INTERNAL_H */
diff --git a/security/keys/key.c b/security/keys/key.c
new file mode 100644
index 0000000..14948cf
--- /dev/null
+++ b/security/keys/key.c
@@ -0,0 +1,1006 @@
+/* Basic authentication token and access key management
+ *
+ * Copyright (C) 2004-2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/poison.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/security.h>
+#include <linux/workqueue.h>
+#include <linux/random.h>
+#include <linux/err.h>
+#include "internal.h"
+
+static struct kmem_cache *key_jar;
+struct rb_root key_serial_tree; /* tree of keys indexed by serial */
+DEFINE_SPINLOCK(key_serial_lock);
+
+struct rb_root key_user_tree; /* tree of quota records indexed by UID */
+DEFINE_SPINLOCK(key_user_lock);
+
+unsigned int key_quota_root_maxkeys = 200; /* root's key count quota */
+unsigned int key_quota_root_maxbytes = 20000; /* root's key space quota */
+unsigned int key_quota_maxkeys = 200; /* general key count quota */
+unsigned int key_quota_maxbytes = 20000; /* general key space quota */
+
+static LIST_HEAD(key_types_list);
+static DECLARE_RWSEM(key_types_sem);
+
+static void key_cleanup(struct work_struct *work);
+static DECLARE_WORK(key_cleanup_task, key_cleanup);
+
+/* we serialise key instantiation and link */
+DEFINE_MUTEX(key_construction_mutex);
+
+/* any key who's type gets unegistered will be re-typed to this */
+static struct key_type key_type_dead = {
+ .name = "dead",
+};
+
+#ifdef KEY_DEBUGGING
+void __key_check(const struct key *key)
+{
+ printk("__key_check: key %p {%08x} should be {%08x}\n",
+ key, key->magic, KEY_DEBUG_MAGIC);
+ BUG();
+}
+#endif
+
+/*****************************************************************************/
+/*
+ * get the key quota record for a user, allocating a new record if one doesn't
+ * already exist
+ */
+struct key_user *key_user_lookup(uid_t uid)
+{
+ struct key_user *candidate = NULL, *user;
+ struct rb_node *parent = NULL;
+ struct rb_node **p;
+
+ try_again:
+ p = &key_user_tree.rb_node;
+ spin_lock(&key_user_lock);
+
+ /* search the tree for a user record with a matching UID */
+ while (*p) {
+ parent = *p;
+ user = rb_entry(parent, struct key_user, node);
+
+ if (uid < user->uid)
+ p = &(*p)->rb_left;
+ else if (uid > user->uid)
+ p = &(*p)->rb_right;
+ else
+ goto found;
+ }
+
+ /* if we get here, we failed to find a match in the tree */
+ if (!candidate) {
+ /* allocate a candidate user record if we don't already have
+ * one */
+ spin_unlock(&key_user_lock);
+
+ user = NULL;
+ candidate = kmalloc(sizeof(struct key_user), GFP_KERNEL);
+ if (unlikely(!candidate))
+ goto out;
+
+ /* the allocation may have scheduled, so we need to repeat the
+ * search lest someone else added the record whilst we were
+ * asleep */
+ goto try_again;
+ }
+
+ /* if we get here, then the user record still hadn't appeared on the
+ * second pass - so we use the candidate record */
+ atomic_set(&candidate->usage, 1);
+ atomic_set(&candidate->nkeys, 0);
+ atomic_set(&candidate->nikeys, 0);
+ candidate->uid = uid;
+ candidate->qnkeys = 0;
+ candidate->qnbytes = 0;
+ spin_lock_init(&candidate->lock);
+ mutex_init(&candidate->cons_lock);
+
+ rb_link_node(&candidate->node, parent, p);
+ rb_insert_color(&candidate->node, &key_user_tree);
+ spin_unlock(&key_user_lock);
+ user = candidate;
+ goto out;
+
+ /* okay - we found a user record for this UID */
+ found:
+ atomic_inc(&user->usage);
+ spin_unlock(&key_user_lock);
+ kfree(candidate);
+ out:
+ return user;
+
+} /* end key_user_lookup() */
+
+/*****************************************************************************/
+/*
+ * dispose of a user structure
+ */
+void key_user_put(struct key_user *user)
+{
+ if (atomic_dec_and_lock(&user->usage, &key_user_lock)) {
+ rb_erase(&user->node, &key_user_tree);
+ spin_unlock(&key_user_lock);
+
+ kfree(user);
+ }
+
+} /* end key_user_put() */
+
+/*****************************************************************************/
+/*
+ * assign a key the next unique serial number
+ * - these are assigned randomly to avoid security issues through covert
+ * channel problems
+ */
+static inline void key_alloc_serial(struct key *key)
+{
+ struct rb_node *parent, **p;
+ struct key *xkey;
+
+ /* propose a random serial number and look for a hole for it in the
+ * serial number tree */
+ do {
+ get_random_bytes(&key->serial, sizeof(key->serial));
+
+ key->serial >>= 1; /* negative numbers are not permitted */
+ } while (key->serial < 3);
+
+ spin_lock(&key_serial_lock);
+
+attempt_insertion:
+ parent = NULL;
+ p = &key_serial_tree.rb_node;
+
+ while (*p) {
+ parent = *p;
+ xkey = rb_entry(parent, struct key, serial_node);
+
+ if (key->serial < xkey->serial)
+ p = &(*p)->rb_left;
+ else if (key->serial > xkey->serial)
+ p = &(*p)->rb_right;
+ else
+ goto serial_exists;
+ }
+
+ /* we've found a suitable hole - arrange for this key to occupy it */
+ rb_link_node(&key->serial_node, parent, p);
+ rb_insert_color(&key->serial_node, &key_serial_tree);
+
+ spin_unlock(&key_serial_lock);
+ return;
+
+ /* we found a key with the proposed serial number - walk the tree from
+ * that point looking for the next unused serial number */
+serial_exists:
+ for (;;) {
+ key->serial++;
+ if (key->serial < 3) {
+ key->serial = 3;
+ goto attempt_insertion;
+ }
+
+ parent = rb_next(parent);
+ if (!parent)
+ goto attempt_insertion;
+
+ xkey = rb_entry(parent, struct key, serial_node);
+ if (key->serial < xkey->serial)
+ goto attempt_insertion;
+ }
+
+} /* end key_alloc_serial() */
+
+/*****************************************************************************/
+/*
+ * allocate a key of the specified type
+ * - update the user's quota to reflect the existence of the key
+ * - called from a key-type operation with key_types_sem read-locked by
+ * key_create_or_update()
+ * - this prevents unregistration of the key type
+ * - upon return the key is as yet uninstantiated; the caller needs to either
+ * instantiate the key or discard it before returning
+ */
+struct key *key_alloc(struct key_type *type, const char *desc,
+ uid_t uid, gid_t gid, struct task_struct *ctx,
+ key_perm_t perm, unsigned long flags)
+{
+ struct key_user *user = NULL;
+ struct key *key;
+ size_t desclen, quotalen;
+ int ret;
+
+ key = ERR_PTR(-EINVAL);
+ if (!desc || !*desc)
+ goto error;
+
+ desclen = strlen(desc) + 1;
+ quotalen = desclen + type->def_datalen;
+
+ /* get hold of the key tracking for this user */
+ user = key_user_lookup(uid);
+ if (!user)
+ goto no_memory_1;
+
+ /* check that the user's quota permits allocation of another key and
+ * its description */
+ if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
+ unsigned maxkeys = (uid == 0) ?
+ key_quota_root_maxkeys : key_quota_maxkeys;
+ unsigned maxbytes = (uid == 0) ?
+ key_quota_root_maxbytes : key_quota_maxbytes;
+
+ spin_lock(&user->lock);
+ if (!(flags & KEY_ALLOC_QUOTA_OVERRUN)) {
+ if (user->qnkeys + 1 >= maxkeys ||
+ user->qnbytes + quotalen >= maxbytes ||
+ user->qnbytes + quotalen < user->qnbytes)
+ goto no_quota;
+ }
+
+ user->qnkeys++;
+ user->qnbytes += quotalen;
+ spin_unlock(&user->lock);
+ }
+
+ /* allocate and initialise the key and its description */
+ key = kmem_cache_alloc(key_jar, GFP_KERNEL);
+ if (!key)
+ goto no_memory_2;
+
+ if (desc) {
+ key->description = kmemdup(desc, desclen, GFP_KERNEL);
+ if (!key->description)
+ goto no_memory_3;
+ }
+
+ atomic_set(&key->usage, 1);
+ init_rwsem(&key->sem);
+ key->type = type;
+ key->user = user;
+ key->quotalen = quotalen;
+ key->datalen = type->def_datalen;
+ key->uid = uid;
+ key->gid = gid;
+ key->perm = perm;
+ key->flags = 0;
+ key->expiry = 0;
+ key->payload.data = NULL;
+ key->security = NULL;
+
+ if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
+ key->flags |= 1 << KEY_FLAG_IN_QUOTA;
+
+ memset(&key->type_data, 0, sizeof(key->type_data));
+
+#ifdef KEY_DEBUGGING
+ key->magic = KEY_DEBUG_MAGIC;
+#endif
+
+ /* let the security module know about the key */
+ ret = security_key_alloc(key, ctx, flags);
+ if (ret < 0)
+ goto security_error;
+
+ /* publish the key by giving it a serial number */
+ atomic_inc(&user->nkeys);
+ key_alloc_serial(key);
+
+error:
+ return key;
+
+security_error:
+ kfree(key->description);
+ kmem_cache_free(key_jar, key);
+ if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
+ spin_lock(&user->lock);
+ user->qnkeys--;
+ user->qnbytes -= quotalen;
+ spin_unlock(&user->lock);
+ }
+ key_user_put(user);
+ key = ERR_PTR(ret);
+ goto error;
+
+no_memory_3:
+ kmem_cache_free(key_jar, key);
+no_memory_2:
+ if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
+ spin_lock(&user->lock);
+ user->qnkeys--;
+ user->qnbytes -= quotalen;
+ spin_unlock(&user->lock);
+ }
+ key_user_put(user);
+no_memory_1:
+ key = ERR_PTR(-ENOMEM);
+ goto error;
+
+no_quota:
+ spin_unlock(&user->lock);
+ key_user_put(user);
+ key = ERR_PTR(-EDQUOT);
+ goto error;
+
+} /* end key_alloc() */
+
+EXPORT_SYMBOL(key_alloc);
+
+/*****************************************************************************/
+/*
+ * reserve an amount of quota for the key's payload
+ */
+int key_payload_reserve(struct key *key, size_t datalen)
+{
+ int delta = (int) datalen - key->datalen;
+ int ret = 0;
+
+ key_check(key);
+
+ /* contemplate the quota adjustment */
+ if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
+ unsigned maxbytes = (key->user->uid == 0) ?
+ key_quota_root_maxbytes : key_quota_maxbytes;
+
+ spin_lock(&key->user->lock);
+
+ if (delta > 0 &&
+ (key->user->qnbytes + delta >= maxbytes ||
+ key->user->qnbytes + delta < key->user->qnbytes)) {
+ ret = -EDQUOT;
+ }
+ else {
+ key->user->qnbytes += delta;
+ key->quotalen += delta;
+ }
+ spin_unlock(&key->user->lock);
+ }
+
+ /* change the recorded data length if that didn't generate an error */
+ if (ret == 0)
+ key->datalen = datalen;
+
+ return ret;
+
+} /* end key_payload_reserve() */
+
+EXPORT_SYMBOL(key_payload_reserve);
+
+/*****************************************************************************/
+/*
+ * instantiate a key and link it into the target keyring atomically
+ * - called with the target keyring's semaphore writelocked
+ */
+static int __key_instantiate_and_link(struct key *key,
+ const void *data,
+ size_t datalen,
+ struct key *keyring,
+ struct key *instkey)
+{
+ int ret, awaken;
+
+ key_check(key);
+ key_check(keyring);
+
+ awaken = 0;
+ ret = -EBUSY;
+
+ mutex_lock(&key_construction_mutex);
+
+ /* can't instantiate twice */
+ if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
+ /* instantiate the key */
+ ret = key->type->instantiate(key, data, datalen);
+
+ if (ret == 0) {
+ /* mark the key as being instantiated */
+ atomic_inc(&key->user->nikeys);
+ set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
+
+ if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
+ awaken = 1;
+
+ /* and link it into the destination keyring */
+ if (keyring)
+ ret = __key_link(keyring, key);
+
+ /* disable the authorisation key */
+ if (instkey)
+ key_revoke(instkey);
+ }
+ }
+
+ mutex_unlock(&key_construction_mutex);
+
+ /* wake up anyone waiting for a key to be constructed */
+ if (awaken)
+ wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT);
+
+ return ret;
+
+} /* end __key_instantiate_and_link() */
+
+/*****************************************************************************/
+/*
+ * instantiate a key and link it into the target keyring atomically
+ */
+int key_instantiate_and_link(struct key *key,
+ const void *data,
+ size_t datalen,
+ struct key *keyring,
+ struct key *instkey)
+{
+ int ret;
+
+ if (keyring)
+ down_write(&keyring->sem);
+
+ ret = __key_instantiate_and_link(key, data, datalen, keyring, instkey);
+
+ if (keyring)
+ up_write(&keyring->sem);
+
+ return ret;
+
+} /* end key_instantiate_and_link() */
+
+EXPORT_SYMBOL(key_instantiate_and_link);
+
+/*****************************************************************************/
+/*
+ * negatively instantiate a key and link it into the target keyring atomically
+ */
+int key_negate_and_link(struct key *key,
+ unsigned timeout,
+ struct key *keyring,
+ struct key *instkey)
+{
+ struct timespec now;
+ int ret, awaken;
+
+ key_check(key);
+ key_check(keyring);
+
+ awaken = 0;
+ ret = -EBUSY;
+
+ if (keyring)
+ down_write(&keyring->sem);
+
+ mutex_lock(&key_construction_mutex);
+
+ /* can't instantiate twice */
+ if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
+ /* mark the key as being negatively instantiated */
+ atomic_inc(&key->user->nikeys);
+ set_bit(KEY_FLAG_NEGATIVE, &key->flags);
+ set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
+ now = current_kernel_time();
+ key->expiry = now.tv_sec + timeout;
+
+ if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
+ awaken = 1;
+
+ ret = 0;
+
+ /* and link it into the destination keyring */
+ if (keyring)
+ ret = __key_link(keyring, key);
+
+ /* disable the authorisation key */
+ if (instkey)
+ key_revoke(instkey);
+ }
+
+ mutex_unlock(&key_construction_mutex);
+
+ if (keyring)
+ up_write(&keyring->sem);
+
+ /* wake up anyone waiting for a key to be constructed */
+ if (awaken)
+ wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT);
+
+ return ret;
+
+} /* end key_negate_and_link() */
+
+EXPORT_SYMBOL(key_negate_and_link);
+
+/*****************************************************************************/
+/*
+ * do cleaning up in process context so that we don't have to disable
+ * interrupts all over the place
+ */
+static void key_cleanup(struct work_struct *work)
+{
+ struct rb_node *_n;
+ struct key *key;
+
+ go_again:
+ /* look for a dead key in the tree */
+ spin_lock(&key_serial_lock);
+
+ for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
+ key = rb_entry(_n, struct key, serial_node);
+
+ if (atomic_read(&key->usage) == 0)
+ goto found_dead_key;
+ }
+
+ spin_unlock(&key_serial_lock);
+ return;
+
+ found_dead_key:
+ /* we found a dead key - once we've removed it from the tree, we can
+ * drop the lock */
+ rb_erase(&key->serial_node, &key_serial_tree);
+ spin_unlock(&key_serial_lock);
+
+ key_check(key);
+
+ security_key_free(key);
+
+ /* deal with the user's key tracking and quota */
+ if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
+ spin_lock(&key->user->lock);
+ key->user->qnkeys--;
+ key->user->qnbytes -= key->quotalen;
+ spin_unlock(&key->user->lock);
+ }
+
+ atomic_dec(&key->user->nkeys);
+ if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
+ atomic_dec(&key->user->nikeys);
+
+ key_user_put(key->user);
+
+ /* now throw away the key memory */
+ if (key->type->destroy)
+ key->type->destroy(key);
+
+ kfree(key->description);
+
+#ifdef KEY_DEBUGGING
+ key->magic = KEY_DEBUG_MAGIC_X;
+#endif
+ kmem_cache_free(key_jar, key);
+
+ /* there may, of course, be more than one key to destroy */
+ goto go_again;
+
+} /* end key_cleanup() */
+
+/*****************************************************************************/
+/*
+ * dispose of a reference to a key
+ * - when all the references are gone, we schedule the cleanup task to come and
+ * pull it out of the tree in definite process context
+ */
+void key_put(struct key *key)
+{
+ if (key) {
+ key_check(key);
+
+ if (atomic_dec_and_test(&key->usage))
+ schedule_work(&key_cleanup_task);
+ }
+
+} /* end key_put() */
+
+EXPORT_SYMBOL(key_put);
+
+/*****************************************************************************/
+/*
+ * find a key by its serial number
+ */
+struct key *key_lookup(key_serial_t id)
+{
+ struct rb_node *n;
+ struct key *key;
+
+ spin_lock(&key_serial_lock);
+
+ /* search the tree for the specified key */
+ n = key_serial_tree.rb_node;
+ while (n) {
+ key = rb_entry(n, struct key, serial_node);
+
+ if (id < key->serial)
+ n = n->rb_left;
+ else if (id > key->serial)
+ n = n->rb_right;
+ else
+ goto found;
+ }
+
+ not_found:
+ key = ERR_PTR(-ENOKEY);
+ goto error;
+
+ found:
+ /* pretend it doesn't exist if it's dead */
+ if (atomic_read(&key->usage) == 0 ||
+ test_bit(KEY_FLAG_DEAD, &key->flags) ||
+ key->type == &key_type_dead)
+ goto not_found;
+
+ /* this races with key_put(), but that doesn't matter since key_put()
+ * doesn't actually change the key
+ */
+ atomic_inc(&key->usage);
+
+ error:
+ spin_unlock(&key_serial_lock);
+ return key;
+
+} /* end key_lookup() */
+
+/*****************************************************************************/
+/*
+ * find and lock the specified key type against removal
+ * - we return with the sem readlocked
+ */
+struct key_type *key_type_lookup(const char *type)
+{
+ struct key_type *ktype;
+
+ down_read(&key_types_sem);
+
+ /* look up the key type to see if it's one of the registered kernel
+ * types */
+ list_for_each_entry(ktype, &key_types_list, link) {
+ if (strcmp(ktype->name, type) == 0)
+ goto found_kernel_type;
+ }
+
+ up_read(&key_types_sem);
+ ktype = ERR_PTR(-ENOKEY);
+
+ found_kernel_type:
+ return ktype;
+
+} /* end key_type_lookup() */
+
+/*****************************************************************************/
+/*
+ * unlock a key type
+ */
+void key_type_put(struct key_type *ktype)
+{
+ up_read(&key_types_sem);
+
+} /* end key_type_put() */
+
+/*****************************************************************************/
+/*
+ * attempt to update an existing key
+ * - the key has an incremented refcount
+ * - we need to put the key if we get an error
+ */
+static inline key_ref_t __key_update(key_ref_t key_ref,
+ const void *payload, size_t plen)
+{
+ struct key *key = key_ref_to_ptr(key_ref);
+ int ret;
+
+ /* need write permission on the key to update it */
+ ret = key_permission(key_ref, KEY_WRITE);
+ if (ret < 0)
+ goto error;
+
+ ret = -EEXIST;
+ if (!key->type->update)
+ goto error;
+
+ down_write(&key->sem);
+
+ ret = key->type->update(key, payload, plen);
+ if (ret == 0)
+ /* updating a negative key instantiates it */
+ clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
+
+ up_write(&key->sem);
+
+ if (ret < 0)
+ goto error;
+out:
+ return key_ref;
+
+error:
+ key_put(key);
+ key_ref = ERR_PTR(ret);
+ goto out;
+
+} /* end __key_update() */
+
+/*****************************************************************************/
+/*
+ * search the specified keyring for a key of the same description; if one is
+ * found, update it, otherwise add a new one
+ */
+key_ref_t key_create_or_update(key_ref_t keyring_ref,
+ const char *type,
+ const char *description,
+ const void *payload,
+ size_t plen,
+ key_perm_t perm,
+ unsigned long flags)
+{
+ struct key_type *ktype;
+ struct key *keyring, *key = NULL;
+ key_ref_t key_ref;
+ int ret;
+
+ /* look up the key type to see if it's one of the registered kernel
+ * types */
+ ktype = key_type_lookup(type);
+ if (IS_ERR(ktype)) {
+ key_ref = ERR_PTR(-ENODEV);
+ goto error;
+ }
+
+ key_ref = ERR_PTR(-EINVAL);
+ if (!ktype->match || !ktype->instantiate)
+ goto error_2;
+
+ keyring = key_ref_to_ptr(keyring_ref);
+
+ key_check(keyring);
+
+ key_ref = ERR_PTR(-ENOTDIR);
+ if (keyring->type != &key_type_keyring)
+ goto error_2;
+
+ down_write(&keyring->sem);
+
+ /* if we're going to allocate a new key, we're going to have
+ * to modify the keyring */
+ ret = key_permission(keyring_ref, KEY_WRITE);
+ if (ret < 0) {
+ key_ref = ERR_PTR(ret);
+ goto error_3;
+ }
+
+ /* if it's possible to update this type of key, search for an existing
+ * key of the same type and description in the destination keyring and
+ * update that instead if possible
+ */
+ if (ktype->update) {
+ key_ref = __keyring_search_one(keyring_ref, ktype, description,
+ 0);
+ if (!IS_ERR(key_ref))
+ goto found_matching_key;
+ }
+
+ /* if the client doesn't provide, decide on the permissions we want */
+ if (perm == KEY_PERM_UNDEF) {
+ perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
+ perm |= KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK | KEY_USR_SETATTR;
+
+ if (ktype->read)
+ perm |= KEY_POS_READ | KEY_USR_READ;
+
+ if (ktype == &key_type_keyring || ktype->update)
+ perm |= KEY_USR_WRITE;
+ }
+
+ /* allocate a new key */
+ key = key_alloc(ktype, description, current->fsuid, current->fsgid,
+ current, perm, flags);
+ if (IS_ERR(key)) {
+ key_ref = ERR_CAST(key);
+ goto error_3;
+ }
+
+ /* instantiate it and link it into the target keyring */
+ ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL);
+ if (ret < 0) {
+ key_put(key);
+ key_ref = ERR_PTR(ret);
+ goto error_3;
+ }
+
+ key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
+
+ error_3:
+ up_write(&keyring->sem);
+ error_2:
+ key_type_put(ktype);
+ error:
+ return key_ref;
+
+ found_matching_key:
+ /* we found a matching key, so we're going to try to update it
+ * - we can drop the locks first as we have the key pinned
+ */
+ up_write(&keyring->sem);
+ key_type_put(ktype);
+
+ key_ref = __key_update(key_ref, payload, plen);
+ goto error;
+
+} /* end key_create_or_update() */
+
+EXPORT_SYMBOL(key_create_or_update);
+
+/*****************************************************************************/
+/*
+ * update a key
+ */
+int key_update(key_ref_t key_ref, const void *payload, size_t plen)
+{
+ struct key *key = key_ref_to_ptr(key_ref);
+ int ret;
+
+ key_check(key);
+
+ /* the key must be writable */
+ ret = key_permission(key_ref, KEY_WRITE);
+ if (ret < 0)
+ goto error;
+
+ /* attempt to update it if supported */
+ ret = -EOPNOTSUPP;
+ if (key->type->update) {
+ down_write(&key->sem);
+
+ ret = key->type->update(key, payload, plen);
+ if (ret == 0)
+ /* updating a negative key instantiates it */
+ clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
+
+ up_write(&key->sem);
+ }
+
+ error:
+ return ret;
+
+} /* end key_update() */
+
+EXPORT_SYMBOL(key_update);
+
+/*****************************************************************************/
+/*
+ * revoke a key
+ */
+void key_revoke(struct key *key)
+{
+ key_check(key);
+
+ /* make sure no one's trying to change or use the key when we mark it
+ * - we tell lockdep that we might nest because we might be revoking an
+ * authorisation key whilst holding the sem on a key we've just
+ * instantiated
+ */
+ down_write_nested(&key->sem, 1);
+ if (!test_and_set_bit(KEY_FLAG_REVOKED, &key->flags) &&
+ key->type->revoke)
+ key->type->revoke(key);
+
+ up_write(&key->sem);
+
+} /* end key_revoke() */
+
+EXPORT_SYMBOL(key_revoke);
+
+/*****************************************************************************/
+/*
+ * register a type of key
+ */
+int register_key_type(struct key_type *ktype)
+{
+ struct key_type *p;
+ int ret;
+
+ ret = -EEXIST;
+ down_write(&key_types_sem);
+
+ /* disallow key types with the same name */
+ list_for_each_entry(p, &key_types_list, link) {
+ if (strcmp(p->name, ktype->name) == 0)
+ goto out;
+ }
+
+ /* store the type */
+ list_add(&ktype->link, &key_types_list);
+ ret = 0;
+
+ out:
+ up_write(&key_types_sem);
+ return ret;
+
+} /* end register_key_type() */
+
+EXPORT_SYMBOL(register_key_type);
+
+/*****************************************************************************/
+/*
+ * unregister a type of key
+ */
+void unregister_key_type(struct key_type *ktype)
+{
+ struct rb_node *_n;
+ struct key *key;
+
+ down_write(&key_types_sem);
+
+ /* withdraw the key type */
+ list_del_init(&ktype->link);
+
+ /* mark all the keys of this type dead */
+ spin_lock(&key_serial_lock);
+
+ for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
+ key = rb_entry(_n, struct key, serial_node);
+
+ if (key->type == ktype)
+ key->type = &key_type_dead;
+ }
+
+ spin_unlock(&key_serial_lock);
+
+ /* make sure everyone revalidates their keys */
+ synchronize_rcu();
+
+ /* we should now be able to destroy the payloads of all the keys of
+ * this type with impunity */
+ spin_lock(&key_serial_lock);
+
+ for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
+ key = rb_entry(_n, struct key, serial_node);
+
+ if (key->type == ktype) {
+ if (ktype->destroy)
+ ktype->destroy(key);
+ memset(&key->payload, KEY_DESTROY, sizeof(key->payload));
+ }
+ }
+
+ spin_unlock(&key_serial_lock);
+ up_write(&key_types_sem);
+
+} /* end unregister_key_type() */
+
+EXPORT_SYMBOL(unregister_key_type);
+
+/*****************************************************************************/
+/*
+ * initialise the key management stuff
+ */
+void __init key_init(void)
+{
+ /* allocate a slab in which we can store keys */
+ key_jar = kmem_cache_create("key_jar", sizeof(struct key),
+ 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
+
+ /* add the special key types */
+ list_add_tail(&key_type_keyring.link, &key_types_list);
+ list_add_tail(&key_type_dead.link, &key_types_list);
+ list_add_tail(&key_type_user.link, &key_types_list);
+
+ /* record the root user tracking */
+ rb_link_node(&root_key_user.node,
+ NULL,
+ &key_user_tree.rb_node);
+
+ rb_insert_color(&root_key_user.node,
+ &key_user_tree);
+
+} /* end key_init() */
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
new file mode 100644
index 0000000..3c0f421
--- /dev/null
+++ b/security/keys/keyctl.c
@@ -0,0 +1,1241 @@
+/* keyctl.c: userspace keyctl operations
+ *
+ * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/keyctl.h>
+#include <linux/fs.h>
+#include <linux/capability.h>
+#include <linux/string.h>
+#include <linux/err.h>
+#include <linux/vmalloc.h>
+#include <linux/security.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+static int key_get_type_from_user(char *type,
+ const char __user *_type,
+ unsigned len)
+{
+ int ret;
+
+ ret = strncpy_from_user(type, _type, len);
+
+ if (ret < 0)
+ return -EFAULT;
+
+ if (ret == 0 || ret >= len)
+ return -EINVAL;
+
+ if (type[0] == '.')
+ return -EPERM;
+
+ type[len - 1] = '\0';
+
+ return 0;
+}
+
+/*****************************************************************************/
+/*
+ * extract the description of a new key from userspace and either add it as a
+ * new key to the specified keyring or update a matching key in that keyring
+ * - the keyring must be writable
+ * - returns the new key's serial number
+ * - implements add_key()
+ */
+SYSCALL_DEFINE5(add_key, const char __user *, _type,
+ const char __user *, _description,
+ const void __user *, _payload,
+ size_t, plen,
+ key_serial_t, ringid)
+{
+ key_ref_t keyring_ref, key_ref;
+ char type[32], *description;
+ void *payload;
+ long ret;
+ bool vm;
+
+ ret = -EINVAL;
+ if (plen > 1024 * 1024 - 1)
+ goto error;
+
+ /* draw all the data into kernel space */
+ ret = key_get_type_from_user(type, _type, sizeof(type));
+ if (ret < 0)
+ goto error;
+
+ description = strndup_user(_description, PAGE_SIZE);
+ if (IS_ERR(description)) {
+ ret = PTR_ERR(description);
+ goto error;
+ }
+
+ /* pull the payload in if one was supplied */
+ payload = NULL;
+
+ vm = false;
+ if (_payload) {
+ ret = -ENOMEM;
+ payload = kmalloc(plen, GFP_KERNEL);
+ if (!payload) {
+ if (plen <= PAGE_SIZE)
+ goto error2;
+ vm = true;
+ payload = vmalloc(plen);
+ if (!payload)
+ goto error2;
+ }
+
+ ret = -EFAULT;
+ if (copy_from_user(payload, _payload, plen) != 0)
+ goto error3;
+ }
+
+ /* find the target keyring (which must be writable) */
+ keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
+ if (IS_ERR(keyring_ref)) {
+ ret = PTR_ERR(keyring_ref);
+ goto error3;
+ }
+
+ /* create or update the requested key and add it to the target
+ * keyring */
+ key_ref = key_create_or_update(keyring_ref, type, description,
+ payload, plen, KEY_PERM_UNDEF,
+ KEY_ALLOC_IN_QUOTA);
+ if (!IS_ERR(key_ref)) {
+ ret = key_ref_to_ptr(key_ref)->serial;
+ key_ref_put(key_ref);
+ }
+ else {
+ ret = PTR_ERR(key_ref);
+ }
+
+ key_ref_put(keyring_ref);
+ error3:
+ if (!vm)
+ kfree(payload);
+ else
+ vfree(payload);
+ error2:
+ kfree(description);
+ error:
+ return ret;
+
+} /* end sys_add_key() */
+
+/*****************************************************************************/
+/*
+ * search the process keyrings for a matching key
+ * - nested keyrings may also be searched if they have Search permission
+ * - if a key is found, it will be attached to the destination keyring if
+ * there's one specified
+ * - /sbin/request-key will be invoked if _callout_info is non-NULL
+ * - the _callout_info string will be passed to /sbin/request-key
+ * - if the _callout_info string is empty, it will be rendered as "-"
+ * - implements request_key()
+ */
+SYSCALL_DEFINE4(request_key, const char __user *, _type,
+ const char __user *, _description,
+ const char __user *, _callout_info,
+ key_serial_t, destringid)
+{
+ struct key_type *ktype;
+ struct key *key;
+ key_ref_t dest_ref;
+ size_t callout_len;
+ char type[32], *description, *callout_info;
+ long ret;
+
+ /* pull the type into kernel space */
+ ret = key_get_type_from_user(type, _type, sizeof(type));
+ if (ret < 0)
+ goto error;
+
+ /* pull the description into kernel space */
+ description = strndup_user(_description, PAGE_SIZE);
+ if (IS_ERR(description)) {
+ ret = PTR_ERR(description);
+ goto error;
+ }
+
+ /* pull the callout info into kernel space */
+ callout_info = NULL;
+ callout_len = 0;
+ if (_callout_info) {
+ callout_info = strndup_user(_callout_info, PAGE_SIZE);
+ if (IS_ERR(callout_info)) {
+ ret = PTR_ERR(callout_info);
+ goto error2;
+ }
+ callout_len = strlen(callout_info);
+ }
+
+ /* get the destination keyring if specified */
+ dest_ref = NULL;
+ if (destringid) {
+ dest_ref = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE);
+ if (IS_ERR(dest_ref)) {
+ ret = PTR_ERR(dest_ref);
+ goto error3;
+ }
+ }
+
+ /* find the key type */
+ ktype = key_type_lookup(type);
+ if (IS_ERR(ktype)) {
+ ret = PTR_ERR(ktype);
+ goto error4;
+ }
+
+ /* do the search */
+ key = request_key_and_link(ktype, description, callout_info,
+ callout_len, NULL, key_ref_to_ptr(dest_ref),
+ KEY_ALLOC_IN_QUOTA);
+ if (IS_ERR(key)) {
+ ret = PTR_ERR(key);
+ goto error5;
+ }
+
+ ret = key->serial;
+
+ key_put(key);
+ error5:
+ key_type_put(ktype);
+ error4:
+ key_ref_put(dest_ref);
+ error3:
+ kfree(callout_info);
+ error2:
+ kfree(description);
+ error:
+ return ret;
+
+} /* end sys_request_key() */
+
+/*****************************************************************************/
+/*
+ * get the ID of the specified process keyring
+ * - the keyring must have search permission to be found
+ * - implements keyctl(KEYCTL_GET_KEYRING_ID)
+ */
+long keyctl_get_keyring_ID(key_serial_t id, int create)
+{
+ key_ref_t key_ref;
+ long ret;
+
+ key_ref = lookup_user_key(NULL, id, create, 0, KEY_SEARCH);
+ if (IS_ERR(key_ref)) {
+ ret = PTR_ERR(key_ref);
+ goto error;
+ }
+
+ ret = key_ref_to_ptr(key_ref)->serial;
+ key_ref_put(key_ref);
+ error:
+ return ret;
+
+} /* end keyctl_get_keyring_ID() */
+
+/*****************************************************************************/
+/*
+ * join the session keyring
+ * - implements keyctl(KEYCTL_JOIN_SESSION_KEYRING)
+ */
+long keyctl_join_session_keyring(const char __user *_name)
+{
+ char *name;
+ long ret;
+
+ /* fetch the name from userspace */
+ name = NULL;
+ if (_name) {
+ name = strndup_user(_name, PAGE_SIZE);
+ if (IS_ERR(name)) {
+ ret = PTR_ERR(name);
+ goto error;
+ }
+ }
+
+ /* join the session */
+ ret = join_session_keyring(name);
+ kfree(name);
+
+ error:
+ return ret;
+
+} /* end keyctl_join_session_keyring() */
+
+/*****************************************************************************/
+/*
+ * update a key's data payload
+ * - the key must be writable
+ * - implements keyctl(KEYCTL_UPDATE)
+ */
+long keyctl_update_key(key_serial_t id,
+ const void __user *_payload,
+ size_t plen)
+{
+ key_ref_t key_ref;
+ void *payload;
+ long ret;
+
+ ret = -EINVAL;
+ if (plen > PAGE_SIZE)
+ goto error;
+
+ /* pull the payload in if one was supplied */
+ payload = NULL;
+ if (_payload) {
+ ret = -ENOMEM;
+ payload = kmalloc(plen, GFP_KERNEL);
+ if (!payload)
+ goto error;
+
+ ret = -EFAULT;
+ if (copy_from_user(payload, _payload, plen) != 0)
+ goto error2;
+ }
+
+ /* find the target key (which must be writable) */
+ key_ref = lookup_user_key(NULL, id, 0, 0, KEY_WRITE);
+ if (IS_ERR(key_ref)) {
+ ret = PTR_ERR(key_ref);
+ goto error2;
+ }
+
+ /* update the key */
+ ret = key_update(key_ref, payload, plen);
+
+ key_ref_put(key_ref);
+ error2:
+ kfree(payload);
+ error:
+ return ret;
+
+} /* end keyctl_update_key() */
+
+/*****************************************************************************/
+/*
+ * revoke a key
+ * - the key must be writable
+ * - implements keyctl(KEYCTL_REVOKE)
+ */
+long keyctl_revoke_key(key_serial_t id)
+{
+ key_ref_t key_ref;
+ long ret;
+
+ key_ref = lookup_user_key(NULL, id, 0, 0, KEY_WRITE);
+ if (IS_ERR(key_ref)) {
+ ret = PTR_ERR(key_ref);
+ goto error;
+ }
+
+ key_revoke(key_ref_to_ptr(key_ref));
+ ret = 0;
+
+ key_ref_put(key_ref);
+ error:
+ return ret;
+
+} /* end keyctl_revoke_key() */
+
+/*****************************************************************************/
+/*
+ * clear the specified process keyring
+ * - the keyring must be writable
+ * - implements keyctl(KEYCTL_CLEAR)
+ */
+long keyctl_keyring_clear(key_serial_t ringid)
+{
+ key_ref_t keyring_ref;
+ long ret;
+
+ keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
+ if (IS_ERR(keyring_ref)) {
+ ret = PTR_ERR(keyring_ref);
+ goto error;
+ }
+
+ ret = keyring_clear(key_ref_to_ptr(keyring_ref));
+
+ key_ref_put(keyring_ref);
+ error:
+ return ret;
+
+} /* end keyctl_keyring_clear() */
+
+/*****************************************************************************/
+/*
+ * link a key into a keyring
+ * - the keyring must be writable
+ * - the key must be linkable
+ * - implements keyctl(KEYCTL_LINK)
+ */
+long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
+{
+ key_ref_t keyring_ref, key_ref;
+ long ret;
+
+ keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
+ if (IS_ERR(keyring_ref)) {
+ ret = PTR_ERR(keyring_ref);
+ goto error;
+ }
+
+ key_ref = lookup_user_key(NULL, id, 1, 0, KEY_LINK);
+ if (IS_ERR(key_ref)) {
+ ret = PTR_ERR(key_ref);
+ goto error2;
+ }
+
+ ret = key_link(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref));
+
+ key_ref_put(key_ref);
+ error2:
+ key_ref_put(keyring_ref);
+ error:
+ return ret;
+
+} /* end keyctl_keyring_link() */
+
+/*****************************************************************************/
+/*
+ * unlink the first attachment of a key from a keyring
+ * - the keyring must be writable
+ * - we don't need any permissions on the key
+ * - implements keyctl(KEYCTL_UNLINK)
+ */
+long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
+{
+ key_ref_t keyring_ref, key_ref;
+ long ret;
+
+ keyring_ref = lookup_user_key(NULL, ringid, 0, 0, KEY_WRITE);
+ if (IS_ERR(keyring_ref)) {
+ ret = PTR_ERR(keyring_ref);
+ goto error;
+ }
+
+ key_ref = lookup_user_key(NULL, id, 0, 0, 0);
+ if (IS_ERR(key_ref)) {
+ ret = PTR_ERR(key_ref);
+ goto error2;
+ }
+
+ ret = key_unlink(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref));
+
+ key_ref_put(key_ref);
+ error2:
+ key_ref_put(keyring_ref);
+ error:
+ return ret;
+
+} /* end keyctl_keyring_unlink() */
+
+/*****************************************************************************/
+/*
+ * describe a user key
+ * - the key must have view permission
+ * - if there's a buffer, we place up to buflen bytes of data into it
+ * - unless there's an error, we return the amount of description available,
+ * irrespective of how much we may have copied
+ * - the description is formatted thus:
+ * type;uid;gid;perm;description<NUL>
+ * - implements keyctl(KEYCTL_DESCRIBE)
+ */
+long keyctl_describe_key(key_serial_t keyid,
+ char __user *buffer,
+ size_t buflen)
+{
+ struct key *key, *instkey;
+ key_ref_t key_ref;
+ char *tmpbuf;
+ long ret;
+
+ key_ref = lookup_user_key(NULL, keyid, 0, 1, KEY_VIEW);
+ if (IS_ERR(key_ref)) {
+ /* viewing a key under construction is permitted if we have the
+ * authorisation token handy */
+ if (PTR_ERR(key_ref) == -EACCES) {
+ instkey = key_get_instantiation_authkey(keyid);
+ if (!IS_ERR(instkey)) {
+ key_put(instkey);
+ key_ref = lookup_user_key(NULL, keyid,
+ 0, 1, 0);
+ if (!IS_ERR(key_ref))
+ goto okay;
+ }
+ }
+
+ ret = PTR_ERR(key_ref);
+ goto error;
+ }
+
+okay:
+ /* calculate how much description we're going to return */
+ ret = -ENOMEM;
+ tmpbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!tmpbuf)
+ goto error2;
+
+ key = key_ref_to_ptr(key_ref);
+
+ ret = snprintf(tmpbuf, PAGE_SIZE - 1,
+ "%s;%d;%d;%08x;%s",
+ key_ref_to_ptr(key_ref)->type->name,
+ key_ref_to_ptr(key_ref)->uid,
+ key_ref_to_ptr(key_ref)->gid,
+ key_ref_to_ptr(key_ref)->perm,
+ key_ref_to_ptr(key_ref)->description ?
+ key_ref_to_ptr(key_ref)->description : ""
+ );
+
+ /* include a NUL char at the end of the data */
+ if (ret > PAGE_SIZE - 1)
+ ret = PAGE_SIZE - 1;
+ tmpbuf[ret] = 0;
+ ret++;
+
+ /* consider returning the data */
+ if (buffer && buflen > 0) {
+ if (buflen > ret)
+ buflen = ret;
+
+ if (copy_to_user(buffer, tmpbuf, buflen) != 0)
+ ret = -EFAULT;
+ }
+
+ kfree(tmpbuf);
+ error2:
+ key_ref_put(key_ref);
+ error:
+ return ret;
+
+} /* end keyctl_describe_key() */
+
+/*****************************************************************************/
+/*
+ * search the specified keyring for a matching key
+ * - the start keyring must be searchable
+ * - nested keyrings may also be searched if they are searchable
+ * - only keys with search permission may be found
+ * - if a key is found, it will be attached to the destination keyring if
+ * there's one specified
+ * - implements keyctl(KEYCTL_SEARCH)
+ */
+long keyctl_keyring_search(key_serial_t ringid,
+ const char __user *_type,
+ const char __user *_description,
+ key_serial_t destringid)
+{
+ struct key_type *ktype;
+ key_ref_t keyring_ref, key_ref, dest_ref;
+ char type[32], *description;
+ long ret;
+
+ /* pull the type and description into kernel space */
+ ret = key_get_type_from_user(type, _type, sizeof(type));
+ if (ret < 0)
+ goto error;
+
+ description = strndup_user(_description, PAGE_SIZE);
+ if (IS_ERR(description)) {
+ ret = PTR_ERR(description);
+ goto error;
+ }
+
+ /* get the keyring at which to begin the search */
+ keyring_ref = lookup_user_key(NULL, ringid, 0, 0, KEY_SEARCH);
+ if (IS_ERR(keyring_ref)) {
+ ret = PTR_ERR(keyring_ref);
+ goto error2;
+ }
+
+ /* get the destination keyring if specified */
+ dest_ref = NULL;
+ if (destringid) {
+ dest_ref = lookup_user_key(NULL, destringid, 1, 0, KEY_WRITE);
+ if (IS_ERR(dest_ref)) {
+ ret = PTR_ERR(dest_ref);
+ goto error3;
+ }
+ }
+
+ /* find the key type */
+ ktype = key_type_lookup(type);
+ if (IS_ERR(ktype)) {
+ ret = PTR_ERR(ktype);
+ goto error4;
+ }
+
+ /* do the search */
+ key_ref = keyring_search(keyring_ref, ktype, description);
+ if (IS_ERR(key_ref)) {
+ ret = PTR_ERR(key_ref);
+
+ /* treat lack or presence of a negative key the same */
+ if (ret == -EAGAIN)
+ ret = -ENOKEY;
+ goto error5;
+ }
+
+ /* link the resulting key to the destination keyring if we can */
+ if (dest_ref) {
+ ret = key_permission(key_ref, KEY_LINK);
+ if (ret < 0)
+ goto error6;
+
+ ret = key_link(key_ref_to_ptr(dest_ref), key_ref_to_ptr(key_ref));
+ if (ret < 0)
+ goto error6;
+ }
+
+ ret = key_ref_to_ptr(key_ref)->serial;
+
+ error6:
+ key_ref_put(key_ref);
+ error5:
+ key_type_put(ktype);
+ error4:
+ key_ref_put(dest_ref);
+ error3:
+ key_ref_put(keyring_ref);
+ error2:
+ kfree(description);
+ error:
+ return ret;
+
+} /* end keyctl_keyring_search() */
+
+/*****************************************************************************/
+/*
+ * read a user key's payload
+ * - the keyring must be readable or the key must be searchable from the
+ * process's keyrings
+ * - if there's a buffer, we place up to buflen bytes of data into it
+ * - unless there's an error, we return the amount of data in the key,
+ * irrespective of how much we may have copied
+ * - implements keyctl(KEYCTL_READ)
+ */
+long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
+{
+ struct key *key;
+ key_ref_t key_ref;
+ long ret;
+
+ /* find the key first */
+ key_ref = lookup_user_key(NULL, keyid, 0, 0, 0);
+ if (IS_ERR(key_ref)) {
+ ret = -ENOKEY;
+ goto error;
+ }
+
+ key = key_ref_to_ptr(key_ref);
+
+ /* see if we can read it directly */
+ ret = key_permission(key_ref, KEY_READ);
+ if (ret == 0)
+ goto can_read_key;
+ if (ret != -EACCES)
+ goto error;
+
+ /* we can't; see if it's searchable from this process's keyrings
+ * - we automatically take account of the fact that it may be
+ * dangling off an instantiation key
+ */
+ if (!is_key_possessed(key_ref)) {
+ ret = -EACCES;
+ goto error2;
+ }
+
+ /* the key is probably readable - now try to read it */
+ can_read_key:
+ ret = key_validate(key);
+ if (ret == 0) {
+ ret = -EOPNOTSUPP;
+ if (key->type->read) {
+ /* read the data with the semaphore held (since we
+ * might sleep) */
+ down_read(&key->sem);
+ ret = key->type->read(key, buffer, buflen);
+ up_read(&key->sem);
+ }
+ }
+
+ error2:
+ key_put(key);
+ error:
+ return ret;
+
+} /* end keyctl_read_key() */
+
+/*****************************************************************************/
+/*
+ * change the ownership of a key
+ * - the keyring owned by the changer
+ * - if the uid or gid is -1, then that parameter is not changed
+ * - implements keyctl(KEYCTL_CHOWN)
+ */
+long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
+{
+ struct key_user *newowner, *zapowner = NULL;
+ struct key *key;
+ key_ref_t key_ref;
+ long ret;
+
+ ret = 0;
+ if (uid == (uid_t) -1 && gid == (gid_t) -1)
+ goto error;
+
+ key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR);
+ if (IS_ERR(key_ref)) {
+ ret = PTR_ERR(key_ref);
+ goto error;
+ }
+
+ key = key_ref_to_ptr(key_ref);
+
+ /* make the changes with the locks held to prevent chown/chown races */
+ ret = -EACCES;
+ down_write(&key->sem);
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ /* only the sysadmin can chown a key to some other UID */
+ if (uid != (uid_t) -1 && key->uid != uid)
+ goto error_put;
+
+ /* only the sysadmin can set the key's GID to a group other
+ * than one of those that the current process subscribes to */
+ if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid))
+ goto error_put;
+ }
+
+ /* change the UID */
+ if (uid != (uid_t) -1 && uid != key->uid) {
+ ret = -ENOMEM;
+ newowner = key_user_lookup(uid);
+ if (!newowner)
+ goto error_put;
+
+ /* transfer the quota burden to the new user */
+ if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
+ unsigned maxkeys = (uid == 0) ?
+ key_quota_root_maxkeys : key_quota_maxkeys;
+ unsigned maxbytes = (uid == 0) ?
+ key_quota_root_maxbytes : key_quota_maxbytes;
+
+ spin_lock(&newowner->lock);
+ if (newowner->qnkeys + 1 >= maxkeys ||
+ newowner->qnbytes + key->quotalen >= maxbytes ||
+ newowner->qnbytes + key->quotalen <
+ newowner->qnbytes)
+ goto quota_overrun;
+
+ newowner->qnkeys++;
+ newowner->qnbytes += key->quotalen;
+ spin_unlock(&newowner->lock);
+
+ spin_lock(&key->user->lock);
+ key->user->qnkeys--;
+ key->user->qnbytes -= key->quotalen;
+ spin_unlock(&key->user->lock);
+ }
+
+ atomic_dec(&key->user->nkeys);
+ atomic_inc(&newowner->nkeys);
+
+ if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
+ atomic_dec(&key->user->nikeys);
+ atomic_inc(&newowner->nikeys);
+ }
+
+ zapowner = key->user;
+ key->user = newowner;
+ key->uid = uid;
+ }
+
+ /* change the GID */
+ if (gid != (gid_t) -1)
+ key->gid = gid;
+
+ ret = 0;
+
+error_put:
+ up_write(&key->sem);
+ key_put(key);
+ if (zapowner)
+ key_user_put(zapowner);
+error:
+ return ret;
+
+quota_overrun:
+ spin_unlock(&newowner->lock);
+ zapowner = newowner;
+ ret = -EDQUOT;
+ goto error_put;
+
+} /* end keyctl_chown_key() */
+
+/*****************************************************************************/
+/*
+ * change the permission mask on a key
+ * - the keyring owned by the changer
+ * - implements keyctl(KEYCTL_SETPERM)
+ */
+long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
+{
+ struct key *key;
+ key_ref_t key_ref;
+ long ret;
+
+ ret = -EINVAL;
+ if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))
+ goto error;
+
+ key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR);
+ if (IS_ERR(key_ref)) {
+ ret = PTR_ERR(key_ref);
+ goto error;
+ }
+
+ key = key_ref_to_ptr(key_ref);
+
+ /* make the changes with the locks held to prevent chown/chmod races */
+ ret = -EACCES;
+ down_write(&key->sem);
+
+ /* if we're not the sysadmin, we can only change a key that we own */
+ if (capable(CAP_SYS_ADMIN) || key->uid == current->fsuid) {
+ key->perm = perm;
+ ret = 0;
+ }
+
+ up_write(&key->sem);
+ key_put(key);
+error:
+ return ret;
+
+} /* end keyctl_setperm_key() */
+
+/*****************************************************************************/
+/*
+ * instantiate the key with the specified payload, and, if one is given, link
+ * the key into the keyring
+ */
+long keyctl_instantiate_key(key_serial_t id,
+ const void __user *_payload,
+ size_t plen,
+ key_serial_t ringid)
+{
+ struct request_key_auth *rka;
+ struct key *instkey;
+ key_ref_t keyring_ref;
+ void *payload;
+ long ret;
+ bool vm = false;
+
+ ret = -EINVAL;
+ if (plen > 1024 * 1024 - 1)
+ goto error;
+
+ /* the appropriate instantiation authorisation key must have been
+ * assumed before calling this */
+ ret = -EPERM;
+ instkey = current->request_key_auth;
+ if (!instkey)
+ goto error;
+
+ rka = instkey->payload.data;
+ if (rka->target_key->serial != id)
+ goto error;
+
+ /* pull the payload in if one was supplied */
+ payload = NULL;
+
+ if (_payload) {
+ ret = -ENOMEM;
+ payload = kmalloc(plen, GFP_KERNEL);
+ if (!payload) {
+ if (plen <= PAGE_SIZE)
+ goto error;
+ vm = true;
+ payload = vmalloc(plen);
+ if (!payload)
+ goto error;
+ }
+
+ ret = -EFAULT;
+ if (copy_from_user(payload, _payload, plen) != 0)
+ goto error2;
+ }
+
+ /* find the destination keyring amongst those belonging to the
+ * requesting task */
+ keyring_ref = NULL;
+ if (ringid) {
+ keyring_ref = lookup_user_key(rka->context, ringid, 1, 0,
+ KEY_WRITE);
+ if (IS_ERR(keyring_ref)) {
+ ret = PTR_ERR(keyring_ref);
+ goto error2;
+ }
+ }
+
+ /* instantiate the key and link it into a keyring */
+ ret = key_instantiate_and_link(rka->target_key, payload, plen,
+ key_ref_to_ptr(keyring_ref), instkey);
+
+ key_ref_put(keyring_ref);
+
+ /* discard the assumed authority if it's just been disabled by
+ * instantiation of the key */
+ if (ret == 0) {
+ key_put(current->request_key_auth);
+ current->request_key_auth = NULL;
+ }
+
+error2:
+ if (!vm)
+ kfree(payload);
+ else
+ vfree(payload);
+error:
+ return ret;
+
+} /* end keyctl_instantiate_key() */
+
+/*****************************************************************************/
+/*
+ * negatively instantiate the key with the given timeout (in seconds), and, if
+ * one is given, link the key into the keyring
+ */
+long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
+{
+ struct request_key_auth *rka;
+ struct key *instkey;
+ key_ref_t keyring_ref;
+ long ret;
+
+ /* the appropriate instantiation authorisation key must have been
+ * assumed before calling this */
+ ret = -EPERM;
+ instkey = current->request_key_auth;
+ if (!instkey)
+ goto error;
+
+ rka = instkey->payload.data;
+ if (rka->target_key->serial != id)
+ goto error;
+
+ /* find the destination keyring if present (which must also be
+ * writable) */
+ keyring_ref = NULL;
+ if (ringid) {
+ keyring_ref = lookup_user_key(NULL, ringid, 1, 0, KEY_WRITE);
+ if (IS_ERR(keyring_ref)) {
+ ret = PTR_ERR(keyring_ref);
+ goto error;
+ }
+ }
+
+ /* instantiate the key and link it into a keyring */
+ ret = key_negate_and_link(rka->target_key, timeout,
+ key_ref_to_ptr(keyring_ref), instkey);
+
+ key_ref_put(keyring_ref);
+
+ /* discard the assumed authority if it's just been disabled by
+ * instantiation of the key */
+ if (ret == 0) {
+ key_put(current->request_key_auth);
+ current->request_key_auth = NULL;
+ }
+
+error:
+ return ret;
+
+} /* end keyctl_negate_key() */
+
+/*****************************************************************************/
+/*
+ * set the default keyring in which request_key() will cache keys
+ * - return the old setting
+ */
+long keyctl_set_reqkey_keyring(int reqkey_defl)
+{
+ int ret;
+
+ switch (reqkey_defl) {
+ case KEY_REQKEY_DEFL_THREAD_KEYRING:
+ ret = install_thread_keyring(current);
+ if (ret < 0)
+ return ret;
+ goto set;
+
+ case KEY_REQKEY_DEFL_PROCESS_KEYRING:
+ ret = install_process_keyring(current);
+ if (ret < 0)
+ return ret;
+
+ case KEY_REQKEY_DEFL_DEFAULT:
+ case KEY_REQKEY_DEFL_SESSION_KEYRING:
+ case KEY_REQKEY_DEFL_USER_KEYRING:
+ case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
+ set:
+ current->jit_keyring = reqkey_defl;
+
+ case KEY_REQKEY_DEFL_NO_CHANGE:
+ return current->jit_keyring;
+
+ case KEY_REQKEY_DEFL_GROUP_KEYRING:
+ default:
+ return -EINVAL;
+ }
+
+} /* end keyctl_set_reqkey_keyring() */
+
+/*****************************************************************************/
+/*
+ * set or clear the timeout for a key
+ */
+long keyctl_set_timeout(key_serial_t id, unsigned timeout)
+{
+ struct timespec now;
+ struct key *key;
+ key_ref_t key_ref;
+ time_t expiry;
+ long ret;
+
+ key_ref = lookup_user_key(NULL, id, 1, 1, KEY_SETATTR);
+ if (IS_ERR(key_ref)) {
+ ret = PTR_ERR(key_ref);
+ goto error;
+ }
+
+ key = key_ref_to_ptr(key_ref);
+
+ /* make the changes with the locks held to prevent races */
+ down_write(&key->sem);
+
+ expiry = 0;
+ if (timeout > 0) {
+ now = current_kernel_time();
+ expiry = now.tv_sec + timeout;
+ }
+
+ key->expiry = expiry;
+
+ up_write(&key->sem);
+ key_put(key);
+
+ ret = 0;
+error:
+ return ret;
+
+} /* end keyctl_set_timeout() */
+
+/*****************************************************************************/
+/*
+ * assume the authority to instantiate the specified key
+ */
+long keyctl_assume_authority(key_serial_t id)
+{
+ struct key *authkey;
+ long ret;
+
+ /* special key IDs aren't permitted */
+ ret = -EINVAL;
+ if (id < 0)
+ goto error;
+
+ /* we divest ourselves of authority if given an ID of 0 */
+ if (id == 0) {
+ key_put(current->request_key_auth);
+ current->request_key_auth = NULL;
+ ret = 0;
+ goto error;
+ }
+
+ /* attempt to assume the authority temporarily granted to us whilst we
+ * instantiate the specified key
+ * - the authorisation key must be in the current task's keyrings
+ * somewhere
+ */
+ authkey = key_get_instantiation_authkey(id);
+ if (IS_ERR(authkey)) {
+ ret = PTR_ERR(authkey);
+ goto error;
+ }
+
+ key_put(current->request_key_auth);
+ current->request_key_auth = authkey;
+ ret = authkey->serial;
+
+error:
+ return ret;
+
+} /* end keyctl_assume_authority() */
+
+/*
+ * get the security label of a key
+ * - the key must grant us view permission
+ * - if there's a buffer, we place up to buflen bytes of data into it
+ * - unless there's an error, we return the amount of information available,
+ * irrespective of how much we may have copied (including the terminal NUL)
+ * - implements keyctl(KEYCTL_GET_SECURITY)
+ */
+long keyctl_get_security(key_serial_t keyid,
+ char __user *buffer,
+ size_t buflen)
+{
+ struct key *key, *instkey;
+ key_ref_t key_ref;
+ char *context;
+ long ret;
+
+ key_ref = lookup_user_key(NULL, keyid, 0, 1, KEY_VIEW);
+ if (IS_ERR(key_ref)) {
+ if (PTR_ERR(key_ref) != -EACCES)
+ return PTR_ERR(key_ref);
+
+ /* viewing a key under construction is also permitted if we
+ * have the authorisation token handy */
+ instkey = key_get_instantiation_authkey(keyid);
+ if (IS_ERR(instkey))
+ return PTR_ERR(key_ref);
+ key_put(instkey);
+
+ key_ref = lookup_user_key(NULL, keyid, 0, 1, 0);
+ if (IS_ERR(key_ref))
+ return PTR_ERR(key_ref);
+ }
+
+ key = key_ref_to_ptr(key_ref);
+ ret = security_key_getsecurity(key, &context);
+ if (ret == 0) {
+ /* if no information was returned, give userspace an empty
+ * string */
+ ret = 1;
+ if (buffer && buflen > 0 &&
+ copy_to_user(buffer, "", 1) != 0)
+ ret = -EFAULT;
+ } else if (ret > 0) {
+ /* return as much data as there's room for */
+ if (buffer && buflen > 0) {
+ if (buflen > ret)
+ buflen = ret;
+
+ if (copy_to_user(buffer, context, buflen) != 0)
+ ret = -EFAULT;
+ }
+
+ kfree(context);
+ }
+
+ key_ref_put(key_ref);
+ return ret;
+}
+
+/*****************************************************************************/
+/*
+ * the key control system call
+ */
+SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
+ unsigned long, arg4, unsigned long, arg5)
+{
+ switch (option) {
+ case KEYCTL_GET_KEYRING_ID:
+ return keyctl_get_keyring_ID((key_serial_t) arg2,
+ (int) arg3);
+
+ case KEYCTL_JOIN_SESSION_KEYRING:
+ return keyctl_join_session_keyring((const char __user *) arg2);
+
+ case KEYCTL_UPDATE:
+ return keyctl_update_key((key_serial_t) arg2,
+ (const void __user *) arg3,
+ (size_t) arg4);
+
+ case KEYCTL_REVOKE:
+ return keyctl_revoke_key((key_serial_t) arg2);
+
+ case KEYCTL_DESCRIBE:
+ return keyctl_describe_key((key_serial_t) arg2,
+ (char __user *) arg3,
+ (unsigned) arg4);
+
+ case KEYCTL_CLEAR:
+ return keyctl_keyring_clear((key_serial_t) arg2);
+
+ case KEYCTL_LINK:
+ return keyctl_keyring_link((key_serial_t) arg2,
+ (key_serial_t) arg3);
+
+ case KEYCTL_UNLINK:
+ return keyctl_keyring_unlink((key_serial_t) arg2,
+ (key_serial_t) arg3);
+
+ case KEYCTL_SEARCH:
+ return keyctl_keyring_search((key_serial_t) arg2,
+ (const char __user *) arg3,
+ (const char __user *) arg4,
+ (key_serial_t) arg5);
+
+ case KEYCTL_READ:
+ return keyctl_read_key((key_serial_t) arg2,
+ (char __user *) arg3,
+ (size_t) arg4);
+
+ case KEYCTL_CHOWN:
+ return keyctl_chown_key((key_serial_t) arg2,
+ (uid_t) arg3,
+ (gid_t) arg4);
+
+ case KEYCTL_SETPERM:
+ return keyctl_setperm_key((key_serial_t) arg2,
+ (key_perm_t) arg3);
+
+ case KEYCTL_INSTANTIATE:
+ return keyctl_instantiate_key((key_serial_t) arg2,
+ (const void __user *) arg3,
+ (size_t) arg4,
+ (key_serial_t) arg5);
+
+ case KEYCTL_NEGATE:
+ return keyctl_negate_key((key_serial_t) arg2,
+ (unsigned) arg3,
+ (key_serial_t) arg4);
+
+ case KEYCTL_SET_REQKEY_KEYRING:
+ return keyctl_set_reqkey_keyring(arg2);
+
+ case KEYCTL_SET_TIMEOUT:
+ return keyctl_set_timeout((key_serial_t) arg2,
+ (unsigned) arg3);
+
+ case KEYCTL_ASSUME_AUTHORITY:
+ return keyctl_assume_authority((key_serial_t) arg2);
+
+ case KEYCTL_GET_SECURITY:
+ return keyctl_get_security((key_serial_t) arg2,
+ (char *) arg3,
+ (size_t) arg4);
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+} /* end sys_keyctl() */
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
new file mode 100644
index 0000000..a9ab8af
--- /dev/null
+++ b/security/keys/keyring.c
@@ -0,0 +1,998 @@
+/* Keyring handling
+ *
+ * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/security.h>
+#include <linux/seq_file.h>
+#include <linux/err.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+/*
+ * when plumbing the depths of the key tree, this sets a hard limit set on how
+ * deep we're willing to go
+ */
+#define KEYRING_SEARCH_MAX_DEPTH 6
+
+/*
+ * we keep all named keyrings in a hash to speed looking them up
+ */
+#define KEYRING_NAME_HASH_SIZE (1 << 5)
+
+static struct list_head keyring_name_hash[KEYRING_NAME_HASH_SIZE];
+static DEFINE_RWLOCK(keyring_name_lock);
+
+static inline unsigned keyring_hash(const char *desc)
+{
+ unsigned bucket = 0;
+
+ for (; *desc; desc++)
+ bucket += (unsigned char) *desc;
+
+ return bucket & (KEYRING_NAME_HASH_SIZE - 1);
+}
+
+/*
+ * the keyring type definition
+ */
+static int keyring_instantiate(struct key *keyring,
+ const void *data, size_t datalen);
+static int keyring_match(const struct key *keyring, const void *criterion);
+static void keyring_revoke(struct key *keyring);
+static void keyring_destroy(struct key *keyring);
+static void keyring_describe(const struct key *keyring, struct seq_file *m);
+static long keyring_read(const struct key *keyring,
+ char __user *buffer, size_t buflen);
+
+struct key_type key_type_keyring = {
+ .name = "keyring",
+ .def_datalen = sizeof(struct keyring_list),
+ .instantiate = keyring_instantiate,
+ .match = keyring_match,
+ .revoke = keyring_revoke,
+ .destroy = keyring_destroy,
+ .describe = keyring_describe,
+ .read = keyring_read,
+};
+
+EXPORT_SYMBOL(key_type_keyring);
+
+/*
+ * semaphore to serialise link/link calls to prevent two link calls in parallel
+ * introducing a cycle
+ */
+static DECLARE_RWSEM(keyring_serialise_link_sem);
+
+/*****************************************************************************/
+/*
+ * publish the name of a keyring so that it can be found by name (if it has
+ * one)
+ */
+static void keyring_publish_name(struct key *keyring)
+{
+ int bucket;
+
+ if (keyring->description) {
+ bucket = keyring_hash(keyring->description);
+
+ write_lock(&keyring_name_lock);
+
+ if (!keyring_name_hash[bucket].next)
+ INIT_LIST_HEAD(&keyring_name_hash[bucket]);
+
+ list_add_tail(&keyring->type_data.link,
+ &keyring_name_hash[bucket]);
+
+ write_unlock(&keyring_name_lock);
+ }
+
+} /* end keyring_publish_name() */
+
+/*****************************************************************************/
+/*
+ * initialise a keyring
+ * - we object if we were given any data
+ */
+static int keyring_instantiate(struct key *keyring,
+ const void *data, size_t datalen)
+{
+ int ret;
+
+ ret = -EINVAL;
+ if (datalen == 0) {
+ /* make the keyring available by name if it has one */
+ keyring_publish_name(keyring);
+ ret = 0;
+ }
+
+ return ret;
+
+} /* end keyring_instantiate() */
+
+/*****************************************************************************/
+/*
+ * match keyrings on their name
+ */
+static int keyring_match(const struct key *keyring, const void *description)
+{
+ return keyring->description &&
+ strcmp(keyring->description, description) == 0;
+
+} /* end keyring_match() */
+
+/*****************************************************************************/
+/*
+ * dispose of the data dangling from the corpse of a keyring
+ */
+static void keyring_destroy(struct key *keyring)
+{
+ struct keyring_list *klist;
+ int loop;
+
+ if (keyring->description) {
+ write_lock(&keyring_name_lock);
+
+ if (keyring->type_data.link.next != NULL &&
+ !list_empty(&keyring->type_data.link))
+ list_del(&keyring->type_data.link);
+
+ write_unlock(&keyring_name_lock);
+ }
+
+ klist = rcu_dereference(keyring->payload.subscriptions);
+ if (klist) {
+ for (loop = klist->nkeys - 1; loop >= 0; loop--)
+ key_put(klist->keys[loop]);
+ kfree(klist);
+ }
+
+} /* end keyring_destroy() */
+
+/*****************************************************************************/
+/*
+ * describe the keyring
+ */
+static void keyring_describe(const struct key *keyring, struct seq_file *m)
+{
+ struct keyring_list *klist;
+
+ if (keyring->description) {
+ seq_puts(m, keyring->description);
+ }
+ else {
+ seq_puts(m, "[anon]");
+ }
+
+ rcu_read_lock();
+ klist = rcu_dereference(keyring->payload.subscriptions);
+ if (klist)
+ seq_printf(m, ": %u/%u", klist->nkeys, klist->maxkeys);
+ else
+ seq_puts(m, ": empty");
+ rcu_read_unlock();
+
+} /* end keyring_describe() */
+
+/*****************************************************************************/
+/*
+ * read a list of key IDs from the keyring's contents
+ * - the keyring's semaphore is read-locked
+ */
+static long keyring_read(const struct key *keyring,
+ char __user *buffer, size_t buflen)
+{
+ struct keyring_list *klist;
+ struct key *key;
+ size_t qty, tmp;
+ int loop, ret;
+
+ ret = 0;
+ klist = rcu_dereference(keyring->payload.subscriptions);
+
+ if (klist) {
+ /* calculate how much data we could return */
+ qty = klist->nkeys * sizeof(key_serial_t);
+
+ if (buffer && buflen > 0) {
+ if (buflen > qty)
+ buflen = qty;
+
+ /* copy the IDs of the subscribed keys into the
+ * buffer */
+ ret = -EFAULT;
+
+ for (loop = 0; loop < klist->nkeys; loop++) {
+ key = klist->keys[loop];
+
+ tmp = sizeof(key_serial_t);
+ if (tmp > buflen)
+ tmp = buflen;
+
+ if (copy_to_user(buffer,
+ &key->serial,
+ tmp) != 0)
+ goto error;
+
+ buflen -= tmp;
+ if (buflen == 0)
+ break;
+ buffer += tmp;
+ }
+ }
+
+ ret = qty;
+ }
+
+ error:
+ return ret;
+
+} /* end keyring_read() */
+
+/*****************************************************************************/
+/*
+ * allocate a keyring and link into the destination keyring
+ */
+struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
+ struct task_struct *ctx, unsigned long flags,
+ struct key *dest)
+{
+ struct key *keyring;
+ int ret;
+
+ keyring = key_alloc(&key_type_keyring, description,
+ uid, gid, ctx,
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
+ flags);
+
+ if (!IS_ERR(keyring)) {
+ ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
+ if (ret < 0) {
+ key_put(keyring);
+ keyring = ERR_PTR(ret);
+ }
+ }
+
+ return keyring;
+
+} /* end keyring_alloc() */
+
+/*****************************************************************************/
+/*
+ * search the supplied keyring tree for a key that matches the criterion
+ * - perform a breadth-then-depth search up to the prescribed limit
+ * - we only find keys on which we have search permission
+ * - we use the supplied match function to see if the description (or other
+ * feature of interest) matches
+ * - we rely on RCU to prevent the keyring lists from disappearing on us
+ * - we return -EAGAIN if we didn't find any matching key
+ * - we return -ENOKEY if we only found negative matching keys
+ * - we propagate the possession attribute from the keyring ref to the key ref
+ */
+key_ref_t keyring_search_aux(key_ref_t keyring_ref,
+ struct task_struct *context,
+ struct key_type *type,
+ const void *description,
+ key_match_func_t match)
+{
+ struct {
+ struct keyring_list *keylist;
+ int kix;
+ } stack[KEYRING_SEARCH_MAX_DEPTH];
+
+ struct keyring_list *keylist;
+ struct timespec now;
+ unsigned long possessed, kflags;
+ struct key *keyring, *key;
+ key_ref_t key_ref;
+ long err;
+ int sp, kix;
+
+ keyring = key_ref_to_ptr(keyring_ref);
+ possessed = is_key_possessed(keyring_ref);
+ key_check(keyring);
+
+ /* top keyring must have search permission to begin the search */
+ err = key_task_permission(keyring_ref, context, KEY_SEARCH);
+ if (err < 0) {
+ key_ref = ERR_PTR(err);
+ goto error;
+ }
+
+ key_ref = ERR_PTR(-ENOTDIR);
+ if (keyring->type != &key_type_keyring)
+ goto error;
+
+ rcu_read_lock();
+
+ now = current_kernel_time();
+ err = -EAGAIN;
+ sp = 0;
+
+ /* firstly we should check to see if this top-level keyring is what we
+ * are looking for */
+ key_ref = ERR_PTR(-EAGAIN);
+ kflags = keyring->flags;
+ if (keyring->type == type && match(keyring, description)) {
+ key = keyring;
+
+ /* check it isn't negative and hasn't expired or been
+ * revoked */
+ if (kflags & (1 << KEY_FLAG_REVOKED))
+ goto error_2;
+ if (key->expiry && now.tv_sec >= key->expiry)
+ goto error_2;
+ key_ref = ERR_PTR(-ENOKEY);
+ if (kflags & (1 << KEY_FLAG_NEGATIVE))
+ goto error_2;
+ goto found;
+ }
+
+ /* otherwise, the top keyring must not be revoked, expired, or
+ * negatively instantiated if we are to search it */
+ key_ref = ERR_PTR(-EAGAIN);
+ if (kflags & ((1 << KEY_FLAG_REVOKED) | (1 << KEY_FLAG_NEGATIVE)) ||
+ (keyring->expiry && now.tv_sec >= keyring->expiry))
+ goto error_2;
+
+ /* start processing a new keyring */
+descend:
+ if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
+ goto not_this_keyring;
+
+ keylist = rcu_dereference(keyring->payload.subscriptions);
+ if (!keylist)
+ goto not_this_keyring;
+
+ /* iterate through the keys in this keyring first */
+ for (kix = 0; kix < keylist->nkeys; kix++) {
+ key = keylist->keys[kix];
+ kflags = key->flags;
+
+ /* ignore keys not of this type */
+ if (key->type != type)
+ continue;
+
+ /* skip revoked keys and expired keys */
+ if (kflags & (1 << KEY_FLAG_REVOKED))
+ continue;
+
+ if (key->expiry && now.tv_sec >= key->expiry)
+ continue;
+
+ /* keys that don't match */
+ if (!match(key, description))
+ continue;
+
+ /* key must have search permissions */
+ if (key_task_permission(make_key_ref(key, possessed),
+ context, KEY_SEARCH) < 0)
+ continue;
+
+ /* we set a different error code if we pass a negative key */
+ if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
+ err = -ENOKEY;
+ continue;
+ }
+
+ goto found;
+ }
+
+ /* search through the keyrings nested in this one */
+ kix = 0;
+ascend:
+ for (; kix < keylist->nkeys; kix++) {
+ key = keylist->keys[kix];
+ if (key->type != &key_type_keyring)
+ continue;
+
+ /* recursively search nested keyrings
+ * - only search keyrings for which we have search permission
+ */
+ if (sp >= KEYRING_SEARCH_MAX_DEPTH)
+ continue;
+
+ if (key_task_permission(make_key_ref(key, possessed),
+ context, KEY_SEARCH) < 0)
+ continue;
+
+ /* stack the current position */
+ stack[sp].keylist = keylist;
+ stack[sp].kix = kix;
+ sp++;
+
+ /* begin again with the new keyring */
+ keyring = key;
+ goto descend;
+ }
+
+ /* the keyring we're looking at was disqualified or didn't contain a
+ * matching key */
+not_this_keyring:
+ if (sp > 0) {
+ /* resume the processing of a keyring higher up in the tree */
+ sp--;
+ keylist = stack[sp].keylist;
+ kix = stack[sp].kix + 1;
+ goto ascend;
+ }
+
+ key_ref = ERR_PTR(err);
+ goto error_2;
+
+ /* we found a viable match */
+found:
+ atomic_inc(&key->usage);
+ key_check(key);
+ key_ref = make_key_ref(key, possessed);
+error_2:
+ rcu_read_unlock();
+error:
+ return key_ref;
+
+} /* end keyring_search_aux() */
+
+/*****************************************************************************/
+/*
+ * search the supplied keyring tree for a key that matches the criterion
+ * - perform a breadth-then-depth search up to the prescribed limit
+ * - we only find keys on which we have search permission
+ * - we readlock the keyrings as we search down the tree
+ * - we return -EAGAIN if we didn't find any matching key
+ * - we return -ENOKEY if we only found negative matching keys
+ */
+key_ref_t keyring_search(key_ref_t keyring,
+ struct key_type *type,
+ const char *description)
+{
+ if (!type->match)
+ return ERR_PTR(-ENOKEY);
+
+ return keyring_search_aux(keyring, current,
+ type, description, type->match);
+
+} /* end keyring_search() */
+
+EXPORT_SYMBOL(keyring_search);
+
+/*****************************************************************************/
+/*
+ * search the given keyring only (no recursion)
+ * - keyring must be locked by caller
+ * - caller must guarantee that the keyring is a keyring
+ */
+key_ref_t __keyring_search_one(key_ref_t keyring_ref,
+ const struct key_type *ktype,
+ const char *description,
+ key_perm_t perm)
+{
+ struct keyring_list *klist;
+ unsigned long possessed;
+ struct key *keyring, *key;
+ int loop;
+
+ keyring = key_ref_to_ptr(keyring_ref);
+ possessed = is_key_possessed(keyring_ref);
+
+ rcu_read_lock();
+
+ klist = rcu_dereference(keyring->payload.subscriptions);
+ if (klist) {
+ for (loop = 0; loop < klist->nkeys; loop++) {
+ key = klist->keys[loop];
+
+ if (key->type == ktype &&
+ (!key->type->match ||
+ key->type->match(key, description)) &&
+ key_permission(make_key_ref(key, possessed),
+ perm) == 0 &&
+ !test_bit(KEY_FLAG_REVOKED, &key->flags)
+ )
+ goto found;
+ }
+ }
+
+ rcu_read_unlock();
+ return ERR_PTR(-ENOKEY);
+
+ found:
+ atomic_inc(&key->usage);
+ rcu_read_unlock();
+ return make_key_ref(key, possessed);
+
+} /* end __keyring_search_one() */
+
+/*****************************************************************************/
+/*
+ * find a keyring with the specified name
+ * - all named keyrings are searched
+ * - normally only finds keyrings with search permission for the current process
+ */
+struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
+{
+ struct key *keyring;
+ int bucket;
+
+ keyring = ERR_PTR(-EINVAL);
+ if (!name)
+ goto error;
+
+ bucket = keyring_hash(name);
+
+ read_lock(&keyring_name_lock);
+
+ if (keyring_name_hash[bucket].next) {
+ /* search this hash bucket for a keyring with a matching name
+ * that's readable and that hasn't been revoked */
+ list_for_each_entry(keyring,
+ &keyring_name_hash[bucket],
+ type_data.link
+ ) {
+ if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
+ continue;
+
+ if (strcmp(keyring->description, name) != 0)
+ continue;
+
+ if (!skip_perm_check &&
+ key_permission(make_key_ref(keyring, 0),
+ KEY_SEARCH) < 0)
+ continue;
+
+ /* we've got a match */
+ atomic_inc(&keyring->usage);
+ read_unlock(&keyring_name_lock);
+ goto error;
+ }
+ }
+
+ read_unlock(&keyring_name_lock);
+ keyring = ERR_PTR(-ENOKEY);
+
+ error:
+ return keyring;
+
+} /* end find_keyring_by_name() */
+
+/*****************************************************************************/
+/*
+ * see if a cycle will will be created by inserting acyclic tree B in acyclic
+ * tree A at the topmost level (ie: as a direct child of A)
+ * - since we are adding B to A at the top level, checking for cycles should
+ * just be a matter of seeing if node A is somewhere in tree B
+ */
+static int keyring_detect_cycle(struct key *A, struct key *B)
+{
+ struct {
+ struct keyring_list *keylist;
+ int kix;
+ } stack[KEYRING_SEARCH_MAX_DEPTH];
+
+ struct keyring_list *keylist;
+ struct key *subtree, *key;
+ int sp, kix, ret;
+
+ rcu_read_lock();
+
+ ret = -EDEADLK;
+ if (A == B)
+ goto cycle_detected;
+
+ subtree = B;
+ sp = 0;
+
+ /* start processing a new keyring */
+ descend:
+ if (test_bit(KEY_FLAG_REVOKED, &subtree->flags))
+ goto not_this_keyring;
+
+ keylist = rcu_dereference(subtree->payload.subscriptions);
+ if (!keylist)
+ goto not_this_keyring;
+ kix = 0;
+
+ ascend:
+ /* iterate through the remaining keys in this keyring */
+ for (; kix < keylist->nkeys; kix++) {
+ key = keylist->keys[kix];
+
+ if (key == A)
+ goto cycle_detected;
+
+ /* recursively check nested keyrings */
+ if (key->type == &key_type_keyring) {
+ if (sp >= KEYRING_SEARCH_MAX_DEPTH)
+ goto too_deep;
+
+ /* stack the current position */
+ stack[sp].keylist = keylist;
+ stack[sp].kix = kix;
+ sp++;
+
+ /* begin again with the new keyring */
+ subtree = key;
+ goto descend;
+ }
+ }
+
+ /* the keyring we're looking at was disqualified or didn't contain a
+ * matching key */
+ not_this_keyring:
+ if (sp > 0) {
+ /* resume the checking of a keyring higher up in the tree */
+ sp--;
+ keylist = stack[sp].keylist;
+ kix = stack[sp].kix + 1;
+ goto ascend;
+ }
+
+ ret = 0; /* no cycles detected */
+
+ error:
+ rcu_read_unlock();
+ return ret;
+
+ too_deep:
+ ret = -ELOOP;
+ goto error;
+
+ cycle_detected:
+ ret = -EDEADLK;
+ goto error;
+
+} /* end keyring_detect_cycle() */
+
+/*****************************************************************************/
+/*
+ * dispose of a keyring list after the RCU grace period
+ */
+static void keyring_link_rcu_disposal(struct rcu_head *rcu)
+{
+ struct keyring_list *klist =
+ container_of(rcu, struct keyring_list, rcu);
+
+ kfree(klist);
+
+} /* end keyring_link_rcu_disposal() */
+
+/*****************************************************************************/
+/*
+ * dispose of a keyring list after the RCU grace period, freeing the unlinked
+ * key
+ */
+static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
+{
+ struct keyring_list *klist =
+ container_of(rcu, struct keyring_list, rcu);
+
+ key_put(klist->keys[klist->delkey]);
+ kfree(klist);
+
+} /* end keyring_unlink_rcu_disposal() */
+
+/*****************************************************************************/
+/*
+ * link a key into to a keyring
+ * - must be called with the keyring's semaphore write-locked
+ * - discard already extant link to matching key if there is one
+ */
+int __key_link(struct key *keyring, struct key *key)
+{
+ struct keyring_list *klist, *nklist;
+ unsigned max;
+ size_t size;
+ int loop, ret;
+
+ ret = -EKEYREVOKED;
+ if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
+ goto error;
+
+ ret = -ENOTDIR;
+ if (keyring->type != &key_type_keyring)
+ goto error;
+
+ /* serialise link/link calls to prevent parallel calls causing a
+ * cycle when applied to two keyring in opposite orders */
+ down_write(&keyring_serialise_link_sem);
+
+ /* check that we aren't going to create a cycle adding one keyring to
+ * another */
+ if (key->type == &key_type_keyring) {
+ ret = keyring_detect_cycle(keyring, key);
+ if (ret < 0)
+ goto error2;
+ }
+
+ /* see if there's a matching key we can displace */
+ klist = keyring->payload.subscriptions;
+
+ if (klist && klist->nkeys > 0) {
+ struct key_type *type = key->type;
+
+ for (loop = klist->nkeys - 1; loop >= 0; loop--) {
+ if (klist->keys[loop]->type == type &&
+ strcmp(klist->keys[loop]->description,
+ key->description) == 0
+ ) {
+ /* found a match - replace with new key */
+ size = sizeof(struct key *) * klist->maxkeys;
+ size += sizeof(*klist);
+ BUG_ON(size > PAGE_SIZE);
+
+ ret = -ENOMEM;
+ nklist = kmemdup(klist, size, GFP_KERNEL);
+ if (!nklist)
+ goto error2;
+
+ /* replace matched key */
+ atomic_inc(&key->usage);
+ nklist->keys[loop] = key;
+
+ rcu_assign_pointer(
+ keyring->payload.subscriptions,
+ nklist);
+
+ /* dispose of the old keyring list and the
+ * displaced key */
+ klist->delkey = loop;
+ call_rcu(&klist->rcu,
+ keyring_unlink_rcu_disposal);
+
+ goto done;
+ }
+ }
+ }
+
+ /* check that we aren't going to overrun the user's quota */
+ ret = key_payload_reserve(keyring,
+ keyring->datalen + KEYQUOTA_LINK_BYTES);
+ if (ret < 0)
+ goto error2;
+
+ klist = keyring->payload.subscriptions;
+
+ if (klist && klist->nkeys < klist->maxkeys) {
+ /* there's sufficient slack space to add directly */
+ atomic_inc(&key->usage);
+
+ klist->keys[klist->nkeys] = key;
+ smp_wmb();
+ klist->nkeys++;
+ smp_wmb();
+ }
+ else {
+ /* grow the key list */
+ max = 4;
+ if (klist)
+ max += klist->maxkeys;
+
+ ret = -ENFILE;
+ if (max > 65535)
+ goto error3;
+ size = sizeof(*klist) + sizeof(struct key *) * max;
+ if (size > PAGE_SIZE)
+ goto error3;
+
+ ret = -ENOMEM;
+ nklist = kmalloc(size, GFP_KERNEL);
+ if (!nklist)
+ goto error3;
+ nklist->maxkeys = max;
+ nklist->nkeys = 0;
+
+ if (klist) {
+ nklist->nkeys = klist->nkeys;
+ memcpy(nklist->keys,
+ klist->keys,
+ sizeof(struct key *) * klist->nkeys);
+ }
+
+ /* add the key into the new space */
+ atomic_inc(&key->usage);
+ nklist->keys[nklist->nkeys++] = key;
+
+ rcu_assign_pointer(keyring->payload.subscriptions, nklist);
+
+ /* dispose of the old keyring list */
+ if (klist)
+ call_rcu(&klist->rcu, keyring_link_rcu_disposal);
+ }
+
+done:
+ ret = 0;
+error2:
+ up_write(&keyring_serialise_link_sem);
+error:
+ return ret;
+
+error3:
+ /* undo the quota changes */
+ key_payload_reserve(keyring,
+ keyring->datalen - KEYQUOTA_LINK_BYTES);
+ goto error2;
+
+} /* end __key_link() */
+
+/*****************************************************************************/
+/*
+ * link a key to a keyring
+ */
+int key_link(struct key *keyring, struct key *key)
+{
+ int ret;
+
+ key_check(keyring);
+ key_check(key);
+
+ down_write(&keyring->sem);
+ ret = __key_link(keyring, key);
+ up_write(&keyring->sem);
+
+ return ret;
+
+} /* end key_link() */
+
+EXPORT_SYMBOL(key_link);
+
+/*****************************************************************************/
+/*
+ * unlink the first link to a key from a keyring
+ */
+int key_unlink(struct key *keyring, struct key *key)
+{
+ struct keyring_list *klist, *nklist;
+ int loop, ret;
+
+ key_check(keyring);
+ key_check(key);
+
+ ret = -ENOTDIR;
+ if (keyring->type != &key_type_keyring)
+ goto error;
+
+ down_write(&keyring->sem);
+
+ klist = keyring->payload.subscriptions;
+ if (klist) {
+ /* search the keyring for the key */
+ for (loop = 0; loop < klist->nkeys; loop++)
+ if (klist->keys[loop] == key)
+ goto key_is_present;
+ }
+
+ up_write(&keyring->sem);
+ ret = -ENOENT;
+ goto error;
+
+key_is_present:
+ /* we need to copy the key list for RCU purposes */
+ nklist = kmalloc(sizeof(*klist) +
+ sizeof(struct key *) * klist->maxkeys,
+ GFP_KERNEL);
+ if (!nklist)
+ goto nomem;
+ nklist->maxkeys = klist->maxkeys;
+ nklist->nkeys = klist->nkeys - 1;
+
+ if (loop > 0)
+ memcpy(&nklist->keys[0],
+ &klist->keys[0],
+ loop * sizeof(struct key *));
+
+ if (loop < nklist->nkeys)
+ memcpy(&nklist->keys[loop],
+ &klist->keys[loop + 1],
+ (nklist->nkeys - loop) * sizeof(struct key *));
+
+ /* adjust the user's quota */
+ key_payload_reserve(keyring,
+ keyring->datalen - KEYQUOTA_LINK_BYTES);
+
+ rcu_assign_pointer(keyring->payload.subscriptions, nklist);
+
+ up_write(&keyring->sem);
+
+ /* schedule for later cleanup */
+ klist->delkey = loop;
+ call_rcu(&klist->rcu, keyring_unlink_rcu_disposal);
+
+ ret = 0;
+
+error:
+ return ret;
+nomem:
+ ret = -ENOMEM;
+ up_write(&keyring->sem);
+ goto error;
+
+} /* end key_unlink() */
+
+EXPORT_SYMBOL(key_unlink);
+
+/*****************************************************************************/
+/*
+ * dispose of a keyring list after the RCU grace period, releasing the keys it
+ * links to
+ */
+static void keyring_clear_rcu_disposal(struct rcu_head *rcu)
+{
+ struct keyring_list *klist;
+ int loop;
+
+ klist = container_of(rcu, struct keyring_list, rcu);
+
+ for (loop = klist->nkeys - 1; loop >= 0; loop--)
+ key_put(klist->keys[loop]);
+
+ kfree(klist);
+
+} /* end keyring_clear_rcu_disposal() */
+
+/*****************************************************************************/
+/*
+ * clear the specified process keyring
+ * - implements keyctl(KEYCTL_CLEAR)
+ */
+int keyring_clear(struct key *keyring)
+{
+ struct keyring_list *klist;
+ int ret;
+
+ ret = -ENOTDIR;
+ if (keyring->type == &key_type_keyring) {
+ /* detach the pointer block with the locks held */
+ down_write(&keyring->sem);
+
+ klist = keyring->payload.subscriptions;
+ if (klist) {
+ /* adjust the quota */
+ key_payload_reserve(keyring,
+ sizeof(struct keyring_list));
+
+ rcu_assign_pointer(keyring->payload.subscriptions,
+ NULL);
+ }
+
+ up_write(&keyring->sem);
+
+ /* free the keys after the locks have been dropped */
+ if (klist)
+ call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
+
+ ret = 0;
+ }
+
+ return ret;
+
+} /* end keyring_clear() */
+
+EXPORT_SYMBOL(keyring_clear);
+
+/*****************************************************************************/
+/*
+ * dispose of the links from a revoked keyring
+ * - called with the key sem write-locked
+ */
+static void keyring_revoke(struct key *keyring)
+{
+ struct keyring_list *klist = keyring->payload.subscriptions;
+
+ /* adjust the quota */
+ key_payload_reserve(keyring, 0);
+
+ if (klist) {
+ rcu_assign_pointer(keyring->payload.subscriptions, NULL);
+ call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
+ }
+
+} /* end keyring_revoke() */
diff --git a/security/keys/permission.c b/security/keys/permission.c
new file mode 100644
index 0000000..3b41f9b
--- /dev/null
+++ b/security/keys/permission.c
@@ -0,0 +1,107 @@
+/* permission.c: key permission determination
+ *
+ * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/security.h>
+#include "internal.h"
+
+/*****************************************************************************/
+/*
+ * check to see whether permission is granted to use a key in the desired way,
+ * but permit the security modules to override
+ */
+int key_task_permission(const key_ref_t key_ref,
+ struct task_struct *context,
+ key_perm_t perm)
+{
+ struct key *key;
+ key_perm_t kperm;
+ int ret;
+
+ key = key_ref_to_ptr(key_ref);
+
+ /* use the second 8-bits of permissions for keys the caller owns */
+ if (key->uid == context->fsuid) {
+ kperm = key->perm >> 16;
+ goto use_these_perms;
+ }
+
+ /* use the third 8-bits of permissions for keys the caller has a group
+ * membership in common with */
+ if (key->gid != -1 && key->perm & KEY_GRP_ALL) {
+ if (key->gid == context->fsgid) {
+ kperm = key->perm >> 8;
+ goto use_these_perms;
+ }
+
+ task_lock(context);
+ ret = groups_search(context->group_info, key->gid);
+ task_unlock(context);
+
+ if (ret) {
+ kperm = key->perm >> 8;
+ goto use_these_perms;
+ }
+ }
+
+ /* otherwise use the least-significant 8-bits */
+ kperm = key->perm;
+
+use_these_perms:
+ /* use the top 8-bits of permissions for keys the caller possesses
+ * - possessor permissions are additive with other permissions
+ */
+ if (is_key_possessed(key_ref))
+ kperm |= key->perm >> 24;
+
+ kperm = kperm & perm & KEY_ALL;
+
+ if (kperm != perm)
+ return -EACCES;
+
+ /* let LSM be the final arbiter */
+ return security_key_permission(key_ref, context, perm);
+
+} /* end key_task_permission() */
+
+EXPORT_SYMBOL(key_task_permission);
+
+/*****************************************************************************/
+/*
+ * validate a key
+ */
+int key_validate(struct key *key)
+{
+ struct timespec now;
+ int ret = 0;
+
+ if (key) {
+ /* check it's still accessible */
+ ret = -EKEYREVOKED;
+ if (test_bit(KEY_FLAG_REVOKED, &key->flags) ||
+ test_bit(KEY_FLAG_DEAD, &key->flags))
+ goto error;
+
+ /* check it hasn't expired */
+ ret = 0;
+ if (key->expiry) {
+ now = current_kernel_time();
+ if (now.tv_sec >= key->expiry)
+ ret = -EKEYEXPIRED;
+ }
+ }
+
+ error:
+ return ret;
+
+} /* end key_validate() */
+
+EXPORT_SYMBOL(key_validate);
diff --git a/security/keys/proc.c b/security/keys/proc.c
new file mode 100644
index 0000000..f619170
--- /dev/null
+++ b/security/keys/proc.c
@@ -0,0 +1,262 @@
+/* proc.c: proc files for key database enumeration
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <asm/errno.h>
+#include "internal.h"
+
+#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
+static int proc_keys_open(struct inode *inode, struct file *file);
+static void *proc_keys_start(struct seq_file *p, loff_t *_pos);
+static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos);
+static void proc_keys_stop(struct seq_file *p, void *v);
+static int proc_keys_show(struct seq_file *m, void *v);
+
+static const struct seq_operations proc_keys_ops = {
+ .start = proc_keys_start,
+ .next = proc_keys_next,
+ .stop = proc_keys_stop,
+ .show = proc_keys_show,
+};
+
+static const struct file_operations proc_keys_fops = {
+ .open = proc_keys_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+#endif
+
+static int proc_key_users_open(struct inode *inode, struct file *file);
+static void *proc_key_users_start(struct seq_file *p, loff_t *_pos);
+static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos);
+static void proc_key_users_stop(struct seq_file *p, void *v);
+static int proc_key_users_show(struct seq_file *m, void *v);
+
+static const struct seq_operations proc_key_users_ops = {
+ .start = proc_key_users_start,
+ .next = proc_key_users_next,
+ .stop = proc_key_users_stop,
+ .show = proc_key_users_show,
+};
+
+static const struct file_operations proc_key_users_fops = {
+ .open = proc_key_users_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+/*****************************************************************************/
+/*
+ * declare the /proc files
+ */
+static int __init key_proc_init(void)
+{
+ struct proc_dir_entry *p;
+
+#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
+ p = proc_create("keys", 0, NULL, &proc_keys_fops);
+ if (!p)
+ panic("Cannot create /proc/keys\n");
+#endif
+
+ p = proc_create("key-users", 0, NULL, &proc_key_users_fops);
+ if (!p)
+ panic("Cannot create /proc/key-users\n");
+
+ return 0;
+
+} /* end key_proc_init() */
+
+__initcall(key_proc_init);
+
+/*****************************************************************************/
+/*
+ * implement "/proc/keys" to provides a list of the keys on the system
+ */
+#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
+
+static int proc_keys_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &proc_keys_ops);
+
+}
+
+static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
+{
+ struct rb_node *_p;
+ loff_t pos = *_pos;
+
+ spin_lock(&key_serial_lock);
+
+ _p = rb_first(&key_serial_tree);
+ while (pos > 0 && _p) {
+ pos--;
+ _p = rb_next(_p);
+ }
+
+ return _p;
+
+}
+
+static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
+{
+ (*_pos)++;
+ return rb_next((struct rb_node *) v);
+
+}
+
+static void proc_keys_stop(struct seq_file *p, void *v)
+{
+ spin_unlock(&key_serial_lock);
+}
+
+static int proc_keys_show(struct seq_file *m, void *v)
+{
+ struct rb_node *_p = v;
+ struct key *key = rb_entry(_p, struct key, serial_node);
+ struct timespec now;
+ unsigned long timo;
+ char xbuf[12];
+ int rc;
+
+ /* check whether the current task is allowed to view the key (assuming
+ * non-possession) */
+ rc = key_task_permission(make_key_ref(key, 0), current, KEY_VIEW);
+ if (rc < 0)
+ return 0;
+
+ now = current_kernel_time();
+
+ rcu_read_lock();
+
+ /* come up with a suitable timeout value */
+ if (key->expiry == 0) {
+ memcpy(xbuf, "perm", 5);
+ }
+ else if (now.tv_sec >= key->expiry) {
+ memcpy(xbuf, "expd", 5);
+ }
+ else {
+ timo = key->expiry - now.tv_sec;
+
+ if (timo < 60)
+ sprintf(xbuf, "%lus", timo);
+ else if (timo < 60*60)
+ sprintf(xbuf, "%lum", timo / 60);
+ else if (timo < 60*60*24)
+ sprintf(xbuf, "%luh", timo / (60*60));
+ else if (timo < 60*60*24*7)
+ sprintf(xbuf, "%lud", timo / (60*60*24));
+ else
+ sprintf(xbuf, "%luw", timo / (60*60*24*7));
+ }
+
+#define showflag(KEY, LETTER, FLAG) \
+ (test_bit(FLAG, &(KEY)->flags) ? LETTER : '-')
+
+ seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",
+ key->serial,
+ showflag(key, 'I', KEY_FLAG_INSTANTIATED),
+ showflag(key, 'R', KEY_FLAG_REVOKED),
+ showflag(key, 'D', KEY_FLAG_DEAD),
+ showflag(key, 'Q', KEY_FLAG_IN_QUOTA),
+ showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
+ showflag(key, 'N', KEY_FLAG_NEGATIVE),
+ atomic_read(&key->usage),
+ xbuf,
+ key->perm,
+ key->uid,
+ key->gid,
+ key->type->name);
+
+#undef showflag
+
+ if (key->type->describe)
+ key->type->describe(key, m);
+ seq_putc(m, '\n');
+
+ rcu_read_unlock();
+
+ return 0;
+
+}
+
+#endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */
+
+/*****************************************************************************/
+/*
+ * implement "/proc/key-users" to provides a list of the key users
+ */
+static int proc_key_users_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &proc_key_users_ops);
+
+}
+
+static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
+{
+ struct rb_node *_p;
+ loff_t pos = *_pos;
+
+ spin_lock(&key_user_lock);
+
+ _p = rb_first(&key_user_tree);
+ while (pos > 0 && _p) {
+ pos--;
+ _p = rb_next(_p);
+ }
+
+ return _p;
+
+}
+
+static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
+{
+ (*_pos)++;
+ return rb_next((struct rb_node *) v);
+
+}
+
+static void proc_key_users_stop(struct seq_file *p, void *v)
+{
+ spin_unlock(&key_user_lock);
+}
+
+static int proc_key_users_show(struct seq_file *m, void *v)
+{
+ struct rb_node *_p = v;
+ struct key_user *user = rb_entry(_p, struct key_user, node);
+ unsigned maxkeys = (user->uid == 0) ?
+ key_quota_root_maxkeys : key_quota_maxkeys;
+ unsigned maxbytes = (user->uid == 0) ?
+ key_quota_root_maxbytes : key_quota_maxbytes;
+
+ seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n",
+ user->uid,
+ atomic_read(&user->usage),
+ atomic_read(&user->nkeys),
+ atomic_read(&user->nikeys),
+ user->qnkeys,
+ maxkeys,
+ user->qnbytes,
+ maxbytes);
+
+ return 0;
+
+}
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
new file mode 100644
index 0000000..45b240a
--- /dev/null
+++ b/security/keys/process_keys.c
@@ -0,0 +1,799 @@
+/* Management of a process's keyrings
+ *
+ * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/keyctl.h>
+#include <linux/fs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+/* session keyring create vs join semaphore */
+static DEFINE_MUTEX(key_session_mutex);
+
+/* user keyring creation semaphore */
+static DEFINE_MUTEX(key_user_keyring_mutex);
+
+/* the root user's tracking struct */
+struct key_user root_key_user = {
+ .usage = ATOMIC_INIT(3),
+ .cons_lock = __MUTEX_INITIALIZER(root_key_user.cons_lock),
+ .lock = __SPIN_LOCK_UNLOCKED(root_key_user.lock),
+ .nkeys = ATOMIC_INIT(2),
+ .nikeys = ATOMIC_INIT(2),
+ .uid = 0,
+};
+
+/*****************************************************************************/
+/*
+ * install user and user session keyrings for a particular UID
+ */
+int install_user_keyrings(struct task_struct *tsk)
+{
+ struct user_struct *user = tsk->user;
+ struct key *uid_keyring, *session_keyring;
+ char buf[20];
+ int ret;
+
+ kenter("%p{%u}", user, user->uid);
+
+ if (user->uid_keyring) {
+ kleave(" = 0 [exist]");
+ return 0;
+ }
+
+ mutex_lock(&key_user_keyring_mutex);
+ ret = 0;
+
+ if (!user->uid_keyring) {
+ /* get the UID-specific keyring
+ * - there may be one in existence already as it may have been
+ * pinned by a session, but the user_struct pointing to it
+ * may have been destroyed by setuid */
+ sprintf(buf, "_uid.%u", user->uid);
+
+ uid_keyring = find_keyring_by_name(buf, true);
+ if (IS_ERR(uid_keyring)) {
+ uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1,
+ tsk, KEY_ALLOC_IN_QUOTA,
+ NULL);
+ if (IS_ERR(uid_keyring)) {
+ ret = PTR_ERR(uid_keyring);
+ goto error;
+ }
+ }
+
+ /* get a default session keyring (which might also exist
+ * already) */
+ sprintf(buf, "_uid_ses.%u", user->uid);
+
+ session_keyring = find_keyring_by_name(buf, true);
+ if (IS_ERR(session_keyring)) {
+ session_keyring =
+ keyring_alloc(buf, user->uid, (gid_t) -1,
+ tsk, KEY_ALLOC_IN_QUOTA, NULL);
+ if (IS_ERR(session_keyring)) {
+ ret = PTR_ERR(session_keyring);
+ goto error_release;
+ }
+
+ /* we install a link from the user session keyring to
+ * the user keyring */
+ ret = key_link(session_keyring, uid_keyring);
+ if (ret < 0)
+ goto error_release_both;
+ }
+
+ /* install the keyrings */
+ user->uid_keyring = uid_keyring;
+ user->session_keyring = session_keyring;
+ }
+
+ mutex_unlock(&key_user_keyring_mutex);
+ kleave(" = 0");
+ return 0;
+
+error_release_both:
+ key_put(session_keyring);
+error_release:
+ key_put(uid_keyring);
+error:
+ mutex_unlock(&key_user_keyring_mutex);
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*****************************************************************************/
+/*
+ * deal with the UID changing
+ */
+void switch_uid_keyring(struct user_struct *new_user)
+{
+#if 0 /* do nothing for now */
+ struct key *old;
+
+ /* switch to the new user's session keyring if we were running under
+ * root's default session keyring */
+ if (new_user->uid != 0 &&
+ current->session_keyring == &root_session_keyring
+ ) {
+ atomic_inc(&new_user->session_keyring->usage);
+
+ task_lock(current);
+ old = current->session_keyring;
+ current->session_keyring = new_user->session_keyring;
+ task_unlock(current);
+
+ key_put(old);
+ }
+#endif
+
+} /* end switch_uid_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a fresh thread keyring, discarding the old one
+ */
+int install_thread_keyring(struct task_struct *tsk)
+{
+ struct key *keyring, *old;
+ char buf[20];
+ int ret;
+
+ sprintf(buf, "_tid.%u", tsk->pid);
+
+ keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
+ KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ if (IS_ERR(keyring)) {
+ ret = PTR_ERR(keyring);
+ goto error;
+ }
+
+ task_lock(tsk);
+ old = tsk->thread_keyring;
+ tsk->thread_keyring = keyring;
+ task_unlock(tsk);
+
+ ret = 0;
+
+ key_put(old);
+error:
+ return ret;
+
+} /* end install_thread_keyring() */
+
+/*****************************************************************************/
+/*
+ * make sure a process keyring is installed
+ */
+int install_process_keyring(struct task_struct *tsk)
+{
+ struct key *keyring;
+ char buf[20];
+ int ret;
+
+ might_sleep();
+
+ if (!tsk->signal->process_keyring) {
+ sprintf(buf, "_pid.%u", tsk->tgid);
+
+ keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
+ KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ if (IS_ERR(keyring)) {
+ ret = PTR_ERR(keyring);
+ goto error;
+ }
+
+ /* attach keyring */
+ spin_lock_irq(&tsk->sighand->siglock);
+ if (!tsk->signal->process_keyring) {
+ tsk->signal->process_keyring = keyring;
+ keyring = NULL;
+ }
+ spin_unlock_irq(&tsk->sighand->siglock);
+
+ key_put(keyring);
+ }
+
+ ret = 0;
+error:
+ return ret;
+
+} /* end install_process_keyring() */
+
+/*****************************************************************************/
+/*
+ * install a session keyring, discarding the old one
+ * - if a keyring is not supplied, an empty one is invented
+ */
+static int install_session_keyring(struct task_struct *tsk,
+ struct key *keyring)
+{
+ unsigned long flags;
+ struct key *old;
+ char buf[20];
+
+ might_sleep();
+
+ /* create an empty session keyring */
+ if (!keyring) {
+ sprintf(buf, "_ses.%u", tsk->tgid);
+
+ flags = KEY_ALLOC_QUOTA_OVERRUN;
+ if (tsk->signal->session_keyring)
+ flags = KEY_ALLOC_IN_QUOTA;
+
+ keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk,
+ flags, NULL);
+ if (IS_ERR(keyring))
+ return PTR_ERR(keyring);
+ }
+ else {
+ atomic_inc(&keyring->usage);
+ }
+
+ /* install the keyring */
+ spin_lock_irq(&tsk->sighand->siglock);
+ old = tsk->signal->session_keyring;
+ rcu_assign_pointer(tsk->signal->session_keyring, keyring);
+ spin_unlock_irq(&tsk->sighand->siglock);
+
+ /* we're using RCU on the pointer, but there's no point synchronising
+ * on it if it didn't previously point to anything */
+ if (old) {
+ synchronize_rcu();
+ key_put(old);
+ }
+
+ return 0;
+
+} /* end install_session_keyring() */
+
+/*****************************************************************************/
+/*
+ * copy the keys in a thread group for fork without CLONE_THREAD
+ */
+int copy_thread_group_keys(struct task_struct *tsk)
+{
+ key_check(current->thread_group->session_keyring);
+ key_check(current->thread_group->process_keyring);
+
+ /* no process keyring yet */
+ tsk->signal->process_keyring = NULL;
+
+ /* same session keyring */
+ rcu_read_lock();
+ tsk->signal->session_keyring =
+ key_get(rcu_dereference(current->signal->session_keyring));
+ rcu_read_unlock();
+
+ return 0;
+
+} /* end copy_thread_group_keys() */
+
+/*****************************************************************************/
+/*
+ * copy the keys for fork
+ */
+int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
+{
+ key_check(tsk->thread_keyring);
+ key_check(tsk->request_key_auth);
+
+ /* no thread keyring yet */
+ tsk->thread_keyring = NULL;
+
+ /* copy the request_key() authorisation for this thread */
+ key_get(tsk->request_key_auth);
+
+ return 0;
+
+} /* end copy_keys() */
+
+/*****************************************************************************/
+/*
+ * dispose of thread group keys upon thread group destruction
+ */
+void exit_thread_group_keys(struct signal_struct *tg)
+{
+ key_put(tg->session_keyring);
+ key_put(tg->process_keyring);
+
+} /* end exit_thread_group_keys() */
+
+/*****************************************************************************/
+/*
+ * dispose of per-thread keys upon thread exit
+ */
+void exit_keys(struct task_struct *tsk)
+{
+ key_put(tsk->thread_keyring);
+ key_put(tsk->request_key_auth);
+
+} /* end exit_keys() */
+
+/*****************************************************************************/
+/*
+ * deal with execve()
+ */
+int exec_keys(struct task_struct *tsk)
+{
+ struct key *old;
+
+ /* newly exec'd tasks don't get a thread keyring */
+ task_lock(tsk);
+ old = tsk->thread_keyring;
+ tsk->thread_keyring = NULL;
+ task_unlock(tsk);
+
+ key_put(old);
+
+ /* discard the process keyring from a newly exec'd task */
+ spin_lock_irq(&tsk->sighand->siglock);
+ old = tsk->signal->process_keyring;
+ tsk->signal->process_keyring = NULL;
+ spin_unlock_irq(&tsk->sighand->siglock);
+
+ key_put(old);
+
+ return 0;
+
+} /* end exec_keys() */
+
+/*****************************************************************************/
+/*
+ * deal with SUID programs
+ * - we might want to make this invent a new session keyring
+ */
+int suid_keys(struct task_struct *tsk)
+{
+ return 0;
+
+} /* end suid_keys() */
+
+/*****************************************************************************/
+/*
+ * the filesystem user ID changed
+ */
+void key_fsuid_changed(struct task_struct *tsk)
+{
+ /* update the ownership of the thread keyring */
+ if (tsk->thread_keyring) {
+ down_write(&tsk->thread_keyring->sem);
+ tsk->thread_keyring->uid = tsk->fsuid;
+ up_write(&tsk->thread_keyring->sem);
+ }
+
+} /* end key_fsuid_changed() */
+
+/*****************************************************************************/
+/*
+ * the filesystem group ID changed
+ */
+void key_fsgid_changed(struct task_struct *tsk)
+{
+ /* update the ownership of the thread keyring */
+ if (tsk->thread_keyring) {
+ down_write(&tsk->thread_keyring->sem);
+ tsk->thread_keyring->gid = tsk->fsgid;
+ up_write(&tsk->thread_keyring->sem);
+ }
+
+} /* end key_fsgid_changed() */
+
+/*****************************************************************************/
+/*
+ * search the process keyrings for the first matching key
+ * - we use the supplied match function to see if the description (or other
+ * feature of interest) matches
+ * - we return -EAGAIN if we didn't find any matching key
+ * - we return -ENOKEY if we found only negative matching keys
+ */
+key_ref_t search_process_keyrings(struct key_type *type,
+ const void *description,
+ key_match_func_t match,
+ struct task_struct *context)
+{
+ struct request_key_auth *rka;
+ key_ref_t key_ref, ret, err;
+
+ might_sleep();
+
+ /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
+ * searchable, but we failed to find a key or we found a negative key;
+ * otherwise we want to return a sample error (probably -EACCES) if
+ * none of the keyrings were searchable
+ *
+ * in terms of priority: success > -ENOKEY > -EAGAIN > other error
+ */
+ key_ref = NULL;
+ ret = NULL;
+ err = ERR_PTR(-EAGAIN);
+
+ /* search the thread keyring first */
+ if (context->thread_keyring) {
+ key_ref = keyring_search_aux(
+ make_key_ref(context->thread_keyring, 1),
+ context, type, description, match);
+ if (!IS_ERR(key_ref))
+ goto found;
+
+ switch (PTR_ERR(key_ref)) {
+ case -EAGAIN: /* no key */
+ if (ret)
+ break;
+ case -ENOKEY: /* negative key */
+ ret = key_ref;
+ break;
+ default:
+ err = key_ref;
+ break;
+ }
+ }
+
+ /* search the process keyring second */
+ if (context->signal->process_keyring) {
+ key_ref = keyring_search_aux(
+ make_key_ref(context->signal->process_keyring, 1),
+ context, type, description, match);
+ if (!IS_ERR(key_ref))
+ goto found;
+
+ switch (PTR_ERR(key_ref)) {
+ case -EAGAIN: /* no key */
+ if (ret)
+ break;
+ case -ENOKEY: /* negative key */
+ ret = key_ref;
+ break;
+ default:
+ err = key_ref;
+ break;
+ }
+ }
+
+ /* search the session keyring */
+ if (context->signal->session_keyring) {
+ rcu_read_lock();
+ key_ref = keyring_search_aux(
+ make_key_ref(rcu_dereference(
+ context->signal->session_keyring),
+ 1),
+ context, type, description, match);
+ rcu_read_unlock();
+
+ if (!IS_ERR(key_ref))
+ goto found;
+
+ switch (PTR_ERR(key_ref)) {
+ case -EAGAIN: /* no key */
+ if (ret)
+ break;
+ case -ENOKEY: /* negative key */
+ ret = key_ref;
+ break;
+ default:
+ err = key_ref;
+ break;
+ }
+ }
+ /* or search the user-session keyring */
+ else if (context->user->session_keyring) {
+ key_ref = keyring_search_aux(
+ make_key_ref(context->user->session_keyring, 1),
+ context, type, description, match);
+ if (!IS_ERR(key_ref))
+ goto found;
+
+ switch (PTR_ERR(key_ref)) {
+ case -EAGAIN: /* no key */
+ if (ret)
+ break;
+ case -ENOKEY: /* negative key */
+ ret = key_ref;
+ break;
+ default:
+ err = key_ref;
+ break;
+ }
+ }
+
+ /* if this process has an instantiation authorisation key, then we also
+ * search the keyrings of the process mentioned there
+ * - we don't permit access to request_key auth keys via this method
+ */
+ if (context->request_key_auth &&
+ context == current &&
+ type != &key_type_request_key_auth
+ ) {
+ /* defend against the auth key being revoked */
+ down_read(&context->request_key_auth->sem);
+
+ if (key_validate(context->request_key_auth) == 0) {
+ rka = context->request_key_auth->payload.data;
+
+ key_ref = search_process_keyrings(type, description,
+ match, rka->context);
+
+ up_read(&context->request_key_auth->sem);
+
+ if (!IS_ERR(key_ref))
+ goto found;
+
+ switch (PTR_ERR(key_ref)) {
+ case -EAGAIN: /* no key */
+ if (ret)
+ break;
+ case -ENOKEY: /* negative key */
+ ret = key_ref;
+ break;
+ default:
+ err = key_ref;
+ break;
+ }
+ } else {
+ up_read(&context->request_key_auth->sem);
+ }
+ }
+
+ /* no key - decide on the error we're going to go for */
+ key_ref = ret ? ret : err;
+
+found:
+ return key_ref;
+
+} /* end search_process_keyrings() */
+
+/*****************************************************************************/
+/*
+ * see if the key we're looking at is the target key
+ */
+static int lookup_user_key_possessed(const struct key *key, const void *target)
+{
+ return key == target;
+
+} /* end lookup_user_key_possessed() */
+
+/*****************************************************************************/
+/*
+ * lookup a key given a key ID from userspace with a given permissions mask
+ * - don't create special keyrings unless so requested
+ * - partially constructed keys aren't found unless requested
+ */
+key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
+ int create, int partial, key_perm_t perm)
+{
+ key_ref_t key_ref, skey_ref;
+ struct key *key;
+ int ret;
+
+ if (!context)
+ context = current;
+
+ key_ref = ERR_PTR(-ENOKEY);
+
+ switch (id) {
+ case KEY_SPEC_THREAD_KEYRING:
+ if (!context->thread_keyring) {
+ if (!create)
+ goto error;
+
+ ret = install_thread_keyring(context);
+ if (ret < 0) {
+ key = ERR_PTR(ret);
+ goto error;
+ }
+ }
+
+ key = context->thread_keyring;
+ atomic_inc(&key->usage);
+ key_ref = make_key_ref(key, 1);
+ break;
+
+ case KEY_SPEC_PROCESS_KEYRING:
+ if (!context->signal->process_keyring) {
+ if (!create)
+ goto error;
+
+ ret = install_process_keyring(context);
+ if (ret < 0) {
+ key = ERR_PTR(ret);
+ goto error;
+ }
+ }
+
+ key = context->signal->process_keyring;
+ atomic_inc(&key->usage);
+ key_ref = make_key_ref(key, 1);
+ break;
+
+ case KEY_SPEC_SESSION_KEYRING:
+ if (!context->signal->session_keyring) {
+ /* always install a session keyring upon access if one
+ * doesn't exist yet */
+ ret = install_user_keyrings(context);
+ if (ret < 0)
+ goto error;
+ ret = install_session_keyring(
+ context, context->user->session_keyring);
+ if (ret < 0)
+ goto error;
+ }
+
+ rcu_read_lock();
+ key = rcu_dereference(context->signal->session_keyring);
+ atomic_inc(&key->usage);
+ rcu_read_unlock();
+ key_ref = make_key_ref(key, 1);
+ break;
+
+ case KEY_SPEC_USER_KEYRING:
+ if (!context->user->uid_keyring) {
+ ret = install_user_keyrings(context);
+ if (ret < 0)
+ goto error;
+ }
+
+ key = context->user->uid_keyring;
+ atomic_inc(&key->usage);
+ key_ref = make_key_ref(key, 1);
+ break;
+
+ case KEY_SPEC_USER_SESSION_KEYRING:
+ if (!context->user->session_keyring) {
+ ret = install_user_keyrings(context);
+ if (ret < 0)
+ goto error;
+ }
+
+ key = context->user->session_keyring;
+ atomic_inc(&key->usage);
+ key_ref = make_key_ref(key, 1);
+ break;
+
+ case KEY_SPEC_GROUP_KEYRING:
+ /* group keyrings are not yet supported */
+ key = ERR_PTR(-EINVAL);
+ goto error;
+
+ case KEY_SPEC_REQKEY_AUTH_KEY:
+ key = context->request_key_auth;
+ if (!key)
+ goto error;
+
+ atomic_inc(&key->usage);
+ key_ref = make_key_ref(key, 1);
+ break;
+
+ default:
+ key_ref = ERR_PTR(-EINVAL);
+ if (id < 1)
+ goto error;
+
+ key = key_lookup(id);
+ if (IS_ERR(key)) {
+ key_ref = ERR_CAST(key);
+ goto error;
+ }
+
+ key_ref = make_key_ref(key, 0);
+
+ /* check to see if we possess the key */
+ skey_ref = search_process_keyrings(key->type, key,
+ lookup_user_key_possessed,
+ current);
+
+ if (!IS_ERR(skey_ref)) {
+ key_put(key);
+ key_ref = skey_ref;
+ }
+
+ break;
+ }
+
+ if (!partial) {
+ ret = wait_for_key_construction(key, true);
+ switch (ret) {
+ case -ERESTARTSYS:
+ goto invalid_key;
+ default:
+ if (perm)
+ goto invalid_key;
+ case 0:
+ break;
+ }
+ } else if (perm) {
+ ret = key_validate(key);
+ if (ret < 0)
+ goto invalid_key;
+ }
+
+ ret = -EIO;
+ if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
+ goto invalid_key;
+
+ /* check the permissions */
+ ret = key_task_permission(key_ref, context, perm);
+ if (ret < 0)
+ goto invalid_key;
+
+error:
+ return key_ref;
+
+invalid_key:
+ key_ref_put(key_ref);
+ key_ref = ERR_PTR(ret);
+ goto error;
+
+} /* end lookup_user_key() */
+
+/*****************************************************************************/
+/*
+ * join the named keyring as the session keyring if possible, or attempt to
+ * create a new one of that name if not
+ * - if the name is NULL, an empty anonymous keyring is installed instead
+ * - named session keyring joining is done with a semaphore held
+ */
+long join_session_keyring(const char *name)
+{
+ struct task_struct *tsk = current;
+ struct key *keyring;
+ long ret;
+
+ /* if no name is provided, install an anonymous keyring */
+ if (!name) {
+ ret = install_session_keyring(tsk, NULL);
+ if (ret < 0)
+ goto error;
+
+ rcu_read_lock();
+ ret = rcu_dereference(tsk->signal->session_keyring)->serial;
+ rcu_read_unlock();
+ goto error;
+ }
+
+ /* allow the user to join or create a named keyring */
+ mutex_lock(&key_session_mutex);
+
+ /* look for an existing keyring of this name */
+ keyring = find_keyring_by_name(name, false);
+ if (PTR_ERR(keyring) == -ENOKEY) {
+ /* not found - try and create a new one */
+ keyring = keyring_alloc(name, tsk->uid, tsk->gid, tsk,
+ KEY_ALLOC_IN_QUOTA, NULL);
+ if (IS_ERR(keyring)) {
+ ret = PTR_ERR(keyring);
+ goto error2;
+ }
+ }
+ else if (IS_ERR(keyring)) {
+ ret = PTR_ERR(keyring);
+ goto error2;
+ }
+
+ /* we've got a keyring - now to install it */
+ ret = install_session_keyring(tsk, keyring);
+ if (ret < 0)
+ goto error2;
+
+ ret = keyring->serial;
+ key_put(keyring);
+
+error2:
+ mutex_unlock(&key_session_mutex);
+error:
+ return ret;
+
+} /* end join_session_keyring() */
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
new file mode 100644
index 0000000..abea08f
--- /dev/null
+++ b/security/keys/request_key.c
@@ -0,0 +1,525 @@
+/* Request a key from userspace
+ *
+ * Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * See Documentation/keys-request-key.txt
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kmod.h>
+#include <linux/err.h>
+#include <linux/keyctl.h>
+#include <linux/slab.h>
+#include "internal.h"
+
+/*
+ * wait_on_bit() sleep function for uninterruptible waiting
+ */
+static int key_wait_bit(void *flags)
+{
+ schedule();
+ return 0;
+}
+
+/*
+ * wait_on_bit() sleep function for interruptible waiting
+ */
+static int key_wait_bit_intr(void *flags)
+{
+ schedule();
+ return signal_pending(current) ? -ERESTARTSYS : 0;
+}
+
+/*
+ * call to complete the construction of a key
+ */
+void complete_request_key(struct key_construction *cons, int error)
+{
+ kenter("{%d,%d},%d", cons->key->serial, cons->authkey->serial, error);
+
+ if (error < 0)
+ key_negate_and_link(cons->key, key_negative_timeout, NULL,
+ cons->authkey);
+ else
+ key_revoke(cons->authkey);
+
+ key_put(cons->key);
+ key_put(cons->authkey);
+ kfree(cons);
+}
+EXPORT_SYMBOL(complete_request_key);
+
+/*
+ * request userspace finish the construction of a key
+ * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>"
+ */
+static int call_sbin_request_key(struct key_construction *cons,
+ const char *op,
+ void *aux)
+{
+ struct task_struct *tsk = current;
+ key_serial_t prkey, sskey;
+ struct key *key = cons->key, *authkey = cons->authkey, *keyring;
+ char *argv[9], *envp[3], uid_str[12], gid_str[12];
+ char key_str[12], keyring_str[3][12];
+ char desc[20];
+ int ret, i;
+
+ kenter("{%d},{%d},%s", key->serial, authkey->serial, op);
+
+ ret = install_user_keyrings(tsk);
+ if (ret < 0)
+ goto error_alloc;
+
+ /* allocate a new session keyring */
+ sprintf(desc, "_req.%u", key->serial);
+
+ keyring = keyring_alloc(desc, current->fsuid, current->fsgid, current,
+ KEY_ALLOC_QUOTA_OVERRUN, NULL);
+ if (IS_ERR(keyring)) {
+ ret = PTR_ERR(keyring);
+ goto error_alloc;
+ }
+
+ /* attach the auth key to the session keyring */
+ ret = __key_link(keyring, authkey);
+ if (ret < 0)
+ goto error_link;
+
+ /* record the UID and GID */
+ sprintf(uid_str, "%d", current->fsuid);
+ sprintf(gid_str, "%d", current->fsgid);
+
+ /* we say which key is under construction */
+ sprintf(key_str, "%d", key->serial);
+
+ /* we specify the process's default keyrings */
+ sprintf(keyring_str[0], "%d",
+ tsk->thread_keyring ? tsk->thread_keyring->serial : 0);
+
+ prkey = 0;
+ if (tsk->signal->process_keyring)
+ prkey = tsk->signal->process_keyring->serial;
+
+ sprintf(keyring_str[1], "%d", prkey);
+
+ if (tsk->signal->session_keyring) {
+ rcu_read_lock();
+ sskey = rcu_dereference(tsk->signal->session_keyring)->serial;
+ rcu_read_unlock();
+ } else {
+ sskey = tsk->user->session_keyring->serial;
+ }
+
+ sprintf(keyring_str[2], "%d", sskey);
+
+ /* set up a minimal environment */
+ i = 0;
+ envp[i++] = "HOME=/";
+ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+ envp[i] = NULL;
+
+ /* set up the argument list */
+ i = 0;
+ argv[i++] = "/sbin/request-key";
+ argv[i++] = (char *) op;
+ argv[i++] = key_str;
+ argv[i++] = uid_str;
+ argv[i++] = gid_str;
+ argv[i++] = keyring_str[0];
+ argv[i++] = keyring_str[1];
+ argv[i++] = keyring_str[2];
+ argv[i] = NULL;
+
+ /* do it */
+ ret = call_usermodehelper_keys(argv[0], argv, envp, keyring,
+ UMH_WAIT_PROC);
+ kdebug("usermode -> 0x%x", ret);
+ if (ret >= 0) {
+ /* ret is the exit/wait code */
+ if (test_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags) ||
+ key_validate(key) < 0)
+ ret = -ENOKEY;
+ else
+ /* ignore any errors from userspace if the key was
+ * instantiated */
+ ret = 0;
+ }
+
+error_link:
+ key_put(keyring);
+
+error_alloc:
+ kleave(" = %d", ret);
+ complete_request_key(cons, ret);
+ return ret;
+}
+
+/*
+ * call out to userspace for key construction
+ * - we ignore program failure and go on key status instead
+ */
+static int construct_key(struct key *key, const void *callout_info,
+ size_t callout_len, void *aux)
+{
+ struct key_construction *cons;
+ request_key_actor_t actor;
+ struct key *authkey;
+ int ret;
+
+ kenter("%d,%p,%zu,%p", key->serial, callout_info, callout_len, aux);
+
+ cons = kmalloc(sizeof(*cons), GFP_KERNEL);
+ if (!cons)
+ return -ENOMEM;
+
+ /* allocate an authorisation key */
+ authkey = request_key_auth_new(key, callout_info, callout_len);
+ if (IS_ERR(authkey)) {
+ kfree(cons);
+ ret = PTR_ERR(authkey);
+ authkey = NULL;
+ } else {
+ cons->authkey = key_get(authkey);
+ cons->key = key_get(key);
+
+ /* make the call */
+ actor = call_sbin_request_key;
+ if (key->type->request_key)
+ actor = key->type->request_key;
+
+ ret = actor(cons, "create", aux);
+
+ /* check that the actor called complete_request_key() prior to
+ * returning an error */
+ WARN_ON(ret < 0 &&
+ !test_bit(KEY_FLAG_REVOKED, &authkey->flags));
+ key_put(authkey);
+ }
+
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
+ * link a key to the appropriate destination keyring
+ * - the caller must hold a write lock on the destination keyring
+ */
+static void construct_key_make_link(struct key *key, struct key *dest_keyring)
+{
+ struct task_struct *tsk = current;
+ struct key *drop = NULL;
+
+ kenter("{%d},%p", key->serial, dest_keyring);
+
+ /* find the appropriate keyring */
+ if (!dest_keyring) {
+ switch (tsk->jit_keyring) {
+ case KEY_REQKEY_DEFL_DEFAULT:
+ case KEY_REQKEY_DEFL_THREAD_KEYRING:
+ dest_keyring = tsk->thread_keyring;
+ if (dest_keyring)
+ break;
+
+ case KEY_REQKEY_DEFL_PROCESS_KEYRING:
+ dest_keyring = tsk->signal->process_keyring;
+ if (dest_keyring)
+ break;
+
+ case KEY_REQKEY_DEFL_SESSION_KEYRING:
+ rcu_read_lock();
+ dest_keyring = key_get(
+ rcu_dereference(tsk->signal->session_keyring));
+ rcu_read_unlock();
+ drop = dest_keyring;
+
+ if (dest_keyring)
+ break;
+
+ case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
+ dest_keyring = tsk->user->session_keyring;
+ break;
+
+ case KEY_REQKEY_DEFL_USER_KEYRING:
+ dest_keyring = tsk->user->uid_keyring;
+ break;
+
+ case KEY_REQKEY_DEFL_GROUP_KEYRING:
+ default:
+ BUG();
+ }
+ }
+
+ /* and attach the key to it */
+ __key_link(dest_keyring, key);
+ key_put(drop);
+ kleave("");
+}
+
+/*
+ * allocate a new key in under-construction state and attempt to link it in to
+ * the requested place
+ * - may return a key that's already under construction instead
+ */
+static int construct_alloc_key(struct key_type *type,
+ const char *description,
+ struct key *dest_keyring,
+ unsigned long flags,
+ struct key_user *user,
+ struct key **_key)
+{
+ struct key *key;
+ key_ref_t key_ref;
+
+ kenter("%s,%s,,,", type->name, description);
+
+ mutex_lock(&user->cons_lock);
+
+ key = key_alloc(type, description,
+ current->fsuid, current->fsgid, current, KEY_POS_ALL,
+ flags);
+ if (IS_ERR(key))
+ goto alloc_failed;
+
+ set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
+
+ if (dest_keyring)
+ down_write(&dest_keyring->sem);
+
+ /* attach the key to the destination keyring under lock, but we do need
+ * to do another check just in case someone beat us to it whilst we
+ * waited for locks */
+ mutex_lock(&key_construction_mutex);
+
+ key_ref = search_process_keyrings(type, description, type->match,
+ current);
+ if (!IS_ERR(key_ref))
+ goto key_already_present;
+
+ if (dest_keyring)
+ construct_key_make_link(key, dest_keyring);
+
+ mutex_unlock(&key_construction_mutex);
+ if (dest_keyring)
+ up_write(&dest_keyring->sem);
+ mutex_unlock(&user->cons_lock);
+ *_key = key;
+ kleave(" = 0 [%d]", key_serial(key));
+ return 0;
+
+key_already_present:
+ mutex_unlock(&key_construction_mutex);
+ if (dest_keyring)
+ up_write(&dest_keyring->sem);
+ mutex_unlock(&user->cons_lock);
+ key_put(key);
+ *_key = key = key_ref_to_ptr(key_ref);
+ kleave(" = -EINPROGRESS [%d]", key_serial(key));
+ return -EINPROGRESS;
+
+alloc_failed:
+ mutex_unlock(&user->cons_lock);
+ *_key = NULL;
+ kleave(" = %ld", PTR_ERR(key));
+ return PTR_ERR(key);
+}
+
+/*
+ * commence key construction
+ */
+static struct key *construct_key_and_link(struct key_type *type,
+ const char *description,
+ const char *callout_info,
+ size_t callout_len,
+ void *aux,
+ struct key *dest_keyring,
+ unsigned long flags)
+{
+ struct key_user *user;
+ struct key *key;
+ int ret;
+
+ user = key_user_lookup(current->fsuid);
+ if (!user)
+ return ERR_PTR(-ENOMEM);
+
+ ret = construct_alloc_key(type, description, dest_keyring, flags, user,
+ &key);
+ key_user_put(user);
+
+ if (ret == 0) {
+ ret = construct_key(key, callout_info, callout_len, aux);
+ if (ret < 0)
+ goto construction_failed;
+ }
+
+ return key;
+
+construction_failed:
+ key_negate_and_link(key, key_negative_timeout, NULL, NULL);
+ key_put(key);
+ return ERR_PTR(ret);
+}
+
+/*
+ * request a key
+ * - search the process's keyrings
+ * - check the list of keys being created or updated
+ * - call out to userspace for a key if supplementary info was provided
+ * - cache the key in an appropriate keyring
+ */
+struct key *request_key_and_link(struct key_type *type,
+ const char *description,
+ const void *callout_info,
+ size_t callout_len,
+ void *aux,
+ struct key *dest_keyring,
+ unsigned long flags)
+{
+ struct key *key;
+ key_ref_t key_ref;
+
+ kenter("%s,%s,%p,%zu,%p,%p,%lx",
+ type->name, description, callout_info, callout_len, aux,
+ dest_keyring, flags);
+
+ /* search all the process keyrings for a key */
+ key_ref = search_process_keyrings(type, description, type->match,
+ current);
+
+ if (!IS_ERR(key_ref)) {
+ key = key_ref_to_ptr(key_ref);
+ } else if (PTR_ERR(key_ref) != -EAGAIN) {
+ key = ERR_CAST(key_ref);
+ } else {
+ /* the search failed, but the keyrings were searchable, so we
+ * should consult userspace if we can */
+ key = ERR_PTR(-ENOKEY);
+ if (!callout_info)
+ goto error;
+
+ key = construct_key_and_link(type, description, callout_info,
+ callout_len, aux, dest_keyring,
+ flags);
+ }
+
+error:
+ kleave(" = %p", key);
+ return key;
+}
+
+/*
+ * wait for construction of a key to complete
+ */
+int wait_for_key_construction(struct key *key, bool intr)
+{
+ int ret;
+
+ ret = wait_on_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT,
+ intr ? key_wait_bit_intr : key_wait_bit,
+ intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+ if (ret < 0)
+ return ret;
+ return key_validate(key);
+}
+EXPORT_SYMBOL(wait_for_key_construction);
+
+/*
+ * request a key
+ * - search the process's keyrings
+ * - check the list of keys being created or updated
+ * - call out to userspace for a key if supplementary info was provided
+ * - waits uninterruptible for creation to complete
+ */
+struct key *request_key(struct key_type *type,
+ const char *description,
+ const char *callout_info)
+{
+ struct key *key;
+ size_t callout_len = 0;
+ int ret;
+
+ if (callout_info)
+ callout_len = strlen(callout_info);
+ key = request_key_and_link(type, description, callout_info, callout_len,
+ NULL, NULL, KEY_ALLOC_IN_QUOTA);
+ if (!IS_ERR(key)) {
+ ret = wait_for_key_construction(key, false);
+ if (ret < 0) {
+ key_put(key);
+ return ERR_PTR(ret);
+ }
+ }
+ return key;
+}
+EXPORT_SYMBOL(request_key);
+
+/*
+ * request a key with auxiliary data for the upcaller
+ * - search the process's keyrings
+ * - check the list of keys being created or updated
+ * - call out to userspace for a key if supplementary info was provided
+ * - waits uninterruptible for creation to complete
+ */
+struct key *request_key_with_auxdata(struct key_type *type,
+ const char *description,
+ const void *callout_info,
+ size_t callout_len,
+ void *aux)
+{
+ struct key *key;
+ int ret;
+
+ key = request_key_and_link(type, description, callout_info, callout_len,
+ aux, NULL, KEY_ALLOC_IN_QUOTA);
+ if (!IS_ERR(key)) {
+ ret = wait_for_key_construction(key, false);
+ if (ret < 0) {
+ key_put(key);
+ return ERR_PTR(ret);
+ }
+ }
+ return key;
+}
+EXPORT_SYMBOL(request_key_with_auxdata);
+
+/*
+ * request a key (allow async construction)
+ * - search the process's keyrings
+ * - check the list of keys being created or updated
+ * - call out to userspace for a key if supplementary info was provided
+ */
+struct key *request_key_async(struct key_type *type,
+ const char *description,
+ const void *callout_info,
+ size_t callout_len)
+{
+ return request_key_and_link(type, description, callout_info,
+ callout_len, NULL, NULL,
+ KEY_ALLOC_IN_QUOTA);
+}
+EXPORT_SYMBOL(request_key_async);
+
+/*
+ * request a key with auxiliary data for the upcaller (allow async construction)
+ * - search the process's keyrings
+ * - check the list of keys being created or updated
+ * - call out to userspace for a key if supplementary info was provided
+ */
+struct key *request_key_async_with_auxdata(struct key_type *type,
+ const char *description,
+ const void *callout_info,
+ size_t callout_len,
+ void *aux)
+{
+ return request_key_and_link(type, description, callout_info,
+ callout_len, aux, NULL, KEY_ALLOC_IN_QUOTA);
+}
+EXPORT_SYMBOL(request_key_async_with_auxdata);
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
new file mode 100644
index 0000000..bd237b0
--- /dev/null
+++ b/security/keys/request_key_auth.c
@@ -0,0 +1,280 @@
+/* request_key_auth.c: request key authorisation controlling key def
+ *
+ * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * See Documentation/keys-request-key.txt
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+static int request_key_auth_instantiate(struct key *, const void *, size_t);
+static void request_key_auth_describe(const struct key *, struct seq_file *);
+static void request_key_auth_revoke(struct key *);
+static void request_key_auth_destroy(struct key *);
+static long request_key_auth_read(const struct key *, char __user *, size_t);
+
+/*
+ * the request-key authorisation key type definition
+ */
+struct key_type key_type_request_key_auth = {
+ .name = ".request_key_auth",
+ .def_datalen = sizeof(struct request_key_auth),
+ .instantiate = request_key_auth_instantiate,
+ .describe = request_key_auth_describe,
+ .revoke = request_key_auth_revoke,
+ .destroy = request_key_auth_destroy,
+ .read = request_key_auth_read,
+};
+
+/*****************************************************************************/
+/*
+ * instantiate a request-key authorisation key
+ */
+static int request_key_auth_instantiate(struct key *key,
+ const void *data,
+ size_t datalen)
+{
+ key->payload.data = (struct request_key_auth *) data;
+ return 0;
+
+} /* end request_key_auth_instantiate() */
+
+/*****************************************************************************/
+/*
+ * reading a request-key authorisation key retrieves the callout information
+ */
+static void request_key_auth_describe(const struct key *key,
+ struct seq_file *m)
+{
+ struct request_key_auth *rka = key->payload.data;
+
+ seq_puts(m, "key:");
+ seq_puts(m, key->description);
+ seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len);
+
+} /* end request_key_auth_describe() */
+
+/*****************************************************************************/
+/*
+ * read the callout_info data
+ * - the key's semaphore is read-locked
+ */
+static long request_key_auth_read(const struct key *key,
+ char __user *buffer, size_t buflen)
+{
+ struct request_key_auth *rka = key->payload.data;
+ size_t datalen;
+ long ret;
+
+ datalen = rka->callout_len;
+ ret = datalen;
+
+ /* we can return the data as is */
+ if (buffer && buflen > 0) {
+ if (buflen > datalen)
+ buflen = datalen;
+
+ if (copy_to_user(buffer, rka->callout_info, buflen) != 0)
+ ret = -EFAULT;
+ }
+
+ return ret;
+
+} /* end request_key_auth_read() */
+
+/*****************************************************************************/
+/*
+ * handle revocation of an authorisation token key
+ * - called with the key sem write-locked
+ */
+static void request_key_auth_revoke(struct key *key)
+{
+ struct request_key_auth *rka = key->payload.data;
+
+ kenter("{%d}", key->serial);
+
+ if (rka->context) {
+ put_task_struct(rka->context);
+ rka->context = NULL;
+ }
+
+} /* end request_key_auth_revoke() */
+
+/*****************************************************************************/
+/*
+ * destroy an instantiation authorisation token key
+ */
+static void request_key_auth_destroy(struct key *key)
+{
+ struct request_key_auth *rka = key->payload.data;
+
+ kenter("{%d}", key->serial);
+
+ if (rka->context) {
+ put_task_struct(rka->context);
+ rka->context = NULL;
+ }
+
+ key_put(rka->target_key);
+ kfree(rka->callout_info);
+ kfree(rka);
+
+} /* end request_key_auth_destroy() */
+
+/*****************************************************************************/
+/*
+ * create an authorisation token for /sbin/request-key or whoever to gain
+ * access to the caller's security data
+ */
+struct key *request_key_auth_new(struct key *target, const void *callout_info,
+ size_t callout_len)
+{
+ struct request_key_auth *rka, *irka;
+ struct key *authkey = NULL;
+ char desc[20];
+ int ret;
+
+ kenter("%d,", target->serial);
+
+ /* allocate a auth record */
+ rka = kmalloc(sizeof(*rka), GFP_KERNEL);
+ if (!rka) {
+ kleave(" = -ENOMEM");
+ return ERR_PTR(-ENOMEM);
+ }
+ rka->callout_info = kmalloc(callout_len, GFP_KERNEL);
+ if (!rka->callout_info) {
+ kleave(" = -ENOMEM");
+ kfree(rka);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* see if the calling process is already servicing the key request of
+ * another process */
+ if (current->request_key_auth) {
+ /* it is - use that instantiation context here too */
+ down_read(&current->request_key_auth->sem);
+
+ /* if the auth key has been revoked, then the key we're
+ * servicing is already instantiated */
+ if (test_bit(KEY_FLAG_REVOKED,
+ &current->request_key_auth->flags))
+ goto auth_key_revoked;
+
+ irka = current->request_key_auth->payload.data;
+ rka->context = irka->context;
+ rka->pid = irka->pid;
+ get_task_struct(rka->context);
+
+ up_read(&current->request_key_auth->sem);
+ }
+ else {
+ /* it isn't - use this process as the context */
+ rka->context = current;
+ rka->pid = current->pid;
+ get_task_struct(rka->context);
+ }
+
+ rka->target_key = key_get(target);
+ memcpy(rka->callout_info, callout_info, callout_len);
+ rka->callout_len = callout_len;
+
+ /* allocate the auth key */
+ sprintf(desc, "%x", target->serial);
+
+ authkey = key_alloc(&key_type_request_key_auth, desc,
+ current->fsuid, current->fsgid, current,
+ KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
+ KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA);
+ if (IS_ERR(authkey)) {
+ ret = PTR_ERR(authkey);
+ goto error_alloc;
+ }
+
+ /* construct and attach to the keyring */
+ ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL);
+ if (ret < 0)
+ goto error_inst;
+
+ kleave(" = {%d}", authkey->serial);
+ return authkey;
+
+auth_key_revoked:
+ up_read(&current->request_key_auth->sem);
+ kfree(rka->callout_info);
+ kfree(rka);
+ kleave("= -EKEYREVOKED");
+ return ERR_PTR(-EKEYREVOKED);
+
+error_inst:
+ key_revoke(authkey);
+ key_put(authkey);
+error_alloc:
+ key_put(rka->target_key);
+ kfree(rka->callout_info);
+ kfree(rka);
+ kleave("= %d", ret);
+ return ERR_PTR(ret);
+
+} /* end request_key_auth_new() */
+
+/*****************************************************************************/
+/*
+ * see if an authorisation key is associated with a particular key
+ */
+static int key_get_instantiation_authkey_match(const struct key *key,
+ const void *_id)
+{
+ struct request_key_auth *rka = key->payload.data;
+ key_serial_t id = (key_serial_t)(unsigned long) _id;
+
+ return rka->target_key->serial == id;
+
+} /* end key_get_instantiation_authkey_match() */
+
+/*****************************************************************************/
+/*
+ * get the authorisation key for instantiation of a specific key if attached to
+ * the current process's keyrings
+ * - this key is inserted into a keyring and that is set as /sbin/request-key's
+ * session keyring
+ * - a target_id of zero specifies any valid token
+ */
+struct key *key_get_instantiation_authkey(key_serial_t target_id)
+{
+ struct key *authkey;
+ key_ref_t authkey_ref;
+
+ authkey_ref = search_process_keyrings(
+ &key_type_request_key_auth,
+ (void *) (unsigned long) target_id,
+ key_get_instantiation_authkey_match,
+ current);
+
+ if (IS_ERR(authkey_ref)) {
+ authkey = ERR_CAST(authkey_ref);
+ goto error;
+ }
+
+ authkey = key_ref_to_ptr(authkey_ref);
+ if (test_bit(KEY_FLAG_REVOKED, &authkey->flags)) {
+ key_put(authkey);
+ authkey = ERR_PTR(-EKEYREVOKED);
+ }
+
+error:
+ return authkey;
+
+} /* end key_get_instantiation_authkey() */
diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c
new file mode 100644
index 0000000..b611d49
--- /dev/null
+++ b/security/keys/sysctl.c
@@ -0,0 +1,50 @@
+/* Key management controls
+ *
+ * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/key.h>
+#include <linux/sysctl.h>
+#include "internal.h"
+
+ctl_table key_sysctls[] = {
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "maxkeys",
+ .data = &key_quota_maxkeys,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "maxbytes",
+ .data = &key_quota_maxbytes,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "root_maxkeys",
+ .data = &key_quota_root_maxkeys,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "root_maxbytes",
+ .data = &key_quota_root_maxbytes,
+ .maxlen = sizeof(unsigned),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+ { .ctl_name = 0 }
+};
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
new file mode 100644
index 0000000..7c687d5
--- /dev/null
+++ b/security/keys/user_defined.c
@@ -0,0 +1,218 @@
+/* user_defined.c: user defined key type
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/err.h>
+#include <keys/user-type.h>
+#include <asm/uaccess.h>
+#include "internal.h"
+
+/*
+ * user defined keys take an arbitrary string as the description and an
+ * arbitrary blob of data as the payload
+ */
+struct key_type key_type_user = {
+ .name = "user",
+ .instantiate = user_instantiate,
+ .update = user_update,
+ .match = user_match,
+ .revoke = user_revoke,
+ .destroy = user_destroy,
+ .describe = user_describe,
+ .read = user_read,
+};
+
+EXPORT_SYMBOL_GPL(key_type_user);
+
+/*****************************************************************************/
+/*
+ * instantiate a user defined key
+ */
+int user_instantiate(struct key *key, const void *data, size_t datalen)
+{
+ struct user_key_payload *upayload;
+ int ret;
+
+ ret = -EINVAL;
+ if (datalen <= 0 || datalen > 32767 || !data)
+ goto error;
+
+ ret = key_payload_reserve(key, datalen);
+ if (ret < 0)
+ goto error;
+
+ ret = -ENOMEM;
+ upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
+ if (!upayload)
+ goto error;
+
+ /* attach the data */
+ upayload->datalen = datalen;
+ memcpy(upayload->data, data, datalen);
+ rcu_assign_pointer(key->payload.data, upayload);
+ ret = 0;
+
+error:
+ return ret;
+
+} /* end user_instantiate() */
+
+EXPORT_SYMBOL_GPL(user_instantiate);
+
+/*****************************************************************************/
+/*
+ * dispose of the old data from an updated user defined key
+ */
+static void user_update_rcu_disposal(struct rcu_head *rcu)
+{
+ struct user_key_payload *upayload;
+
+ upayload = container_of(rcu, struct user_key_payload, rcu);
+
+ kfree(upayload);
+
+} /* end user_update_rcu_disposal() */
+
+/*****************************************************************************/
+/*
+ * update a user defined key
+ * - the key's semaphore is write-locked
+ */
+int user_update(struct key *key, const void *data, size_t datalen)
+{
+ struct user_key_payload *upayload, *zap;
+ int ret;
+
+ ret = -EINVAL;
+ if (datalen <= 0 || datalen > 32767 || !data)
+ goto error;
+
+ /* construct a replacement payload */
+ ret = -ENOMEM;
+ upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
+ if (!upayload)
+ goto error;
+
+ upayload->datalen = datalen;
+ memcpy(upayload->data, data, datalen);
+
+ /* check the quota and attach the new data */
+ zap = upayload;
+
+ ret = key_payload_reserve(key, datalen);
+
+ if (ret == 0) {
+ /* attach the new data, displacing the old */
+ zap = key->payload.data;
+ rcu_assign_pointer(key->payload.data, upayload);
+ key->expiry = 0;
+ }
+
+ call_rcu(&zap->rcu, user_update_rcu_disposal);
+
+error:
+ return ret;
+
+} /* end user_update() */
+
+EXPORT_SYMBOL_GPL(user_update);
+
+/*****************************************************************************/
+/*
+ * match users on their name
+ */
+int user_match(const struct key *key, const void *description)
+{
+ return strcmp(key->description, description) == 0;
+
+} /* end user_match() */
+
+EXPORT_SYMBOL_GPL(user_match);
+
+/*****************************************************************************/
+/*
+ * dispose of the links from a revoked keyring
+ * - called with the key sem write-locked
+ */
+void user_revoke(struct key *key)
+{
+ struct user_key_payload *upayload = key->payload.data;
+
+ /* clear the quota */
+ key_payload_reserve(key, 0);
+
+ if (upayload) {
+ rcu_assign_pointer(key->payload.data, NULL);
+ call_rcu(&upayload->rcu, user_update_rcu_disposal);
+ }
+
+} /* end user_revoke() */
+
+EXPORT_SYMBOL(user_revoke);
+
+/*****************************************************************************/
+/*
+ * dispose of the data dangling from the corpse of a user key
+ */
+void user_destroy(struct key *key)
+{
+ struct user_key_payload *upayload = key->payload.data;
+
+ kfree(upayload);
+
+} /* end user_destroy() */
+
+EXPORT_SYMBOL_GPL(user_destroy);
+
+/*****************************************************************************/
+/*
+ * describe the user key
+ */
+void user_describe(const struct key *key, struct seq_file *m)
+{
+ seq_puts(m, key->description);
+
+ seq_printf(m, ": %u", key->datalen);
+
+} /* end user_describe() */
+
+EXPORT_SYMBOL_GPL(user_describe);
+
+/*****************************************************************************/
+/*
+ * read the key data
+ * - the key's semaphore is read-locked
+ */
+long user_read(const struct key *key, char __user *buffer, size_t buflen)
+{
+ struct user_key_payload *upayload;
+ long ret;
+
+ upayload = rcu_dereference(key->payload.data);
+ ret = upayload->datalen;
+
+ /* we can return the data as is */
+ if (buffer && buflen > 0) {
+ if (buflen > upayload->datalen)
+ buflen = upayload->datalen;
+
+ if (copy_to_user(buffer, upayload->data, buflen) != 0)
+ ret = -EFAULT;
+ }
+
+ return ret;
+
+} /* end user_read() */
+
+EXPORT_SYMBOL_GPL(user_read);
diff --git a/security/root_plug.c b/security/root_plug.c
new file mode 100644
index 0000000..c3f68b5
--- /dev/null
+++ b/security/root_plug.c
@@ -0,0 +1,105 @@
+/*
+ * Root Plug sample LSM module
+ *
+ * Originally written for a Linux Journal.
+ *
+ * Copyright (C) 2002 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * Prevents any programs running with egid == 0 if a specific USB device
+ * is not present in the system. Yes, it can be gotten around, but is a
+ * nice starting point for people to play with, and learn the LSM
+ * interface.
+ *
+ * If you want to turn this into something with a semblance of security,
+ * you need to hook the task_* functions also.
+ *
+ * See http://www.linuxjournal.com/article.php?sid=6279 for more information
+ * about this code.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/security.h>
+#include <linux/usb.h>
+#include <linux/moduleparam.h>
+
+/* default is a generic type of usb to serial converter */
+static int vendor_id = 0x0557;
+static int product_id = 0x2008;
+
+module_param(vendor_id, uint, 0400);
+module_param(product_id, uint, 0400);
+
+/* should we print out debug messages */
+static int debug = 0;
+
+module_param(debug, bool, 0600);
+
+#define MY_NAME "root_plug"
+
+#define root_dbg(fmt, arg...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: %s: " fmt , \
+ MY_NAME , __func__ , \
+ ## arg); \
+ } while (0)
+
+static int rootplug_bprm_check_security (struct linux_binprm *bprm)
+{
+ struct usb_device *dev;
+
+ root_dbg("file %s, e_uid = %d, e_gid = %d\n",
+ bprm->filename, bprm->e_uid, bprm->e_gid);
+
+ if (bprm->e_gid == 0) {
+ dev = usb_find_device(vendor_id, product_id);
+ if (!dev) {
+ root_dbg("e_gid = 0, and device not found, "
+ "task not allowed to run...\n");
+ return -EPERM;
+ }
+ usb_put_dev(dev);
+ }
+
+ return 0;
+}
+
+static struct security_operations rootplug_security_ops = {
+ /* Use the capability functions for some of the hooks */
+ .ptrace_may_access = cap_ptrace_may_access,
+ .ptrace_traceme = cap_ptrace_traceme,
+ .capget = cap_capget,
+ .capset_check = cap_capset_check,
+ .capset_set = cap_capset_set,
+ .capable = cap_capable,
+
+ .bprm_apply_creds = cap_bprm_apply_creds,
+ .bprm_set_security = cap_bprm_set_security,
+
+ .task_post_setuid = cap_task_post_setuid,
+ .task_reparent_to_init = cap_task_reparent_to_init,
+ .task_prctl = cap_task_prctl,
+
+ .bprm_check_security = rootplug_bprm_check_security,
+};
+
+static int __init rootplug_init (void)
+{
+ /* register ourselves with the security framework */
+ if (register_security (&rootplug_security_ops)) {
+ printk (KERN_INFO
+ "Failure registering Root Plug module with the kernel\n");
+ return -EINVAL;
+ }
+ printk (KERN_INFO "Root Plug module initialized, "
+ "vendor_id = %4.4x, product id = %4.4x\n", vendor_id, product_id);
+ return 0;
+}
+
+security_initcall (rootplug_init);
diff --git a/security/security.c b/security/security.c
new file mode 100644
index 0000000..dad3183
--- /dev/null
+++ b/security/security.c
@@ -0,0 +1,1170 @@
+/*
+ * Security plug functions
+ *
+ * Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com>
+ * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
+ * Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/capability.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/security.h>
+
+/* Boot-time LSM user choice */
+static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1];
+
+/* things that live in capability.c */
+extern struct security_operations default_security_ops;
+extern void security_fixup_ops(struct security_operations *ops);
+
+struct security_operations *security_ops; /* Initialized to NULL */
+
+/* amount of vm to protect from userspace access */
+unsigned long mmap_min_addr = CONFIG_SECURITY_DEFAULT_MMAP_MIN_ADDR;
+
+static inline int verify(struct security_operations *ops)
+{
+ /* verify the security_operations structure exists */
+ if (!ops)
+ return -EINVAL;
+ security_fixup_ops(ops);
+ return 0;
+}
+
+static void __init do_security_initcalls(void)
+{
+ initcall_t *call;
+ call = __security_initcall_start;
+ while (call < __security_initcall_end) {
+ (*call) ();
+ call++;
+ }
+}
+
+/**
+ * security_init - initializes the security framework
+ *
+ * This should be called early in the kernel initialization sequence.
+ */
+int __init security_init(void)
+{
+ printk(KERN_INFO "Security Framework initialized\n");
+
+ security_fixup_ops(&default_security_ops);
+ security_ops = &default_security_ops;
+ do_security_initcalls();
+
+ return 0;
+}
+
+/* Save user chosen LSM */
+static int __init choose_lsm(char *str)
+{
+ strncpy(chosen_lsm, str, SECURITY_NAME_MAX);
+ return 1;
+}
+__setup("security=", choose_lsm);
+
+/**
+ * security_module_enable - Load given security module on boot ?
+ * @ops: a pointer to the struct security_operations that is to be checked.
+ *
+ * Each LSM must pass this method before registering its own operations
+ * to avoid security registration races. This method may also be used
+ * to check if your LSM is currently loaded during kernel initialization.
+ *
+ * Return true if:
+ * -The passed LSM is the one chosen by user at boot time,
+ * -or user didn't specify a specific LSM and we're the first to ask
+ * for registration permission,
+ * -or the passed LSM is currently loaded.
+ * Otherwise, return false.
+ */
+int __init security_module_enable(struct security_operations *ops)
+{
+ if (!*chosen_lsm)
+ strncpy(chosen_lsm, ops->name, SECURITY_NAME_MAX);
+ else if (strncmp(ops->name, chosen_lsm, SECURITY_NAME_MAX))
+ return 0;
+
+ return 1;
+}
+
+/**
+ * register_security - registers a security framework with the kernel
+ * @ops: a pointer to the struct security_options that is to be registered
+ *
+ * This function allows a security module to register itself with the
+ * kernel security subsystem. Some rudimentary checking is done on the @ops
+ * value passed to this function. You'll need to check first if your LSM
+ * is allowed to register its @ops by calling security_module_enable(@ops).
+ *
+ * If there is already a security module registered with the kernel,
+ * an error will be returned. Otherwise %0 is returned on success.
+ */
+int register_security(struct security_operations *ops)
+{
+ if (verify(ops)) {
+ printk(KERN_DEBUG "%s could not verify "
+ "security_operations structure.\n", __func__);
+ return -EINVAL;
+ }
+
+ if (security_ops != &default_security_ops)
+ return -EAGAIN;
+
+ security_ops = ops;
+
+ return 0;
+}
+
+/* Security operations */
+
+int security_ptrace_may_access(struct task_struct *child, unsigned int mode)
+{
+ return security_ops->ptrace_may_access(child, mode);
+}
+
+int security_ptrace_traceme(struct task_struct *parent)
+{
+ return security_ops->ptrace_traceme(parent);
+}
+
+int security_capget(struct task_struct *target,
+ kernel_cap_t *effective,
+ kernel_cap_t *inheritable,
+ kernel_cap_t *permitted)
+{
+ return security_ops->capget(target, effective, inheritable, permitted);
+}
+
+int security_capset_check(struct task_struct *target,
+ kernel_cap_t *effective,
+ kernel_cap_t *inheritable,
+ kernel_cap_t *permitted)
+{
+ return security_ops->capset_check(target, effective, inheritable, permitted);
+}
+
+void security_capset_set(struct task_struct *target,
+ kernel_cap_t *effective,
+ kernel_cap_t *inheritable,
+ kernel_cap_t *permitted)
+{
+ security_ops->capset_set(target, effective, inheritable, permitted);
+}
+
+int security_capable(struct task_struct *tsk, int cap)
+{
+ return security_ops->capable(tsk, cap);
+}
+
+int security_acct(struct file *file)
+{
+ return security_ops->acct(file);
+}
+
+int security_sysctl(struct ctl_table *table, int op)
+{
+ return security_ops->sysctl(table, op);
+}
+
+int security_quotactl(int cmds, int type, int id, struct super_block *sb)
+{
+ return security_ops->quotactl(cmds, type, id, sb);
+}
+
+int security_quota_on(struct dentry *dentry)
+{
+ return security_ops->quota_on(dentry);
+}
+
+int security_syslog(int type)
+{
+ return security_ops->syslog(type);
+}
+
+int security_settime(struct timespec *ts, struct timezone *tz)
+{
+ return security_ops->settime(ts, tz);
+}
+
+int security_vm_enough_memory(long pages)
+{
+ WARN_ON(current->mm == NULL);
+ return security_ops->vm_enough_memory(current->mm, pages);
+}
+
+int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
+{
+ WARN_ON(mm == NULL);
+ return security_ops->vm_enough_memory(mm, pages);
+}
+
+int security_vm_enough_memory_kern(long pages)
+{
+ /* If current->mm is a kernel thread then we will pass NULL,
+ for this specific case that is fine */
+ return security_ops->vm_enough_memory(current->mm, pages);
+}
+
+int security_bprm_alloc(struct linux_binprm *bprm)
+{
+ return security_ops->bprm_alloc_security(bprm);
+}
+
+void security_bprm_free(struct linux_binprm *bprm)
+{
+ security_ops->bprm_free_security(bprm);
+}
+
+void security_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+{
+ security_ops->bprm_apply_creds(bprm, unsafe);
+}
+
+void security_bprm_post_apply_creds(struct linux_binprm *bprm)
+{
+ security_ops->bprm_post_apply_creds(bprm);
+}
+
+int security_bprm_set(struct linux_binprm *bprm)
+{
+ return security_ops->bprm_set_security(bprm);
+}
+
+int security_bprm_check(struct linux_binprm *bprm)
+{
+ return security_ops->bprm_check_security(bprm);
+}
+
+int security_bprm_secureexec(struct linux_binprm *bprm)
+{
+ return security_ops->bprm_secureexec(bprm);
+}
+
+int security_sb_alloc(struct super_block *sb)
+{
+ return security_ops->sb_alloc_security(sb);
+}
+
+void security_sb_free(struct super_block *sb)
+{
+ security_ops->sb_free_security(sb);
+}
+
+int security_sb_copy_data(char *orig, char *copy)
+{
+ return security_ops->sb_copy_data(orig, copy);
+}
+EXPORT_SYMBOL(security_sb_copy_data);
+
+int security_sb_kern_mount(struct super_block *sb, void *data)
+{
+ return security_ops->sb_kern_mount(sb, data);
+}
+
+int security_sb_show_options(struct seq_file *m, struct super_block *sb)
+{
+ return security_ops->sb_show_options(m, sb);
+}
+
+int security_sb_statfs(struct dentry *dentry)
+{
+ return security_ops->sb_statfs(dentry);
+}
+
+int security_sb_mount(char *dev_name, struct path *path,
+ char *type, unsigned long flags, void *data)
+{
+ return security_ops->sb_mount(dev_name, path, type, flags, data);
+}
+
+int security_sb_check_sb(struct vfsmount *mnt, struct path *path)
+{
+ return security_ops->sb_check_sb(mnt, path);
+}
+
+int security_sb_umount(struct vfsmount *mnt, int flags)
+{
+ return security_ops->sb_umount(mnt, flags);
+}
+
+void security_sb_umount_close(struct vfsmount *mnt)
+{
+ security_ops->sb_umount_close(mnt);
+}
+
+void security_sb_umount_busy(struct vfsmount *mnt)
+{
+ security_ops->sb_umount_busy(mnt);
+}
+
+void security_sb_post_remount(struct vfsmount *mnt, unsigned long flags, void *data)
+{
+ security_ops->sb_post_remount(mnt, flags, data);
+}
+
+void security_sb_post_addmount(struct vfsmount *mnt, struct path *mountpoint)
+{
+ security_ops->sb_post_addmount(mnt, mountpoint);
+}
+
+int security_sb_pivotroot(struct path *old_path, struct path *new_path)
+{
+ return security_ops->sb_pivotroot(old_path, new_path);
+}
+
+void security_sb_post_pivotroot(struct path *old_path, struct path *new_path)
+{
+ security_ops->sb_post_pivotroot(old_path, new_path);
+}
+
+int security_sb_set_mnt_opts(struct super_block *sb,
+ struct security_mnt_opts *opts)
+{
+ return security_ops->sb_set_mnt_opts(sb, opts);
+}
+EXPORT_SYMBOL(security_sb_set_mnt_opts);
+
+void security_sb_clone_mnt_opts(const struct super_block *oldsb,
+ struct super_block *newsb)
+{
+ security_ops->sb_clone_mnt_opts(oldsb, newsb);
+}
+EXPORT_SYMBOL(security_sb_clone_mnt_opts);
+
+int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
+{
+ return security_ops->sb_parse_opts_str(options, opts);
+}
+EXPORT_SYMBOL(security_sb_parse_opts_str);
+
+int security_inode_alloc(struct inode *inode)
+{
+ inode->i_security = NULL;
+ return security_ops->inode_alloc_security(inode);
+}
+
+void security_inode_free(struct inode *inode)
+{
+ security_ops->inode_free_security(inode);
+}
+
+int security_inode_init_security(struct inode *inode, struct inode *dir,
+ char **name, void **value, size_t *len)
+{
+ if (unlikely(IS_PRIVATE(inode)))
+ return -EOPNOTSUPP;
+ return security_ops->inode_init_security(inode, dir, name, value, len);
+}
+EXPORT_SYMBOL(security_inode_init_security);
+
+int security_inode_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ if (unlikely(IS_PRIVATE(dir)))
+ return 0;
+ return security_ops->inode_create(dir, dentry, mode);
+}
+
+int security_inode_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ if (unlikely(IS_PRIVATE(old_dentry->d_inode)))
+ return 0;
+ return security_ops->inode_link(old_dentry, dir, new_dentry);
+}
+
+int security_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+ if (unlikely(IS_PRIVATE(dentry->d_inode)))
+ return 0;
+ return security_ops->inode_unlink(dir, dentry);
+}
+
+int security_inode_symlink(struct inode *dir, struct dentry *dentry,
+ const char *old_name)
+{
+ if (unlikely(IS_PRIVATE(dir)))
+ return 0;
+ return security_ops->inode_symlink(dir, dentry, old_name);
+}
+
+int security_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ if (unlikely(IS_PRIVATE(dir)))
+ return 0;
+ return security_ops->inode_mkdir(dir, dentry, mode);
+}
+
+int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ if (unlikely(IS_PRIVATE(dentry->d_inode)))
+ return 0;
+ return security_ops->inode_rmdir(dir, dentry);
+}
+
+int security_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+ if (unlikely(IS_PRIVATE(dir)))
+ return 0;
+ return security_ops->inode_mknod(dir, dentry, mode, dev);
+}
+
+int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ if (unlikely(IS_PRIVATE(old_dentry->d_inode) ||
+ (new_dentry->d_inode && IS_PRIVATE(new_dentry->d_inode))))
+ return 0;
+ return security_ops->inode_rename(old_dir, old_dentry,
+ new_dir, new_dentry);
+}
+
+int security_inode_readlink(struct dentry *dentry)
+{
+ if (unlikely(IS_PRIVATE(dentry->d_inode)))
+ return 0;
+ return security_ops->inode_readlink(dentry);
+}
+
+int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ if (unlikely(IS_PRIVATE(dentry->d_inode)))
+ return 0;
+ return security_ops->inode_follow_link(dentry, nd);
+}
+
+int security_inode_permission(struct inode *inode, int mask)
+{
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+ return security_ops->inode_permission(inode, mask);
+}
+EXPORT_SYMBOL(security_inode_permission);
+
+int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
+{
+ if (unlikely(IS_PRIVATE(dentry->d_inode)))
+ return 0;
+ return security_ops->inode_setattr(dentry, attr);
+}
+EXPORT_SYMBOL_GPL(security_inode_setattr);
+
+int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
+{
+ if (unlikely(IS_PRIVATE(dentry->d_inode)))
+ return 0;
+ return security_ops->inode_getattr(mnt, dentry);
+}
+
+void security_inode_delete(struct inode *inode)
+{
+ if (unlikely(IS_PRIVATE(inode)))
+ return;
+ security_ops->inode_delete(inode);
+}
+
+int security_inode_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ if (unlikely(IS_PRIVATE(dentry->d_inode)))
+ return 0;
+ return security_ops->inode_setxattr(dentry, name, value, size, flags);
+}
+
+void security_inode_post_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ if (unlikely(IS_PRIVATE(dentry->d_inode)))
+ return;
+ security_ops->inode_post_setxattr(dentry, name, value, size, flags);
+}
+
+int security_inode_getxattr(struct dentry *dentry, const char *name)
+{
+ if (unlikely(IS_PRIVATE(dentry->d_inode)))
+ return 0;
+ return security_ops->inode_getxattr(dentry, name);
+}
+
+int security_inode_listxattr(struct dentry *dentry)
+{
+ if (unlikely(IS_PRIVATE(dentry->d_inode)))
+ return 0;
+ return security_ops->inode_listxattr(dentry);
+}
+
+int security_inode_removexattr(struct dentry *dentry, const char *name)
+{
+ if (unlikely(IS_PRIVATE(dentry->d_inode)))
+ return 0;
+ return security_ops->inode_removexattr(dentry, name);
+}
+
+int security_inode_need_killpriv(struct dentry *dentry)
+{
+ return security_ops->inode_need_killpriv(dentry);
+}
+
+int security_inode_killpriv(struct dentry *dentry)
+{
+ return security_ops->inode_killpriv(dentry);
+}
+
+int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc)
+{
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+ return security_ops->inode_getsecurity(inode, name, buffer, alloc);
+}
+
+int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)
+{
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+ return security_ops->inode_setsecurity(inode, name, value, size, flags);
+}
+
+int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
+{
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+ return security_ops->inode_listsecurity(inode, buffer, buffer_size);
+}
+
+void security_inode_getsecid(const struct inode *inode, u32 *secid)
+{
+ security_ops->inode_getsecid(inode, secid);
+}
+
+int security_file_permission(struct file *file, int mask)
+{
+ return security_ops->file_permission(file, mask);
+}
+
+int security_file_alloc(struct file *file)
+{
+ return security_ops->file_alloc_security(file);
+}
+
+void security_file_free(struct file *file)
+{
+ security_ops->file_free_security(file);
+}
+
+int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return security_ops->file_ioctl(file, cmd, arg);
+}
+
+int security_file_mmap(struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags,
+ unsigned long addr, unsigned long addr_only)
+{
+ return security_ops->file_mmap(file, reqprot, prot, flags, addr, addr_only);
+}
+
+int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+ unsigned long prot)
+{
+ return security_ops->file_mprotect(vma, reqprot, prot);
+}
+
+int security_file_lock(struct file *file, unsigned int cmd)
+{
+ return security_ops->file_lock(file, cmd);
+}
+
+int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return security_ops->file_fcntl(file, cmd, arg);
+}
+
+int security_file_set_fowner(struct file *file)
+{
+ return security_ops->file_set_fowner(file);
+}
+
+int security_file_send_sigiotask(struct task_struct *tsk,
+ struct fown_struct *fown, int sig)
+{
+ return security_ops->file_send_sigiotask(tsk, fown, sig);
+}
+
+int security_file_receive(struct file *file)
+{
+ return security_ops->file_receive(file);
+}
+
+int security_dentry_open(struct file *file)
+{
+ return security_ops->dentry_open(file);
+}
+
+int security_task_create(unsigned long clone_flags)
+{
+ return security_ops->task_create(clone_flags);
+}
+
+int security_task_alloc(struct task_struct *p)
+{
+ return security_ops->task_alloc_security(p);
+}
+
+void security_task_free(struct task_struct *p)
+{
+ security_ops->task_free_security(p);
+}
+
+int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
+{
+ return security_ops->task_setuid(id0, id1, id2, flags);
+}
+
+int security_task_post_setuid(uid_t old_ruid, uid_t old_euid,
+ uid_t old_suid, int flags)
+{
+ return security_ops->task_post_setuid(old_ruid, old_euid, old_suid, flags);
+}
+
+int security_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
+{
+ return security_ops->task_setgid(id0, id1, id2, flags);
+}
+
+int security_task_setpgid(struct task_struct *p, pid_t pgid)
+{
+ return security_ops->task_setpgid(p, pgid);
+}
+
+int security_task_getpgid(struct task_struct *p)
+{
+ return security_ops->task_getpgid(p);
+}
+
+int security_task_getsid(struct task_struct *p)
+{
+ return security_ops->task_getsid(p);
+}
+
+void security_task_getsecid(struct task_struct *p, u32 *secid)
+{
+ security_ops->task_getsecid(p, secid);
+}
+EXPORT_SYMBOL(security_task_getsecid);
+
+int security_task_setgroups(struct group_info *group_info)
+{
+ return security_ops->task_setgroups(group_info);
+}
+
+int security_task_setnice(struct task_struct *p, int nice)
+{
+ return security_ops->task_setnice(p, nice);
+}
+
+int security_task_setioprio(struct task_struct *p, int ioprio)
+{
+ return security_ops->task_setioprio(p, ioprio);
+}
+
+int security_task_getioprio(struct task_struct *p)
+{
+ return security_ops->task_getioprio(p);
+}
+
+int security_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
+{
+ return security_ops->task_setrlimit(resource, new_rlim);
+}
+
+int security_task_setscheduler(struct task_struct *p,
+ int policy, struct sched_param *lp)
+{
+ return security_ops->task_setscheduler(p, policy, lp);
+}
+
+int security_task_getscheduler(struct task_struct *p)
+{
+ return security_ops->task_getscheduler(p);
+}
+
+int security_task_movememory(struct task_struct *p)
+{
+ return security_ops->task_movememory(p);
+}
+
+int security_task_kill(struct task_struct *p, struct siginfo *info,
+ int sig, u32 secid)
+{
+ return security_ops->task_kill(p, info, sig, secid);
+}
+
+int security_task_wait(struct task_struct *p)
+{
+ return security_ops->task_wait(p);
+}
+
+int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
+ unsigned long arg4, unsigned long arg5, long *rc_p)
+{
+ return security_ops->task_prctl(option, arg2, arg3, arg4, arg5, rc_p);
+}
+
+void security_task_reparent_to_init(struct task_struct *p)
+{
+ security_ops->task_reparent_to_init(p);
+}
+
+void security_task_to_inode(struct task_struct *p, struct inode *inode)
+{
+ security_ops->task_to_inode(p, inode);
+}
+
+int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
+{
+ return security_ops->ipc_permission(ipcp, flag);
+}
+
+void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
+{
+ security_ops->ipc_getsecid(ipcp, secid);
+}
+
+int security_msg_msg_alloc(struct msg_msg *msg)
+{
+ return security_ops->msg_msg_alloc_security(msg);
+}
+
+void security_msg_msg_free(struct msg_msg *msg)
+{
+ security_ops->msg_msg_free_security(msg);
+}
+
+int security_msg_queue_alloc(struct msg_queue *msq)
+{
+ return security_ops->msg_queue_alloc_security(msq);
+}
+
+void security_msg_queue_free(struct msg_queue *msq)
+{
+ security_ops->msg_queue_free_security(msq);
+}
+
+int security_msg_queue_associate(struct msg_queue *msq, int msqflg)
+{
+ return security_ops->msg_queue_associate(msq, msqflg);
+}
+
+int security_msg_queue_msgctl(struct msg_queue *msq, int cmd)
+{
+ return security_ops->msg_queue_msgctl(msq, cmd);
+}
+
+int security_msg_queue_msgsnd(struct msg_queue *msq,
+ struct msg_msg *msg, int msqflg)
+{
+ return security_ops->msg_queue_msgsnd(msq, msg, msqflg);
+}
+
+int security_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
+ struct task_struct *target, long type, int mode)
+{
+ return security_ops->msg_queue_msgrcv(msq, msg, target, type, mode);
+}
+
+int security_shm_alloc(struct shmid_kernel *shp)
+{
+ return security_ops->shm_alloc_security(shp);
+}
+
+void security_shm_free(struct shmid_kernel *shp)
+{
+ security_ops->shm_free_security(shp);
+}
+
+int security_shm_associate(struct shmid_kernel *shp, int shmflg)
+{
+ return security_ops->shm_associate(shp, shmflg);
+}
+
+int security_shm_shmctl(struct shmid_kernel *shp, int cmd)
+{
+ return security_ops->shm_shmctl(shp, cmd);
+}
+
+int security_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, int shmflg)
+{
+ return security_ops->shm_shmat(shp, shmaddr, shmflg);
+}
+
+int security_sem_alloc(struct sem_array *sma)
+{
+ return security_ops->sem_alloc_security(sma);
+}
+
+void security_sem_free(struct sem_array *sma)
+{
+ security_ops->sem_free_security(sma);
+}
+
+int security_sem_associate(struct sem_array *sma, int semflg)
+{
+ return security_ops->sem_associate(sma, semflg);
+}
+
+int security_sem_semctl(struct sem_array *sma, int cmd)
+{
+ return security_ops->sem_semctl(sma, cmd);
+}
+
+int security_sem_semop(struct sem_array *sma, struct sembuf *sops,
+ unsigned nsops, int alter)
+{
+ return security_ops->sem_semop(sma, sops, nsops, alter);
+}
+
+void security_d_instantiate(struct dentry *dentry, struct inode *inode)
+{
+ if (unlikely(inode && IS_PRIVATE(inode)))
+ return;
+ security_ops->d_instantiate(dentry, inode);
+}
+EXPORT_SYMBOL(security_d_instantiate);
+
+int security_getprocattr(struct task_struct *p, char *name, char **value)
+{
+ return security_ops->getprocattr(p, name, value);
+}
+
+int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
+{
+ return security_ops->setprocattr(p, name, value, size);
+}
+
+int security_netlink_send(struct sock *sk, struct sk_buff *skb)
+{
+ return security_ops->netlink_send(sk, skb);
+}
+
+int security_netlink_recv(struct sk_buff *skb, int cap)
+{
+ return security_ops->netlink_recv(skb, cap);
+}
+EXPORT_SYMBOL(security_netlink_recv);
+
+int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+{
+ return security_ops->secid_to_secctx(secid, secdata, seclen);
+}
+EXPORT_SYMBOL(security_secid_to_secctx);
+
+int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
+{
+ return security_ops->secctx_to_secid(secdata, seclen, secid);
+}
+EXPORT_SYMBOL(security_secctx_to_secid);
+
+void security_release_secctx(char *secdata, u32 seclen)
+{
+ security_ops->release_secctx(secdata, seclen);
+}
+EXPORT_SYMBOL(security_release_secctx);
+
+#ifdef CONFIG_SECURITY_NETWORK
+
+int security_unix_stream_connect(struct socket *sock, struct socket *other,
+ struct sock *newsk)
+{
+ return security_ops->unix_stream_connect(sock, other, newsk);
+}
+EXPORT_SYMBOL(security_unix_stream_connect);
+
+int security_unix_may_send(struct socket *sock, struct socket *other)
+{
+ return security_ops->unix_may_send(sock, other);
+}
+EXPORT_SYMBOL(security_unix_may_send);
+
+int security_socket_create(int family, int type, int protocol, int kern)
+{
+ return security_ops->socket_create(family, type, protocol, kern);
+}
+
+int security_socket_post_create(struct socket *sock, int family,
+ int type, int protocol, int kern)
+{
+ return security_ops->socket_post_create(sock, family, type,
+ protocol, kern);
+}
+
+int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
+{
+ return security_ops->socket_bind(sock, address, addrlen);
+}
+
+int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
+{
+ return security_ops->socket_connect(sock, address, addrlen);
+}
+
+int security_socket_listen(struct socket *sock, int backlog)
+{
+ return security_ops->socket_listen(sock, backlog);
+}
+
+int security_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ return security_ops->socket_accept(sock, newsock);
+}
+
+void security_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+ security_ops->socket_post_accept(sock, newsock);
+}
+
+int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
+{
+ return security_ops->socket_sendmsg(sock, msg, size);
+}
+
+int security_socket_recvmsg(struct socket *sock, struct msghdr *msg,
+ int size, int flags)
+{
+ return security_ops->socket_recvmsg(sock, msg, size, flags);
+}
+
+int security_socket_getsockname(struct socket *sock)
+{
+ return security_ops->socket_getsockname(sock);
+}
+
+int security_socket_getpeername(struct socket *sock)
+{
+ return security_ops->socket_getpeername(sock);
+}
+
+int security_socket_getsockopt(struct socket *sock, int level, int optname)
+{
+ return security_ops->socket_getsockopt(sock, level, optname);
+}
+
+int security_socket_setsockopt(struct socket *sock, int level, int optname)
+{
+ return security_ops->socket_setsockopt(sock, level, optname);
+}
+
+int security_socket_shutdown(struct socket *sock, int how)
+{
+ return security_ops->socket_shutdown(sock, how);
+}
+
+int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ return security_ops->socket_sock_rcv_skb(sk, skb);
+}
+EXPORT_SYMBOL(security_sock_rcv_skb);
+
+int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
+ int __user *optlen, unsigned len)
+{
+ return security_ops->socket_getpeersec_stream(sock, optval, optlen, len);
+}
+
+int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)
+{
+ return security_ops->socket_getpeersec_dgram(sock, skb, secid);
+}
+EXPORT_SYMBOL(security_socket_getpeersec_dgram);
+
+int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
+{
+ return security_ops->sk_alloc_security(sk, family, priority);
+}
+
+void security_sk_free(struct sock *sk)
+{
+ security_ops->sk_free_security(sk);
+}
+
+void security_sk_clone(const struct sock *sk, struct sock *newsk)
+{
+ security_ops->sk_clone_security(sk, newsk);
+}
+
+void security_sk_classify_flow(struct sock *sk, struct flowi *fl)
+{
+ security_ops->sk_getsecid(sk, &fl->secid);
+}
+EXPORT_SYMBOL(security_sk_classify_flow);
+
+void security_req_classify_flow(const struct request_sock *req, struct flowi *fl)
+{
+ security_ops->req_classify_flow(req, fl);
+}
+EXPORT_SYMBOL(security_req_classify_flow);
+
+void security_sock_graft(struct sock *sk, struct socket *parent)
+{
+ security_ops->sock_graft(sk, parent);
+}
+EXPORT_SYMBOL(security_sock_graft);
+
+int security_inet_conn_request(struct sock *sk,
+ struct sk_buff *skb, struct request_sock *req)
+{
+ return security_ops->inet_conn_request(sk, skb, req);
+}
+EXPORT_SYMBOL(security_inet_conn_request);
+
+void security_inet_csk_clone(struct sock *newsk,
+ const struct request_sock *req)
+{
+ security_ops->inet_csk_clone(newsk, req);
+}
+
+void security_inet_conn_established(struct sock *sk,
+ struct sk_buff *skb)
+{
+ security_ops->inet_conn_established(sk, skb);
+}
+
+#endif /* CONFIG_SECURITY_NETWORK */
+
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+
+int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_user_sec_ctx *sec_ctx)
+{
+ return security_ops->xfrm_policy_alloc_security(ctxp, sec_ctx);
+}
+EXPORT_SYMBOL(security_xfrm_policy_alloc);
+
+int security_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
+ struct xfrm_sec_ctx **new_ctxp)
+{
+ return security_ops->xfrm_policy_clone_security(old_ctx, new_ctxp);
+}
+
+void security_xfrm_policy_free(struct xfrm_sec_ctx *ctx)
+{
+ security_ops->xfrm_policy_free_security(ctx);
+}
+EXPORT_SYMBOL(security_xfrm_policy_free);
+
+int security_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
+{
+ return security_ops->xfrm_policy_delete_security(ctx);
+}
+
+int security_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx)
+{
+ return security_ops->xfrm_state_alloc_security(x, sec_ctx, 0);
+}
+EXPORT_SYMBOL(security_xfrm_state_alloc);
+
+int security_xfrm_state_alloc_acquire(struct xfrm_state *x,
+ struct xfrm_sec_ctx *polsec, u32 secid)
+{
+ if (!polsec)
+ return 0;
+ /*
+ * We want the context to be taken from secid which is usually
+ * from the sock.
+ */
+ return security_ops->xfrm_state_alloc_security(x, NULL, secid);
+}
+
+int security_xfrm_state_delete(struct xfrm_state *x)
+{
+ return security_ops->xfrm_state_delete_security(x);
+}
+EXPORT_SYMBOL(security_xfrm_state_delete);
+
+void security_xfrm_state_free(struct xfrm_state *x)
+{
+ security_ops->xfrm_state_free_security(x);
+}
+
+int security_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir)
+{
+ return security_ops->xfrm_policy_lookup(ctx, fl_secid, dir);
+}
+
+int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
+ struct xfrm_policy *xp, struct flowi *fl)
+{
+ return security_ops->xfrm_state_pol_flow_match(x, xp, fl);
+}
+
+int security_xfrm_decode_session(struct sk_buff *skb, u32 *secid)
+{
+ return security_ops->xfrm_decode_session(skb, secid, 1);
+}
+
+void security_skb_classify_flow(struct sk_buff *skb, struct flowi *fl)
+{
+ int rc = security_ops->xfrm_decode_session(skb, &fl->secid, 0);
+
+ BUG_ON(rc);
+}
+EXPORT_SYMBOL(security_skb_classify_flow);
+
+#endif /* CONFIG_SECURITY_NETWORK_XFRM */
+
+#ifdef CONFIG_KEYS
+
+int security_key_alloc(struct key *key, struct task_struct *tsk, unsigned long flags)
+{
+ return security_ops->key_alloc(key, tsk, flags);
+}
+
+void security_key_free(struct key *key)
+{
+ security_ops->key_free(key);
+}
+
+int security_key_permission(key_ref_t key_ref,
+ struct task_struct *context, key_perm_t perm)
+{
+ return security_ops->key_permission(key_ref, context, perm);
+}
+
+int security_key_getsecurity(struct key *key, char **_buffer)
+{
+ return security_ops->key_getsecurity(key, _buffer);
+}
+
+#endif /* CONFIG_KEYS */
+
+#ifdef CONFIG_AUDIT
+
+int security_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule)
+{
+ return security_ops->audit_rule_init(field, op, rulestr, lsmrule);
+}
+
+int security_audit_rule_known(struct audit_krule *krule)
+{
+ return security_ops->audit_rule_known(krule);
+}
+
+void security_audit_rule_free(void *lsmrule)
+{
+ security_ops->audit_rule_free(lsmrule);
+}
+
+int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
+ struct audit_context *actx)
+{
+ return security_ops->audit_rule_match(secid, field, op, lsmrule, actx);
+}
+
+#endif /* CONFIG_AUDIT */
diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
new file mode 100644
index 0000000..26301dd
--- /dev/null
+++ b/security/selinux/Kconfig
@@ -0,0 +1,160 @@
+config SECURITY_SELINUX
+ bool "NSA SELinux Support"
+ depends on SECURITY_NETWORK && AUDIT && NET && INET
+ select NETWORK_SECMARK
+ default n
+ help
+ This selects NSA Security-Enhanced Linux (SELinux).
+ You will also need a policy configuration and a labeled filesystem.
+ If you are unsure how to answer this question, answer N.
+
+config SECURITY_SELINUX_BOOTPARAM
+ bool "NSA SELinux boot parameter"
+ depends on SECURITY_SELINUX
+ default n
+ help
+ This option adds a kernel parameter 'selinux', which allows SELinux
+ to be disabled at boot. If this option is selected, SELinux
+ functionality can be disabled with selinux=0 on the kernel
+ command line. The purpose of this option is to allow a single
+ kernel image to be distributed with SELinux built in, but not
+ necessarily enabled.
+
+ If you are unsure how to answer this question, answer N.
+
+config SECURITY_SELINUX_BOOTPARAM_VALUE
+ int "NSA SELinux boot parameter default value"
+ depends on SECURITY_SELINUX_BOOTPARAM
+ range 0 1
+ default 1
+ help
+ This option sets the default value for the kernel parameter
+ 'selinux', which allows SELinux to be disabled at boot. If this
+ option is set to 0 (zero), the SELinux kernel parameter will
+ default to 0, disabling SELinux at bootup. If this option is
+ set to 1 (one), the SELinux kernel parameter will default to 1,
+ enabling SELinux at bootup.
+
+ If you are unsure how to answer this question, answer 1.
+
+config SECURITY_SELINUX_DISABLE
+ bool "NSA SELinux runtime disable"
+ depends on SECURITY_SELINUX
+ default n
+ help
+ This option enables writing to a selinuxfs node 'disable', which
+ allows SELinux to be disabled at runtime prior to the policy load.
+ SELinux will then remain disabled until the next boot.
+ This option is similar to the selinux=0 boot parameter, but is to
+ support runtime disabling of SELinux, e.g. from /sbin/init, for
+ portability across platforms where boot parameters are difficult
+ to employ.
+
+ If you are unsure how to answer this question, answer N.
+
+config SECURITY_SELINUX_DEVELOP
+ bool "NSA SELinux Development Support"
+ depends on SECURITY_SELINUX
+ default y
+ help
+ This enables the development support option of NSA SELinux,
+ which is useful for experimenting with SELinux and developing
+ policies. If unsure, say Y. With this option enabled, the
+ kernel will start in permissive mode (log everything, deny nothing)
+ unless you specify enforcing=1 on the kernel command line. You
+ can interactively toggle the kernel between enforcing mode and
+ permissive mode (if permitted by the policy) via /selinux/enforce.
+
+config SECURITY_SELINUX_AVC_STATS
+ bool "NSA SELinux AVC Statistics"
+ depends on SECURITY_SELINUX
+ default y
+ help
+ This option collects access vector cache statistics to
+ /selinux/avc/cache_stats, which may be monitored via
+ tools such as avcstat.
+
+config SECURITY_SELINUX_CHECKREQPROT_VALUE
+ int "NSA SELinux checkreqprot default value"
+ depends on SECURITY_SELINUX
+ range 0 1
+ default 1
+ help
+ This option sets the default value for the 'checkreqprot' flag
+ that determines whether SELinux checks the protection requested
+ by the application or the protection that will be applied by the
+ kernel (including any implied execute for read-implies-exec) for
+ mmap and mprotect calls. If this option is set to 0 (zero),
+ SELinux will default to checking the protection that will be applied
+ by the kernel. If this option is set to 1 (one), SELinux will
+ default to checking the protection requested by the application.
+ The checkreqprot flag may be changed from the default via the
+ 'checkreqprot=' boot parameter. It may also be changed at runtime
+ via /selinux/checkreqprot if authorized by policy.
+
+ If you are unsure how to answer this question, answer 1.
+
+config SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT
+ bool "NSA SELinux enable new secmark network controls by default"
+ depends on SECURITY_SELINUX
+ default n
+ help
+ This option determines whether the new secmark-based network
+ controls will be enabled by default. If not, the old internal
+ per-packet controls will be enabled by default, preserving
+ old behavior.
+
+ If you enable the new controls, you will need updated
+ SELinux userspace libraries, tools and policy. Typically,
+ your distribution will provide these and enable the new controls
+ in the kernel they also distribute.
+
+ Note that this option can be overridden at boot with the
+ selinux_compat_net parameter, and after boot via
+ /selinux/compat_net. See Documentation/kernel-parameters.txt
+ for details on this parameter.
+
+ If you enable the new network controls, you will likely
+ also require the SECMARK and CONNSECMARK targets, as
+ well as any conntrack helpers for protocols which you
+ wish to control.
+
+ If you are unsure what to do here, select N.
+
+config SECURITY_SELINUX_POLICYDB_VERSION_MAX
+ bool "NSA SELinux maximum supported policy format version"
+ depends on SECURITY_SELINUX
+ default n
+ help
+ This option enables the maximum policy format version supported
+ by SELinux to be set to a particular value. This value is reported
+ to userspace via /selinux/policyvers and used at policy load time.
+ It can be adjusted downward to support legacy userland (init) that
+ does not correctly handle kernels that support newer policy versions.
+
+ Examples:
+ For the Fedora Core 3 or 4 Linux distributions, enable this option
+ and set the value via the next option. For Fedora Core 5 and later,
+ do not enable this option.
+
+ If you are unsure how to answer this question, answer N.
+
+config SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
+ int "NSA SELinux maximum supported policy format version value"
+ depends on SECURITY_SELINUX_POLICYDB_VERSION_MAX
+ range 15 23
+ default 19
+ help
+ This option sets the value for the maximum policy format version
+ supported by SELinux.
+
+ Examples:
+ For Fedora Core 3, use 18.
+ For Fedora Core 4, use 19.
+
+ If you are unsure how to answer this question, look for the
+ policy format version supported by your policy toolchain, by
+ running 'checkpolicy -V'. Or look at what policy you have
+ installed under /etc/selinux/$SELINUXTYPE/policy, where
+ SELINUXTYPE is defined in your /etc/selinux/config.
+
diff --git a/security/selinux/Makefile b/security/selinux/Makefile
new file mode 100644
index 0000000..d47fc5e
--- /dev/null
+++ b/security/selinux/Makefile
@@ -0,0 +1,22 @@
+#
+# Makefile for building the SELinux module as part of the kernel tree.
+#
+
+obj-$(CONFIG_SECURITY_SELINUX) := selinux.o ss/
+
+selinux-y := avc.o \
+ hooks.o \
+ selinuxfs.o \
+ netlink.o \
+ nlmsgtab.o \
+ netif.o \
+ netnode.o \
+ netport.o \
+ exports.o
+
+selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o
+
+selinux-$(CONFIG_NETLABEL) += netlabel.o
+
+EXTRA_CFLAGS += -Isecurity/selinux/include
+
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
new file mode 100644
index 0000000..cb30c7e
--- /dev/null
+++ b/security/selinux/avc.c
@@ -0,0 +1,949 @@
+/*
+ * Implementation of the kernel access vector cache (AVC).
+ *
+ * Authors: Stephen Smalley, <sds@epoch.ncsc.mil>
+ * James Morris <jmorris@redhat.com>
+ *
+ * Update: KaiGai, Kohei <kaigai@ak.jp.nec.com>
+ * Replaced the avc_lock spinlock by RCU.
+ *
+ * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/dcache.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/percpu.h>
+#include <net/sock.h>
+#include <linux/un.h>
+#include <net/af_unix.h>
+#include <linux/ip.h>
+#include <linux/audit.h>
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
+#include "avc.h"
+#include "avc_ss.h"
+
+static const struct av_perm_to_string av_perm_to_string[] = {
+#define S_(c, v, s) { c, v, s },
+#include "av_perm_to_string.h"
+#undef S_
+};
+
+static const char *class_to_string[] = {
+#define S_(s) s,
+#include "class_to_string.h"
+#undef S_
+};
+
+#define TB_(s) static const char *s[] = {
+#define TE_(s) };
+#define S_(s) s,
+#include "common_perm_to_string.h"
+#undef TB_
+#undef TE_
+#undef S_
+
+static const struct av_inherit av_inherit[] = {
+#define S_(c, i, b) { c, common_##i##_perm_to_string, b },
+#include "av_inherit.h"
+#undef S_
+};
+
+const struct selinux_class_perm selinux_class_perm = {
+ av_perm_to_string,
+ ARRAY_SIZE(av_perm_to_string),
+ class_to_string,
+ ARRAY_SIZE(class_to_string),
+ av_inherit,
+ ARRAY_SIZE(av_inherit)
+};
+
+#define AVC_CACHE_SLOTS 512
+#define AVC_DEF_CACHE_THRESHOLD 512
+#define AVC_CACHE_RECLAIM 16
+
+#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
+#define avc_cache_stats_incr(field) \
+do { \
+ per_cpu(avc_cache_stats, get_cpu()).field++; \
+ put_cpu(); \
+} while (0)
+#else
+#define avc_cache_stats_incr(field) do {} while (0)
+#endif
+
+struct avc_entry {
+ u32 ssid;
+ u32 tsid;
+ u16 tclass;
+ struct av_decision avd;
+ atomic_t used; /* used recently */
+};
+
+struct avc_node {
+ struct avc_entry ae;
+ struct list_head list;
+ struct rcu_head rhead;
+};
+
+struct avc_cache {
+ struct list_head slots[AVC_CACHE_SLOTS];
+ spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */
+ atomic_t lru_hint; /* LRU hint for reclaim scan */
+ atomic_t active_nodes;
+ u32 latest_notif; /* latest revocation notification */
+};
+
+struct avc_callback_node {
+ int (*callback) (u32 event, u32 ssid, u32 tsid,
+ u16 tclass, u32 perms,
+ u32 *out_retained);
+ u32 events;
+ u32 ssid;
+ u32 tsid;
+ u16 tclass;
+ u32 perms;
+ struct avc_callback_node *next;
+};
+
+/* Exported via selinufs */
+unsigned int avc_cache_threshold = AVC_DEF_CACHE_THRESHOLD;
+
+#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
+DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 };
+#endif
+
+static struct avc_cache avc_cache;
+static struct avc_callback_node *avc_callbacks;
+static struct kmem_cache *avc_node_cachep;
+
+static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
+{
+ return (ssid ^ (tsid<<2) ^ (tclass<<4)) & (AVC_CACHE_SLOTS - 1);
+}
+
+/**
+ * avc_dump_av - Display an access vector in human-readable form.
+ * @tclass: target security class
+ * @av: access vector
+ */
+void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
+{
+ const char **common_pts = NULL;
+ u32 common_base = 0;
+ int i, i2, perm;
+
+ if (av == 0) {
+ audit_log_format(ab, " null");
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(av_inherit); i++) {
+ if (av_inherit[i].tclass == tclass) {
+ common_pts = av_inherit[i].common_pts;
+ common_base = av_inherit[i].common_base;
+ break;
+ }
+ }
+
+ audit_log_format(ab, " {");
+ i = 0;
+ perm = 1;
+ while (perm < common_base) {
+ if (perm & av) {
+ audit_log_format(ab, " %s", common_pts[i]);
+ av &= ~perm;
+ }
+ i++;
+ perm <<= 1;
+ }
+
+ while (i < sizeof(av) * 8) {
+ if (perm & av) {
+ for (i2 = 0; i2 < ARRAY_SIZE(av_perm_to_string); i2++) {
+ if ((av_perm_to_string[i2].tclass == tclass) &&
+ (av_perm_to_string[i2].value == perm))
+ break;
+ }
+ if (i2 < ARRAY_SIZE(av_perm_to_string)) {
+ audit_log_format(ab, " %s",
+ av_perm_to_string[i2].name);
+ av &= ~perm;
+ }
+ }
+ i++;
+ perm <<= 1;
+ }
+
+ if (av)
+ audit_log_format(ab, " 0x%x", av);
+
+ audit_log_format(ab, " }");
+}
+
+/**
+ * avc_dump_query - Display a SID pair and a class in human-readable form.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ */
+static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tclass)
+{
+ int rc;
+ char *scontext;
+ u32 scontext_len;
+
+ rc = security_sid_to_context(ssid, &scontext, &scontext_len);
+ if (rc)
+ audit_log_format(ab, "ssid=%d", ssid);
+ else {
+ audit_log_format(ab, "scontext=%s", scontext);
+ kfree(scontext);
+ }
+
+ rc = security_sid_to_context(tsid, &scontext, &scontext_len);
+ if (rc)
+ audit_log_format(ab, " tsid=%d", tsid);
+ else {
+ audit_log_format(ab, " tcontext=%s", scontext);
+ kfree(scontext);
+ }
+
+ BUG_ON(tclass >= ARRAY_SIZE(class_to_string) || !class_to_string[tclass]);
+ audit_log_format(ab, " tclass=%s", class_to_string[tclass]);
+}
+
+/**
+ * avc_init - Initialize the AVC.
+ *
+ * Initialize the access vector cache.
+ */
+void __init avc_init(void)
+{
+ int i;
+
+ for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+ INIT_LIST_HEAD(&avc_cache.slots[i]);
+ spin_lock_init(&avc_cache.slots_lock[i]);
+ }
+ atomic_set(&avc_cache.active_nodes, 0);
+ atomic_set(&avc_cache.lru_hint, 0);
+
+ avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
+ 0, SLAB_PANIC, NULL);
+
+ audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n");
+}
+
+int avc_get_hash_stats(char *page)
+{
+ int i, chain_len, max_chain_len, slots_used;
+ struct avc_node *node;
+
+ rcu_read_lock();
+
+ slots_used = 0;
+ max_chain_len = 0;
+ for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+ if (!list_empty(&avc_cache.slots[i])) {
+ slots_used++;
+ chain_len = 0;
+ list_for_each_entry_rcu(node, &avc_cache.slots[i], list)
+ chain_len++;
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ }
+ }
+
+ rcu_read_unlock();
+
+ return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
+ "longest chain: %d\n",
+ atomic_read(&avc_cache.active_nodes),
+ slots_used, AVC_CACHE_SLOTS, max_chain_len);
+}
+
+static void avc_node_free(struct rcu_head *rhead)
+{
+ struct avc_node *node = container_of(rhead, struct avc_node, rhead);
+ kmem_cache_free(avc_node_cachep, node);
+ avc_cache_stats_incr(frees);
+}
+
+static void avc_node_delete(struct avc_node *node)
+{
+ list_del_rcu(&node->list);
+ call_rcu(&node->rhead, avc_node_free);
+ atomic_dec(&avc_cache.active_nodes);
+}
+
+static void avc_node_kill(struct avc_node *node)
+{
+ kmem_cache_free(avc_node_cachep, node);
+ avc_cache_stats_incr(frees);
+ atomic_dec(&avc_cache.active_nodes);
+}
+
+static void avc_node_replace(struct avc_node *new, struct avc_node *old)
+{
+ list_replace_rcu(&old->list, &new->list);
+ call_rcu(&old->rhead, avc_node_free);
+ atomic_dec(&avc_cache.active_nodes);
+}
+
+static inline int avc_reclaim_node(void)
+{
+ struct avc_node *node;
+ int hvalue, try, ecx;
+ unsigned long flags;
+
+ for (try = 0, ecx = 0; try < AVC_CACHE_SLOTS; try++) {
+ hvalue = atomic_inc_return(&avc_cache.lru_hint) & (AVC_CACHE_SLOTS - 1);
+
+ if (!spin_trylock_irqsave(&avc_cache.slots_lock[hvalue], flags))
+ continue;
+
+ rcu_read_lock();
+ list_for_each_entry(node, &avc_cache.slots[hvalue], list) {
+ if (atomic_dec_and_test(&node->ae.used)) {
+ /* Recently Unused */
+ avc_node_delete(node);
+ avc_cache_stats_incr(reclaims);
+ ecx++;
+ if (ecx >= AVC_CACHE_RECLAIM) {
+ rcu_read_unlock();
+ spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
+ goto out;
+ }
+ }
+ }
+ rcu_read_unlock();
+ spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flags);
+ }
+out:
+ return ecx;
+}
+
+static struct avc_node *avc_alloc_node(void)
+{
+ struct avc_node *node;
+
+ node = kmem_cache_zalloc(avc_node_cachep, GFP_ATOMIC);
+ if (!node)
+ goto out;
+
+ INIT_RCU_HEAD(&node->rhead);
+ INIT_LIST_HEAD(&node->list);
+ atomic_set(&node->ae.used, 1);
+ avc_cache_stats_incr(allocations);
+
+ if (atomic_inc_return(&avc_cache.active_nodes) > avc_cache_threshold)
+ avc_reclaim_node();
+
+out:
+ return node;
+}
+
+static void avc_node_populate(struct avc_node *node, u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
+{
+ node->ae.ssid = ssid;
+ node->ae.tsid = tsid;
+ node->ae.tclass = tclass;
+ memcpy(&node->ae.avd, &ae->avd, sizeof(node->ae.avd));
+}
+
+static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
+{
+ struct avc_node *node, *ret = NULL;
+ int hvalue;
+
+ hvalue = avc_hash(ssid, tsid, tclass);
+ list_for_each_entry_rcu(node, &avc_cache.slots[hvalue], list) {
+ if (ssid == node->ae.ssid &&
+ tclass == node->ae.tclass &&
+ tsid == node->ae.tsid) {
+ ret = node;
+ break;
+ }
+ }
+
+ if (ret == NULL) {
+ /* cache miss */
+ goto out;
+ }
+
+ /* cache hit */
+ if (atomic_read(&ret->ae.used) != 1)
+ atomic_set(&ret->ae.used, 1);
+out:
+ return ret;
+}
+
+/**
+ * avc_lookup - Look up an AVC entry.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions, interpreted based on @tclass
+ *
+ * Look up an AVC entry that is valid for the
+ * @requested permissions between the SID pair
+ * (@ssid, @tsid), interpreting the permissions
+ * based on @tclass. If a valid AVC entry exists,
+ * then this function return the avc_node.
+ * Otherwise, this function returns NULL.
+ */
+static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass, u32 requested)
+{
+ struct avc_node *node;
+
+ avc_cache_stats_incr(lookups);
+ node = avc_search_node(ssid, tsid, tclass);
+
+ if (node && ((node->ae.avd.decided & requested) == requested)) {
+ avc_cache_stats_incr(hits);
+ goto out;
+ }
+
+ node = NULL;
+ avc_cache_stats_incr(misses);
+out:
+ return node;
+}
+
+static int avc_latest_notif_update(int seqno, int is_insert)
+{
+ int ret = 0;
+ static DEFINE_SPINLOCK(notif_lock);
+ unsigned long flag;
+
+ spin_lock_irqsave(&notif_lock, flag);
+ if (is_insert) {
+ if (seqno < avc_cache.latest_notif) {
+ printk(KERN_WARNING "SELinux: avc: seqno %d < latest_notif %d\n",
+ seqno, avc_cache.latest_notif);
+ ret = -EAGAIN;
+ }
+ } else {
+ if (seqno > avc_cache.latest_notif)
+ avc_cache.latest_notif = seqno;
+ }
+ spin_unlock_irqrestore(&notif_lock, flag);
+
+ return ret;
+}
+
+/**
+ * avc_insert - Insert an AVC entry.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @ae: AVC entry
+ *
+ * Insert an AVC entry for the SID pair
+ * (@ssid, @tsid) and class @tclass.
+ * The access vectors and the sequence number are
+ * normally provided by the security server in
+ * response to a security_compute_av() call. If the
+ * sequence number @ae->avd.seqno is not less than the latest
+ * revocation notification, then the function copies
+ * the access vectors into a cache entry, returns
+ * avc_node inserted. Otherwise, this function returns NULL.
+ */
+static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct avc_entry *ae)
+{
+ struct avc_node *pos, *node = NULL;
+ int hvalue;
+ unsigned long flag;
+
+ if (avc_latest_notif_update(ae->avd.seqno, 1))
+ goto out;
+
+ node = avc_alloc_node();
+ if (node) {
+ hvalue = avc_hash(ssid, tsid, tclass);
+ avc_node_populate(node, ssid, tsid, tclass, ae);
+
+ spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
+ list_for_each_entry(pos, &avc_cache.slots[hvalue], list) {
+ if (pos->ae.ssid == ssid &&
+ pos->ae.tsid == tsid &&
+ pos->ae.tclass == tclass) {
+ avc_node_replace(node, pos);
+ goto found;
+ }
+ }
+ list_add_rcu(&node->list, &avc_cache.slots[hvalue]);
+found:
+ spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
+ }
+out:
+ return node;
+}
+
+static inline void avc_print_ipv6_addr(struct audit_buffer *ab,
+ struct in6_addr *addr, __be16 port,
+ char *name1, char *name2)
+{
+ if (!ipv6_addr_any(addr))
+ audit_log_format(ab, " %s=" NIP6_FMT, name1, NIP6(*addr));
+ if (port)
+ audit_log_format(ab, " %s=%d", name2, ntohs(port));
+}
+
+static inline void avc_print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
+ __be16 port, char *name1, char *name2)
+{
+ if (addr)
+ audit_log_format(ab, " %s=" NIPQUAD_FMT, name1, NIPQUAD(addr));
+ if (port)
+ audit_log_format(ab, " %s=%d", name2, ntohs(port));
+}
+
+/**
+ * avc_audit - Audit the granting or denial of permissions.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions
+ * @avd: access vector decisions
+ * @result: result from avc_has_perm_noaudit
+ * @a: auxiliary audit data
+ *
+ * Audit the granting or denial of permissions in accordance
+ * with the policy. This function is typically called by
+ * avc_has_perm() after a permission check, but can also be
+ * called directly by callers who use avc_has_perm_noaudit()
+ * in order to separate the permission check from the auditing.
+ * For example, this separation is useful when the permission check must
+ * be performed under a lock, to allow the lock to be released
+ * before calling the auditing code.
+ */
+void avc_audit(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct av_decision *avd, int result, struct avc_audit_data *a)
+{
+ struct task_struct *tsk = current;
+ struct inode *inode = NULL;
+ u32 denied, audited;
+ struct audit_buffer *ab;
+
+ denied = requested & ~avd->allowed;
+ if (denied) {
+ audited = denied;
+ if (!(audited & avd->auditdeny))
+ return;
+ } else if (result) {
+ audited = denied = requested;
+ } else {
+ audited = requested;
+ if (!(audited & avd->auditallow))
+ return;
+ }
+
+ ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_AVC);
+ if (!ab)
+ return; /* audit_panic has been called */
+ audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted");
+ avc_dump_av(ab, tclass, audited);
+ audit_log_format(ab, " for ");
+ if (a && a->tsk)
+ tsk = a->tsk;
+ if (tsk && tsk->pid) {
+ audit_log_format(ab, " pid=%d comm=", tsk->pid);
+ audit_log_untrustedstring(ab, tsk->comm);
+ }
+ if (a) {
+ switch (a->type) {
+ case AVC_AUDIT_DATA_IPC:
+ audit_log_format(ab, " key=%d", a->u.ipc_id);
+ break;
+ case AVC_AUDIT_DATA_CAP:
+ audit_log_format(ab, " capability=%d", a->u.cap);
+ break;
+ case AVC_AUDIT_DATA_FS:
+ if (a->u.fs.path.dentry) {
+ struct dentry *dentry = a->u.fs.path.dentry;
+ if (a->u.fs.path.mnt) {
+ audit_log_d_path(ab, "path=",
+ &a->u.fs.path);
+ } else {
+ audit_log_format(ab, " name=");
+ audit_log_untrustedstring(ab, dentry->d_name.name);
+ }
+ inode = dentry->d_inode;
+ } else if (a->u.fs.inode) {
+ struct dentry *dentry;
+ inode = a->u.fs.inode;
+ dentry = d_find_alias(inode);
+ if (dentry) {
+ audit_log_format(ab, " name=");
+ audit_log_untrustedstring(ab, dentry->d_name.name);
+ dput(dentry);
+ }
+ }
+ if (inode)
+ audit_log_format(ab, " dev=%s ino=%lu",
+ inode->i_sb->s_id,
+ inode->i_ino);
+ break;
+ case AVC_AUDIT_DATA_NET:
+ if (a->u.net.sk) {
+ struct sock *sk = a->u.net.sk;
+ struct unix_sock *u;
+ int len = 0;
+ char *p = NULL;
+
+ switch (sk->sk_family) {
+ case AF_INET: {
+ struct inet_sock *inet = inet_sk(sk);
+
+ avc_print_ipv4_addr(ab, inet->rcv_saddr,
+ inet->sport,
+ "laddr", "lport");
+ avc_print_ipv4_addr(ab, inet->daddr,
+ inet->dport,
+ "faddr", "fport");
+ break;
+ }
+ case AF_INET6: {
+ struct inet_sock *inet = inet_sk(sk);
+ struct ipv6_pinfo *inet6 = inet6_sk(sk);
+
+ avc_print_ipv6_addr(ab, &inet6->rcv_saddr,
+ inet->sport,
+ "laddr", "lport");
+ avc_print_ipv6_addr(ab, &inet6->daddr,
+ inet->dport,
+ "faddr", "fport");
+ break;
+ }
+ case AF_UNIX:
+ u = unix_sk(sk);
+ if (u->dentry) {
+ struct path path = {
+ .dentry = u->dentry,
+ .mnt = u->mnt
+ };
+ audit_log_d_path(ab, "path=",
+ &path);
+ break;
+ }
+ if (!u->addr)
+ break;
+ len = u->addr->len-sizeof(short);
+ p = &u->addr->name->sun_path[0];
+ audit_log_format(ab, " path=");
+ if (*p)
+ audit_log_untrustedstring(ab, p);
+ else
+ audit_log_n_hex(ab, p, len);
+ break;
+ }
+ }
+
+ switch (a->u.net.family) {
+ case AF_INET:
+ avc_print_ipv4_addr(ab, a->u.net.v4info.saddr,
+ a->u.net.sport,
+ "saddr", "src");
+ avc_print_ipv4_addr(ab, a->u.net.v4info.daddr,
+ a->u.net.dport,
+ "daddr", "dest");
+ break;
+ case AF_INET6:
+ avc_print_ipv6_addr(ab, &a->u.net.v6info.saddr,
+ a->u.net.sport,
+ "saddr", "src");
+ avc_print_ipv6_addr(ab, &a->u.net.v6info.daddr,
+ a->u.net.dport,
+ "daddr", "dest");
+ break;
+ }
+ if (a->u.net.netif > 0) {
+ struct net_device *dev;
+
+ /* NOTE: we always use init's namespace */
+ dev = dev_get_by_index(&init_net,
+ a->u.net.netif);
+ if (dev) {
+ audit_log_format(ab, " netif=%s",
+ dev->name);
+ dev_put(dev);
+ }
+ }
+ break;
+ }
+ }
+ audit_log_format(ab, " ");
+ avc_dump_query(ab, ssid, tsid, tclass);
+ audit_log_end(ab);
+}
+
+/**
+ * avc_add_callback - Register a callback for security events.
+ * @callback: callback function
+ * @events: security events
+ * @ssid: source security identifier or %SECSID_WILD
+ * @tsid: target security identifier or %SECSID_WILD
+ * @tclass: target security class
+ * @perms: permissions
+ *
+ * Register a callback function for events in the set @events
+ * related to the SID pair (@ssid, @tsid) and
+ * and the permissions @perms, interpreting
+ * @perms based on @tclass. Returns %0 on success or
+ * -%ENOMEM if insufficient memory exists to add the callback.
+ */
+int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
+ u16 tclass, u32 perms,
+ u32 *out_retained),
+ u32 events, u32 ssid, u32 tsid,
+ u16 tclass, u32 perms)
+{
+ struct avc_callback_node *c;
+ int rc = 0;
+
+ c = kmalloc(sizeof(*c), GFP_ATOMIC);
+ if (!c) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ c->callback = callback;
+ c->events = events;
+ c->ssid = ssid;
+ c->tsid = tsid;
+ c->perms = perms;
+ c->next = avc_callbacks;
+ avc_callbacks = c;
+out:
+ return rc;
+}
+
+static inline int avc_sidcmp(u32 x, u32 y)
+{
+ return (x == y || x == SECSID_WILD || y == SECSID_WILD);
+}
+
+/**
+ * avc_update_node Update an AVC entry
+ * @event : Updating event
+ * @perms : Permission mask bits
+ * @ssid,@tsid,@tclass : identifier of an AVC entry
+ *
+ * if a valid AVC entry doesn't exist,this function returns -ENOENT.
+ * if kmalloc() called internal returns NULL, this function returns -ENOMEM.
+ * otherwise, this function update the AVC entry. The original AVC-entry object
+ * will release later by RCU.
+ */
+static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass)
+{
+ int hvalue, rc = 0;
+ unsigned long flag;
+ struct avc_node *pos, *node, *orig = NULL;
+
+ node = avc_alloc_node();
+ if (!node) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* Lock the target slot */
+ hvalue = avc_hash(ssid, tsid, tclass);
+ spin_lock_irqsave(&avc_cache.slots_lock[hvalue], flag);
+
+ list_for_each_entry(pos, &avc_cache.slots[hvalue], list) {
+ if (ssid == pos->ae.ssid &&
+ tsid == pos->ae.tsid &&
+ tclass == pos->ae.tclass){
+ orig = pos;
+ break;
+ }
+ }
+
+ if (!orig) {
+ rc = -ENOENT;
+ avc_node_kill(node);
+ goto out_unlock;
+ }
+
+ /*
+ * Copy and replace original node.
+ */
+
+ avc_node_populate(node, ssid, tsid, tclass, &orig->ae);
+
+ switch (event) {
+ case AVC_CALLBACK_GRANT:
+ node->ae.avd.allowed |= perms;
+ break;
+ case AVC_CALLBACK_TRY_REVOKE:
+ case AVC_CALLBACK_REVOKE:
+ node->ae.avd.allowed &= ~perms;
+ break;
+ case AVC_CALLBACK_AUDITALLOW_ENABLE:
+ node->ae.avd.auditallow |= perms;
+ break;
+ case AVC_CALLBACK_AUDITALLOW_DISABLE:
+ node->ae.avd.auditallow &= ~perms;
+ break;
+ case AVC_CALLBACK_AUDITDENY_ENABLE:
+ node->ae.avd.auditdeny |= perms;
+ break;
+ case AVC_CALLBACK_AUDITDENY_DISABLE:
+ node->ae.avd.auditdeny &= ~perms;
+ break;
+ }
+ avc_node_replace(node, orig);
+out_unlock:
+ spin_unlock_irqrestore(&avc_cache.slots_lock[hvalue], flag);
+out:
+ return rc;
+}
+
+/**
+ * avc_ss_reset - Flush the cache and revalidate migrated permissions.
+ * @seqno: policy sequence number
+ */
+int avc_ss_reset(u32 seqno)
+{
+ struct avc_callback_node *c;
+ int i, rc = 0, tmprc;
+ unsigned long flag;
+ struct avc_node *node;
+
+ for (i = 0; i < AVC_CACHE_SLOTS; i++) {
+ spin_lock_irqsave(&avc_cache.slots_lock[i], flag);
+ /*
+ * With preemptable RCU, the outer spinlock does not
+ * prevent RCU grace periods from ending.
+ */
+ rcu_read_lock();
+ list_for_each_entry(node, &avc_cache.slots[i], list)
+ avc_node_delete(node);
+ rcu_read_unlock();
+ spin_unlock_irqrestore(&avc_cache.slots_lock[i], flag);
+ }
+
+ for (c = avc_callbacks; c; c = c->next) {
+ if (c->events & AVC_CALLBACK_RESET) {
+ tmprc = c->callback(AVC_CALLBACK_RESET,
+ 0, 0, 0, 0, NULL);
+ /* save the first error encountered for the return
+ value and continue processing the callbacks */
+ if (!rc)
+ rc = tmprc;
+ }
+ }
+
+ avc_latest_notif_update(seqno, 0);
+ return rc;
+}
+
+/**
+ * avc_has_perm_noaudit - Check permissions but perform no auditing.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions, interpreted based on @tclass
+ * @flags: AVC_STRICT or 0
+ * @avd: access vector decisions
+ *
+ * Check the AVC to determine whether the @requested permissions are granted
+ * for the SID pair (@ssid, @tsid), interpreting the permissions
+ * based on @tclass, and call the security server on a cache miss to obtain
+ * a new decision and add it to the cache. Return a copy of the decisions
+ * in @avd. Return %0 if all @requested permissions are granted,
+ * -%EACCES if any permissions are denied, or another -errno upon
+ * other errors. This function is typically called by avc_has_perm(),
+ * but may also be called directly to separate permission checking from
+ * auditing, e.g. in cases where a lock must be held for the check but
+ * should be released for the auditing.
+ */
+int avc_has_perm_noaudit(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ unsigned flags,
+ struct av_decision *avd)
+{
+ struct avc_node *node;
+ struct avc_entry entry, *p_ae;
+ int rc = 0;
+ u32 denied;
+
+ BUG_ON(!requested);
+
+ rcu_read_lock();
+
+ node = avc_lookup(ssid, tsid, tclass, requested);
+ if (!node) {
+ rcu_read_unlock();
+ rc = security_compute_av(ssid, tsid, tclass, requested, &entry.avd);
+ if (rc)
+ goto out;
+ rcu_read_lock();
+ node = avc_insert(ssid, tsid, tclass, &entry);
+ }
+
+ p_ae = node ? &node->ae : &entry;
+
+ if (avd)
+ memcpy(avd, &p_ae->avd, sizeof(*avd));
+
+ denied = requested & ~(p_ae->avd.allowed);
+
+ if (denied) {
+ if (flags & AVC_STRICT)
+ rc = -EACCES;
+ else if (!selinux_enforcing || security_permissive_sid(ssid))
+ avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
+ tsid, tclass);
+ else
+ rc = -EACCES;
+ }
+
+ rcu_read_unlock();
+out:
+ return rc;
+}
+
+/**
+ * avc_has_perm - Check permissions and perform any appropriate auditing.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions, interpreted based on @tclass
+ * @auditdata: auxiliary audit data
+ *
+ * Check the AVC to determine whether the @requested permissions are granted
+ * for the SID pair (@ssid, @tsid), interpreting the permissions
+ * based on @tclass, and call the security server on a cache miss to obtain
+ * a new decision and add it to the cache. Audit the granting or denial of
+ * permissions in accordance with the policy. Return %0 if all @requested
+ * permissions are granted, -%EACCES if any permissions are denied, or
+ * another -errno upon other errors.
+ */
+int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
+ u32 requested, struct avc_audit_data *auditdata)
+{
+ struct av_decision avd;
+ int rc;
+
+ rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
+ avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
+ return rc;
+}
+
+u32 avc_policy_seqno(void)
+{
+ return avc_cache.latest_notif;
+}
diff --git a/security/selinux/exports.c b/security/selinux/exports.c
new file mode 100644
index 0000000..64af2d3
--- /dev/null
+++ b/security/selinux/exports.c
@@ -0,0 +1,61 @@
+/*
+ * SELinux services exported to the rest of the kernel.
+ *
+ * Author: James Morris <jmorris@redhat.com>
+ *
+ * Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * Copyright (C) 2006 Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ * Copyright (C) 2006 IBM Corporation, Timothy R. Chavez <tinytim@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/selinux.h>
+#include <linux/fs.h>
+#include <linux/ipc.h>
+#include <asm/atomic.h>
+
+#include "security.h"
+#include "objsec.h"
+
+/* SECMARK reference count */
+extern atomic_t selinux_secmark_refcount;
+
+int selinux_string_to_sid(char *str, u32 *sid)
+{
+ if (selinux_enabled)
+ return security_context_to_sid(str, strlen(str), sid);
+ else {
+ *sid = 0;
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(selinux_string_to_sid);
+
+int selinux_secmark_relabel_packet_permission(u32 sid)
+{
+ if (selinux_enabled) {
+ struct task_security_struct *tsec = current->security;
+
+ return avc_has_perm(tsec->sid, sid, SECCLASS_PACKET,
+ PACKET__RELABELTO, NULL);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(selinux_secmark_relabel_packet_permission);
+
+void selinux_secmark_refcount_inc(void)
+{
+ atomic_inc(&selinux_secmark_refcount);
+}
+EXPORT_SYMBOL_GPL(selinux_secmark_refcount_inc);
+
+void selinux_secmark_refcount_dec(void)
+{
+ atomic_dec(&selinux_secmark_refcount);
+}
+EXPORT_SYMBOL_GPL(selinux_secmark_refcount_dec);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
new file mode 100644
index 0000000..f85597a
--- /dev/null
+++ b/security/selinux/hooks.c
@@ -0,0 +1,5867 @@
+/*
+ * NSA Security-Enhanced Linux (SELinux) security module
+ *
+ * This file contains the SELinux hook function implementations.
+ *
+ * Authors: Stephen Smalley, <sds@epoch.ncsc.mil>
+ * Chris Vance, <cvance@nai.com>
+ * Wayne Salamon, <wsalamon@nai.com>
+ * James Morris <jmorris@redhat.com>
+ *
+ * Copyright (C) 2001,2002 Networks Associates Technology, Inc.
+ * Copyright (C) 2003-2008 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * Eric Paris <eparis@redhat.com>
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ * <dgoeddel@trustedcs.com>
+ * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
+ * Paul Moore <paul.moore@hp.com>
+ * Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
+ * Yuichi Nakamura <ynakam@hitachisoft.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/tracehook.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/security.h>
+#include <linux/xattr.h>
+#include <linux/capability.h>
+#include <linux/unistd.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+#include <linux/spinlock.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fdtable.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/proc_fs.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/tty.h>
+#include <net/icmp.h>
+#include <net/ip.h> /* for local_port_range[] */
+#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */
+#include <net/net_namespace.h>
+#include <net/netlabel.h>
+#include <linux/uaccess.h>
+#include <asm/ioctls.h>
+#include <asm/atomic.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h> /* for network interface checks */
+#include <linux/netlink.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/dccp.h>
+#include <linux/quota.h>
+#include <linux/un.h> /* for Unix socket types */
+#include <net/af_unix.h> /* for Unix socket types */
+#include <linux/parser.h>
+#include <linux/nfs_mount.h>
+#include <net/ipv6.h>
+#include <linux/hugetlb.h>
+#include <linux/personality.h>
+#include <linux/sysctl.h>
+#include <linux/audit.h>
+#include <linux/string.h>
+#include <linux/selinux.h>
+#include <linux/mutex.h>
+#include <linux/posix-timers.h>
+
+#include "avc.h"
+#include "objsec.h"
+#include "netif.h"
+#include "netnode.h"
+#include "netport.h"
+#include "xfrm.h"
+#include "netlabel.h"
+#include "audit.h"
+
+#define XATTR_SELINUX_SUFFIX "selinux"
+#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
+
+#define NUM_SEL_MNT_OPTS 4
+
+extern unsigned int policydb_loaded_version;
+extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
+extern int selinux_compat_net;
+extern struct security_operations *security_ops;
+
+/* SECMARK reference count */
+atomic_t selinux_secmark_refcount = ATOMIC_INIT(0);
+
+#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
+int selinux_enforcing;
+
+static int __init enforcing_setup(char *str)
+{
+ unsigned long enforcing;
+ if (!strict_strtoul(str, 0, &enforcing))
+ selinux_enforcing = enforcing ? 1 : 0;
+ return 1;
+}
+__setup("enforcing=", enforcing_setup);
+#endif
+
+#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
+int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;
+
+static int __init selinux_enabled_setup(char *str)
+{
+ unsigned long enabled;
+ if (!strict_strtoul(str, 0, &enabled))
+ selinux_enabled = enabled ? 1 : 0;
+ return 1;
+}
+__setup("selinux=", selinux_enabled_setup);
+#else
+int selinux_enabled = 1;
+#endif
+
+
+/*
+ * Minimal support for a secondary security module,
+ * just to allow the use of the capability module.
+ */
+static struct security_operations *secondary_ops;
+
+/* Lists of inode and superblock security structures initialized
+ before the policy was loaded. */
+static LIST_HEAD(superblock_security_head);
+static DEFINE_SPINLOCK(sb_security_lock);
+
+static struct kmem_cache *sel_inode_cache;
+
+/**
+ * selinux_secmark_enabled - Check to see if SECMARK is currently enabled
+ *
+ * Description:
+ * This function checks the SECMARK reference counter to see if any SECMARK
+ * targets are currently configured, if the reference counter is greater than
+ * zero SECMARK is considered to be enabled. Returns true (1) if SECMARK is
+ * enabled, false (0) if SECMARK is disabled.
+ *
+ */
+static int selinux_secmark_enabled(void)
+{
+ return (atomic_read(&selinux_secmark_refcount) > 0);
+}
+
+/* Allocate and free functions for each kind of security blob. */
+
+static int task_alloc_security(struct task_struct *task)
+{
+ struct task_security_struct *tsec;
+
+ tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
+ if (!tsec)
+ return -ENOMEM;
+
+ tsec->osid = tsec->sid = SECINITSID_UNLABELED;
+ task->security = tsec;
+
+ return 0;
+}
+
+static void task_free_security(struct task_struct *task)
+{
+ struct task_security_struct *tsec = task->security;
+ task->security = NULL;
+ kfree(tsec);
+}
+
+static int inode_alloc_security(struct inode *inode)
+{
+ struct task_security_struct *tsec = current->security;
+ struct inode_security_struct *isec;
+
+ isec = kmem_cache_zalloc(sel_inode_cache, GFP_NOFS);
+ if (!isec)
+ return -ENOMEM;
+
+ mutex_init(&isec->lock);
+ INIT_LIST_HEAD(&isec->list);
+ isec->inode = inode;
+ isec->sid = SECINITSID_UNLABELED;
+ isec->sclass = SECCLASS_FILE;
+ isec->task_sid = tsec->sid;
+ inode->i_security = isec;
+
+ return 0;
+}
+
+static void inode_free_security(struct inode *inode)
+{
+ struct inode_security_struct *isec = inode->i_security;
+ struct superblock_security_struct *sbsec = inode->i_sb->s_security;
+
+ spin_lock(&sbsec->isec_lock);
+ if (!list_empty(&isec->list))
+ list_del_init(&isec->list);
+ spin_unlock(&sbsec->isec_lock);
+
+ inode->i_security = NULL;
+ kmem_cache_free(sel_inode_cache, isec);
+}
+
+static int file_alloc_security(struct file *file)
+{
+ struct task_security_struct *tsec = current->security;
+ struct file_security_struct *fsec;
+
+ fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);
+ if (!fsec)
+ return -ENOMEM;
+
+ fsec->sid = tsec->sid;
+ fsec->fown_sid = tsec->sid;
+ file->f_security = fsec;
+
+ return 0;
+}
+
+static void file_free_security(struct file *file)
+{
+ struct file_security_struct *fsec = file->f_security;
+ file->f_security = NULL;
+ kfree(fsec);
+}
+
+static int superblock_alloc_security(struct super_block *sb)
+{
+ struct superblock_security_struct *sbsec;
+
+ sbsec = kzalloc(sizeof(struct superblock_security_struct), GFP_KERNEL);
+ if (!sbsec)
+ return -ENOMEM;
+
+ mutex_init(&sbsec->lock);
+ INIT_LIST_HEAD(&sbsec->list);
+ INIT_LIST_HEAD(&sbsec->isec_head);
+ spin_lock_init(&sbsec->isec_lock);
+ sbsec->sb = sb;
+ sbsec->sid = SECINITSID_UNLABELED;
+ sbsec->def_sid = SECINITSID_FILE;
+ sbsec->mntpoint_sid = SECINITSID_UNLABELED;
+ sb->s_security = sbsec;
+
+ return 0;
+}
+
+static void superblock_free_security(struct super_block *sb)
+{
+ struct superblock_security_struct *sbsec = sb->s_security;
+
+ spin_lock(&sb_security_lock);
+ if (!list_empty(&sbsec->list))
+ list_del_init(&sbsec->list);
+ spin_unlock(&sb_security_lock);
+
+ sb->s_security = NULL;
+ kfree(sbsec);
+}
+
+static int sk_alloc_security(struct sock *sk, int family, gfp_t priority)
+{
+ struct sk_security_struct *ssec;
+
+ ssec = kzalloc(sizeof(*ssec), priority);
+ if (!ssec)
+ return -ENOMEM;
+
+ ssec->peer_sid = SECINITSID_UNLABELED;
+ ssec->sid = SECINITSID_UNLABELED;
+ sk->sk_security = ssec;
+
+ selinux_netlbl_sk_security_reset(ssec, family);
+
+ return 0;
+}
+
+static void sk_free_security(struct sock *sk)
+{
+ struct sk_security_struct *ssec = sk->sk_security;
+
+ sk->sk_security = NULL;
+ selinux_netlbl_sk_security_free(ssec);
+ kfree(ssec);
+}
+
+/* The security server must be initialized before
+ any labeling or access decisions can be provided. */
+extern int ss_initialized;
+
+/* The file system's label must be initialized prior to use. */
+
+static char *labeling_behaviors[6] = {
+ "uses xattr",
+ "uses transition SIDs",
+ "uses task SIDs",
+ "uses genfs_contexts",
+ "not configured for labeling",
+ "uses mountpoint labeling",
+};
+
+static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
+
+static inline int inode_doinit(struct inode *inode)
+{
+ return inode_doinit_with_dentry(inode, NULL);
+}
+
+enum {
+ Opt_error = -1,
+ Opt_context = 1,
+ Opt_fscontext = 2,
+ Opt_defcontext = 3,
+ Opt_rootcontext = 4,
+};
+
+static const match_table_t tokens = {
+ {Opt_context, CONTEXT_STR "%s"},
+ {Opt_fscontext, FSCONTEXT_STR "%s"},
+ {Opt_defcontext, DEFCONTEXT_STR "%s"},
+ {Opt_rootcontext, ROOTCONTEXT_STR "%s"},
+ {Opt_error, NULL},
+};
+
+#define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n"
+
+static int may_context_mount_sb_relabel(u32 sid,
+ struct superblock_security_struct *sbsec,
+ struct task_security_struct *tsec)
+{
+ int rc;
+
+ rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ FILESYSTEM__RELABELFROM, NULL);
+ if (rc)
+ return rc;
+
+ rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM,
+ FILESYSTEM__RELABELTO, NULL);
+ return rc;
+}
+
+static int may_context_mount_inode_relabel(u32 sid,
+ struct superblock_security_struct *sbsec,
+ struct task_security_struct *tsec)
+{
+ int rc;
+ rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ FILESYSTEM__RELABELFROM, NULL);
+ if (rc)
+ return rc;
+
+ rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ FILESYSTEM__ASSOCIATE, NULL);
+ return rc;
+}
+
+static int sb_finish_set_opts(struct super_block *sb)
+{
+ struct superblock_security_struct *sbsec = sb->s_security;
+ struct dentry *root = sb->s_root;
+ struct inode *root_inode = root->d_inode;
+ int rc = 0;
+
+ if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
+ /* Make sure that the xattr handler exists and that no
+ error other than -ENODATA is returned by getxattr on
+ the root directory. -ENODATA is ok, as this may be
+ the first boot of the SELinux kernel before we have
+ assigned xattr values to the filesystem. */
+ if (!root_inode->i_op->getxattr) {
+ printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
+ "xattr support\n", sb->s_id, sb->s_type->name);
+ rc = -EOPNOTSUPP;
+ goto out;
+ }
+ rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
+ if (rc < 0 && rc != -ENODATA) {
+ if (rc == -EOPNOTSUPP)
+ printk(KERN_WARNING "SELinux: (dev %s, type "
+ "%s) has no security xattr handler\n",
+ sb->s_id, sb->s_type->name);
+ else
+ printk(KERN_WARNING "SELinux: (dev %s, type "
+ "%s) getxattr errno %d\n", sb->s_id,
+ sb->s_type->name, -rc);
+ goto out;
+ }
+ }
+
+ sbsec->initialized = 1;
+
+ if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
+ printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
+ sb->s_id, sb->s_type->name);
+ else
+ printk(KERN_DEBUG "SELinux: initialized (dev %s, type %s), %s\n",
+ sb->s_id, sb->s_type->name,
+ labeling_behaviors[sbsec->behavior-1]);
+
+ /* Initialize the root inode. */
+ rc = inode_doinit_with_dentry(root_inode, root);
+
+ /* Initialize any other inodes associated with the superblock, e.g.
+ inodes created prior to initial policy load or inodes created
+ during get_sb by a pseudo filesystem that directly
+ populates itself. */
+ spin_lock(&sbsec->isec_lock);
+next_inode:
+ if (!list_empty(&sbsec->isec_head)) {
+ struct inode_security_struct *isec =
+ list_entry(sbsec->isec_head.next,
+ struct inode_security_struct, list);
+ struct inode *inode = isec->inode;
+ spin_unlock(&sbsec->isec_lock);
+ inode = igrab(inode);
+ if (inode) {
+ if (!IS_PRIVATE(inode))
+ inode_doinit(inode);
+ iput(inode);
+ }
+ spin_lock(&sbsec->isec_lock);
+ list_del_init(&isec->list);
+ goto next_inode;
+ }
+ spin_unlock(&sbsec->isec_lock);
+out:
+ return rc;
+}
+
+/*
+ * This function should allow an FS to ask what it's mount security
+ * options were so it can use those later for submounts, displaying
+ * mount options, or whatever.
+ */
+static int selinux_get_mnt_opts(const struct super_block *sb,
+ struct security_mnt_opts *opts)
+{
+ int rc = 0, i;
+ struct superblock_security_struct *sbsec = sb->s_security;
+ char *context = NULL;
+ u32 len;
+ char tmp;
+
+ security_init_mnt_opts(opts);
+
+ if (!sbsec->initialized)
+ return -EINVAL;
+
+ if (!ss_initialized)
+ return -EINVAL;
+
+ /*
+ * if we ever use sbsec flags for anything other than tracking mount
+ * settings this is going to need a mask
+ */
+ tmp = sbsec->flags;
+ /* count the number of mount options for this sb */
+ for (i = 0; i < 8; i++) {
+ if (tmp & 0x01)
+ opts->num_mnt_opts++;
+ tmp >>= 1;
+ }
+
+ opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
+ if (!opts->mnt_opts) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+
+ opts->mnt_opts_flags = kcalloc(opts->num_mnt_opts, sizeof(int), GFP_ATOMIC);
+ if (!opts->mnt_opts_flags) {
+ rc = -ENOMEM;
+ goto out_free;
+ }
+
+ i = 0;
+ if (sbsec->flags & FSCONTEXT_MNT) {
+ rc = security_sid_to_context(sbsec->sid, &context, &len);
+ if (rc)
+ goto out_free;
+ opts->mnt_opts[i] = context;
+ opts->mnt_opts_flags[i++] = FSCONTEXT_MNT;
+ }
+ if (sbsec->flags & CONTEXT_MNT) {
+ rc = security_sid_to_context(sbsec->mntpoint_sid, &context, &len);
+ if (rc)
+ goto out_free;
+ opts->mnt_opts[i] = context;
+ opts->mnt_opts_flags[i++] = CONTEXT_MNT;
+ }
+ if (sbsec->flags & DEFCONTEXT_MNT) {
+ rc = security_sid_to_context(sbsec->def_sid, &context, &len);
+ if (rc)
+ goto out_free;
+ opts->mnt_opts[i] = context;
+ opts->mnt_opts_flags[i++] = DEFCONTEXT_MNT;
+ }
+ if (sbsec->flags & ROOTCONTEXT_MNT) {
+ struct inode *root = sbsec->sb->s_root->d_inode;
+ struct inode_security_struct *isec = root->i_security;
+
+ rc = security_sid_to_context(isec->sid, &context, &len);
+ if (rc)
+ goto out_free;
+ opts->mnt_opts[i] = context;
+ opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
+ }
+
+ BUG_ON(i != opts->num_mnt_opts);
+
+ return 0;
+
+out_free:
+ security_free_mnt_opts(opts);
+ return rc;
+}
+
+static int bad_option(struct superblock_security_struct *sbsec, char flag,
+ u32 old_sid, u32 new_sid)
+{
+ /* check if the old mount command had the same options */
+ if (sbsec->initialized)
+ if (!(sbsec->flags & flag) ||
+ (old_sid != new_sid))
+ return 1;
+
+ /* check if we were passed the same options twice,
+ * aka someone passed context=a,context=b
+ */
+ if (!sbsec->initialized)
+ if (sbsec->flags & flag)
+ return 1;
+ return 0;
+}
+
+/*
+ * Allow filesystems with binary mount data to explicitly set mount point
+ * labeling information.
+ */
+static int selinux_set_mnt_opts(struct super_block *sb,
+ struct security_mnt_opts *opts)
+{
+ int rc = 0, i;
+ struct task_security_struct *tsec = current->security;
+ struct superblock_security_struct *sbsec = sb->s_security;
+ const char *name = sb->s_type->name;
+ struct inode *inode = sbsec->sb->s_root->d_inode;
+ struct inode_security_struct *root_isec = inode->i_security;
+ u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
+ u32 defcontext_sid = 0;
+ char **mount_options = opts->mnt_opts;
+ int *flags = opts->mnt_opts_flags;
+ int num_opts = opts->num_mnt_opts;
+
+ mutex_lock(&sbsec->lock);
+
+ if (!ss_initialized) {
+ if (!num_opts) {
+ /* Defer initialization until selinux_complete_init,
+ after the initial policy is loaded and the security
+ server is ready to handle calls. */
+ spin_lock(&sb_security_lock);
+ if (list_empty(&sbsec->list))
+ list_add(&sbsec->list, &superblock_security_head);
+ spin_unlock(&sb_security_lock);
+ goto out;
+ }
+ rc = -EINVAL;
+ printk(KERN_WARNING "SELinux: Unable to set superblock options "
+ "before the security server is initialized\n");
+ goto out;
+ }
+
+ /*
+ * Binary mount data FS will come through this function twice. Once
+ * from an explicit call and once from the generic calls from the vfs.
+ * Since the generic VFS calls will not contain any security mount data
+ * we need to skip the double mount verification.
+ *
+ * This does open a hole in which we will not notice if the first
+ * mount using this sb set explict options and a second mount using
+ * this sb does not set any security options. (The first options
+ * will be used for both mounts)
+ */
+ if (sbsec->initialized && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
+ && (num_opts == 0))
+ goto out;
+
+ /*
+ * parse the mount options, check if they are valid sids.
+ * also check if someone is trying to mount the same sb more
+ * than once with different security options.
+ */
+ for (i = 0; i < num_opts; i++) {
+ u32 sid;
+ rc = security_context_to_sid(mount_options[i],
+ strlen(mount_options[i]), &sid);
+ if (rc) {
+ printk(KERN_WARNING "SELinux: security_context_to_sid"
+ "(%s) failed for (dev %s, type %s) errno=%d\n",
+ mount_options[i], sb->s_id, name, rc);
+ goto out;
+ }
+ switch (flags[i]) {
+ case FSCONTEXT_MNT:
+ fscontext_sid = sid;
+
+ if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid,
+ fscontext_sid))
+ goto out_double_mount;
+
+ sbsec->flags |= FSCONTEXT_MNT;
+ break;
+ case CONTEXT_MNT:
+ context_sid = sid;
+
+ if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid,
+ context_sid))
+ goto out_double_mount;
+
+ sbsec->flags |= CONTEXT_MNT;
+ break;
+ case ROOTCONTEXT_MNT:
+ rootcontext_sid = sid;
+
+ if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid,
+ rootcontext_sid))
+ goto out_double_mount;
+
+ sbsec->flags |= ROOTCONTEXT_MNT;
+
+ break;
+ case DEFCONTEXT_MNT:
+ defcontext_sid = sid;
+
+ if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
+ defcontext_sid))
+ goto out_double_mount;
+
+ sbsec->flags |= DEFCONTEXT_MNT;
+
+ break;
+ default:
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (sbsec->initialized) {
+ /* previously mounted with options, but not on this attempt? */
+ if (sbsec->flags && !num_opts)
+ goto out_double_mount;
+ rc = 0;
+ goto out;
+ }
+
+ if (strcmp(sb->s_type->name, "proc") == 0)
+ sbsec->proc = 1;
+
+ /* Determine the labeling behavior to use for this filesystem type. */
+ rc = security_fs_use(sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+ if (rc) {
+ printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
+ __func__, sb->s_type->name, rc);
+ goto out;
+ }
+
+ /* sets the context of the superblock for the fs being mounted. */
+ if (fscontext_sid) {
+
+ rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, tsec);
+ if (rc)
+ goto out;
+
+ sbsec->sid = fscontext_sid;
+ }
+
+ /*
+ * Switch to using mount point labeling behavior.
+ * sets the label used on all file below the mountpoint, and will set
+ * the superblock context if not already set.
+ */
+ if (context_sid) {
+ if (!fscontext_sid) {
+ rc = may_context_mount_sb_relabel(context_sid, sbsec, tsec);
+ if (rc)
+ goto out;
+ sbsec->sid = context_sid;
+ } else {
+ rc = may_context_mount_inode_relabel(context_sid, sbsec, tsec);
+ if (rc)
+ goto out;
+ }
+ if (!rootcontext_sid)
+ rootcontext_sid = context_sid;
+
+ sbsec->mntpoint_sid = context_sid;
+ sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
+ }
+
+ if (rootcontext_sid) {
+ rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, tsec);
+ if (rc)
+ goto out;
+
+ root_isec->sid = rootcontext_sid;
+ root_isec->initialized = 1;
+ }
+
+ if (defcontext_sid) {
+ if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
+ rc = -EINVAL;
+ printk(KERN_WARNING "SELinux: defcontext option is "
+ "invalid for this filesystem type\n");
+ goto out;
+ }
+
+ if (defcontext_sid != sbsec->def_sid) {
+ rc = may_context_mount_inode_relabel(defcontext_sid,
+ sbsec, tsec);
+ if (rc)
+ goto out;
+ }
+
+ sbsec->def_sid = defcontext_sid;
+ }
+
+ rc = sb_finish_set_opts(sb);
+out:
+ mutex_unlock(&sbsec->lock);
+ return rc;
+out_double_mount:
+ rc = -EINVAL;
+ printk(KERN_WARNING "SELinux: mount invalid. Same superblock, different "
+ "security settings for (dev %s, type %s)\n", sb->s_id, name);
+ goto out;
+}
+
+static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
+ struct super_block *newsb)
+{
+ const struct superblock_security_struct *oldsbsec = oldsb->s_security;
+ struct superblock_security_struct *newsbsec = newsb->s_security;
+
+ int set_fscontext = (oldsbsec->flags & FSCONTEXT_MNT);
+ int set_context = (oldsbsec->flags & CONTEXT_MNT);
+ int set_rootcontext = (oldsbsec->flags & ROOTCONTEXT_MNT);
+
+ /*
+ * if the parent was able to be mounted it clearly had no special lsm
+ * mount options. thus we can safely put this sb on the list and deal
+ * with it later
+ */
+ if (!ss_initialized) {
+ spin_lock(&sb_security_lock);
+ if (list_empty(&newsbsec->list))
+ list_add(&newsbsec->list, &superblock_security_head);
+ spin_unlock(&sb_security_lock);
+ return;
+ }
+
+ /* how can we clone if the old one wasn't set up?? */
+ BUG_ON(!oldsbsec->initialized);
+
+ /* if fs is reusing a sb, just let its options stand... */
+ if (newsbsec->initialized)
+ return;
+
+ mutex_lock(&newsbsec->lock);
+
+ newsbsec->flags = oldsbsec->flags;
+
+ newsbsec->sid = oldsbsec->sid;
+ newsbsec->def_sid = oldsbsec->def_sid;
+ newsbsec->behavior = oldsbsec->behavior;
+
+ if (set_context) {
+ u32 sid = oldsbsec->mntpoint_sid;
+
+ if (!set_fscontext)
+ newsbsec->sid = sid;
+ if (!set_rootcontext) {
+ struct inode *newinode = newsb->s_root->d_inode;
+ struct inode_security_struct *newisec = newinode->i_security;
+ newisec->sid = sid;
+ }
+ newsbsec->mntpoint_sid = sid;
+ }
+ if (set_rootcontext) {
+ const struct inode *oldinode = oldsb->s_root->d_inode;
+ const struct inode_security_struct *oldisec = oldinode->i_security;
+ struct inode *newinode = newsb->s_root->d_inode;
+ struct inode_security_struct *newisec = newinode->i_security;
+
+ newisec->sid = oldisec->sid;
+ }
+
+ sb_finish_set_opts(newsb);
+ mutex_unlock(&newsbsec->lock);
+}
+
+static int selinux_parse_opts_str(char *options,
+ struct security_mnt_opts *opts)
+{
+ char *p;
+ char *context = NULL, *defcontext = NULL;
+ char *fscontext = NULL, *rootcontext = NULL;
+ int rc, num_mnt_opts = 0;
+
+ opts->num_mnt_opts = 0;
+
+ /* Standard string-based options. */
+ while ((p = strsep(&options, "|")) != NULL) {
+ int token;
+ substring_t args[MAX_OPT_ARGS];
+
+ if (!*p)
+ continue;
+
+ token = match_token(p, tokens, args);
+
+ switch (token) {
+ case Opt_context:
+ if (context || defcontext) {
+ rc = -EINVAL;
+ printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+ goto out_err;
+ }
+ context = match_strdup(&args[0]);
+ if (!context) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+ break;
+
+ case Opt_fscontext:
+ if (fscontext) {
+ rc = -EINVAL;
+ printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+ goto out_err;
+ }
+ fscontext = match_strdup(&args[0]);
+ if (!fscontext) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+ break;
+
+ case Opt_rootcontext:
+ if (rootcontext) {
+ rc = -EINVAL;
+ printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+ goto out_err;
+ }
+ rootcontext = match_strdup(&args[0]);
+ if (!rootcontext) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+ break;
+
+ case Opt_defcontext:
+ if (context || defcontext) {
+ rc = -EINVAL;
+ printk(KERN_WARNING SEL_MOUNT_FAIL_MSG);
+ goto out_err;
+ }
+ defcontext = match_strdup(&args[0]);
+ if (!defcontext) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
+ break;
+
+ default:
+ rc = -EINVAL;
+ printk(KERN_WARNING "SELinux: unknown mount option\n");
+ goto out_err;
+
+ }
+ }
+
+ rc = -ENOMEM;
+ opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_ATOMIC);
+ if (!opts->mnt_opts)
+ goto out_err;
+
+ opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int), GFP_ATOMIC);
+ if (!opts->mnt_opts_flags) {
+ kfree(opts->mnt_opts);
+ goto out_err;
+ }
+
+ if (fscontext) {
+ opts->mnt_opts[num_mnt_opts] = fscontext;
+ opts->mnt_opts_flags[num_mnt_opts++] = FSCONTEXT_MNT;
+ }
+ if (context) {
+ opts->mnt_opts[num_mnt_opts] = context;
+ opts->mnt_opts_flags[num_mnt_opts++] = CONTEXT_MNT;
+ }
+ if (rootcontext) {
+ opts->mnt_opts[num_mnt_opts] = rootcontext;
+ opts->mnt_opts_flags[num_mnt_opts++] = ROOTCONTEXT_MNT;
+ }
+ if (defcontext) {
+ opts->mnt_opts[num_mnt_opts] = defcontext;
+ opts->mnt_opts_flags[num_mnt_opts++] = DEFCONTEXT_MNT;
+ }
+
+ opts->num_mnt_opts = num_mnt_opts;
+ return 0;
+
+out_err:
+ kfree(context);
+ kfree(defcontext);
+ kfree(fscontext);
+ kfree(rootcontext);
+ return rc;
+}
+/*
+ * string mount options parsing and call set the sbsec
+ */
+static int superblock_doinit(struct super_block *sb, void *data)
+{
+ int rc = 0;
+ char *options = data;
+ struct security_mnt_opts opts;
+
+ security_init_mnt_opts(&opts);
+
+ if (!data)
+ goto out;
+
+ BUG_ON(sb->s_type->fs_flags & FS_BINARY_MOUNTDATA);
+
+ rc = selinux_parse_opts_str(options, &opts);
+ if (rc)
+ goto out_err;
+
+out:
+ rc = selinux_set_mnt_opts(sb, &opts);
+
+out_err:
+ security_free_mnt_opts(&opts);
+ return rc;
+}
+
+static void selinux_write_opts(struct seq_file *m,
+ struct security_mnt_opts *opts)
+{
+ int i;
+ char *prefix;
+
+ for (i = 0; i < opts->num_mnt_opts; i++) {
+ char *has_comma = strchr(opts->mnt_opts[i], ',');
+
+ switch (opts->mnt_opts_flags[i]) {
+ case CONTEXT_MNT:
+ prefix = CONTEXT_STR;
+ break;
+ case FSCONTEXT_MNT:
+ prefix = FSCONTEXT_STR;
+ break;
+ case ROOTCONTEXT_MNT:
+ prefix = ROOTCONTEXT_STR;
+ break;
+ case DEFCONTEXT_MNT:
+ prefix = DEFCONTEXT_STR;
+ break;
+ default:
+ BUG();
+ };
+ /* we need a comma before each option */
+ seq_putc(m, ',');
+ seq_puts(m, prefix);
+ if (has_comma)
+ seq_putc(m, '\"');
+ seq_puts(m, opts->mnt_opts[i]);
+ if (has_comma)
+ seq_putc(m, '\"');
+ }
+}
+
+static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
+{
+ struct security_mnt_opts opts;
+ int rc;
+
+ rc = selinux_get_mnt_opts(sb, &opts);
+ if (rc) {
+ /* before policy load we may get EINVAL, don't show anything */
+ if (rc == -EINVAL)
+ rc = 0;
+ return rc;
+ }
+
+ selinux_write_opts(m, &opts);
+
+ security_free_mnt_opts(&opts);
+
+ return rc;
+}
+
+static inline u16 inode_mode_to_security_class(umode_t mode)
+{
+ switch (mode & S_IFMT) {
+ case S_IFSOCK:
+ return SECCLASS_SOCK_FILE;
+ case S_IFLNK:
+ return SECCLASS_LNK_FILE;
+ case S_IFREG:
+ return SECCLASS_FILE;
+ case S_IFBLK:
+ return SECCLASS_BLK_FILE;
+ case S_IFDIR:
+ return SECCLASS_DIR;
+ case S_IFCHR:
+ return SECCLASS_CHR_FILE;
+ case S_IFIFO:
+ return SECCLASS_FIFO_FILE;
+
+ }
+
+ return SECCLASS_FILE;
+}
+
+static inline int default_protocol_stream(int protocol)
+{
+ return (protocol == IPPROTO_IP || protocol == IPPROTO_TCP);
+}
+
+static inline int default_protocol_dgram(int protocol)
+{
+ return (protocol == IPPROTO_IP || protocol == IPPROTO_UDP);
+}
+
+static inline u16 socket_type_to_security_class(int family, int type, int protocol)
+{
+ switch (family) {
+ case PF_UNIX:
+ switch (type) {
+ case SOCK_STREAM:
+ case SOCK_SEQPACKET:
+ return SECCLASS_UNIX_STREAM_SOCKET;
+ case SOCK_DGRAM:
+ return SECCLASS_UNIX_DGRAM_SOCKET;
+ }
+ break;
+ case PF_INET:
+ case PF_INET6:
+ switch (type) {
+ case SOCK_STREAM:
+ if (default_protocol_stream(protocol))
+ return SECCLASS_TCP_SOCKET;
+ else
+ return SECCLASS_RAWIP_SOCKET;
+ case SOCK_DGRAM:
+ if (default_protocol_dgram(protocol))
+ return SECCLASS_UDP_SOCKET;
+ else
+ return SECCLASS_RAWIP_SOCKET;
+ case SOCK_DCCP:
+ return SECCLASS_DCCP_SOCKET;
+ default:
+ return SECCLASS_RAWIP_SOCKET;
+ }
+ break;
+ case PF_NETLINK:
+ switch (protocol) {
+ case NETLINK_ROUTE:
+ return SECCLASS_NETLINK_ROUTE_SOCKET;
+ case NETLINK_FIREWALL:
+ return SECCLASS_NETLINK_FIREWALL_SOCKET;
+ case NETLINK_INET_DIAG:
+ return SECCLASS_NETLINK_TCPDIAG_SOCKET;
+ case NETLINK_NFLOG:
+ return SECCLASS_NETLINK_NFLOG_SOCKET;
+ case NETLINK_XFRM:
+ return SECCLASS_NETLINK_XFRM_SOCKET;
+ case NETLINK_SELINUX:
+ return SECCLASS_NETLINK_SELINUX_SOCKET;
+ case NETLINK_AUDIT:
+ return SECCLASS_NETLINK_AUDIT_SOCKET;
+ case NETLINK_IP6_FW:
+ return SECCLASS_NETLINK_IP6FW_SOCKET;
+ case NETLINK_DNRTMSG:
+ return SECCLASS_NETLINK_DNRT_SOCKET;
+ case NETLINK_KOBJECT_UEVENT:
+ return SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET;
+ default:
+ return SECCLASS_NETLINK_SOCKET;
+ }
+ case PF_PACKET:
+ return SECCLASS_PACKET_SOCKET;
+ case PF_KEY:
+ return SECCLASS_KEY_SOCKET;
+ case PF_APPLETALK:
+ return SECCLASS_APPLETALK_SOCKET;
+ }
+
+ return SECCLASS_SOCKET;
+}
+
+#ifdef CONFIG_PROC_FS
+static int selinux_proc_get_sid(struct proc_dir_entry *de,
+ u16 tclass,
+ u32 *sid)
+{
+ int buflen, rc;
+ char *buffer, *path, *end;
+
+ buffer = (char *)__get_free_page(GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ buflen = PAGE_SIZE;
+ end = buffer+buflen;
+ *--end = '\0';
+ buflen--;
+ path = end-1;
+ *path = '/';
+ while (de && de != de->parent) {
+ buflen -= de->namelen + 1;
+ if (buflen < 0)
+ break;
+ end -= de->namelen;
+ memcpy(end, de->name, de->namelen);
+ *--end = '/';
+ path = end;
+ de = de->parent;
+ }
+ rc = security_genfs_sid("proc", path, tclass, sid);
+ free_page((unsigned long)buffer);
+ return rc;
+}
+#else
+static int selinux_proc_get_sid(struct proc_dir_entry *de,
+ u16 tclass,
+ u32 *sid)
+{
+ return -EINVAL;
+}
+#endif
+
+/* The inode's security attributes must be initialized before first use. */
+static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry)
+{
+ struct superblock_security_struct *sbsec = NULL;
+ struct inode_security_struct *isec = inode->i_security;
+ u32 sid;
+ struct dentry *dentry;
+#define INITCONTEXTLEN 255
+ char *context = NULL;
+ unsigned len = 0;
+ int rc = 0;
+
+ if (isec->initialized)
+ goto out;
+
+ mutex_lock(&isec->lock);
+ if (isec->initialized)
+ goto out_unlock;
+
+ sbsec = inode->i_sb->s_security;
+ if (!sbsec->initialized) {
+ /* Defer initialization until selinux_complete_init,
+ after the initial policy is loaded and the security
+ server is ready to handle calls. */
+ spin_lock(&sbsec->isec_lock);
+ if (list_empty(&isec->list))
+ list_add(&isec->list, &sbsec->isec_head);
+ spin_unlock(&sbsec->isec_lock);
+ goto out_unlock;
+ }
+
+ switch (sbsec->behavior) {
+ case SECURITY_FS_USE_XATTR:
+ if (!inode->i_op->getxattr) {
+ isec->sid = sbsec->def_sid;
+ break;
+ }
+
+ /* Need a dentry, since the xattr API requires one.
+ Life would be simpler if we could just pass the inode. */
+ if (opt_dentry) {
+ /* Called from d_instantiate or d_splice_alias. */
+ dentry = dget(opt_dentry);
+ } else {
+ /* Called from selinux_complete_init, try to find a dentry. */
+ dentry = d_find_alias(inode);
+ }
+ if (!dentry) {
+ printk(KERN_WARNING "SELinux: %s: no dentry for dev=%s "
+ "ino=%ld\n", __func__, inode->i_sb->s_id,
+ inode->i_ino);
+ goto out_unlock;
+ }
+
+ len = INITCONTEXTLEN;
+ context = kmalloc(len, GFP_NOFS);
+ if (!context) {
+ rc = -ENOMEM;
+ dput(dentry);
+ goto out_unlock;
+ }
+ rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
+ context, len);
+ if (rc == -ERANGE) {
+ /* Need a larger buffer. Query for the right size. */
+ rc = inode->i_op->getxattr(dentry, XATTR_NAME_SELINUX,
+ NULL, 0);
+ if (rc < 0) {
+ dput(dentry);
+ goto out_unlock;
+ }
+ kfree(context);
+ len = rc;
+ context = kmalloc(len, GFP_NOFS);
+ if (!context) {
+ rc = -ENOMEM;
+ dput(dentry);
+ goto out_unlock;
+ }
+ rc = inode->i_op->getxattr(dentry,
+ XATTR_NAME_SELINUX,
+ context, len);
+ }
+ dput(dentry);
+ if (rc < 0) {
+ if (rc != -ENODATA) {
+ printk(KERN_WARNING "SELinux: %s: getxattr returned "
+ "%d for dev=%s ino=%ld\n", __func__,
+ -rc, inode->i_sb->s_id, inode->i_ino);
+ kfree(context);
+ goto out_unlock;
+ }
+ /* Map ENODATA to the default file SID */
+ sid = sbsec->def_sid;
+ rc = 0;
+ } else {
+ rc = security_context_to_sid_default(context, rc, &sid,
+ sbsec->def_sid,
+ GFP_NOFS);
+ if (rc) {
+ printk(KERN_WARNING "SELinux: %s: context_to_sid(%s) "
+ "returned %d for dev=%s ino=%ld\n",
+ __func__, context, -rc,
+ inode->i_sb->s_id, inode->i_ino);
+ kfree(context);
+ /* Leave with the unlabeled SID */
+ rc = 0;
+ break;
+ }
+ }
+ kfree(context);
+ isec->sid = sid;
+ break;
+ case SECURITY_FS_USE_TASK:
+ isec->sid = isec->task_sid;
+ break;
+ case SECURITY_FS_USE_TRANS:
+ /* Default to the fs SID. */
+ isec->sid = sbsec->sid;
+
+ /* Try to obtain a transition SID. */
+ isec->sclass = inode_mode_to_security_class(inode->i_mode);
+ rc = security_transition_sid(isec->task_sid,
+ sbsec->sid,
+ isec->sclass,
+ &sid);
+ if (rc)
+ goto out_unlock;
+ isec->sid = sid;
+ break;
+ case SECURITY_FS_USE_MNTPOINT:
+ isec->sid = sbsec->mntpoint_sid;
+ break;
+ default:
+ /* Default to the fs superblock SID. */
+ isec->sid = sbsec->sid;
+
+ if (sbsec->proc && !S_ISLNK(inode->i_mode)) {
+ struct proc_inode *proci = PROC_I(inode);
+ if (proci->pde) {
+ isec->sclass = inode_mode_to_security_class(inode->i_mode);
+ rc = selinux_proc_get_sid(proci->pde,
+ isec->sclass,
+ &sid);
+ if (rc)
+ goto out_unlock;
+ isec->sid = sid;
+ }
+ }
+ break;
+ }
+
+ isec->initialized = 1;
+
+out_unlock:
+ mutex_unlock(&isec->lock);
+out:
+ if (isec->sclass == SECCLASS_FILE)
+ isec->sclass = inode_mode_to_security_class(inode->i_mode);
+ return rc;
+}
+
+/* Convert a Linux signal to an access vector. */
+static inline u32 signal_to_av(int sig)
+{
+ u32 perm = 0;
+
+ switch (sig) {
+ case SIGCHLD:
+ /* Commonly granted from child to parent. */
+ perm = PROCESS__SIGCHLD;
+ break;
+ case SIGKILL:
+ /* Cannot be caught or ignored */
+ perm = PROCESS__SIGKILL;
+ break;
+ case SIGSTOP:
+ /* Cannot be caught or ignored */
+ perm = PROCESS__SIGSTOP;
+ break;
+ default:
+ /* All other signals. */
+ perm = PROCESS__SIGNAL;
+ break;
+ }
+
+ return perm;
+}
+
+/* Check permission betweeen a pair of tasks, e.g. signal checks,
+ fork check, ptrace check, etc. */
+static int task_has_perm(struct task_struct *tsk1,
+ struct task_struct *tsk2,
+ u32 perms)
+{
+ struct task_security_struct *tsec1, *tsec2;
+
+ tsec1 = tsk1->security;
+ tsec2 = tsk2->security;
+ return avc_has_perm(tsec1->sid, tsec2->sid,
+ SECCLASS_PROCESS, perms, NULL);
+}
+
+#if CAP_LAST_CAP > 63
+#error Fix SELinux to handle capabilities > 63.
+#endif
+
+/* Check whether a task is allowed to use a capability. */
+static int task_has_capability(struct task_struct *tsk,
+ int cap)
+{
+ struct task_security_struct *tsec;
+ struct avc_audit_data ad;
+ u16 sclass;
+ u32 av = CAP_TO_MASK(cap);
+
+ tsec = tsk->security;
+
+ AVC_AUDIT_DATA_INIT(&ad, CAP);
+ ad.tsk = tsk;
+ ad.u.cap = cap;
+
+ switch (CAP_TO_INDEX(cap)) {
+ case 0:
+ sclass = SECCLASS_CAPABILITY;
+ break;
+ case 1:
+ sclass = SECCLASS_CAPABILITY2;
+ break;
+ default:
+ printk(KERN_ERR
+ "SELinux: out of range capability %d\n", cap);
+ BUG();
+ }
+ return avc_has_perm(tsec->sid, tsec->sid, sclass, av, &ad);
+}
+
+/* Check whether a task is allowed to use a system operation. */
+static int task_has_system(struct task_struct *tsk,
+ u32 perms)
+{
+ struct task_security_struct *tsec;
+
+ tsec = tsk->security;
+
+ return avc_has_perm(tsec->sid, SECINITSID_KERNEL,
+ SECCLASS_SYSTEM, perms, NULL);
+}
+
+/* Check whether a task has a particular permission to an inode.
+ The 'adp' parameter is optional and allows other audit
+ data to be passed (e.g. the dentry). */
+static int inode_has_perm(struct task_struct *tsk,
+ struct inode *inode,
+ u32 perms,
+ struct avc_audit_data *adp)
+{
+ struct task_security_struct *tsec;
+ struct inode_security_struct *isec;
+ struct avc_audit_data ad;
+
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
+ tsec = tsk->security;
+ isec = inode->i_security;
+
+ if (!adp) {
+ adp = &ad;
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.inode = inode;
+ }
+
+ return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, adp);
+}
+
+/* Same as inode_has_perm, but pass explicit audit data containing
+ the dentry to help the auditing code to more easily generate the
+ pathname if needed. */
+static inline int dentry_has_perm(struct task_struct *tsk,
+ struct vfsmount *mnt,
+ struct dentry *dentry,
+ u32 av)
+{
+ struct inode *inode = dentry->d_inode;
+ struct avc_audit_data ad;
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.path.mnt = mnt;
+ ad.u.fs.path.dentry = dentry;
+ return inode_has_perm(tsk, inode, av, &ad);
+}
+
+/* Check whether a task can use an open file descriptor to
+ access an inode in a given way. Check access to the
+ descriptor itself, and then use dentry_has_perm to
+ check a particular permission to the file.
+ Access to the descriptor is implicitly granted if it
+ has the same SID as the process. If av is zero, then
+ access to the file is not checked, e.g. for cases
+ where only the descriptor is affected like seek. */
+static int file_has_perm(struct task_struct *tsk,
+ struct file *file,
+ u32 av)
+{
+ struct task_security_struct *tsec = tsk->security;
+ struct file_security_struct *fsec = file->f_security;
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct avc_audit_data ad;
+ int rc;
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.path = file->f_path;
+
+ if (tsec->sid != fsec->sid) {
+ rc = avc_has_perm(tsec->sid, fsec->sid,
+ SECCLASS_FD,
+ FD__USE,
+ &ad);
+ if (rc)
+ return rc;
+ }
+
+ /* av is zero if only checking access to the descriptor. */
+ if (av)
+ return inode_has_perm(tsk, inode, av, &ad);
+
+ return 0;
+}
+
+/* Check whether a task can create a file. */
+static int may_create(struct inode *dir,
+ struct dentry *dentry,
+ u16 tclass)
+{
+ struct task_security_struct *tsec;
+ struct inode_security_struct *dsec;
+ struct superblock_security_struct *sbsec;
+ u32 newsid;
+ struct avc_audit_data ad;
+ int rc;
+
+ tsec = current->security;
+ dsec = dir->i_security;
+ sbsec = dir->i_sb->s_security;
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.path.dentry = dentry;
+
+ rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR,
+ DIR__ADD_NAME | DIR__SEARCH,
+ &ad);
+ if (rc)
+ return rc;
+
+ if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
+ newsid = tsec->create_sid;
+ } else {
+ rc = security_transition_sid(tsec->sid, dsec->sid, tclass,
+ &newsid);
+ if (rc)
+ return rc;
+ }
+
+ rc = avc_has_perm(tsec->sid, newsid, tclass, FILE__CREATE, &ad);
+ if (rc)
+ return rc;
+
+ return avc_has_perm(newsid, sbsec->sid,
+ SECCLASS_FILESYSTEM,
+ FILESYSTEM__ASSOCIATE, &ad);
+}
+
+/* Check whether a task can create a key. */
+static int may_create_key(u32 ksid,
+ struct task_struct *ctx)
+{
+ struct task_security_struct *tsec;
+
+ tsec = ctx->security;
+
+ return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL);
+}
+
+#define MAY_LINK 0
+#define MAY_UNLINK 1
+#define MAY_RMDIR 2
+
+/* Check whether a task can link, unlink, or rmdir a file/directory. */
+static int may_link(struct inode *dir,
+ struct dentry *dentry,
+ int kind)
+
+{
+ struct task_security_struct *tsec;
+ struct inode_security_struct *dsec, *isec;
+ struct avc_audit_data ad;
+ u32 av;
+ int rc;
+
+ tsec = current->security;
+ dsec = dir->i_security;
+ isec = dentry->d_inode->i_security;
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.path.dentry = dentry;
+
+ av = DIR__SEARCH;
+ av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME);
+ rc = avc_has_perm(tsec->sid, dsec->sid, SECCLASS_DIR, av, &ad);
+ if (rc)
+ return rc;
+
+ switch (kind) {
+ case MAY_LINK:
+ av = FILE__LINK;
+ break;
+ case MAY_UNLINK:
+ av = FILE__UNLINK;
+ break;
+ case MAY_RMDIR:
+ av = DIR__RMDIR;
+ break;
+ default:
+ printk(KERN_WARNING "SELinux: %s: unrecognized kind %d\n",
+ __func__, kind);
+ return 0;
+ }
+
+ rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass, av, &ad);
+ return rc;
+}
+
+static inline int may_rename(struct inode *old_dir,
+ struct dentry *old_dentry,
+ struct inode *new_dir,
+ struct dentry *new_dentry)
+{
+ struct task_security_struct *tsec;
+ struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
+ struct avc_audit_data ad;
+ u32 av;
+ int old_is_dir, new_is_dir;
+ int rc;
+
+ tsec = current->security;
+ old_dsec = old_dir->i_security;
+ old_isec = old_dentry->d_inode->i_security;
+ old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
+ new_dsec = new_dir->i_security;
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+
+ ad.u.fs.path.dentry = old_dentry;
+ rc = avc_has_perm(tsec->sid, old_dsec->sid, SECCLASS_DIR,
+ DIR__REMOVE_NAME | DIR__SEARCH, &ad);
+ if (rc)
+ return rc;
+ rc = avc_has_perm(tsec->sid, old_isec->sid,
+ old_isec->sclass, FILE__RENAME, &ad);
+ if (rc)
+ return rc;
+ if (old_is_dir && new_dir != old_dir) {
+ rc = avc_has_perm(tsec->sid, old_isec->sid,
+ old_isec->sclass, DIR__REPARENT, &ad);
+ if (rc)
+ return rc;
+ }
+
+ ad.u.fs.path.dentry = new_dentry;
+ av = DIR__ADD_NAME | DIR__SEARCH;
+ if (new_dentry->d_inode)
+ av |= DIR__REMOVE_NAME;
+ rc = avc_has_perm(tsec->sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
+ if (rc)
+ return rc;
+ if (new_dentry->d_inode) {
+ new_isec = new_dentry->d_inode->i_security;
+ new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
+ rc = avc_has_perm(tsec->sid, new_isec->sid,
+ new_isec->sclass,
+ (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+/* Check whether a task can perform a filesystem operation. */
+static int superblock_has_perm(struct task_struct *tsk,
+ struct super_block *sb,
+ u32 perms,
+ struct avc_audit_data *ad)
+{
+ struct task_security_struct *tsec;
+ struct superblock_security_struct *sbsec;
+
+ tsec = tsk->security;
+ sbsec = sb->s_security;
+ return avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM,
+ perms, ad);
+}
+
+/* Convert a Linux mode and permission mask to an access vector. */
+static inline u32 file_mask_to_av(int mode, int mask)
+{
+ u32 av = 0;
+
+ if ((mode & S_IFMT) != S_IFDIR) {
+ if (mask & MAY_EXEC)
+ av |= FILE__EXECUTE;
+ if (mask & MAY_READ)
+ av |= FILE__READ;
+
+ if (mask & MAY_APPEND)
+ av |= FILE__APPEND;
+ else if (mask & MAY_WRITE)
+ av |= FILE__WRITE;
+
+ } else {
+ if (mask & MAY_EXEC)
+ av |= DIR__SEARCH;
+ if (mask & MAY_WRITE)
+ av |= DIR__WRITE;
+ if (mask & MAY_READ)
+ av |= DIR__READ;
+ }
+
+ return av;
+}
+
+/*
+ * Convert a file mask to an access vector and include the correct open
+ * open permission.
+ */
+static inline u32 open_file_mask_to_av(int mode, int mask)
+{
+ u32 av = file_mask_to_av(mode, mask);
+
+ if (selinux_policycap_openperm) {
+ /*
+ * lnk files and socks do not really have an 'open'
+ */
+ if (S_ISREG(mode))
+ av |= FILE__OPEN;
+ else if (S_ISCHR(mode))
+ av |= CHR_FILE__OPEN;
+ else if (S_ISBLK(mode))
+ av |= BLK_FILE__OPEN;
+ else if (S_ISFIFO(mode))
+ av |= FIFO_FILE__OPEN;
+ else if (S_ISDIR(mode))
+ av |= DIR__OPEN;
+ else
+ printk(KERN_ERR "SELinux: WARNING: inside %s with "
+ "unknown mode:%x\n", __func__, mode);
+ }
+ return av;
+}
+
+/* Convert a Linux file to an access vector. */
+static inline u32 file_to_av(struct file *file)
+{
+ u32 av = 0;
+
+ if (file->f_mode & FMODE_READ)
+ av |= FILE__READ;
+ if (file->f_mode & FMODE_WRITE) {
+ if (file->f_flags & O_APPEND)
+ av |= FILE__APPEND;
+ else
+ av |= FILE__WRITE;
+ }
+ if (!av) {
+ /*
+ * Special file opened with flags 3 for ioctl-only use.
+ */
+ av = FILE__IOCTL;
+ }
+
+ return av;
+}
+
+/* Hook functions begin here. */
+
+static int selinux_ptrace_may_access(struct task_struct *child,
+ unsigned int mode)
+{
+ int rc;
+
+ rc = secondary_ops->ptrace_may_access(child, mode);
+ if (rc)
+ return rc;
+
+ if (mode == PTRACE_MODE_READ) {
+ struct task_security_struct *tsec = current->security;
+ struct task_security_struct *csec = child->security;
+ return avc_has_perm(tsec->sid, csec->sid,
+ SECCLASS_FILE, FILE__READ, NULL);
+ }
+
+ return task_has_perm(current, child, PROCESS__PTRACE);
+}
+
+static int selinux_ptrace_traceme(struct task_struct *parent)
+{
+ int rc;
+
+ rc = secondary_ops->ptrace_traceme(parent);
+ if (rc)
+ return rc;
+
+ return task_has_perm(parent, current, PROCESS__PTRACE);
+}
+
+static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
+ kernel_cap_t *inheritable, kernel_cap_t *permitted)
+{
+ int error;
+
+ error = task_has_perm(current, target, PROCESS__GETCAP);
+ if (error)
+ return error;
+
+ return secondary_ops->capget(target, effective, inheritable, permitted);
+}
+
+static int selinux_capset_check(struct task_struct *target, kernel_cap_t *effective,
+ kernel_cap_t *inheritable, kernel_cap_t *permitted)
+{
+ int error;
+
+ error = secondary_ops->capset_check(target, effective, inheritable, permitted);
+ if (error)
+ return error;
+
+ return task_has_perm(current, target, PROCESS__SETCAP);
+}
+
+static void selinux_capset_set(struct task_struct *target, kernel_cap_t *effective,
+ kernel_cap_t *inheritable, kernel_cap_t *permitted)
+{
+ secondary_ops->capset_set(target, effective, inheritable, permitted);
+}
+
+static int selinux_capable(struct task_struct *tsk, int cap)
+{
+ int rc;
+
+ rc = secondary_ops->capable(tsk, cap);
+ if (rc)
+ return rc;
+
+ return task_has_capability(tsk, cap);
+}
+
+static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid)
+{
+ int buflen, rc;
+ char *buffer, *path, *end;
+
+ rc = -ENOMEM;
+ buffer = (char *)__get_free_page(GFP_KERNEL);
+ if (!buffer)
+ goto out;
+
+ buflen = PAGE_SIZE;
+ end = buffer+buflen;
+ *--end = '\0';
+ buflen--;
+ path = end-1;
+ *path = '/';
+ while (table) {
+ const char *name = table->procname;
+ size_t namelen = strlen(name);
+ buflen -= namelen + 1;
+ if (buflen < 0)
+ goto out_free;
+ end -= namelen;
+ memcpy(end, name, namelen);
+ *--end = '/';
+ path = end;
+ table = table->parent;
+ }
+ buflen -= 4;
+ if (buflen < 0)
+ goto out_free;
+ end -= 4;
+ memcpy(end, "/sys", 4);
+ path = end;
+ rc = security_genfs_sid("proc", path, tclass, sid);
+out_free:
+ free_page((unsigned long)buffer);
+out:
+ return rc;
+}
+
+static int selinux_sysctl(ctl_table *table, int op)
+{
+ int error = 0;
+ u32 av;
+ struct task_security_struct *tsec;
+ u32 tsid;
+ int rc;
+
+ rc = secondary_ops->sysctl(table, op);
+ if (rc)
+ return rc;
+
+ tsec = current->security;
+
+ rc = selinux_sysctl_get_sid(table, (op == 0001) ?
+ SECCLASS_DIR : SECCLASS_FILE, &tsid);
+ if (rc) {
+ /* Default to the well-defined sysctl SID. */
+ tsid = SECINITSID_SYSCTL;
+ }
+
+ /* The op values are "defined" in sysctl.c, thereby creating
+ * a bad coupling between this module and sysctl.c */
+ if (op == 001) {
+ error = avc_has_perm(tsec->sid, tsid,
+ SECCLASS_DIR, DIR__SEARCH, NULL);
+ } else {
+ av = 0;
+ if (op & 004)
+ av |= FILE__READ;
+ if (op & 002)
+ av |= FILE__WRITE;
+ if (av)
+ error = avc_has_perm(tsec->sid, tsid,
+ SECCLASS_FILE, av, NULL);
+ }
+
+ return error;
+}
+
+static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
+{
+ int rc = 0;
+
+ if (!sb)
+ return 0;
+
+ switch (cmds) {
+ case Q_SYNC:
+ case Q_QUOTAON:
+ case Q_QUOTAOFF:
+ case Q_SETINFO:
+ case Q_SETQUOTA:
+ rc = superblock_has_perm(current, sb, FILESYSTEM__QUOTAMOD,
+ NULL);
+ break;
+ case Q_GETFMT:
+ case Q_GETINFO:
+ case Q_GETQUOTA:
+ rc = superblock_has_perm(current, sb, FILESYSTEM__QUOTAGET,
+ NULL);
+ break;
+ default:
+ rc = 0; /* let the kernel handle invalid cmds */
+ break;
+ }
+ return rc;
+}
+
+static int selinux_quota_on(struct dentry *dentry)
+{
+ return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON);
+}
+
+static int selinux_syslog(int type)
+{
+ int rc;
+
+ rc = secondary_ops->syslog(type);
+ if (rc)
+ return rc;
+
+ switch (type) {
+ case 3: /* Read last kernel messages */
+ case 10: /* Return size of the log buffer */
+ rc = task_has_system(current, SYSTEM__SYSLOG_READ);
+ break;
+ case 6: /* Disable logging to console */
+ case 7: /* Enable logging to console */
+ case 8: /* Set level of messages printed to console */
+ rc = task_has_system(current, SYSTEM__SYSLOG_CONSOLE);
+ break;
+ case 0: /* Close log */
+ case 1: /* Open log */
+ case 2: /* Read from log */
+ case 4: /* Read/clear last kernel messages */
+ case 5: /* Clear ring buffer */
+ default:
+ rc = task_has_system(current, SYSTEM__SYSLOG_MOD);
+ break;
+ }
+ return rc;
+}
+
+/*
+ * Check that a process has enough memory to allocate a new virtual
+ * mapping. 0 means there is enough memory for the allocation to
+ * succeed and -ENOMEM implies there is not.
+ *
+ * Note that secondary_ops->capable and task_has_perm_noaudit return 0
+ * if the capability is granted, but __vm_enough_memory requires 1 if
+ * the capability is granted.
+ *
+ * Do not audit the selinux permission check, as this is applied to all
+ * processes that allocate mappings.
+ */
+static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
+{
+ int rc, cap_sys_admin = 0;
+ struct task_security_struct *tsec = current->security;
+
+ rc = secondary_ops->capable(current, CAP_SYS_ADMIN);
+ if (rc == 0)
+ rc = avc_has_perm_noaudit(tsec->sid, tsec->sid,
+ SECCLASS_CAPABILITY,
+ CAP_TO_MASK(CAP_SYS_ADMIN),
+ 0,
+ NULL);
+
+ if (rc == 0)
+ cap_sys_admin = 1;
+
+ return __vm_enough_memory(mm, pages, cap_sys_admin);
+}
+
+/* binprm security operations */
+
+static int selinux_bprm_alloc_security(struct linux_binprm *bprm)
+{
+ struct bprm_security_struct *bsec;
+
+ bsec = kzalloc(sizeof(struct bprm_security_struct), GFP_KERNEL);
+ if (!bsec)
+ return -ENOMEM;
+
+ bsec->sid = SECINITSID_UNLABELED;
+ bsec->set = 0;
+
+ bprm->security = bsec;
+ return 0;
+}
+
+static int selinux_bprm_set_security(struct linux_binprm *bprm)
+{
+ struct task_security_struct *tsec;
+ struct inode *inode = bprm->file->f_path.dentry->d_inode;
+ struct inode_security_struct *isec;
+ struct bprm_security_struct *bsec;
+ u32 newsid;
+ struct avc_audit_data ad;
+ int rc;
+
+ rc = secondary_ops->bprm_set_security(bprm);
+ if (rc)
+ return rc;
+
+ bsec = bprm->security;
+
+ if (bsec->set)
+ return 0;
+
+ tsec = current->security;
+ isec = inode->i_security;
+
+ /* Default to the current task SID. */
+ bsec->sid = tsec->sid;
+
+ /* Reset fs, key, and sock SIDs on execve. */
+ tsec->create_sid = 0;
+ tsec->keycreate_sid = 0;
+ tsec->sockcreate_sid = 0;
+
+ if (tsec->exec_sid) {
+ newsid = tsec->exec_sid;
+ /* Reset exec SID on execve. */
+ tsec->exec_sid = 0;
+ } else {
+ /* Check for a default transition on this program. */
+ rc = security_transition_sid(tsec->sid, isec->sid,
+ SECCLASS_PROCESS, &newsid);
+ if (rc)
+ return rc;
+ }
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.path = bprm->file->f_path;
+
+ if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
+ newsid = tsec->sid;
+
+ if (tsec->sid == newsid) {
+ rc = avc_has_perm(tsec->sid, isec->sid,
+ SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
+ if (rc)
+ return rc;
+ } else {
+ /* Check permissions for the transition. */
+ rc = avc_has_perm(tsec->sid, newsid,
+ SECCLASS_PROCESS, PROCESS__TRANSITION, &ad);
+ if (rc)
+ return rc;
+
+ rc = avc_has_perm(newsid, isec->sid,
+ SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
+ if (rc)
+ return rc;
+
+ /* Clear any possibly unsafe personality bits on exec: */
+ current->personality &= ~PER_CLEAR_ON_SETID;
+
+ /* Set the security field to the new SID. */
+ bsec->sid = newsid;
+ }
+
+ bsec->set = 1;
+ return 0;
+}
+
+static int selinux_bprm_check_security(struct linux_binprm *bprm)
+{
+ return secondary_ops->bprm_check_security(bprm);
+}
+
+
+static int selinux_bprm_secureexec(struct linux_binprm *bprm)
+{
+ struct task_security_struct *tsec = current->security;
+ int atsecure = 0;
+
+ if (tsec->osid != tsec->sid) {
+ /* Enable secure mode for SIDs transitions unless
+ the noatsecure permission is granted between
+ the two SIDs, i.e. ahp returns 0. */
+ atsecure = avc_has_perm(tsec->osid, tsec->sid,
+ SECCLASS_PROCESS,
+ PROCESS__NOATSECURE, NULL);
+ }
+
+ return (atsecure || secondary_ops->bprm_secureexec(bprm));
+}
+
+static void selinux_bprm_free_security(struct linux_binprm *bprm)
+{
+ kfree(bprm->security);
+ bprm->security = NULL;
+}
+
+extern struct vfsmount *selinuxfs_mount;
+extern struct dentry *selinux_null;
+
+/* Derived from fs/exec.c:flush_old_files. */
+static inline void flush_unauthorized_files(struct files_struct *files)
+{
+ struct avc_audit_data ad;
+ struct file *file, *devnull = NULL;
+ struct tty_struct *tty;
+ struct fdtable *fdt;
+ long j = -1;
+ int drop_tty = 0;
+
+ tty = get_current_tty();
+ if (tty) {
+ file_list_lock();
+ if (!list_empty(&tty->tty_files)) {
+ struct inode *inode;
+
+ /* Revalidate access to controlling tty.
+ Use inode_has_perm on the tty inode directly rather
+ than using file_has_perm, as this particular open
+ file may belong to another process and we are only
+ interested in the inode-based check here. */
+ file = list_first_entry(&tty->tty_files, struct file, f_u.fu_list);
+ inode = file->f_path.dentry->d_inode;
+ if (inode_has_perm(current, inode,
+ FILE__READ | FILE__WRITE, NULL)) {
+ drop_tty = 1;
+ }
+ }
+ file_list_unlock();
+ tty_kref_put(tty);
+ }
+ /* Reset controlling tty. */
+ if (drop_tty)
+ no_tty();
+
+ /* Revalidate access to inherited open files. */
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+
+ spin_lock(&files->file_lock);
+ for (;;) {
+ unsigned long set, i;
+ int fd;
+
+ j++;
+ i = j * __NFDBITS;
+ fdt = files_fdtable(files);
+ if (i >= fdt->max_fds)
+ break;
+ set = fdt->open_fds->fds_bits[j];
+ if (!set)
+ continue;
+ spin_unlock(&files->file_lock);
+ for ( ; set ; i++, set >>= 1) {
+ if (set & 1) {
+ file = fget(i);
+ if (!file)
+ continue;
+ if (file_has_perm(current,
+ file,
+ file_to_av(file))) {
+ sys_close(i);
+ fd = get_unused_fd();
+ if (fd != i) {
+ if (fd >= 0)
+ put_unused_fd(fd);
+ fput(file);
+ continue;
+ }
+ if (devnull) {
+ get_file(devnull);
+ } else {
+ devnull = dentry_open(dget(selinux_null), mntget(selinuxfs_mount), O_RDWR);
+ if (IS_ERR(devnull)) {
+ devnull = NULL;
+ put_unused_fd(fd);
+ fput(file);
+ continue;
+ }
+ }
+ fd_install(fd, devnull);
+ }
+ fput(file);
+ }
+ }
+ spin_lock(&files->file_lock);
+
+ }
+ spin_unlock(&files->file_lock);
+}
+
+static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe)
+{
+ struct task_security_struct *tsec;
+ struct bprm_security_struct *bsec;
+ u32 sid;
+ int rc;
+
+ secondary_ops->bprm_apply_creds(bprm, unsafe);
+
+ tsec = current->security;
+
+ bsec = bprm->security;
+ sid = bsec->sid;
+
+ tsec->osid = tsec->sid;
+ bsec->unsafe = 0;
+ if (tsec->sid != sid) {
+ /* Check for shared state. If not ok, leave SID
+ unchanged and kill. */
+ if (unsafe & LSM_UNSAFE_SHARE) {
+ rc = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
+ PROCESS__SHARE, NULL);
+ if (rc) {
+ bsec->unsafe = 1;
+ return;
+ }
+ }
+
+ /* Check for ptracing, and update the task SID if ok.
+ Otherwise, leave SID unchanged and kill. */
+ if (unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
+ struct task_struct *tracer;
+ struct task_security_struct *sec;
+ u32 ptsid = 0;
+
+ rcu_read_lock();
+ tracer = tracehook_tracer_task(current);
+ if (likely(tracer != NULL)) {
+ sec = tracer->security;
+ ptsid = sec->sid;
+ }
+ rcu_read_unlock();
+
+ if (ptsid != 0) {
+ rc = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
+ PROCESS__PTRACE, NULL);
+ if (rc) {
+ bsec->unsafe = 1;
+ return;
+ }
+ }
+ }
+ tsec->sid = sid;
+ }
+}
+
+/*
+ * called after apply_creds without the task lock held
+ */
+static void selinux_bprm_post_apply_creds(struct linux_binprm *bprm)
+{
+ struct task_security_struct *tsec;
+ struct rlimit *rlim, *initrlim;
+ struct itimerval itimer;
+ struct bprm_security_struct *bsec;
+ int rc, i;
+
+ tsec = current->security;
+ bsec = bprm->security;
+
+ if (bsec->unsafe) {
+ force_sig_specific(SIGKILL, current);
+ return;
+ }
+ if (tsec->osid == tsec->sid)
+ return;
+
+ /* Close files for which the new task SID is not authorized. */
+ flush_unauthorized_files(current->files);
+
+ /* Check whether the new SID can inherit signal state
+ from the old SID. If not, clear itimers to avoid
+ subsequent signal generation and flush and unblock
+ signals. This must occur _after_ the task SID has
+ been updated so that any kill done after the flush
+ will be checked against the new SID. */
+ rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
+ PROCESS__SIGINH, NULL);
+ if (rc) {
+ memset(&itimer, 0, sizeof itimer);
+ for (i = 0; i < 3; i++)
+ do_setitimer(i, &itimer, NULL);
+ flush_signals(current);
+ spin_lock_irq(&current->sighand->siglock);
+ flush_signal_handlers(current, 1);
+ sigemptyset(&current->blocked);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
+ }
+
+ /* Always clear parent death signal on SID transitions. */
+ current->pdeath_signal = 0;
+
+ /* Check whether the new SID can inherit resource limits
+ from the old SID. If not, reset all soft limits to
+ the lower of the current task's hard limit and the init
+ task's soft limit. Note that the setting of hard limits
+ (even to lower them) can be controlled by the setrlimit
+ check. The inclusion of the init task's soft limit into
+ the computation is to avoid resetting soft limits higher
+ than the default soft limit for cases where the default
+ is lower than the hard limit, e.g. RLIMIT_CORE or
+ RLIMIT_STACK.*/
+ rc = avc_has_perm(tsec->osid, tsec->sid, SECCLASS_PROCESS,
+ PROCESS__RLIMITINH, NULL);
+ if (rc) {
+ for (i = 0; i < RLIM_NLIMITS; i++) {
+ rlim = current->signal->rlim + i;
+ initrlim = init_task.signal->rlim+i;
+ rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur);
+ }
+ update_rlimit_cpu(rlim->rlim_cur);
+ }
+
+ /* Wake up the parent if it is waiting so that it can
+ recheck wait permission to the new task SID. */
+ wake_up_interruptible(&current->parent->signal->wait_chldexit);
+}
+
+/* superblock security operations */
+
+static int selinux_sb_alloc_security(struct super_block *sb)
+{
+ return superblock_alloc_security(sb);
+}
+
+static void selinux_sb_free_security(struct super_block *sb)
+{
+ superblock_free_security(sb);
+}
+
+static inline int match_prefix(char *prefix, int plen, char *option, int olen)
+{
+ if (plen > olen)
+ return 0;
+
+ return !memcmp(prefix, option, plen);
+}
+
+static inline int selinux_option(char *option, int len)
+{
+ return (match_prefix(CONTEXT_STR, sizeof(CONTEXT_STR)-1, option, len) ||
+ match_prefix(FSCONTEXT_STR, sizeof(FSCONTEXT_STR)-1, option, len) ||
+ match_prefix(DEFCONTEXT_STR, sizeof(DEFCONTEXT_STR)-1, option, len) ||
+ match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len));
+}
+
+static inline void take_option(char **to, char *from, int *first, int len)
+{
+ if (!*first) {
+ **to = ',';
+ *to += 1;
+ } else
+ *first = 0;
+ memcpy(*to, from, len);
+ *to += len;
+}
+
+static inline void take_selinux_option(char **to, char *from, int *first,
+ int len)
+{
+ int current_size = 0;
+
+ if (!*first) {
+ **to = '|';
+ *to += 1;
+ } else
+ *first = 0;
+
+ while (current_size < len) {
+ if (*from != '"') {
+ **to = *from;
+ *to += 1;
+ }
+ from += 1;
+ current_size += 1;
+ }
+}
+
+static int selinux_sb_copy_data(char *orig, char *copy)
+{
+ int fnosec, fsec, rc = 0;
+ char *in_save, *in_curr, *in_end;
+ char *sec_curr, *nosec_save, *nosec;
+ int open_quote = 0;
+
+ in_curr = orig;
+ sec_curr = copy;
+
+ nosec = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!nosec) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ nosec_save = nosec;
+ fnosec = fsec = 1;
+ in_save = in_end = orig;
+
+ do {
+ if (*in_end == '"')
+ open_quote = !open_quote;
+ if ((*in_end == ',' && open_quote == 0) ||
+ *in_end == '\0') {
+ int len = in_end - in_curr;
+
+ if (selinux_option(in_curr, len))
+ take_selinux_option(&sec_curr, in_curr, &fsec, len);
+ else
+ take_option(&nosec, in_curr, &fnosec, len);
+
+ in_curr = in_end + 1;
+ }
+ } while (*in_end++);
+
+ strcpy(in_save, nosec_save);
+ free_page((unsigned long)nosec_save);
+out:
+ return rc;
+}
+
+static int selinux_sb_kern_mount(struct super_block *sb, void *data)
+{
+ struct avc_audit_data ad;
+ int rc;
+
+ rc = superblock_doinit(sb, data);
+ if (rc)
+ return rc;
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.path.dentry = sb->s_root;
+ return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad);
+}
+
+static int selinux_sb_statfs(struct dentry *dentry)
+{
+ struct avc_audit_data ad;
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.path.dentry = dentry->d_sb->s_root;
+ return superblock_has_perm(current, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
+}
+
+static int selinux_mount(char *dev_name,
+ struct path *path,
+ char *type,
+ unsigned long flags,
+ void *data)
+{
+ int rc;
+
+ rc = secondary_ops->sb_mount(dev_name, path, type, flags, data);
+ if (rc)
+ return rc;
+
+ if (flags & MS_REMOUNT)
+ return superblock_has_perm(current, path->mnt->mnt_sb,
+ FILESYSTEM__REMOUNT, NULL);
+ else
+ return dentry_has_perm(current, path->mnt, path->dentry,
+ FILE__MOUNTON);
+}
+
+static int selinux_umount(struct vfsmount *mnt, int flags)
+{
+ int rc;
+
+ rc = secondary_ops->sb_umount(mnt, flags);
+ if (rc)
+ return rc;
+
+ return superblock_has_perm(current, mnt->mnt_sb,
+ FILESYSTEM__UNMOUNT, NULL);
+}
+
+/* inode security operations */
+
+static int selinux_inode_alloc_security(struct inode *inode)
+{
+ return inode_alloc_security(inode);
+}
+
+static void selinux_inode_free_security(struct inode *inode)
+{
+ inode_free_security(inode);
+}
+
+static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
+ char **name, void **value,
+ size_t *len)
+{
+ struct task_security_struct *tsec;
+ struct inode_security_struct *dsec;
+ struct superblock_security_struct *sbsec;
+ u32 newsid, clen;
+ int rc;
+ char *namep = NULL, *context;
+
+ tsec = current->security;
+ dsec = dir->i_security;
+ sbsec = dir->i_sb->s_security;
+
+ if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
+ newsid = tsec->create_sid;
+ } else {
+ rc = security_transition_sid(tsec->sid, dsec->sid,
+ inode_mode_to_security_class(inode->i_mode),
+ &newsid);
+ if (rc) {
+ printk(KERN_WARNING "%s: "
+ "security_transition_sid failed, rc=%d (dev=%s "
+ "ino=%ld)\n",
+ __func__,
+ -rc, inode->i_sb->s_id, inode->i_ino);
+ return rc;
+ }
+ }
+
+ /* Possibly defer initialization to selinux_complete_init. */
+ if (sbsec->initialized) {
+ struct inode_security_struct *isec = inode->i_security;
+ isec->sclass = inode_mode_to_security_class(inode->i_mode);
+ isec->sid = newsid;
+ isec->initialized = 1;
+ }
+
+ if (!ss_initialized || sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+ return -EOPNOTSUPP;
+
+ if (name) {
+ namep = kstrdup(XATTR_SELINUX_SUFFIX, GFP_NOFS);
+ if (!namep)
+ return -ENOMEM;
+ *name = namep;
+ }
+
+ if (value && len) {
+ rc = security_sid_to_context_force(newsid, &context, &clen);
+ if (rc) {
+ kfree(namep);
+ return rc;
+ }
+ *value = context;
+ *len = clen;
+ }
+
+ return 0;
+}
+
+static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask)
+{
+ return may_create(dir, dentry, SECCLASS_FILE);
+}
+
+static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+{
+ int rc;
+
+ rc = secondary_ops->inode_link(old_dentry, dir, new_dentry);
+ if (rc)
+ return rc;
+ return may_link(dir, old_dentry, MAY_LINK);
+}
+
+static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int rc;
+
+ rc = secondary_ops->inode_unlink(dir, dentry);
+ if (rc)
+ return rc;
+ return may_link(dir, dentry, MAY_UNLINK);
+}
+
+static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name)
+{
+ return may_create(dir, dentry, SECCLASS_LNK_FILE);
+}
+
+static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, int mask)
+{
+ return may_create(dir, dentry, SECCLASS_DIR);
+}
+
+static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ return may_link(dir, dentry, MAY_RMDIR);
+}
+
+static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+{
+ int rc;
+
+ rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
+ if (rc)
+ return rc;
+
+ return may_create(dir, dentry, inode_mode_to_security_class(mode));
+}
+
+static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
+ struct inode *new_inode, struct dentry *new_dentry)
+{
+ return may_rename(old_inode, old_dentry, new_inode, new_dentry);
+}
+
+static int selinux_inode_readlink(struct dentry *dentry)
+{
+ return dentry_has_perm(current, NULL, dentry, FILE__READ);
+}
+
+static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
+{
+ int rc;
+
+ rc = secondary_ops->inode_follow_link(dentry, nameidata);
+ if (rc)
+ return rc;
+ return dentry_has_perm(current, NULL, dentry, FILE__READ);
+}
+
+static int selinux_inode_permission(struct inode *inode, int mask)
+{
+ int rc;
+
+ rc = secondary_ops->inode_permission(inode, mask);
+ if (rc)
+ return rc;
+
+ if (!mask) {
+ /* No permission to check. Existence test. */
+ return 0;
+ }
+
+ return inode_has_perm(current, inode,
+ open_file_mask_to_av(inode->i_mode, mask), NULL);
+}
+
+static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ int rc;
+
+ rc = secondary_ops->inode_setattr(dentry, iattr);
+ if (rc)
+ return rc;
+
+ if (iattr->ia_valid & ATTR_FORCE)
+ return 0;
+
+ if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
+ ATTR_ATIME_SET | ATTR_MTIME_SET))
+ return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
+
+ return dentry_has_perm(current, NULL, dentry, FILE__WRITE);
+}
+
+static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
+{
+ return dentry_has_perm(current, mnt, dentry, FILE__GETATTR);
+}
+
+static int selinux_inode_setotherxattr(struct dentry *dentry, const char *name)
+{
+ if (!strncmp(name, XATTR_SECURITY_PREFIX,
+ sizeof XATTR_SECURITY_PREFIX - 1)) {
+ if (!strcmp(name, XATTR_NAME_CAPS)) {
+ if (!capable(CAP_SETFCAP))
+ return -EPERM;
+ } else if (!capable(CAP_SYS_ADMIN)) {
+ /* A different attribute in the security namespace.
+ Restrict to administrator. */
+ return -EPERM;
+ }
+ }
+
+ /* Not an attribute we recognize, so just check the
+ ordinary setattr permission. */
+ return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
+}
+
+static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ struct task_security_struct *tsec = current->security;
+ struct inode *inode = dentry->d_inode;
+ struct inode_security_struct *isec = inode->i_security;
+ struct superblock_security_struct *sbsec;
+ struct avc_audit_data ad;
+ u32 newsid;
+ int rc = 0;
+
+ if (strcmp(name, XATTR_NAME_SELINUX))
+ return selinux_inode_setotherxattr(dentry, name);
+
+ sbsec = inode->i_sb->s_security;
+ if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+ return -EOPNOTSUPP;
+
+ if (!is_owner_or_cap(inode))
+ return -EPERM;
+
+ AVC_AUDIT_DATA_INIT(&ad, FS);
+ ad.u.fs.path.dentry = dentry;
+
+ rc = avc_has_perm(tsec->sid, isec->sid, isec->sclass,
+ FILE__RELABELFROM, &ad);
+ if (rc)
+ return rc;
+
+ rc = security_context_to_sid(value, size, &newsid);
+ if (rc == -EINVAL) {
+ if (!capable(CAP_MAC_ADMIN))
+ return rc;
+ rc = security_context_to_sid_force(value, size, &newsid);
+ }
+ if (rc)
+ return rc;
+
+ rc = avc_has_perm(tsec->sid, newsid, isec->sclass,
+ FILE__RELABELTO, &ad);
+ if (rc)
+ return rc;
+
+ rc = security_validate_transition(isec->sid, newsid, tsec->sid,
+ isec->sclass);
+ if (rc)
+ return rc;
+
+ return avc_has_perm(newsid,
+ sbsec->sid,
+ SECCLASS_FILESYSTEM,
+ FILESYSTEM__ASSOCIATE,
+ &ad);
+}
+
+static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size,
+ int flags)
+{
+ struct inode *inode = dentry->d_inode;
+ struct inode_security_struct *isec = inode->i_security;
+ u32 newsid;
+ int rc;
+
+ if (strcmp(name, XATTR_NAME_SELINUX)) {
+ /* Not an attribute we recognize, so nothing to do. */
+ return;
+ }
+
+ rc = security_context_to_sid_force(value, size, &newsid);
+ if (rc) {
+ printk(KERN_ERR "SELinux: unable to map context to SID"
+ "for (%s, %lu), rc=%d\n",
+ inode->i_sb->s_id, inode->i_ino, -rc);
+ return;
+ }
+
+ isec->sid = newsid;
+ return;
+}
+
+static int selinux_inode_getxattr(struct dentry *dentry, const char *name)
+{
+ return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
+}
+
+static int selinux_inode_listxattr(struct dentry *dentry)
+{
+ return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
+}
+
+static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
+{
+ if (strcmp(name, XATTR_NAME_SELINUX))
+ return selinux_inode_setotherxattr(dentry, name);
+
+ /* No one is allowed to remove a SELinux security label.
+ You can change the label, but all data must be labeled. */
+ return -EACCES;
+}
+
+/*
+ * Copy the inode security context value to the user.
+ *
+ * Permission check is handled by selinux_inode_getxattr hook.
+ */
+static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc)
+{
+ u32 size;
+ int error;
+ char *context = NULL;
+ struct task_security_struct *tsec = current->security;
+ struct inode_security_struct *isec = inode->i_security;
+
+ if (strcmp(name, XATTR_SELINUX_SUFFIX))
+ return -EOPNOTSUPP;
+
+ /*
+ * If the caller has CAP_MAC_ADMIN, then get the raw context
+ * value even if it is not defined by current policy; otherwise,
+ * use the in-core value under current policy.
+ * Use the non-auditing forms of the permission checks since
+ * getxattr may be called by unprivileged processes commonly
+ * and lack of permission just means that we fall back to the
+ * in-core context value, not a denial.
+ */
+ error = secondary_ops->capable(current, CAP_MAC_ADMIN);
+ if (!error)
+ error = avc_has_perm_noaudit(tsec->sid, tsec->sid,
+ SECCLASS_CAPABILITY2,
+ CAPABILITY2__MAC_ADMIN,
+ 0,
+ NULL);
+ if (!error)
+ error = security_sid_to_context_force(isec->sid, &context,
+ &size);
+ else
+ error = security_sid_to_context(isec->sid, &context, &size);
+ if (error)
+ return error;
+ error = size;
+ if (alloc) {
+ *buffer = context;
+ goto out_nofree;
+ }
+ kfree(context);
+out_nofree:
+ return error;
+}
+
+static int selinux_inode_setsecurity(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ struct inode_security_struct *isec = inode->i_security;
+ u32 newsid;
+ int rc;
+
+ if (strcmp(name, XATTR_SELINUX_SUFFIX))
+ return -EOPNOTSUPP;
+
+ if (!value || !size)
+ return -EACCES;
+
+ rc = security_context_to_sid((void *)value, size, &newsid);
+ if (rc)
+ return rc;
+
+ isec->sid = newsid;
+ return 0;
+}
+
+static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
+{
+ const int len = sizeof(XATTR_NAME_SELINUX);
+ if (buffer && len <= buffer_size)
+ memcpy(buffer, XATTR_NAME_SELINUX, len);
+ return len;
+}
+
+static int selinux_inode_need_killpriv(struct dentry *dentry)
+{
+ return secondary_ops->inode_need_killpriv(dentry);
+}
+
+static int selinux_inode_killpriv(struct dentry *dentry)
+{
+ return secondary_ops->inode_killpriv(dentry);
+}
+
+static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)
+{
+ struct inode_security_struct *isec = inode->i_security;
+ *secid = isec->sid;
+}
+
+/* file security operations */
+
+static int selinux_revalidate_file_permission(struct file *file, int mask)
+{
+ int rc;
+ struct inode *inode = file->f_path.dentry->d_inode;
+
+ if (!mask) {
+ /* No permission to check. Existence test. */
+ return 0;
+ }
+
+ /* file_mask_to_av won't add FILE__WRITE if MAY_APPEND is set */
+ if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
+ mask |= MAY_APPEND;
+
+ rc = file_has_perm(current, file,
+ file_mask_to_av(inode->i_mode, mask));
+ if (rc)
+ return rc;
+
+ return selinux_netlbl_inode_permission(inode, mask);
+}
+
+static int selinux_file_permission(struct file *file, int mask)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct task_security_struct *tsec = current->security;
+ struct file_security_struct *fsec = file->f_security;
+ struct inode_security_struct *isec = inode->i_security;
+
+ if (!mask) {
+ /* No permission to check. Existence test. */
+ return 0;
+ }
+
+ if (tsec->sid == fsec->sid && fsec->isid == isec->sid
+ && fsec->pseqno == avc_policy_seqno())
+ return selinux_netlbl_inode_permission(inode, mask);
+
+ return selinux_revalidate_file_permission(file, mask);
+}
+
+static int selinux_file_alloc_security(struct file *file)
+{
+ return file_alloc_security(file);
+}
+
+static void selinux_file_free_security(struct file *file)
+{
+ file_free_security(file);
+}
+
+static int selinux_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ u32 av = 0;
+
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ av |= FILE__WRITE;
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ av |= FILE__READ;
+ if (!av)
+ av = FILE__IOCTL;
+
+ return file_has_perm(current, file, av);
+}
+
+static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
+{
+#ifndef CONFIG_PPC32
+ if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
+ /*
+ * We are making executable an anonymous mapping or a
+ * private file mapping that will also be writable.
+ * This has an additional check.
+ */
+ int rc = task_has_perm(current, current, PROCESS__EXECMEM);
+ if (rc)
+ return rc;
+ }
+#endif
+
+ if (file) {
+ /* read access is always possible with a mapping */
+ u32 av = FILE__READ;
+
+ /* write access only matters if the mapping is shared */
+ if (shared && (prot & PROT_WRITE))
+ av |= FILE__WRITE;
+
+ if (prot & PROT_EXEC)
+ av |= FILE__EXECUTE;
+
+ return file_has_perm(current, file, av);
+ }
+ return 0;
+}
+
+static int selinux_file_mmap(struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags,
+ unsigned long addr, unsigned long addr_only)
+{
+ int rc = 0;
+ u32 sid = ((struct task_security_struct *)(current->security))->sid;
+
+ if (addr < mmap_min_addr)
+ rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT,
+ MEMPROTECT__MMAP_ZERO, NULL);
+ if (rc || addr_only)
+ return rc;
+
+ if (selinux_checkreqprot)
+ prot = reqprot;
+
+ return file_map_prot_check(file, prot,
+ (flags & MAP_TYPE) == MAP_SHARED);
+}
+
+static int selinux_file_mprotect(struct vm_area_struct *vma,
+ unsigned long reqprot,
+ unsigned long prot)
+{
+ int rc;
+
+ rc = secondary_ops->file_mprotect(vma, reqprot, prot);
+ if (rc)
+ return rc;
+
+ if (selinux_checkreqprot)
+ prot = reqprot;
+
+#ifndef CONFIG_PPC32
+ if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
+ rc = 0;
+ if (vma->vm_start >= vma->vm_mm->start_brk &&
+ vma->vm_end <= vma->vm_mm->brk) {
+ rc = task_has_perm(current, current,
+ PROCESS__EXECHEAP);
+ } else if (!vma->vm_file &&
+ vma->vm_start <= vma->vm_mm->start_stack &&
+ vma->vm_end >= vma->vm_mm->start_stack) {
+ rc = task_has_perm(current, current, PROCESS__EXECSTACK);
+ } else if (vma->vm_file && vma->anon_vma) {
+ /*
+ * We are making executable a file mapping that has
+ * had some COW done. Since pages might have been
+ * written, check ability to execute the possibly
+ * modified content. This typically should only
+ * occur for text relocations.
+ */
+ rc = file_has_perm(current, vma->vm_file,
+ FILE__EXECMOD);
+ }
+ if (rc)
+ return rc;
+ }
+#endif
+
+ return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
+}
+
+static int selinux_file_lock(struct file *file, unsigned int cmd)
+{
+ return file_has_perm(current, file, FILE__LOCK);
+}
+
+static int selinux_file_fcntl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+
+ switch (cmd) {
+ case F_SETFL:
+ if (!file->f_path.dentry || !file->f_path.dentry->d_inode) {
+ err = -EINVAL;
+ break;
+ }
+
+ if ((file->f_flags & O_APPEND) && !(arg & O_APPEND)) {
+ err = file_has_perm(current, file, FILE__WRITE);
+ break;
+ }
+ /* fall through */
+ case F_SETOWN:
+ case F_SETSIG:
+ case F_GETFL:
+ case F_GETOWN:
+ case F_GETSIG:
+ /* Just check FD__USE permission */
+ err = file_has_perm(current, file, 0);
+ break;
+ case F_GETLK:
+ case F_SETLK:
+ case F_SETLKW:
+#if BITS_PER_LONG == 32
+ case F_GETLK64:
+ case F_SETLK64:
+ case F_SETLKW64:
+#endif
+ if (!file->f_path.dentry || !file->f_path.dentry->d_inode) {
+ err = -EINVAL;
+ break;
+ }
+ err = file_has_perm(current, file, FILE__LOCK);
+ break;
+ }
+
+ return err;
+}
+
+static int selinux_file_set_fowner(struct file *file)
+{
+ struct task_security_struct *tsec;
+ struct file_security_struct *fsec;
+
+ tsec = current->security;
+ fsec = file->f_security;
+ fsec->fown_sid = tsec->sid;
+
+ return 0;
+}
+
+static int selinux_file_send_sigiotask(struct task_struct *tsk,
+ struct fown_struct *fown, int signum)
+{
+ struct file *file;
+ u32 perm;
+ struct task_security_struct *tsec;
+ struct file_security_struct *fsec;
+
+ /* struct fown_struct is never outside the context of a struct file */
+ file = container_of(fown, struct file, f_owner);
+
+ tsec = tsk->security;
+ fsec = file->f_security;
+
+ if (!signum)
+ perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */
+ else
+ perm = signal_to_av(signum);
+
+ return avc_has_perm(fsec->fown_sid, tsec->sid,
+ SECCLASS_PROCESS, perm, NULL);
+}
+
+static int selinux_file_receive(struct file *file)
+{
+ return file_has_perm(current, file, file_to_av(file));
+}
+
+static int selinux_dentry_open(struct file *file)
+{
+ struct file_security_struct *fsec;
+ struct inode *inode;
+ struct inode_security_struct *isec;
+ inode = file->f_path.dentry->d_inode;
+ fsec = file->f_security;
+ isec = inode->i_security;
+ /*
+ * Save inode label and policy sequence number
+ * at open-time so that selinux_file_permission
+ * can determine whether revalidation is necessary.
+ * Task label is already saved in the file security
+ * struct as its SID.
+ */
+ fsec->isid = isec->sid;
+ fsec->pseqno = avc_policy_seqno();
+ /*
+ * Since the inode label or policy seqno may have changed
+ * between the selinux_inode_permission check and the saving
+ * of state above, recheck that access is still permitted.
+ * Otherwise, access might never be revalidated against the
+ * new inode label or new policy.
+ * This check is not redundant - do not remove.
+ */
+ return inode_has_perm(current, inode, file_to_av(file), NULL);
+}
+
+/* task security operations */
+
+static int selinux_task_create(unsigned long clone_flags)
+{
+ int rc;
+
+ rc = secondary_ops->task_create(clone_flags);
+ if (rc)
+ return rc;
+
+ return task_has_perm(current, current, PROCESS__FORK);
+}
+
+static int selinux_task_alloc_security(struct task_struct *tsk)
+{
+ struct task_security_struct *tsec1, *tsec2;
+ int rc;
+
+ tsec1 = current->security;
+
+ rc = task_alloc_security(tsk);
+ if (rc)
+ return rc;
+ tsec2 = tsk->security;
+
+ tsec2->osid = tsec1->osid;
+ tsec2->sid = tsec1->sid;
+
+ /* Retain the exec, fs, key, and sock SIDs across fork */
+ tsec2->exec_sid = tsec1->exec_sid;
+ tsec2->create_sid = tsec1->create_sid;
+ tsec2->keycreate_sid = tsec1->keycreate_sid;
+ tsec2->sockcreate_sid = tsec1->sockcreate_sid;
+
+ return 0;
+}
+
+static void selinux_task_free_security(struct task_struct *tsk)
+{
+ task_free_security(tsk);
+}
+
+static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
+{
+ /* Since setuid only affects the current process, and
+ since the SELinux controls are not based on the Linux
+ identity attributes, SELinux does not need to control
+ this operation. However, SELinux does control the use
+ of the CAP_SETUID and CAP_SETGID capabilities using the
+ capable hook. */
+ return 0;
+}
+
+static int selinux_task_post_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
+{
+ return secondary_ops->task_post_setuid(id0, id1, id2, flags);
+}
+
+static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
+{
+ /* See the comment for setuid above. */
+ return 0;
+}
+
+static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
+{
+ return task_has_perm(current, p, PROCESS__SETPGID);
+}
+
+static int selinux_task_getpgid(struct task_struct *p)
+{
+ return task_has_perm(current, p, PROCESS__GETPGID);
+}
+
+static int selinux_task_getsid(struct task_struct *p)
+{
+ return task_has_perm(current, p, PROCESS__GETSESSION);
+}
+
+static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
+{
+ struct task_security_struct *tsec = p->security;
+ *secid = tsec->sid;
+}
+
+static int selinux_task_setgroups(struct group_info *group_info)
+{
+ /* See the comment for setuid above. */
+ return 0;
+}
+
+static int selinux_task_setnice(struct task_struct *p, int nice)
+{
+ int rc;
+
+ rc = secondary_ops->task_setnice(p, nice);
+ if (rc)
+ return rc;
+
+ return task_has_perm(current, p, PROCESS__SETSCHED);
+}
+
+static int selinux_task_setioprio(struct task_struct *p, int ioprio)
+{
+ int rc;
+
+ rc = secondary_ops->task_setioprio(p, ioprio);
+ if (rc)
+ return rc;
+
+ return task_has_perm(current, p, PROCESS__SETSCHED);
+}
+
+static int selinux_task_getioprio(struct task_struct *p)
+{
+ return task_has_perm(current, p, PROCESS__GETSCHED);
+}
+
+static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
+{
+ struct rlimit *old_rlim = current->signal->rlim + resource;
+ int rc;
+
+ rc = secondary_ops->task_setrlimit(resource, new_rlim);
+ if (rc)
+ return rc;
+
+ /* Control the ability to change the hard limit (whether
+ lowering or raising it), so that the hard limit can
+ later be used as a safe reset point for the soft limit
+ upon context transitions. See selinux_bprm_apply_creds. */
+ if (old_rlim->rlim_max != new_rlim->rlim_max)
+ return task_has_perm(current, current, PROCESS__SETRLIMIT);
+
+ return 0;
+}
+
+static int selinux_task_setscheduler(struct task_struct *p, int policy, struct sched_param *lp)
+{
+ int rc;
+
+ rc = secondary_ops->task_setscheduler(p, policy, lp);
+ if (rc)
+ return rc;
+
+ return task_has_perm(current, p, PROCESS__SETSCHED);
+}
+
+static int selinux_task_getscheduler(struct task_struct *p)
+{
+ return task_has_perm(current, p, PROCESS__GETSCHED);
+}
+
+static int selinux_task_movememory(struct task_struct *p)
+{
+ return task_has_perm(current, p, PROCESS__SETSCHED);
+}
+
+static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
+ int sig, u32 secid)
+{
+ u32 perm;
+ int rc;
+ struct task_security_struct *tsec;
+
+ rc = secondary_ops->task_kill(p, info, sig, secid);
+ if (rc)
+ return rc;
+
+ if (!sig)
+ perm = PROCESS__SIGNULL; /* null signal; existence test */
+ else
+ perm = signal_to_av(sig);
+ tsec = p->security;
+ if (secid)
+ rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL);
+ else
+ rc = task_has_perm(current, p, perm);
+ return rc;
+}
+
+static int selinux_task_prctl(int option,
+ unsigned long arg2,
+ unsigned long arg3,
+ unsigned long arg4,
+ unsigned long arg5,
+ long *rc_p)
+{
+ /* The current prctl operations do not appear to require
+ any SELinux controls since they merely observe or modify
+ the state of the current process. */
+ return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5, rc_p);
+}
+
+static int selinux_task_wait(struct task_struct *p)
+{
+ return task_has_perm(p, current, PROCESS__SIGCHLD);
+}
+
+static void selinux_task_reparent_to_init(struct task_struct *p)
+{
+ struct task_security_struct *tsec;
+
+ secondary_ops->task_reparent_to_init(p);
+
+ tsec = p->security;
+ tsec->osid = tsec->sid;
+ tsec->sid = SECINITSID_KERNEL;
+ return;
+}
+
+static void selinux_task_to_inode(struct task_struct *p,
+ struct inode *inode)
+{
+ struct task_security_struct *tsec = p->security;
+ struct inode_security_struct *isec = inode->i_security;
+
+ isec->sid = tsec->sid;
+ isec->initialized = 1;
+ return;
+}
+
+/* Returns error only if unable to parse addresses */
+static int selinux_parse_skb_ipv4(struct sk_buff *skb,
+ struct avc_audit_data *ad, u8 *proto)
+{
+ int offset, ihlen, ret = -EINVAL;
+ struct iphdr _iph, *ih;
+
+ offset = skb_network_offset(skb);
+ ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph);
+ if (ih == NULL)
+ goto out;
+
+ ihlen = ih->ihl * 4;
+ if (ihlen < sizeof(_iph))
+ goto out;
+
+ ad->u.net.v4info.saddr = ih->saddr;
+ ad->u.net.v4info.daddr = ih->daddr;
+ ret = 0;
+
+ if (proto)
+ *proto = ih->protocol;
+
+ switch (ih->protocol) {
+ case IPPROTO_TCP: {
+ struct tcphdr _tcph, *th;
+
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ break;
+
+ offset += ihlen;
+ th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
+ if (th == NULL)
+ break;
+
+ ad->u.net.sport = th->source;
+ ad->u.net.dport = th->dest;
+ break;
+ }
+
+ case IPPROTO_UDP: {
+ struct udphdr _udph, *uh;
+
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ break;
+
+ offset += ihlen;
+ uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
+ if (uh == NULL)
+ break;
+
+ ad->u.net.sport = uh->source;
+ ad->u.net.dport = uh->dest;
+ break;
+ }
+
+ case IPPROTO_DCCP: {
+ struct dccp_hdr _dccph, *dh;
+
+ if (ntohs(ih->frag_off) & IP_OFFSET)
+ break;
+
+ offset += ihlen;
+ dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph);
+ if (dh == NULL)
+ break;
+
+ ad->u.net.sport = dh->dccph_sport;
+ ad->u.net.dport = dh->dccph_dport;
+ break;
+ }
+
+ default:
+ break;
+ }
+out:
+ return ret;
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+/* Returns error only if unable to parse addresses */
+static int selinux_parse_skb_ipv6(struct sk_buff *skb,
+ struct avc_audit_data *ad, u8 *proto)
+{
+ u8 nexthdr;
+ int ret = -EINVAL, offset;
+ struct ipv6hdr _ipv6h, *ip6;
+
+ offset = skb_network_offset(skb);
+ ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h);
+ if (ip6 == NULL)
+ goto out;
+
+ ipv6_addr_copy(&ad->u.net.v6info.saddr, &ip6->saddr);
+ ipv6_addr_copy(&ad->u.net.v6info.daddr, &ip6->daddr);
+ ret = 0;
+
+ nexthdr = ip6->nexthdr;
+ offset += sizeof(_ipv6h);
+ offset = ipv6_skip_exthdr(skb, offset, &nexthdr);
+ if (offset < 0)
+ goto out;
+
+ if (proto)
+ *proto = nexthdr;
+
+ switch (nexthdr) {
+ case IPPROTO_TCP: {
+ struct tcphdr _tcph, *th;
+
+ th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
+ if (th == NULL)
+ break;
+
+ ad->u.net.sport = th->source;
+ ad->u.net.dport = th->dest;
+ break;
+ }
+
+ case IPPROTO_UDP: {
+ struct udphdr _udph, *uh;
+
+ uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
+ if (uh == NULL)
+ break;
+
+ ad->u.net.sport = uh->source;
+ ad->u.net.dport = uh->dest;
+ break;
+ }
+
+ case IPPROTO_DCCP: {
+ struct dccp_hdr _dccph, *dh;
+
+ dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph);
+ if (dh == NULL)
+ break;
+
+ ad->u.net.sport = dh->dccph_sport;
+ ad->u.net.dport = dh->dccph_dport;
+ break;
+ }
+
+ /* includes fragments */
+ default:
+ break;
+ }
+out:
+ return ret;
+}
+
+#endif /* IPV6 */
+
+static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
+ char **_addrp, int src, u8 *proto)
+{
+ char *addrp;
+ int ret;
+
+ switch (ad->u.net.family) {
+ case PF_INET:
+ ret = selinux_parse_skb_ipv4(skb, ad, proto);
+ if (ret)
+ goto parse_error;
+ addrp = (char *)(src ? &ad->u.net.v4info.saddr :
+ &ad->u.net.v4info.daddr);
+ goto okay;
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ ret = selinux_parse_skb_ipv6(skb, ad, proto);
+ if (ret)
+ goto parse_error;
+ addrp = (char *)(src ? &ad->u.net.v6info.saddr :
+ &ad->u.net.v6info.daddr);
+ goto okay;
+#endif /* IPV6 */
+ default:
+ addrp = NULL;
+ goto okay;
+ }
+
+parse_error:
+ printk(KERN_WARNING
+ "SELinux: failure in selinux_parse_skb(),"
+ " unable to parse packet\n");
+ return ret;
+
+okay:
+ if (_addrp)
+ *_addrp = addrp;
+ return 0;
+}
+
+/**
+ * selinux_skb_peerlbl_sid - Determine the peer label of a packet
+ * @skb: the packet
+ * @family: protocol family
+ * @sid: the packet's peer label SID
+ *
+ * Description:
+ * Check the various different forms of network peer labeling and determine
+ * the peer label/SID for the packet; most of the magic actually occurs in
+ * the security server function security_net_peersid_cmp(). The function
+ * returns zero if the value in @sid is valid (although it may be SECSID_NULL)
+ * or -EACCES if @sid is invalid due to inconsistencies with the different
+ * peer labels.
+ *
+ */
+static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid)
+{
+ int err;
+ u32 xfrm_sid;
+ u32 nlbl_sid;
+ u32 nlbl_type;
+
+ selinux_skb_xfrm_sid(skb, &xfrm_sid);
+ selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid);
+
+ err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid);
+ if (unlikely(err)) {
+ printk(KERN_WARNING
+ "SELinux: failure in selinux_skb_peerlbl_sid(),"
+ " unable to determine packet's peer label\n");
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+/* socket security operations */
+static int socket_has_perm(struct task_struct *task, struct socket *sock,
+ u32 perms)
+{
+ struct inode_security_struct *isec;
+ struct task_security_struct *tsec;
+ struct avc_audit_data ad;
+ int err = 0;
+
+ tsec = task->security;
+ isec = SOCK_INODE(sock)->i_security;
+
+ if (isec->sid == SECINITSID_KERNEL)
+ goto out;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.sk = sock->sk;
+ err = avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
+
+out:
+ return err;
+}
+
+static int selinux_socket_create(int family, int type,
+ int protocol, int kern)
+{
+ int err = 0;
+ struct task_security_struct *tsec;
+ u32 newsid;
+
+ if (kern)
+ goto out;
+
+ tsec = current->security;
+ newsid = tsec->sockcreate_sid ? : tsec->sid;
+ err = avc_has_perm(tsec->sid, newsid,
+ socket_type_to_security_class(family, type,
+ protocol), SOCKET__CREATE, NULL);
+
+out:
+ return err;
+}
+
+static int selinux_socket_post_create(struct socket *sock, int family,
+ int type, int protocol, int kern)
+{
+ int err = 0;
+ struct inode_security_struct *isec;
+ struct task_security_struct *tsec;
+ struct sk_security_struct *sksec;
+ u32 newsid;
+
+ isec = SOCK_INODE(sock)->i_security;
+
+ tsec = current->security;
+ newsid = tsec->sockcreate_sid ? : tsec->sid;
+ isec->sclass = socket_type_to_security_class(family, type, protocol);
+ isec->sid = kern ? SECINITSID_KERNEL : newsid;
+ isec->initialized = 1;
+
+ if (sock->sk) {
+ sksec = sock->sk->sk_security;
+ sksec->sid = isec->sid;
+ sksec->sclass = isec->sclass;
+ err = selinux_netlbl_socket_post_create(sock);
+ }
+
+ return err;
+}
+
+/* Range of port numbers used to automatically bind.
+ Need to determine whether we should perform a name_bind
+ permission check between the socket and the port number. */
+
+static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
+{
+ u16 family;
+ int err;
+
+ err = socket_has_perm(current, sock, SOCKET__BIND);
+ if (err)
+ goto out;
+
+ /*
+ * If PF_INET or PF_INET6, check name_bind permission for the port.
+ * Multiple address binding for SCTP is not supported yet: we just
+ * check the first address now.
+ */
+ family = sock->sk->sk_family;
+ if (family == PF_INET || family == PF_INET6) {
+ char *addrp;
+ struct inode_security_struct *isec;
+ struct task_security_struct *tsec;
+ struct avc_audit_data ad;
+ struct sockaddr_in *addr4 = NULL;
+ struct sockaddr_in6 *addr6 = NULL;
+ unsigned short snum;
+ struct sock *sk = sock->sk;
+ u32 sid, node_perm;
+
+ tsec = current->security;
+ isec = SOCK_INODE(sock)->i_security;
+
+ if (family == PF_INET) {
+ addr4 = (struct sockaddr_in *)address;
+ snum = ntohs(addr4->sin_port);
+ addrp = (char *)&addr4->sin_addr.s_addr;
+ } else {
+ addr6 = (struct sockaddr_in6 *)address;
+ snum = ntohs(addr6->sin6_port);
+ addrp = (char *)&addr6->sin6_addr.s6_addr;
+ }
+
+ if (snum) {
+ int low, high;
+
+ inet_get_local_port_range(&low, &high);
+
+ if (snum < max(PROT_SOCK, low) || snum > high) {
+ err = sel_netport_sid(sk->sk_protocol,
+ snum, &sid);
+ if (err)
+ goto out;
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.sport = htons(snum);
+ ad.u.net.family = family;
+ err = avc_has_perm(isec->sid, sid,
+ isec->sclass,
+ SOCKET__NAME_BIND, &ad);
+ if (err)
+ goto out;
+ }
+ }
+
+ switch (isec->sclass) {
+ case SECCLASS_TCP_SOCKET:
+ node_perm = TCP_SOCKET__NODE_BIND;
+ break;
+
+ case SECCLASS_UDP_SOCKET:
+ node_perm = UDP_SOCKET__NODE_BIND;
+ break;
+
+ case SECCLASS_DCCP_SOCKET:
+ node_perm = DCCP_SOCKET__NODE_BIND;
+ break;
+
+ default:
+ node_perm = RAWIP_SOCKET__NODE_BIND;
+ break;
+ }
+
+ err = sel_netnode_sid(addrp, family, &sid);
+ if (err)
+ goto out;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.sport = htons(snum);
+ ad.u.net.family = family;
+
+ if (family == PF_INET)
+ ad.u.net.v4info.saddr = addr4->sin_addr.s_addr;
+ else
+ ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr);
+
+ err = avc_has_perm(isec->sid, sid,
+ isec->sclass, node_perm, &ad);
+ if (err)
+ goto out;
+ }
+out:
+ return err;
+}
+
+static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
+{
+ struct sock *sk = sock->sk;
+ struct inode_security_struct *isec;
+ int err;
+
+ err = socket_has_perm(current, sock, SOCKET__CONNECT);
+ if (err)
+ return err;
+
+ /*
+ * If a TCP or DCCP socket, check name_connect permission for the port.
+ */
+ isec = SOCK_INODE(sock)->i_security;
+ if (isec->sclass == SECCLASS_TCP_SOCKET ||
+ isec->sclass == SECCLASS_DCCP_SOCKET) {
+ struct avc_audit_data ad;
+ struct sockaddr_in *addr4 = NULL;
+ struct sockaddr_in6 *addr6 = NULL;
+ unsigned short snum;
+ u32 sid, perm;
+
+ if (sk->sk_family == PF_INET) {
+ addr4 = (struct sockaddr_in *)address;
+ if (addrlen < sizeof(struct sockaddr_in))
+ return -EINVAL;
+ snum = ntohs(addr4->sin_port);
+ } else {
+ addr6 = (struct sockaddr_in6 *)address;
+ if (addrlen < SIN6_LEN_RFC2133)
+ return -EINVAL;
+ snum = ntohs(addr6->sin6_port);
+ }
+
+ err = sel_netport_sid(sk->sk_protocol, snum, &sid);
+ if (err)
+ goto out;
+
+ perm = (isec->sclass == SECCLASS_TCP_SOCKET) ?
+ TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.dport = htons(snum);
+ ad.u.net.family = sk->sk_family;
+ err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad);
+ if (err)
+ goto out;
+ }
+
+ err = selinux_netlbl_socket_connect(sk, address);
+
+out:
+ return err;
+}
+
+static int selinux_socket_listen(struct socket *sock, int backlog)
+{
+ return socket_has_perm(current, sock, SOCKET__LISTEN);
+}
+
+static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ int err;
+ struct inode_security_struct *isec;
+ struct inode_security_struct *newisec;
+
+ err = socket_has_perm(current, sock, SOCKET__ACCEPT);
+ if (err)
+ return err;
+
+ newisec = SOCK_INODE(newsock)->i_security;
+
+ isec = SOCK_INODE(sock)->i_security;
+ newisec->sclass = isec->sclass;
+ newisec->sid = isec->sid;
+ newisec->initialized = 1;
+
+ return 0;
+}
+
+static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
+ int size)
+{
+ int rc;
+
+ rc = socket_has_perm(current, sock, SOCKET__WRITE);
+ if (rc)
+ return rc;
+
+ return selinux_netlbl_inode_permission(SOCK_INODE(sock), MAY_WRITE);
+}
+
+static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
+ int size, int flags)
+{
+ return socket_has_perm(current, sock, SOCKET__READ);
+}
+
+static int selinux_socket_getsockname(struct socket *sock)
+{
+ return socket_has_perm(current, sock, SOCKET__GETATTR);
+}
+
+static int selinux_socket_getpeername(struct socket *sock)
+{
+ return socket_has_perm(current, sock, SOCKET__GETATTR);
+}
+
+static int selinux_socket_setsockopt(struct socket *sock, int level, int optname)
+{
+ int err;
+
+ err = socket_has_perm(current, sock, SOCKET__SETOPT);
+ if (err)
+ return err;
+
+ return selinux_netlbl_socket_setsockopt(sock, level, optname);
+}
+
+static int selinux_socket_getsockopt(struct socket *sock, int level,
+ int optname)
+{
+ return socket_has_perm(current, sock, SOCKET__GETOPT);
+}
+
+static int selinux_socket_shutdown(struct socket *sock, int how)
+{
+ return socket_has_perm(current, sock, SOCKET__SHUTDOWN);
+}
+
+static int selinux_socket_unix_stream_connect(struct socket *sock,
+ struct socket *other,
+ struct sock *newsk)
+{
+ struct sk_security_struct *ssec;
+ struct inode_security_struct *isec;
+ struct inode_security_struct *other_isec;
+ struct avc_audit_data ad;
+ int err;
+
+ err = secondary_ops->unix_stream_connect(sock, other, newsk);
+ if (err)
+ return err;
+
+ isec = SOCK_INODE(sock)->i_security;
+ other_isec = SOCK_INODE(other)->i_security;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.sk = other->sk;
+
+ err = avc_has_perm(isec->sid, other_isec->sid,
+ isec->sclass,
+ UNIX_STREAM_SOCKET__CONNECTTO, &ad);
+ if (err)
+ return err;
+
+ /* connecting socket */
+ ssec = sock->sk->sk_security;
+ ssec->peer_sid = other_isec->sid;
+
+ /* server child socket */
+ ssec = newsk->sk_security;
+ ssec->peer_sid = isec->sid;
+ err = security_sid_mls_copy(other_isec->sid, ssec->peer_sid, &ssec->sid);
+
+ return err;
+}
+
+static int selinux_socket_unix_may_send(struct socket *sock,
+ struct socket *other)
+{
+ struct inode_security_struct *isec;
+ struct inode_security_struct *other_isec;
+ struct avc_audit_data ad;
+ int err;
+
+ isec = SOCK_INODE(sock)->i_security;
+ other_isec = SOCK_INODE(other)->i_security;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.sk = other->sk;
+
+ err = avc_has_perm(isec->sid, other_isec->sid,
+ isec->sclass, SOCKET__SENDTO, &ad);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family,
+ u32 peer_sid,
+ struct avc_audit_data *ad)
+{
+ int err;
+ u32 if_sid;
+ u32 node_sid;
+
+ err = sel_netif_sid(ifindex, &if_sid);
+ if (err)
+ return err;
+ err = avc_has_perm(peer_sid, if_sid,
+ SECCLASS_NETIF, NETIF__INGRESS, ad);
+ if (err)
+ return err;
+
+ err = sel_netnode_sid(addrp, family, &node_sid);
+ if (err)
+ return err;
+ return avc_has_perm(peer_sid, node_sid,
+ SECCLASS_NODE, NODE__RECVFROM, ad);
+}
+
+static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk,
+ struct sk_buff *skb,
+ struct avc_audit_data *ad,
+ u16 family,
+ char *addrp)
+{
+ int err;
+ struct sk_security_struct *sksec = sk->sk_security;
+ u16 sk_class;
+ u32 netif_perm, node_perm, recv_perm;
+ u32 port_sid, node_sid, if_sid, sk_sid;
+
+ sk_sid = sksec->sid;
+ sk_class = sksec->sclass;
+
+ switch (sk_class) {
+ case SECCLASS_UDP_SOCKET:
+ netif_perm = NETIF__UDP_RECV;
+ node_perm = NODE__UDP_RECV;
+ recv_perm = UDP_SOCKET__RECV_MSG;
+ break;
+ case SECCLASS_TCP_SOCKET:
+ netif_perm = NETIF__TCP_RECV;
+ node_perm = NODE__TCP_RECV;
+ recv_perm = TCP_SOCKET__RECV_MSG;
+ break;
+ case SECCLASS_DCCP_SOCKET:
+ netif_perm = NETIF__DCCP_RECV;
+ node_perm = NODE__DCCP_RECV;
+ recv_perm = DCCP_SOCKET__RECV_MSG;
+ break;
+ default:
+ netif_perm = NETIF__RAWIP_RECV;
+ node_perm = NODE__RAWIP_RECV;
+ recv_perm = 0;
+ break;
+ }
+
+ err = sel_netif_sid(skb->iif, &if_sid);
+ if (err)
+ return err;
+ err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
+ if (err)
+ return err;
+
+ err = sel_netnode_sid(addrp, family, &node_sid);
+ if (err)
+ return err;
+ err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad);
+ if (err)
+ return err;
+
+ if (!recv_perm)
+ return 0;
+ err = sel_netport_sid(sk->sk_protocol,
+ ntohs(ad->u.net.sport), &port_sid);
+ if (unlikely(err)) {
+ printk(KERN_WARNING
+ "SELinux: failure in"
+ " selinux_sock_rcv_skb_iptables_compat(),"
+ " network port label not found\n");
+ return err;
+ }
+ return avc_has_perm(sk_sid, port_sid, sk_class, recv_perm, ad);
+}
+
+static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
+ u16 family)
+{
+ int err;
+ struct sk_security_struct *sksec = sk->sk_security;
+ u32 peer_sid;
+ u32 sk_sid = sksec->sid;
+ struct avc_audit_data ad;
+ char *addrp;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = skb->iif;
+ ad.u.net.family = family;
+ err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
+ if (err)
+ return err;
+
+ if (selinux_compat_net)
+ err = selinux_sock_rcv_skb_iptables_compat(sk, skb, &ad,
+ family, addrp);
+ else
+ err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
+ PACKET__RECV, &ad);
+ if (err)
+ return err;
+
+ if (selinux_policycap_netpeer) {
+ err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
+ if (err)
+ return err;
+ err = avc_has_perm(sk_sid, peer_sid,
+ SECCLASS_PEER, PEER__RECV, &ad);
+ if (err)
+ selinux_netlbl_err(skb, err, 0);
+ } else {
+ err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad);
+ if (err)
+ return err;
+ err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad);
+ }
+
+ return err;
+}
+
+static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ int err;
+ struct sk_security_struct *sksec = sk->sk_security;
+ u16 family = sk->sk_family;
+ u32 sk_sid = sksec->sid;
+ struct avc_audit_data ad;
+ char *addrp;
+ u8 secmark_active;
+ u8 peerlbl_active;
+
+ if (family != PF_INET && family != PF_INET6)
+ return 0;
+
+ /* Handle mapped IPv4 packets arriving via IPv6 sockets */
+ if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+ family = PF_INET;
+
+ /* If any sort of compatibility mode is enabled then handoff processing
+ * to the selinux_sock_rcv_skb_compat() function to deal with the
+ * special handling. We do this in an attempt to keep this function
+ * as fast and as clean as possible. */
+ if (selinux_compat_net || !selinux_policycap_netpeer)
+ return selinux_sock_rcv_skb_compat(sk, skb, family);
+
+ secmark_active = selinux_secmark_enabled();
+ peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+ if (!secmark_active && !peerlbl_active)
+ return 0;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = skb->iif;
+ ad.u.net.family = family;
+ err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
+ if (err)
+ return err;
+
+ if (peerlbl_active) {
+ u32 peer_sid;
+
+ err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
+ if (err)
+ return err;
+ err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family,
+ peer_sid, &ad);
+ if (err) {
+ selinux_netlbl_err(skb, err, 0);
+ return err;
+ }
+ err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
+ PEER__RECV, &ad);
+ if (err)
+ selinux_netlbl_err(skb, err, 0);
+ }
+
+ if (secmark_active) {
+ err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
+ PACKET__RECV, &ad);
+ if (err)
+ return err;
+ }
+
+ return err;
+}
+
+static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *optval,
+ int __user *optlen, unsigned len)
+{
+ int err = 0;
+ char *scontext;
+ u32 scontext_len;
+ struct sk_security_struct *ssec;
+ struct inode_security_struct *isec;
+ u32 peer_sid = SECSID_NULL;
+
+ isec = SOCK_INODE(sock)->i_security;
+
+ if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET ||
+ isec->sclass == SECCLASS_TCP_SOCKET) {
+ ssec = sock->sk->sk_security;
+ peer_sid = ssec->peer_sid;
+ }
+ if (peer_sid == SECSID_NULL) {
+ err = -ENOPROTOOPT;
+ goto out;
+ }
+
+ err = security_sid_to_context(peer_sid, &scontext, &scontext_len);
+
+ if (err)
+ goto out;
+
+ if (scontext_len > len) {
+ err = -ERANGE;
+ goto out_len;
+ }
+
+ if (copy_to_user(optval, scontext, scontext_len))
+ err = -EFAULT;
+
+out_len:
+ if (put_user(scontext_len, optlen))
+ err = -EFAULT;
+
+ kfree(scontext);
+out:
+ return err;
+}
+
+static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)
+{
+ u32 peer_secid = SECSID_NULL;
+ u16 family;
+
+ if (skb && skb->protocol == htons(ETH_P_IP))
+ family = PF_INET;
+ else if (skb && skb->protocol == htons(ETH_P_IPV6))
+ family = PF_INET6;
+ else if (sock)
+ family = sock->sk->sk_family;
+ else
+ goto out;
+
+ if (sock && family == PF_UNIX)
+ selinux_inode_getsecid(SOCK_INODE(sock), &peer_secid);
+ else if (skb)
+ selinux_skb_peerlbl_sid(skb, family, &peer_secid);
+
+out:
+ *secid = peer_secid;
+ if (peer_secid == SECSID_NULL)
+ return -EINVAL;
+ return 0;
+}
+
+static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
+{
+ return sk_alloc_security(sk, family, priority);
+}
+
+static void selinux_sk_free_security(struct sock *sk)
+{
+ sk_free_security(sk);
+}
+
+static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
+{
+ struct sk_security_struct *ssec = sk->sk_security;
+ struct sk_security_struct *newssec = newsk->sk_security;
+
+ newssec->sid = ssec->sid;
+ newssec->peer_sid = ssec->peer_sid;
+ newssec->sclass = ssec->sclass;
+
+ selinux_netlbl_sk_security_reset(newssec, newsk->sk_family);
+}
+
+static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
+{
+ if (!sk)
+ *secid = SECINITSID_ANY_SOCKET;
+ else {
+ struct sk_security_struct *sksec = sk->sk_security;
+
+ *secid = sksec->sid;
+ }
+}
+
+static void selinux_sock_graft(struct sock *sk, struct socket *parent)
+{
+ struct inode_security_struct *isec = SOCK_INODE(parent)->i_security;
+ struct sk_security_struct *sksec = sk->sk_security;
+
+ if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||
+ sk->sk_family == PF_UNIX)
+ isec->sid = sksec->sid;
+ sksec->sclass = isec->sclass;
+}
+
+static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req)
+{
+ struct sk_security_struct *sksec = sk->sk_security;
+ int err;
+ u16 family = sk->sk_family;
+ u32 newsid;
+ u32 peersid;
+
+ /* handle mapped IPv4 packets arriving via IPv6 sockets */
+ if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+ family = PF_INET;
+
+ err = selinux_skb_peerlbl_sid(skb, family, &peersid);
+ if (err)
+ return err;
+ if (peersid == SECSID_NULL) {
+ req->secid = sksec->sid;
+ req->peer_secid = SECSID_NULL;
+ return 0;
+ }
+
+ err = security_sid_mls_copy(sksec->sid, peersid, &newsid);
+ if (err)
+ return err;
+
+ req->secid = newsid;
+ req->peer_secid = peersid;
+ return 0;
+}
+
+static void selinux_inet_csk_clone(struct sock *newsk,
+ const struct request_sock *req)
+{
+ struct sk_security_struct *newsksec = newsk->sk_security;
+
+ newsksec->sid = req->secid;
+ newsksec->peer_sid = req->peer_secid;
+ /* NOTE: Ideally, we should also get the isec->sid for the
+ new socket in sync, but we don't have the isec available yet.
+ So we will wait until sock_graft to do it, by which
+ time it will have been created and available. */
+
+ /* We don't need to take any sort of lock here as we are the only
+ * thread with access to newsksec */
+ selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family);
+}
+
+static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
+{
+ u16 family = sk->sk_family;
+ struct sk_security_struct *sksec = sk->sk_security;
+
+ /* handle mapped IPv4 packets arriving via IPv6 sockets */
+ if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+ family = PF_INET;
+
+ selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid);
+
+ selinux_netlbl_inet_conn_established(sk, family);
+}
+
+static void selinux_req_classify_flow(const struct request_sock *req,
+ struct flowi *fl)
+{
+ fl->secid = req->secid;
+}
+
+static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
+{
+ int err = 0;
+ u32 perm;
+ struct nlmsghdr *nlh;
+ struct socket *sock = sk->sk_socket;
+ struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+
+ if (skb->len < NLMSG_SPACE(0)) {
+ err = -EINVAL;
+ goto out;
+ }
+ nlh = nlmsg_hdr(skb);
+
+ err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm);
+ if (err) {
+ if (err == -EINVAL) {
+ audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR,
+ "SELinux: unrecognized netlink message"
+ " type=%hu for sclass=%hu\n",
+ nlh->nlmsg_type, isec->sclass);
+ if (!selinux_enforcing)
+ err = 0;
+ }
+
+ /* Ignore */
+ if (err == -ENOENT)
+ err = 0;
+ goto out;
+ }
+
+ err = socket_has_perm(current, sock, perm);
+out:
+ return err;
+}
+
+#ifdef CONFIG_NETFILTER
+
+static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
+ u16 family)
+{
+ int err;
+ char *addrp;
+ u32 peer_sid;
+ struct avc_audit_data ad;
+ u8 secmark_active;
+ u8 netlbl_active;
+ u8 peerlbl_active;
+
+ if (!selinux_policycap_netpeer)
+ return NF_ACCEPT;
+
+ secmark_active = selinux_secmark_enabled();
+ netlbl_active = netlbl_enabled();
+ peerlbl_active = netlbl_active || selinux_xfrm_enabled();
+ if (!secmark_active && !peerlbl_active)
+ return NF_ACCEPT;
+
+ if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
+ return NF_DROP;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = ifindex;
+ ad.u.net.family = family;
+ if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)
+ return NF_DROP;
+
+ if (peerlbl_active) {
+ err = selinux_inet_sys_rcv_skb(ifindex, addrp, family,
+ peer_sid, &ad);
+ if (err) {
+ selinux_netlbl_err(skb, err, 1);
+ return NF_DROP;
+ }
+ }
+
+ if (secmark_active)
+ if (avc_has_perm(peer_sid, skb->secmark,
+ SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
+ return NF_DROP;
+
+ if (netlbl_active)
+ /* we do this in the FORWARD path and not the POST_ROUTING
+ * path because we want to make sure we apply the necessary
+ * labeling before IPsec is applied so we can leverage AH
+ * protection */
+ if (selinux_netlbl_skbuff_setsid(skb, family, peer_sid) != 0)
+ return NF_DROP;
+
+ return NF_ACCEPT;
+}
+
+static unsigned int selinux_ipv4_forward(unsigned int hooknum,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return selinux_ip_forward(skb, in->ifindex, PF_INET);
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static unsigned int selinux_ipv6_forward(unsigned int hooknum,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return selinux_ip_forward(skb, in->ifindex, PF_INET6);
+}
+#endif /* IPV6 */
+
+static unsigned int selinux_ip_output(struct sk_buff *skb,
+ u16 family)
+{
+ u32 sid;
+
+ if (!netlbl_enabled())
+ return NF_ACCEPT;
+
+ /* we do this in the LOCAL_OUT path and not the POST_ROUTING path
+ * because we want to make sure we apply the necessary labeling
+ * before IPsec is applied so we can leverage AH protection */
+ if (skb->sk) {
+ struct sk_security_struct *sksec = skb->sk->sk_security;
+ sid = sksec->sid;
+ } else
+ sid = SECINITSID_KERNEL;
+ if (selinux_netlbl_skbuff_setsid(skb, family, sid) != 0)
+ return NF_DROP;
+
+ return NF_ACCEPT;
+}
+
+static unsigned int selinux_ipv4_output(unsigned int hooknum,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return selinux_ip_output(skb, PF_INET);
+}
+
+static int selinux_ip_postroute_iptables_compat(struct sock *sk,
+ int ifindex,
+ struct avc_audit_data *ad,
+ u16 family, char *addrp)
+{
+ int err;
+ struct sk_security_struct *sksec = sk->sk_security;
+ u16 sk_class;
+ u32 netif_perm, node_perm, send_perm;
+ u32 port_sid, node_sid, if_sid, sk_sid;
+
+ sk_sid = sksec->sid;
+ sk_class = sksec->sclass;
+
+ switch (sk_class) {
+ case SECCLASS_UDP_SOCKET:
+ netif_perm = NETIF__UDP_SEND;
+ node_perm = NODE__UDP_SEND;
+ send_perm = UDP_SOCKET__SEND_MSG;
+ break;
+ case SECCLASS_TCP_SOCKET:
+ netif_perm = NETIF__TCP_SEND;
+ node_perm = NODE__TCP_SEND;
+ send_perm = TCP_SOCKET__SEND_MSG;
+ break;
+ case SECCLASS_DCCP_SOCKET:
+ netif_perm = NETIF__DCCP_SEND;
+ node_perm = NODE__DCCP_SEND;
+ send_perm = DCCP_SOCKET__SEND_MSG;
+ break;
+ default:
+ netif_perm = NETIF__RAWIP_SEND;
+ node_perm = NODE__RAWIP_SEND;
+ send_perm = 0;
+ break;
+ }
+
+ err = sel_netif_sid(ifindex, &if_sid);
+ if (err)
+ return err;
+ err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
+ return err;
+
+ err = sel_netnode_sid(addrp, family, &node_sid);
+ if (err)
+ return err;
+ err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad);
+ if (err)
+ return err;
+
+ if (send_perm != 0)
+ return 0;
+
+ err = sel_netport_sid(sk->sk_protocol,
+ ntohs(ad->u.net.dport), &port_sid);
+ if (unlikely(err)) {
+ printk(KERN_WARNING
+ "SELinux: failure in"
+ " selinux_ip_postroute_iptables_compat(),"
+ " network port label not found\n");
+ return err;
+ }
+ return avc_has_perm(sk_sid, port_sid, sk_class, send_perm, ad);
+}
+
+static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
+ int ifindex,
+ u16 family)
+{
+ struct sock *sk = skb->sk;
+ struct sk_security_struct *sksec;
+ struct avc_audit_data ad;
+ char *addrp;
+ u8 proto;
+
+ if (sk == NULL)
+ return NF_ACCEPT;
+ sksec = sk->sk_security;
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = ifindex;
+ ad.u.net.family = family;
+ if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto))
+ return NF_DROP;
+
+ if (selinux_compat_net) {
+ if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex,
+ &ad, family, addrp))
+ return NF_DROP;
+ } else {
+ if (avc_has_perm(sksec->sid, skb->secmark,
+ SECCLASS_PACKET, PACKET__SEND, &ad))
+ return NF_DROP;
+ }
+
+ if (selinux_policycap_netpeer)
+ if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
+ return NF_DROP;
+
+ return NF_ACCEPT;
+}
+
+static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
+ u16 family)
+{
+ u32 secmark_perm;
+ u32 peer_sid;
+ struct sock *sk;
+ struct avc_audit_data ad;
+ char *addrp;
+ u8 secmark_active;
+ u8 peerlbl_active;
+
+ /* If any sort of compatibility mode is enabled then handoff processing
+ * to the selinux_ip_postroute_compat() function to deal with the
+ * special handling. We do this in an attempt to keep this function
+ * as fast and as clean as possible. */
+ if (selinux_compat_net || !selinux_policycap_netpeer)
+ return selinux_ip_postroute_compat(skb, ifindex, family);
+
+ /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
+ * packet transformation so allow the packet to pass without any checks
+ * since we'll have another chance to perform access control checks
+ * when the packet is on it's final way out.
+ * NOTE: there appear to be some IPv6 multicast cases where skb->dst
+ * is NULL, in this case go ahead and apply access control. */
+ if (skb->dst != NULL && skb->dst->xfrm != NULL)
+ return NF_ACCEPT;
+
+ secmark_active = selinux_secmark_enabled();
+ peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled();
+ if (!secmark_active && !peerlbl_active)
+ return NF_ACCEPT;
+
+ /* if the packet is being forwarded then get the peer label from the
+ * packet itself; otherwise check to see if it is from a local
+ * application or the kernel, if from an application get the peer label
+ * from the sending socket, otherwise use the kernel's sid */
+ sk = skb->sk;
+ if (sk == NULL) {
+ switch (family) {
+ case PF_INET:
+ if (IPCB(skb)->flags & IPSKB_FORWARDED)
+ secmark_perm = PACKET__FORWARD_OUT;
+ else
+ secmark_perm = PACKET__SEND;
+ break;
+ case PF_INET6:
+ if (IP6CB(skb)->flags & IP6SKB_FORWARDED)
+ secmark_perm = PACKET__FORWARD_OUT;
+ else
+ secmark_perm = PACKET__SEND;
+ break;
+ default:
+ return NF_DROP;
+ }
+ if (secmark_perm == PACKET__FORWARD_OUT) {
+ if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
+ return NF_DROP;
+ } else
+ peer_sid = SECINITSID_KERNEL;
+ } else {
+ struct sk_security_struct *sksec = sk->sk_security;
+ peer_sid = sksec->sid;
+ secmark_perm = PACKET__SEND;
+ }
+
+ AVC_AUDIT_DATA_INIT(&ad, NET);
+ ad.u.net.netif = ifindex;
+ ad.u.net.family = family;
+ if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL))
+ return NF_DROP;
+
+ if (secmark_active)
+ if (avc_has_perm(peer_sid, skb->secmark,
+ SECCLASS_PACKET, secmark_perm, &ad))
+ return NF_DROP;
+
+ if (peerlbl_active) {
+ u32 if_sid;
+ u32 node_sid;
+
+ if (sel_netif_sid(ifindex, &if_sid))
+ return NF_DROP;
+ if (avc_has_perm(peer_sid, if_sid,
+ SECCLASS_NETIF, NETIF__EGRESS, &ad))
+ return NF_DROP;
+
+ if (sel_netnode_sid(addrp, family, &node_sid))
+ return NF_DROP;
+ if (avc_has_perm(peer_sid, node_sid,
+ SECCLASS_NODE, NODE__SENDTO, &ad))
+ return NF_DROP;
+ }
+
+ return NF_ACCEPT;
+}
+
+static unsigned int selinux_ipv4_postroute(unsigned int hooknum,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return selinux_ip_postroute(skb, out->ifindex, PF_INET);
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static unsigned int selinux_ipv6_postroute(unsigned int hooknum,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ return selinux_ip_postroute(skb, out->ifindex, PF_INET6);
+}
+#endif /* IPV6 */
+
+#endif /* CONFIG_NETFILTER */
+
+static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
+{
+ int err;
+
+ err = secondary_ops->netlink_send(sk, skb);
+ if (err)
+ return err;
+
+ if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS)
+ err = selinux_nlmsg_perm(sk, skb);
+
+ return err;
+}
+
+static int selinux_netlink_recv(struct sk_buff *skb, int capability)
+{
+ int err;
+ struct avc_audit_data ad;
+
+ err = secondary_ops->netlink_recv(skb, capability);
+ if (err)
+ return err;
+
+ AVC_AUDIT_DATA_INIT(&ad, CAP);
+ ad.u.cap = capability;
+
+ return avc_has_perm(NETLINK_CB(skb).sid, NETLINK_CB(skb).sid,
+ SECCLASS_CAPABILITY, CAP_TO_MASK(capability), &ad);
+}
+
+static int ipc_alloc_security(struct task_struct *task,
+ struct kern_ipc_perm *perm,
+ u16 sclass)
+{
+ struct task_security_struct *tsec = task->security;
+ struct ipc_security_struct *isec;
+
+ isec = kzalloc(sizeof(struct ipc_security_struct), GFP_KERNEL);
+ if (!isec)
+ return -ENOMEM;
+
+ isec->sclass = sclass;
+ isec->sid = tsec->sid;
+ perm->security = isec;
+
+ return 0;
+}
+
+static void ipc_free_security(struct kern_ipc_perm *perm)
+{
+ struct ipc_security_struct *isec = perm->security;
+ perm->security = NULL;
+ kfree(isec);
+}
+
+static int msg_msg_alloc_security(struct msg_msg *msg)
+{
+ struct msg_security_struct *msec;
+
+ msec = kzalloc(sizeof(struct msg_security_struct), GFP_KERNEL);
+ if (!msec)
+ return -ENOMEM;
+
+ msec->sid = SECINITSID_UNLABELED;
+ msg->security = msec;
+
+ return 0;
+}
+
+static void msg_msg_free_security(struct msg_msg *msg)
+{
+ struct msg_security_struct *msec = msg->security;
+
+ msg->security = NULL;
+ kfree(msec);
+}
+
+static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
+ u32 perms)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+
+ tsec = current->security;
+ isec = ipc_perms->security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = ipc_perms->key;
+
+ return avc_has_perm(tsec->sid, isec->sid, isec->sclass, perms, &ad);
+}
+
+static int selinux_msg_msg_alloc_security(struct msg_msg *msg)
+{
+ return msg_msg_alloc_security(msg);
+}
+
+static void selinux_msg_msg_free_security(struct msg_msg *msg)
+{
+ msg_msg_free_security(msg);
+}
+
+/* message queue security operations */
+static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+ int rc;
+
+ rc = ipc_alloc_security(current, &msq->q_perm, SECCLASS_MSGQ);
+ if (rc)
+ return rc;
+
+ tsec = current->security;
+ isec = msq->q_perm.security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = msq->q_perm.key;
+
+ rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+ MSGQ__CREATE, &ad);
+ if (rc) {
+ ipc_free_security(&msq->q_perm);
+ return rc;
+ }
+ return 0;
+}
+
+static void selinux_msg_queue_free_security(struct msg_queue *msq)
+{
+ ipc_free_security(&msq->q_perm);
+}
+
+static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+
+ tsec = current->security;
+ isec = msq->q_perm.security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = msq->q_perm.key;
+
+ return avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+ MSGQ__ASSOCIATE, &ad);
+}
+
+static int selinux_msg_queue_msgctl(struct msg_queue *msq, int cmd)
+{
+ int err;
+ int perms;
+
+ switch (cmd) {
+ case IPC_INFO:
+ case MSG_INFO:
+ /* No specific object, just general system-wide information. */
+ return task_has_system(current, SYSTEM__IPC_INFO);
+ case IPC_STAT:
+ case MSG_STAT:
+ perms = MSGQ__GETATTR | MSGQ__ASSOCIATE;
+ break;
+ case IPC_SET:
+ perms = MSGQ__SETATTR;
+ break;
+ case IPC_RMID:
+ perms = MSGQ__DESTROY;
+ break;
+ default:
+ return 0;
+ }
+
+ err = ipc_has_perm(&msq->q_perm, perms);
+ return err;
+}
+
+static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, int msqflg)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct msg_security_struct *msec;
+ struct avc_audit_data ad;
+ int rc;
+
+ tsec = current->security;
+ isec = msq->q_perm.security;
+ msec = msg->security;
+
+ /*
+ * First time through, need to assign label to the message
+ */
+ if (msec->sid == SECINITSID_UNLABELED) {
+ /*
+ * Compute new sid based on current process and
+ * message queue this message will be stored in
+ */
+ rc = security_transition_sid(tsec->sid,
+ isec->sid,
+ SECCLASS_MSG,
+ &msec->sid);
+ if (rc)
+ return rc;
+ }
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = msq->q_perm.key;
+
+ /* Can this process write to the queue? */
+ rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_MSGQ,
+ MSGQ__WRITE, &ad);
+ if (!rc)
+ /* Can this process send the message */
+ rc = avc_has_perm(tsec->sid, msec->sid,
+ SECCLASS_MSG, MSG__SEND, &ad);
+ if (!rc)
+ /* Can the message be put in the queue? */
+ rc = avc_has_perm(msec->sid, isec->sid,
+ SECCLASS_MSGQ, MSGQ__ENQUEUE, &ad);
+
+ return rc;
+}
+
+static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
+ struct task_struct *target,
+ long type, int mode)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct msg_security_struct *msec;
+ struct avc_audit_data ad;
+ int rc;
+
+ tsec = target->security;
+ isec = msq->q_perm.security;
+ msec = msg->security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = msq->q_perm.key;
+
+ rc = avc_has_perm(tsec->sid, isec->sid,
+ SECCLASS_MSGQ, MSGQ__READ, &ad);
+ if (!rc)
+ rc = avc_has_perm(tsec->sid, msec->sid,
+ SECCLASS_MSG, MSG__RECEIVE, &ad);
+ return rc;
+}
+
+/* Shared Memory security operations */
+static int selinux_shm_alloc_security(struct shmid_kernel *shp)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+ int rc;
+
+ rc = ipc_alloc_security(current, &shp->shm_perm, SECCLASS_SHM);
+ if (rc)
+ return rc;
+
+ tsec = current->security;
+ isec = shp->shm_perm.security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = shp->shm_perm.key;
+
+ rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+ SHM__CREATE, &ad);
+ if (rc) {
+ ipc_free_security(&shp->shm_perm);
+ return rc;
+ }
+ return 0;
+}
+
+static void selinux_shm_free_security(struct shmid_kernel *shp)
+{
+ ipc_free_security(&shp->shm_perm);
+}
+
+static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+
+ tsec = current->security;
+ isec = shp->shm_perm.security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = shp->shm_perm.key;
+
+ return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SHM,
+ SHM__ASSOCIATE, &ad);
+}
+
+/* Note, at this point, shp is locked down */
+static int selinux_shm_shmctl(struct shmid_kernel *shp, int cmd)
+{
+ int perms;
+ int err;
+
+ switch (cmd) {
+ case IPC_INFO:
+ case SHM_INFO:
+ /* No specific object, just general system-wide information. */
+ return task_has_system(current, SYSTEM__IPC_INFO);
+ case IPC_STAT:
+ case SHM_STAT:
+ perms = SHM__GETATTR | SHM__ASSOCIATE;
+ break;
+ case IPC_SET:
+ perms = SHM__SETATTR;
+ break;
+ case SHM_LOCK:
+ case SHM_UNLOCK:
+ perms = SHM__LOCK;
+ break;
+ case IPC_RMID:
+ perms = SHM__DESTROY;
+ break;
+ default:
+ return 0;
+ }
+
+ err = ipc_has_perm(&shp->shm_perm, perms);
+ return err;
+}
+
+static int selinux_shm_shmat(struct shmid_kernel *shp,
+ char __user *shmaddr, int shmflg)
+{
+ u32 perms;
+ int rc;
+
+ rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg);
+ if (rc)
+ return rc;
+
+ if (shmflg & SHM_RDONLY)
+ perms = SHM__READ;
+ else
+ perms = SHM__READ | SHM__WRITE;
+
+ return ipc_has_perm(&shp->shm_perm, perms);
+}
+
+/* Semaphore security operations */
+static int selinux_sem_alloc_security(struct sem_array *sma)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+ int rc;
+
+ rc = ipc_alloc_security(current, &sma->sem_perm, SECCLASS_SEM);
+ if (rc)
+ return rc;
+
+ tsec = current->security;
+ isec = sma->sem_perm.security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = sma->sem_perm.key;
+
+ rc = avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+ SEM__CREATE, &ad);
+ if (rc) {
+ ipc_free_security(&sma->sem_perm);
+ return rc;
+ }
+ return 0;
+}
+
+static void selinux_sem_free_security(struct sem_array *sma)
+{
+ ipc_free_security(&sma->sem_perm);
+}
+
+static int selinux_sem_associate(struct sem_array *sma, int semflg)
+{
+ struct task_security_struct *tsec;
+ struct ipc_security_struct *isec;
+ struct avc_audit_data ad;
+
+ tsec = current->security;
+ isec = sma->sem_perm.security;
+
+ AVC_AUDIT_DATA_INIT(&ad, IPC);
+ ad.u.ipc_id = sma->sem_perm.key;
+
+ return avc_has_perm(tsec->sid, isec->sid, SECCLASS_SEM,
+ SEM__ASSOCIATE, &ad);
+}
+
+/* Note, at this point, sma is locked down */
+static int selinux_sem_semctl(struct sem_array *sma, int cmd)
+{
+ int err;
+ u32 perms;
+
+ switch (cmd) {
+ case IPC_INFO:
+ case SEM_INFO:
+ /* No specific object, just general system-wide information. */
+ return task_has_system(current, SYSTEM__IPC_INFO);
+ case GETPID:
+ case GETNCNT:
+ case GETZCNT:
+ perms = SEM__GETATTR;
+ break;
+ case GETVAL:
+ case GETALL:
+ perms = SEM__READ;
+ break;
+ case SETVAL:
+ case SETALL:
+ perms = SEM__WRITE;
+ break;
+ case IPC_RMID:
+ perms = SEM__DESTROY;
+ break;
+ case IPC_SET:
+ perms = SEM__SETATTR;
+ break;
+ case IPC_STAT:
+ case SEM_STAT:
+ perms = SEM__GETATTR | SEM__ASSOCIATE;
+ break;
+ default:
+ return 0;
+ }
+
+ err = ipc_has_perm(&sma->sem_perm, perms);
+ return err;
+}
+
+static int selinux_sem_semop(struct sem_array *sma,
+ struct sembuf *sops, unsigned nsops, int alter)
+{
+ u32 perms;
+
+ if (alter)
+ perms = SEM__READ | SEM__WRITE;
+ else
+ perms = SEM__READ;
+
+ return ipc_has_perm(&sma->sem_perm, perms);
+}
+
+static int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
+{
+ u32 av = 0;
+
+ av = 0;
+ if (flag & S_IRUGO)
+ av |= IPC__UNIX_READ;
+ if (flag & S_IWUGO)
+ av |= IPC__UNIX_WRITE;
+
+ if (av == 0)
+ return 0;
+
+ return ipc_has_perm(ipcp, av);
+}
+
+static void selinux_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
+{
+ struct ipc_security_struct *isec = ipcp->security;
+ *secid = isec->sid;
+}
+
+static void selinux_d_instantiate(struct dentry *dentry, struct inode *inode)
+{
+ if (inode)
+ inode_doinit_with_dentry(inode, dentry);
+}
+
+static int selinux_getprocattr(struct task_struct *p,
+ char *name, char **value)
+{
+ struct task_security_struct *tsec;
+ u32 sid;
+ int error;
+ unsigned len;
+
+ if (current != p) {
+ error = task_has_perm(current, p, PROCESS__GETATTR);
+ if (error)
+ return error;
+ }
+
+ tsec = p->security;
+
+ if (!strcmp(name, "current"))
+ sid = tsec->sid;
+ else if (!strcmp(name, "prev"))
+ sid = tsec->osid;
+ else if (!strcmp(name, "exec"))
+ sid = tsec->exec_sid;
+ else if (!strcmp(name, "fscreate"))
+ sid = tsec->create_sid;
+ else if (!strcmp(name, "keycreate"))
+ sid = tsec->keycreate_sid;
+ else if (!strcmp(name, "sockcreate"))
+ sid = tsec->sockcreate_sid;
+ else
+ return -EINVAL;
+
+ if (!sid)
+ return 0;
+
+ error = security_sid_to_context(sid, value, &len);
+ if (error)
+ return error;
+ return len;
+}
+
+static int selinux_setprocattr(struct task_struct *p,
+ char *name, void *value, size_t size)
+{
+ struct task_security_struct *tsec;
+ struct task_struct *tracer;
+ u32 sid = 0;
+ int error;
+ char *str = value;
+
+ if (current != p) {
+ /* SELinux only allows a process to change its own
+ security attributes. */
+ return -EACCES;
+ }
+
+ /*
+ * Basic control over ability to set these attributes at all.
+ * current == p, but we'll pass them separately in case the
+ * above restriction is ever removed.
+ */
+ if (!strcmp(name, "exec"))
+ error = task_has_perm(current, p, PROCESS__SETEXEC);
+ else if (!strcmp(name, "fscreate"))
+ error = task_has_perm(current, p, PROCESS__SETFSCREATE);
+ else if (!strcmp(name, "keycreate"))
+ error = task_has_perm(current, p, PROCESS__SETKEYCREATE);
+ else if (!strcmp(name, "sockcreate"))
+ error = task_has_perm(current, p, PROCESS__SETSOCKCREATE);
+ else if (!strcmp(name, "current"))
+ error = task_has_perm(current, p, PROCESS__SETCURRENT);
+ else
+ error = -EINVAL;
+ if (error)
+ return error;
+
+ /* Obtain a SID for the context, if one was specified. */
+ if (size && str[1] && str[1] != '\n') {
+ if (str[size-1] == '\n') {
+ str[size-1] = 0;
+ size--;
+ }
+ error = security_context_to_sid(value, size, &sid);
+ if (error == -EINVAL && !strcmp(name, "fscreate")) {
+ if (!capable(CAP_MAC_ADMIN))
+ return error;
+ error = security_context_to_sid_force(value, size,
+ &sid);
+ }
+ if (error)
+ return error;
+ }
+
+ /* Permission checking based on the specified context is
+ performed during the actual operation (execve,
+ open/mkdir/...), when we know the full context of the
+ operation. See selinux_bprm_set_security for the execve
+ checks and may_create for the file creation checks. The
+ operation will then fail if the context is not permitted. */
+ tsec = p->security;
+ if (!strcmp(name, "exec"))
+ tsec->exec_sid = sid;
+ else if (!strcmp(name, "fscreate"))
+ tsec->create_sid = sid;
+ else if (!strcmp(name, "keycreate")) {
+ error = may_create_key(sid, p);
+ if (error)
+ return error;
+ tsec->keycreate_sid = sid;
+ } else if (!strcmp(name, "sockcreate"))
+ tsec->sockcreate_sid = sid;
+ else if (!strcmp(name, "current")) {
+ struct av_decision avd;
+
+ if (sid == 0)
+ return -EINVAL;
+ /*
+ * SELinux allows to change context in the following case only.
+ * - Single threaded processes.
+ * - Multi threaded processes intend to change its context into
+ * more restricted domain (defined by TYPEBOUNDS statement).
+ */
+ if (atomic_read(&p->mm->mm_users) != 1) {
+ struct task_struct *g, *t;
+ struct mm_struct *mm = p->mm;
+ read_lock(&tasklist_lock);
+ do_each_thread(g, t) {
+ if (t->mm == mm && t != p) {
+ read_unlock(&tasklist_lock);
+ error = security_bounded_transition(tsec->sid, sid);
+ if (!error)
+ goto boundary_ok;
+
+ return error;
+ }
+ } while_each_thread(g, t);
+ read_unlock(&tasklist_lock);
+ }
+boundary_ok:
+
+ /* Check permissions for the transition. */
+ error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS,
+ PROCESS__DYNTRANSITION, NULL);
+ if (error)
+ return error;
+
+ /* Check for ptracing, and update the task SID if ok.
+ Otherwise, leave SID unchanged and fail. */
+ task_lock(p);
+ rcu_read_lock();
+ tracer = tracehook_tracer_task(p);
+ if (tracer != NULL) {
+ struct task_security_struct *ptsec = tracer->security;
+ u32 ptsid = ptsec->sid;
+ rcu_read_unlock();
+ error = avc_has_perm_noaudit(ptsid, sid,
+ SECCLASS_PROCESS,
+ PROCESS__PTRACE, 0, &avd);
+ if (!error)
+ tsec->sid = sid;
+ task_unlock(p);
+ avc_audit(ptsid, sid, SECCLASS_PROCESS,
+ PROCESS__PTRACE, &avd, error, NULL);
+ if (error)
+ return error;
+ } else {
+ rcu_read_unlock();
+ tsec->sid = sid;
+ task_unlock(p);
+ }
+ } else
+ return -EINVAL;
+
+ return size;
+}
+
+static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+{
+ return security_sid_to_context(secid, secdata, seclen);
+}
+
+static int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
+{
+ return security_context_to_sid(secdata, seclen, secid);
+}
+
+static void selinux_release_secctx(char *secdata, u32 seclen)
+{
+ kfree(secdata);
+}
+
+#ifdef CONFIG_KEYS
+
+static int selinux_key_alloc(struct key *k, struct task_struct *tsk,
+ unsigned long flags)
+{
+ struct task_security_struct *tsec = tsk->security;
+ struct key_security_struct *ksec;
+
+ ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL);
+ if (!ksec)
+ return -ENOMEM;
+
+ if (tsec->keycreate_sid)
+ ksec->sid = tsec->keycreate_sid;
+ else
+ ksec->sid = tsec->sid;
+ k->security = ksec;
+
+ return 0;
+}
+
+static void selinux_key_free(struct key *k)
+{
+ struct key_security_struct *ksec = k->security;
+
+ k->security = NULL;
+ kfree(ksec);
+}
+
+static int selinux_key_permission(key_ref_t key_ref,
+ struct task_struct *ctx,
+ key_perm_t perm)
+{
+ struct key *key;
+ struct task_security_struct *tsec;
+ struct key_security_struct *ksec;
+
+ key = key_ref_to_ptr(key_ref);
+
+ tsec = ctx->security;
+ ksec = key->security;
+
+ /* if no specific permissions are requested, we skip the
+ permission check. No serious, additional covert channels
+ appear to be created. */
+ if (perm == 0)
+ return 0;
+
+ return avc_has_perm(tsec->sid, ksec->sid,
+ SECCLASS_KEY, perm, NULL);
+}
+
+static int selinux_key_getsecurity(struct key *key, char **_buffer)
+{
+ struct key_security_struct *ksec = key->security;
+ char *context = NULL;
+ unsigned len;
+ int rc;
+
+ rc = security_sid_to_context(ksec->sid, &context, &len);
+ if (!rc)
+ rc = len;
+ *_buffer = context;
+ return rc;
+}
+
+#endif
+
+static struct security_operations selinux_ops = {
+ .name = "selinux",
+
+ .ptrace_may_access = selinux_ptrace_may_access,
+ .ptrace_traceme = selinux_ptrace_traceme,
+ .capget = selinux_capget,
+ .capset_check = selinux_capset_check,
+ .capset_set = selinux_capset_set,
+ .sysctl = selinux_sysctl,
+ .capable = selinux_capable,
+ .quotactl = selinux_quotactl,
+ .quota_on = selinux_quota_on,
+ .syslog = selinux_syslog,
+ .vm_enough_memory = selinux_vm_enough_memory,
+
+ .netlink_send = selinux_netlink_send,
+ .netlink_recv = selinux_netlink_recv,
+
+ .bprm_alloc_security = selinux_bprm_alloc_security,
+ .bprm_free_security = selinux_bprm_free_security,
+ .bprm_apply_creds = selinux_bprm_apply_creds,
+ .bprm_post_apply_creds = selinux_bprm_post_apply_creds,
+ .bprm_set_security = selinux_bprm_set_security,
+ .bprm_check_security = selinux_bprm_check_security,
+ .bprm_secureexec = selinux_bprm_secureexec,
+
+ .sb_alloc_security = selinux_sb_alloc_security,
+ .sb_free_security = selinux_sb_free_security,
+ .sb_copy_data = selinux_sb_copy_data,
+ .sb_kern_mount = selinux_sb_kern_mount,
+ .sb_show_options = selinux_sb_show_options,
+ .sb_statfs = selinux_sb_statfs,
+ .sb_mount = selinux_mount,
+ .sb_umount = selinux_umount,
+ .sb_set_mnt_opts = selinux_set_mnt_opts,
+ .sb_clone_mnt_opts = selinux_sb_clone_mnt_opts,
+ .sb_parse_opts_str = selinux_parse_opts_str,
+
+
+ .inode_alloc_security = selinux_inode_alloc_security,
+ .inode_free_security = selinux_inode_free_security,
+ .inode_init_security = selinux_inode_init_security,
+ .inode_create = selinux_inode_create,
+ .inode_link = selinux_inode_link,
+ .inode_unlink = selinux_inode_unlink,
+ .inode_symlink = selinux_inode_symlink,
+ .inode_mkdir = selinux_inode_mkdir,
+ .inode_rmdir = selinux_inode_rmdir,
+ .inode_mknod = selinux_inode_mknod,
+ .inode_rename = selinux_inode_rename,
+ .inode_readlink = selinux_inode_readlink,
+ .inode_follow_link = selinux_inode_follow_link,
+ .inode_permission = selinux_inode_permission,
+ .inode_setattr = selinux_inode_setattr,
+ .inode_getattr = selinux_inode_getattr,
+ .inode_setxattr = selinux_inode_setxattr,
+ .inode_post_setxattr = selinux_inode_post_setxattr,
+ .inode_getxattr = selinux_inode_getxattr,
+ .inode_listxattr = selinux_inode_listxattr,
+ .inode_removexattr = selinux_inode_removexattr,
+ .inode_getsecurity = selinux_inode_getsecurity,
+ .inode_setsecurity = selinux_inode_setsecurity,
+ .inode_listsecurity = selinux_inode_listsecurity,
+ .inode_need_killpriv = selinux_inode_need_killpriv,
+ .inode_killpriv = selinux_inode_killpriv,
+ .inode_getsecid = selinux_inode_getsecid,
+
+ .file_permission = selinux_file_permission,
+ .file_alloc_security = selinux_file_alloc_security,
+ .file_free_security = selinux_file_free_security,
+ .file_ioctl = selinux_file_ioctl,
+ .file_mmap = selinux_file_mmap,
+ .file_mprotect = selinux_file_mprotect,
+ .file_lock = selinux_file_lock,
+ .file_fcntl = selinux_file_fcntl,
+ .file_set_fowner = selinux_file_set_fowner,
+ .file_send_sigiotask = selinux_file_send_sigiotask,
+ .file_receive = selinux_file_receive,
+
+ .dentry_open = selinux_dentry_open,
+
+ .task_create = selinux_task_create,
+ .task_alloc_security = selinux_task_alloc_security,
+ .task_free_security = selinux_task_free_security,
+ .task_setuid = selinux_task_setuid,
+ .task_post_setuid = selinux_task_post_setuid,
+ .task_setgid = selinux_task_setgid,
+ .task_setpgid = selinux_task_setpgid,
+ .task_getpgid = selinux_task_getpgid,
+ .task_getsid = selinux_task_getsid,
+ .task_getsecid = selinux_task_getsecid,
+ .task_setgroups = selinux_task_setgroups,
+ .task_setnice = selinux_task_setnice,
+ .task_setioprio = selinux_task_setioprio,
+ .task_getioprio = selinux_task_getioprio,
+ .task_setrlimit = selinux_task_setrlimit,
+ .task_setscheduler = selinux_task_setscheduler,
+ .task_getscheduler = selinux_task_getscheduler,
+ .task_movememory = selinux_task_movememory,
+ .task_kill = selinux_task_kill,
+ .task_wait = selinux_task_wait,
+ .task_prctl = selinux_task_prctl,
+ .task_reparent_to_init = selinux_task_reparent_to_init,
+ .task_to_inode = selinux_task_to_inode,
+
+ .ipc_permission = selinux_ipc_permission,
+ .ipc_getsecid = selinux_ipc_getsecid,
+
+ .msg_msg_alloc_security = selinux_msg_msg_alloc_security,
+ .msg_msg_free_security = selinux_msg_msg_free_security,
+
+ .msg_queue_alloc_security = selinux_msg_queue_alloc_security,
+ .msg_queue_free_security = selinux_msg_queue_free_security,
+ .msg_queue_associate = selinux_msg_queue_associate,
+ .msg_queue_msgctl = selinux_msg_queue_msgctl,
+ .msg_queue_msgsnd = selinux_msg_queue_msgsnd,
+ .msg_queue_msgrcv = selinux_msg_queue_msgrcv,
+
+ .shm_alloc_security = selinux_shm_alloc_security,
+ .shm_free_security = selinux_shm_free_security,
+ .shm_associate = selinux_shm_associate,
+ .shm_shmctl = selinux_shm_shmctl,
+ .shm_shmat = selinux_shm_shmat,
+
+ .sem_alloc_security = selinux_sem_alloc_security,
+ .sem_free_security = selinux_sem_free_security,
+ .sem_associate = selinux_sem_associate,
+ .sem_semctl = selinux_sem_semctl,
+ .sem_semop = selinux_sem_semop,
+
+ .d_instantiate = selinux_d_instantiate,
+
+ .getprocattr = selinux_getprocattr,
+ .setprocattr = selinux_setprocattr,
+
+ .secid_to_secctx = selinux_secid_to_secctx,
+ .secctx_to_secid = selinux_secctx_to_secid,
+ .release_secctx = selinux_release_secctx,
+
+ .unix_stream_connect = selinux_socket_unix_stream_connect,
+ .unix_may_send = selinux_socket_unix_may_send,
+
+ .socket_create = selinux_socket_create,
+ .socket_post_create = selinux_socket_post_create,
+ .socket_bind = selinux_socket_bind,
+ .socket_connect = selinux_socket_connect,
+ .socket_listen = selinux_socket_listen,
+ .socket_accept = selinux_socket_accept,
+ .socket_sendmsg = selinux_socket_sendmsg,
+ .socket_recvmsg = selinux_socket_recvmsg,
+ .socket_getsockname = selinux_socket_getsockname,
+ .socket_getpeername = selinux_socket_getpeername,
+ .socket_getsockopt = selinux_socket_getsockopt,
+ .socket_setsockopt = selinux_socket_setsockopt,
+ .socket_shutdown = selinux_socket_shutdown,
+ .socket_sock_rcv_skb = selinux_socket_sock_rcv_skb,
+ .socket_getpeersec_stream = selinux_socket_getpeersec_stream,
+ .socket_getpeersec_dgram = selinux_socket_getpeersec_dgram,
+ .sk_alloc_security = selinux_sk_alloc_security,
+ .sk_free_security = selinux_sk_free_security,
+ .sk_clone_security = selinux_sk_clone_security,
+ .sk_getsecid = selinux_sk_getsecid,
+ .sock_graft = selinux_sock_graft,
+ .inet_conn_request = selinux_inet_conn_request,
+ .inet_csk_clone = selinux_inet_csk_clone,
+ .inet_conn_established = selinux_inet_conn_established,
+ .req_classify_flow = selinux_req_classify_flow,
+
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+ .xfrm_policy_alloc_security = selinux_xfrm_policy_alloc,
+ .xfrm_policy_clone_security = selinux_xfrm_policy_clone,
+ .xfrm_policy_free_security = selinux_xfrm_policy_free,
+ .xfrm_policy_delete_security = selinux_xfrm_policy_delete,
+ .xfrm_state_alloc_security = selinux_xfrm_state_alloc,
+ .xfrm_state_free_security = selinux_xfrm_state_free,
+ .xfrm_state_delete_security = selinux_xfrm_state_delete,
+ .xfrm_policy_lookup = selinux_xfrm_policy_lookup,
+ .xfrm_state_pol_flow_match = selinux_xfrm_state_pol_flow_match,
+ .xfrm_decode_session = selinux_xfrm_decode_session,
+#endif
+
+#ifdef CONFIG_KEYS
+ .key_alloc = selinux_key_alloc,
+ .key_free = selinux_key_free,
+ .key_permission = selinux_key_permission,
+ .key_getsecurity = selinux_key_getsecurity,
+#endif
+
+#ifdef CONFIG_AUDIT
+ .audit_rule_init = selinux_audit_rule_init,
+ .audit_rule_known = selinux_audit_rule_known,
+ .audit_rule_match = selinux_audit_rule_match,
+ .audit_rule_free = selinux_audit_rule_free,
+#endif
+};
+
+static __init int selinux_init(void)
+{
+ struct task_security_struct *tsec;
+
+ if (!security_module_enable(&selinux_ops)) {
+ selinux_enabled = 0;
+ return 0;
+ }
+
+ if (!selinux_enabled) {
+ printk(KERN_INFO "SELinux: Disabled at boot.\n");
+ return 0;
+ }
+
+ printk(KERN_INFO "SELinux: Initializing.\n");
+
+ /* Set the security state for the initial task. */
+ if (task_alloc_security(current))
+ panic("SELinux: Failed to initialize initial task.\n");
+ tsec = current->security;
+ tsec->osid = tsec->sid = SECINITSID_KERNEL;
+
+ sel_inode_cache = kmem_cache_create("selinux_inode_security",
+ sizeof(struct inode_security_struct),
+ 0, SLAB_PANIC, NULL);
+ avc_init();
+
+ secondary_ops = security_ops;
+ if (!secondary_ops)
+ panic("SELinux: No initial security operations\n");
+ if (register_security(&selinux_ops))
+ panic("SELinux: Unable to register with kernel.\n");
+
+ if (selinux_enforcing)
+ printk(KERN_DEBUG "SELinux: Starting in enforcing mode\n");
+ else
+ printk(KERN_DEBUG "SELinux: Starting in permissive mode\n");
+
+ return 0;
+}
+
+void selinux_complete_init(void)
+{
+ printk(KERN_DEBUG "SELinux: Completing initialization.\n");
+
+ /* Set up any superblocks initialized prior to the policy load. */
+ printk(KERN_DEBUG "SELinux: Setting up existing superblocks.\n");
+ spin_lock(&sb_lock);
+ spin_lock(&sb_security_lock);
+next_sb:
+ if (!list_empty(&superblock_security_head)) {
+ struct superblock_security_struct *sbsec =
+ list_entry(superblock_security_head.next,
+ struct superblock_security_struct,
+ list);
+ struct super_block *sb = sbsec->sb;
+ sb->s_count++;
+ spin_unlock(&sb_security_lock);
+ spin_unlock(&sb_lock);
+ down_read(&sb->s_umount);
+ if (sb->s_root)
+ superblock_doinit(sb, NULL);
+ drop_super(sb);
+ spin_lock(&sb_lock);
+ spin_lock(&sb_security_lock);
+ list_del_init(&sbsec->list);
+ goto next_sb;
+ }
+ spin_unlock(&sb_security_lock);
+ spin_unlock(&sb_lock);
+}
+
+/* SELinux requires early initialization in order to label
+ all processes and objects when they are created. */
+security_initcall(selinux_init);
+
+#if defined(CONFIG_NETFILTER)
+
+static struct nf_hook_ops selinux_ipv4_ops[] = {
+ {
+ .hook = selinux_ipv4_postroute,
+ .owner = THIS_MODULE,
+ .pf = PF_INET,
+ .hooknum = NF_INET_POST_ROUTING,
+ .priority = NF_IP_PRI_SELINUX_LAST,
+ },
+ {
+ .hook = selinux_ipv4_forward,
+ .owner = THIS_MODULE,
+ .pf = PF_INET,
+ .hooknum = NF_INET_FORWARD,
+ .priority = NF_IP_PRI_SELINUX_FIRST,
+ },
+ {
+ .hook = selinux_ipv4_output,
+ .owner = THIS_MODULE,
+ .pf = PF_INET,
+ .hooknum = NF_INET_LOCAL_OUT,
+ .priority = NF_IP_PRI_SELINUX_FIRST,
+ }
+};
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+
+static struct nf_hook_ops selinux_ipv6_ops[] = {
+ {
+ .hook = selinux_ipv6_postroute,
+ .owner = THIS_MODULE,
+ .pf = PF_INET6,
+ .hooknum = NF_INET_POST_ROUTING,
+ .priority = NF_IP6_PRI_SELINUX_LAST,
+ },
+ {
+ .hook = selinux_ipv6_forward,
+ .owner = THIS_MODULE,
+ .pf = PF_INET6,
+ .hooknum = NF_INET_FORWARD,
+ .priority = NF_IP6_PRI_SELINUX_FIRST,
+ }
+};
+
+#endif /* IPV6 */
+
+static int __init selinux_nf_ip_init(void)
+{
+ int err = 0;
+
+ if (!selinux_enabled)
+ goto out;
+
+ printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n");
+
+ err = nf_register_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops));
+ if (err)
+ panic("SELinux: nf_register_hooks for IPv4: error %d\n", err);
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
+ if (err)
+ panic("SELinux: nf_register_hooks for IPv6: error %d\n", err);
+#endif /* IPV6 */
+
+out:
+ return err;
+}
+
+__initcall(selinux_nf_ip_init);
+
+#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+static void selinux_nf_ip_exit(void)
+{
+ printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n");
+
+ nf_unregister_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops));
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ nf_unregister_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
+#endif /* IPV6 */
+}
+#endif
+
+#else /* CONFIG_NETFILTER */
+
+#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+#define selinux_nf_ip_exit()
+#endif
+
+#endif /* CONFIG_NETFILTER */
+
+#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+static int selinux_disabled;
+
+int selinux_disable(void)
+{
+ extern void exit_sel_fs(void);
+
+ if (ss_initialized) {
+ /* Not permitted after initial policy load. */
+ return -EINVAL;
+ }
+
+ if (selinux_disabled) {
+ /* Only do this once. */
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "SELinux: Disabled at runtime.\n");
+
+ selinux_disabled = 1;
+ selinux_enabled = 0;
+
+ /* Reset security_ops to the secondary module, dummy or capability. */
+ security_ops = secondary_ops;
+
+ /* Unregister netfilter hooks. */
+ selinux_nf_ip_exit();
+
+ /* Unregister selinuxfs. */
+ exit_sel_fs();
+
+ return 0;
+}
+#endif
diff --git a/security/selinux/include/audit.h b/security/selinux/include/audit.h
new file mode 100644
index 0000000..1bdf973
--- /dev/null
+++ b/security/selinux/include/audit.h
@@ -0,0 +1,65 @@
+/*
+ * SELinux support for the Audit LSM hooks
+ *
+ * Most of below header was moved from include/linux/selinux.h which
+ * is released under below copyrights:
+ *
+ * Author: James Morris <jmorris@redhat.com>
+ *
+ * Copyright (C) 2005 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * Copyright (C) 2006 Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ * Copyright (C) 2006 IBM Corporation, Timothy R. Chavez <tinytim@us.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef _SELINUX_AUDIT_H
+#define _SELINUX_AUDIT_H
+
+/**
+ * selinux_audit_rule_init - alloc/init an selinux audit rule structure.
+ * @field: the field this rule refers to
+ * @op: the operater the rule uses
+ * @rulestr: the text "target" of the rule
+ * @rule: pointer to the new rule structure returned via this
+ *
+ * Returns 0 if successful, -errno if not. On success, the rule structure
+ * will be allocated internally. The caller must free this structure with
+ * selinux_audit_rule_free() after use.
+ */
+int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **rule);
+
+/**
+ * selinux_audit_rule_free - free an selinux audit rule structure.
+ * @rule: pointer to the audit rule to be freed
+ *
+ * This will free all memory associated with the given rule.
+ * If @rule is NULL, no operation is performed.
+ */
+void selinux_audit_rule_free(void *rule);
+
+/**
+ * selinux_audit_rule_match - determine if a context ID matches a rule.
+ * @sid: the context ID to check
+ * @field: the field this rule refers to
+ * @op: the operater the rule uses
+ * @rule: pointer to the audit rule to check against
+ * @actx: the audit context (can be NULL) associated with the check
+ *
+ * Returns 1 if the context id matches the rule, 0 if it does not, and
+ * -errno on failure.
+ */
+int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *rule,
+ struct audit_context *actx);
+
+/**
+ * selinux_audit_rule_known - check to see if rule contains selinux fields.
+ * @rule: rule to be checked
+ * Returns 1 if there are selinux fields specified in the rule, 0 otherwise.
+ */
+int selinux_audit_rule_known(struct audit_krule *krule);
+
+#endif /* _SELINUX_AUDIT_H */
+
diff --git a/security/selinux/include/av_inherit.h b/security/selinux/include/av_inherit.h
new file mode 100644
index 0000000..8377a4b
--- /dev/null
+++ b/security/selinux/include/av_inherit.h
@@ -0,0 +1,33 @@
+/* This file is automatically generated. Do not edit. */
+ S_(SECCLASS_DIR, file, 0x00020000UL)
+ S_(SECCLASS_FILE, file, 0x00020000UL)
+ S_(SECCLASS_LNK_FILE, file, 0x00020000UL)
+ S_(SECCLASS_CHR_FILE, file, 0x00020000UL)
+ S_(SECCLASS_BLK_FILE, file, 0x00020000UL)
+ S_(SECCLASS_SOCK_FILE, file, 0x00020000UL)
+ S_(SECCLASS_FIFO_FILE, file, 0x00020000UL)
+ S_(SECCLASS_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_TCP_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_UDP_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_RAWIP_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_PACKET_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_KEY_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_UNIX_STREAM_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_UNIX_DGRAM_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_IPC, ipc, 0x00000200UL)
+ S_(SECCLASS_SEM, ipc, 0x00000200UL)
+ S_(SECCLASS_MSGQ, ipc, 0x00000200UL)
+ S_(SECCLASS_SHM, ipc, 0x00000200UL)
+ S_(SECCLASS_NETLINK_ROUTE_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_FIREWALL_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_NFLOG_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_XFRM_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_SELINUX_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_AUDIT_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_IP6FW_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_DNRT_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_APPLETALK_SOCKET, socket, 0x00400000UL)
+ S_(SECCLASS_DCCP_SOCKET, socket, 0x00400000UL)
diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h
new file mode 100644
index 0000000..1223b4f
--- /dev/null
+++ b/security/selinux/include/av_perm_to_string.h
@@ -0,0 +1,178 @@
+/* This file is automatically generated. Do not edit. */
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__MOUNT, "mount")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__REMOUNT, "remount")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__UNMOUNT, "unmount")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__GETATTR, "getattr")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__RELABELFROM, "relabelfrom")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__RELABELTO, "relabelto")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__TRANSITION, "transition")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__ASSOCIATE, "associate")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__QUOTAMOD, "quotamod")
+ S_(SECCLASS_FILESYSTEM, FILESYSTEM__QUOTAGET, "quotaget")
+ S_(SECCLASS_DIR, DIR__ADD_NAME, "add_name")
+ S_(SECCLASS_DIR, DIR__REMOVE_NAME, "remove_name")
+ S_(SECCLASS_DIR, DIR__REPARENT, "reparent")
+ S_(SECCLASS_DIR, DIR__SEARCH, "search")
+ S_(SECCLASS_DIR, DIR__RMDIR, "rmdir")
+ S_(SECCLASS_DIR, DIR__OPEN, "open")
+ S_(SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, "execute_no_trans")
+ S_(SECCLASS_FILE, FILE__ENTRYPOINT, "entrypoint")
+ S_(SECCLASS_FILE, FILE__EXECMOD, "execmod")
+ S_(SECCLASS_FILE, FILE__OPEN, "open")
+ S_(SECCLASS_CHR_FILE, CHR_FILE__EXECUTE_NO_TRANS, "execute_no_trans")
+ S_(SECCLASS_CHR_FILE, CHR_FILE__ENTRYPOINT, "entrypoint")
+ S_(SECCLASS_CHR_FILE, CHR_FILE__EXECMOD, "execmod")
+ S_(SECCLASS_CHR_FILE, CHR_FILE__OPEN, "open")
+ S_(SECCLASS_BLK_FILE, BLK_FILE__OPEN, "open")
+ S_(SECCLASS_FIFO_FILE, FIFO_FILE__OPEN, "open")
+ S_(SECCLASS_FD, FD__USE, "use")
+ S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__CONNECTTO, "connectto")
+ S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NEWCONN, "newconn")
+ S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__ACCEPTFROM, "acceptfrom")
+ S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NODE_BIND, "node_bind")
+ S_(SECCLASS_TCP_SOCKET, TCP_SOCKET__NAME_CONNECT, "name_connect")
+ S_(SECCLASS_UDP_SOCKET, UDP_SOCKET__NODE_BIND, "node_bind")
+ S_(SECCLASS_RAWIP_SOCKET, RAWIP_SOCKET__NODE_BIND, "node_bind")
+ S_(SECCLASS_NODE, NODE__TCP_RECV, "tcp_recv")
+ S_(SECCLASS_NODE, NODE__TCP_SEND, "tcp_send")
+ S_(SECCLASS_NODE, NODE__UDP_RECV, "udp_recv")
+ S_(SECCLASS_NODE, NODE__UDP_SEND, "udp_send")
+ S_(SECCLASS_NODE, NODE__RAWIP_RECV, "rawip_recv")
+ S_(SECCLASS_NODE, NODE__RAWIP_SEND, "rawip_send")
+ S_(SECCLASS_NODE, NODE__ENFORCE_DEST, "enforce_dest")
+ S_(SECCLASS_NODE, NODE__DCCP_RECV, "dccp_recv")
+ S_(SECCLASS_NODE, NODE__DCCP_SEND, "dccp_send")
+ S_(SECCLASS_NODE, NODE__RECVFROM, "recvfrom")
+ S_(SECCLASS_NODE, NODE__SENDTO, "sendto")
+ S_(SECCLASS_NETIF, NETIF__TCP_RECV, "tcp_recv")
+ S_(SECCLASS_NETIF, NETIF__TCP_SEND, "tcp_send")
+ S_(SECCLASS_NETIF, NETIF__UDP_RECV, "udp_recv")
+ S_(SECCLASS_NETIF, NETIF__UDP_SEND, "udp_send")
+ S_(SECCLASS_NETIF, NETIF__RAWIP_RECV, "rawip_recv")
+ S_(SECCLASS_NETIF, NETIF__RAWIP_SEND, "rawip_send")
+ S_(SECCLASS_NETIF, NETIF__DCCP_RECV, "dccp_recv")
+ S_(SECCLASS_NETIF, NETIF__DCCP_SEND, "dccp_send")
+ S_(SECCLASS_NETIF, NETIF__INGRESS, "ingress")
+ S_(SECCLASS_NETIF, NETIF__EGRESS, "egress")
+ S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__CONNECTTO, "connectto")
+ S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__NEWCONN, "newconn")
+ S_(SECCLASS_UNIX_STREAM_SOCKET, UNIX_STREAM_SOCKET__ACCEPTFROM, "acceptfrom")
+ S_(SECCLASS_PROCESS, PROCESS__FORK, "fork")
+ S_(SECCLASS_PROCESS, PROCESS__TRANSITION, "transition")
+ S_(SECCLASS_PROCESS, PROCESS__SIGCHLD, "sigchld")
+ S_(SECCLASS_PROCESS, PROCESS__SIGKILL, "sigkill")
+ S_(SECCLASS_PROCESS, PROCESS__SIGSTOP, "sigstop")
+ S_(SECCLASS_PROCESS, PROCESS__SIGNULL, "signull")
+ S_(SECCLASS_PROCESS, PROCESS__SIGNAL, "signal")
+ S_(SECCLASS_PROCESS, PROCESS__PTRACE, "ptrace")
+ S_(SECCLASS_PROCESS, PROCESS__GETSCHED, "getsched")
+ S_(SECCLASS_PROCESS, PROCESS__SETSCHED, "setsched")
+ S_(SECCLASS_PROCESS, PROCESS__GETSESSION, "getsession")
+ S_(SECCLASS_PROCESS, PROCESS__GETPGID, "getpgid")
+ S_(SECCLASS_PROCESS, PROCESS__SETPGID, "setpgid")
+ S_(SECCLASS_PROCESS, PROCESS__GETCAP, "getcap")
+ S_(SECCLASS_PROCESS, PROCESS__SETCAP, "setcap")
+ S_(SECCLASS_PROCESS, PROCESS__SHARE, "share")
+ S_(SECCLASS_PROCESS, PROCESS__GETATTR, "getattr")
+ S_(SECCLASS_PROCESS, PROCESS__SETEXEC, "setexec")
+ S_(SECCLASS_PROCESS, PROCESS__SETFSCREATE, "setfscreate")
+ S_(SECCLASS_PROCESS, PROCESS__NOATSECURE, "noatsecure")
+ S_(SECCLASS_PROCESS, PROCESS__SIGINH, "siginh")
+ S_(SECCLASS_PROCESS, PROCESS__SETRLIMIT, "setrlimit")
+ S_(SECCLASS_PROCESS, PROCESS__RLIMITINH, "rlimitinh")
+ S_(SECCLASS_PROCESS, PROCESS__DYNTRANSITION, "dyntransition")
+ S_(SECCLASS_PROCESS, PROCESS__SETCURRENT, "setcurrent")
+ S_(SECCLASS_PROCESS, PROCESS__EXECMEM, "execmem")
+ S_(SECCLASS_PROCESS, PROCESS__EXECSTACK, "execstack")
+ S_(SECCLASS_PROCESS, PROCESS__EXECHEAP, "execheap")
+ S_(SECCLASS_PROCESS, PROCESS__SETKEYCREATE, "setkeycreate")
+ S_(SECCLASS_PROCESS, PROCESS__SETSOCKCREATE, "setsockcreate")
+ S_(SECCLASS_MSGQ, MSGQ__ENQUEUE, "enqueue")
+ S_(SECCLASS_MSG, MSG__SEND, "send")
+ S_(SECCLASS_MSG, MSG__RECEIVE, "receive")
+ S_(SECCLASS_SHM, SHM__LOCK, "lock")
+ S_(SECCLASS_SECURITY, SECURITY__COMPUTE_AV, "compute_av")
+ S_(SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE, "compute_create")
+ S_(SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER, "compute_member")
+ S_(SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, "check_context")
+ S_(SECCLASS_SECURITY, SECURITY__LOAD_POLICY, "load_policy")
+ S_(SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL, "compute_relabel")
+ S_(SECCLASS_SECURITY, SECURITY__COMPUTE_USER, "compute_user")
+ S_(SECCLASS_SECURITY, SECURITY__SETENFORCE, "setenforce")
+ S_(SECCLASS_SECURITY, SECURITY__SETBOOL, "setbool")
+ S_(SECCLASS_SECURITY, SECURITY__SETSECPARAM, "setsecparam")
+ S_(SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT, "setcheckreqprot")
+ S_(SECCLASS_SYSTEM, SYSTEM__IPC_INFO, "ipc_info")
+ S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read")
+ S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
+ S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__CHOWN, "chown")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_OVERRIDE, "dac_override")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_READ_SEARCH, "dac_read_search")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__FOWNER, "fowner")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__FSETID, "fsetid")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__KILL, "kill")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SETGID, "setgid")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SETUID, "setuid")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SETPCAP, "setpcap")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__LINUX_IMMUTABLE, "linux_immutable")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__NET_BIND_SERVICE, "net_bind_service")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__NET_BROADCAST, "net_broadcast")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__NET_ADMIN, "net_admin")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__NET_RAW, "net_raw")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__IPC_LOCK, "ipc_lock")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__IPC_OWNER, "ipc_owner")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_MODULE, "sys_module")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_RAWIO, "sys_rawio")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_CHROOT, "sys_chroot")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_PTRACE, "sys_ptrace")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_PACCT, "sys_pacct")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_ADMIN, "sys_admin")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_BOOT, "sys_boot")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_NICE, "sys_nice")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_RESOURCE, "sys_resource")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_TIME, "sys_time")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SYS_TTY_CONFIG, "sys_tty_config")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__MKNOD, "mknod")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__LEASE, "lease")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__AUDIT_WRITE, "audit_write")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__AUDIT_CONTROL, "audit_control")
+ S_(SECCLASS_CAPABILITY, CAPABILITY__SETFCAP, "setfcap")
+ S_(SECCLASS_CAPABILITY2, CAPABILITY2__MAC_OVERRIDE, "mac_override")
+ S_(SECCLASS_CAPABILITY2, CAPABILITY2__MAC_ADMIN, "mac_admin")
+ S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_READ, "nlmsg_read")
+ S_(SECCLASS_NETLINK_ROUTE_SOCKET, NETLINK_ROUTE_SOCKET__NLMSG_WRITE, "nlmsg_write")
+ S_(SECCLASS_NETLINK_FIREWALL_SOCKET, NETLINK_FIREWALL_SOCKET__NLMSG_READ, "nlmsg_read")
+ S_(SECCLASS_NETLINK_FIREWALL_SOCKET, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE, "nlmsg_write")
+ S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, NETLINK_TCPDIAG_SOCKET__NLMSG_READ, "nlmsg_read")
+ S_(SECCLASS_NETLINK_TCPDIAG_SOCKET, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE, "nlmsg_write")
+ S_(SECCLASS_NETLINK_XFRM_SOCKET, NETLINK_XFRM_SOCKET__NLMSG_READ, "nlmsg_read")
+ S_(SECCLASS_NETLINK_XFRM_SOCKET, NETLINK_XFRM_SOCKET__NLMSG_WRITE, "nlmsg_write")
+ S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READ, "nlmsg_read")
+ S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE, "nlmsg_write")
+ S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_RELAY, "nlmsg_relay")
+ S_(SECCLASS_NETLINK_AUDIT_SOCKET, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV, "nlmsg_readpriv")
+ S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_READ, "nlmsg_read")
+ S_(SECCLASS_NETLINK_IP6FW_SOCKET, NETLINK_IP6FW_SOCKET__NLMSG_WRITE, "nlmsg_write")
+ S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto")
+ S_(SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, "recvfrom")
+ S_(SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, "setcontext")
+ S_(SECCLASS_ASSOCIATION, ASSOCIATION__POLMATCH, "polmatch")
+ S_(SECCLASS_PACKET, PACKET__SEND, "send")
+ S_(SECCLASS_PACKET, PACKET__RECV, "recv")
+ S_(SECCLASS_PACKET, PACKET__RELABELTO, "relabelto")
+ S_(SECCLASS_PACKET, PACKET__FLOW_IN, "flow_in")
+ S_(SECCLASS_PACKET, PACKET__FLOW_OUT, "flow_out")
+ S_(SECCLASS_PACKET, PACKET__FORWARD_IN, "forward_in")
+ S_(SECCLASS_PACKET, PACKET__FORWARD_OUT, "forward_out")
+ S_(SECCLASS_KEY, KEY__VIEW, "view")
+ S_(SECCLASS_KEY, KEY__READ, "read")
+ S_(SECCLASS_KEY, KEY__WRITE, "write")
+ S_(SECCLASS_KEY, KEY__SEARCH, "search")
+ S_(SECCLASS_KEY, KEY__LINK, "link")
+ S_(SECCLASS_KEY, KEY__SETATTR, "setattr")
+ S_(SECCLASS_KEY, KEY__CREATE, "create")
+ S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NODE_BIND, "node_bind")
+ S_(SECCLASS_DCCP_SOCKET, DCCP_SOCKET__NAME_CONNECT, "name_connect")
+ S_(SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, "mmap_zero")
+ S_(SECCLASS_PEER, PEER__RECV, "recv")
diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h
new file mode 100644
index 0000000..c4c5116
--- /dev/null
+++ b/security/selinux/include/av_permissions.h
@@ -0,0 +1,843 @@
+/* This file is automatically generated. Do not edit. */
+#define COMMON_FILE__IOCTL 0x00000001UL
+#define COMMON_FILE__READ 0x00000002UL
+#define COMMON_FILE__WRITE 0x00000004UL
+#define COMMON_FILE__CREATE 0x00000008UL
+#define COMMON_FILE__GETATTR 0x00000010UL
+#define COMMON_FILE__SETATTR 0x00000020UL
+#define COMMON_FILE__LOCK 0x00000040UL
+#define COMMON_FILE__RELABELFROM 0x00000080UL
+#define COMMON_FILE__RELABELTO 0x00000100UL
+#define COMMON_FILE__APPEND 0x00000200UL
+#define COMMON_FILE__UNLINK 0x00000400UL
+#define COMMON_FILE__LINK 0x00000800UL
+#define COMMON_FILE__RENAME 0x00001000UL
+#define COMMON_FILE__EXECUTE 0x00002000UL
+#define COMMON_FILE__SWAPON 0x00004000UL
+#define COMMON_FILE__QUOTAON 0x00008000UL
+#define COMMON_FILE__MOUNTON 0x00010000UL
+#define COMMON_SOCKET__IOCTL 0x00000001UL
+#define COMMON_SOCKET__READ 0x00000002UL
+#define COMMON_SOCKET__WRITE 0x00000004UL
+#define COMMON_SOCKET__CREATE 0x00000008UL
+#define COMMON_SOCKET__GETATTR 0x00000010UL
+#define COMMON_SOCKET__SETATTR 0x00000020UL
+#define COMMON_SOCKET__LOCK 0x00000040UL
+#define COMMON_SOCKET__RELABELFROM 0x00000080UL
+#define COMMON_SOCKET__RELABELTO 0x00000100UL
+#define COMMON_SOCKET__APPEND 0x00000200UL
+#define COMMON_SOCKET__BIND 0x00000400UL
+#define COMMON_SOCKET__CONNECT 0x00000800UL
+#define COMMON_SOCKET__LISTEN 0x00001000UL
+#define COMMON_SOCKET__ACCEPT 0x00002000UL
+#define COMMON_SOCKET__GETOPT 0x00004000UL
+#define COMMON_SOCKET__SETOPT 0x00008000UL
+#define COMMON_SOCKET__SHUTDOWN 0x00010000UL
+#define COMMON_SOCKET__RECVFROM 0x00020000UL
+#define COMMON_SOCKET__SENDTO 0x00040000UL
+#define COMMON_SOCKET__RECV_MSG 0x00080000UL
+#define COMMON_SOCKET__SEND_MSG 0x00100000UL
+#define COMMON_SOCKET__NAME_BIND 0x00200000UL
+#define COMMON_IPC__CREATE 0x00000001UL
+#define COMMON_IPC__DESTROY 0x00000002UL
+#define COMMON_IPC__GETATTR 0x00000004UL
+#define COMMON_IPC__SETATTR 0x00000008UL
+#define COMMON_IPC__READ 0x00000010UL
+#define COMMON_IPC__WRITE 0x00000020UL
+#define COMMON_IPC__ASSOCIATE 0x00000040UL
+#define COMMON_IPC__UNIX_READ 0x00000080UL
+#define COMMON_IPC__UNIX_WRITE 0x00000100UL
+#define FILESYSTEM__MOUNT 0x00000001UL
+#define FILESYSTEM__REMOUNT 0x00000002UL
+#define FILESYSTEM__UNMOUNT 0x00000004UL
+#define FILESYSTEM__GETATTR 0x00000008UL
+#define FILESYSTEM__RELABELFROM 0x00000010UL
+#define FILESYSTEM__RELABELTO 0x00000020UL
+#define FILESYSTEM__TRANSITION 0x00000040UL
+#define FILESYSTEM__ASSOCIATE 0x00000080UL
+#define FILESYSTEM__QUOTAMOD 0x00000100UL
+#define FILESYSTEM__QUOTAGET 0x00000200UL
+#define DIR__IOCTL 0x00000001UL
+#define DIR__READ 0x00000002UL
+#define DIR__WRITE 0x00000004UL
+#define DIR__CREATE 0x00000008UL
+#define DIR__GETATTR 0x00000010UL
+#define DIR__SETATTR 0x00000020UL
+#define DIR__LOCK 0x00000040UL
+#define DIR__RELABELFROM 0x00000080UL
+#define DIR__RELABELTO 0x00000100UL
+#define DIR__APPEND 0x00000200UL
+#define DIR__UNLINK 0x00000400UL
+#define DIR__LINK 0x00000800UL
+#define DIR__RENAME 0x00001000UL
+#define DIR__EXECUTE 0x00002000UL
+#define DIR__SWAPON 0x00004000UL
+#define DIR__QUOTAON 0x00008000UL
+#define DIR__MOUNTON 0x00010000UL
+#define DIR__ADD_NAME 0x00020000UL
+#define DIR__REMOVE_NAME 0x00040000UL
+#define DIR__REPARENT 0x00080000UL
+#define DIR__SEARCH 0x00100000UL
+#define DIR__RMDIR 0x00200000UL
+#define DIR__OPEN 0x00400000UL
+#define FILE__IOCTL 0x00000001UL
+#define FILE__READ 0x00000002UL
+#define FILE__WRITE 0x00000004UL
+#define FILE__CREATE 0x00000008UL
+#define FILE__GETATTR 0x00000010UL
+#define FILE__SETATTR 0x00000020UL
+#define FILE__LOCK 0x00000040UL
+#define FILE__RELABELFROM 0x00000080UL
+#define FILE__RELABELTO 0x00000100UL
+#define FILE__APPEND 0x00000200UL
+#define FILE__UNLINK 0x00000400UL
+#define FILE__LINK 0x00000800UL
+#define FILE__RENAME 0x00001000UL
+#define FILE__EXECUTE 0x00002000UL
+#define FILE__SWAPON 0x00004000UL
+#define FILE__QUOTAON 0x00008000UL
+#define FILE__MOUNTON 0x00010000UL
+#define FILE__EXECUTE_NO_TRANS 0x00020000UL
+#define FILE__ENTRYPOINT 0x00040000UL
+#define FILE__EXECMOD 0x00080000UL
+#define FILE__OPEN 0x00100000UL
+#define LNK_FILE__IOCTL 0x00000001UL
+#define LNK_FILE__READ 0x00000002UL
+#define LNK_FILE__WRITE 0x00000004UL
+#define LNK_FILE__CREATE 0x00000008UL
+#define LNK_FILE__GETATTR 0x00000010UL
+#define LNK_FILE__SETATTR 0x00000020UL
+#define LNK_FILE__LOCK 0x00000040UL
+#define LNK_FILE__RELABELFROM 0x00000080UL
+#define LNK_FILE__RELABELTO 0x00000100UL
+#define LNK_FILE__APPEND 0x00000200UL
+#define LNK_FILE__UNLINK 0x00000400UL
+#define LNK_FILE__LINK 0x00000800UL
+#define LNK_FILE__RENAME 0x00001000UL
+#define LNK_FILE__EXECUTE 0x00002000UL
+#define LNK_FILE__SWAPON 0x00004000UL
+#define LNK_FILE__QUOTAON 0x00008000UL
+#define LNK_FILE__MOUNTON 0x00010000UL
+#define CHR_FILE__IOCTL 0x00000001UL
+#define CHR_FILE__READ 0x00000002UL
+#define CHR_FILE__WRITE 0x00000004UL
+#define CHR_FILE__CREATE 0x00000008UL
+#define CHR_FILE__GETATTR 0x00000010UL
+#define CHR_FILE__SETATTR 0x00000020UL
+#define CHR_FILE__LOCK 0x00000040UL
+#define CHR_FILE__RELABELFROM 0x00000080UL
+#define CHR_FILE__RELABELTO 0x00000100UL
+#define CHR_FILE__APPEND 0x00000200UL
+#define CHR_FILE__UNLINK 0x00000400UL
+#define CHR_FILE__LINK 0x00000800UL
+#define CHR_FILE__RENAME 0x00001000UL
+#define CHR_FILE__EXECUTE 0x00002000UL
+#define CHR_FILE__SWAPON 0x00004000UL
+#define CHR_FILE__QUOTAON 0x00008000UL
+#define CHR_FILE__MOUNTON 0x00010000UL
+#define CHR_FILE__EXECUTE_NO_TRANS 0x00020000UL
+#define CHR_FILE__ENTRYPOINT 0x00040000UL
+#define CHR_FILE__EXECMOD 0x00080000UL
+#define CHR_FILE__OPEN 0x00100000UL
+#define BLK_FILE__IOCTL 0x00000001UL
+#define BLK_FILE__READ 0x00000002UL
+#define BLK_FILE__WRITE 0x00000004UL
+#define BLK_FILE__CREATE 0x00000008UL
+#define BLK_FILE__GETATTR 0x00000010UL
+#define BLK_FILE__SETATTR 0x00000020UL
+#define BLK_FILE__LOCK 0x00000040UL
+#define BLK_FILE__RELABELFROM 0x00000080UL
+#define BLK_FILE__RELABELTO 0x00000100UL
+#define BLK_FILE__APPEND 0x00000200UL
+#define BLK_FILE__UNLINK 0x00000400UL
+#define BLK_FILE__LINK 0x00000800UL
+#define BLK_FILE__RENAME 0x00001000UL
+#define BLK_FILE__EXECUTE 0x00002000UL
+#define BLK_FILE__SWAPON 0x00004000UL
+#define BLK_FILE__QUOTAON 0x00008000UL
+#define BLK_FILE__MOUNTON 0x00010000UL
+#define BLK_FILE__OPEN 0x00020000UL
+#define SOCK_FILE__IOCTL 0x00000001UL
+#define SOCK_FILE__READ 0x00000002UL
+#define SOCK_FILE__WRITE 0x00000004UL
+#define SOCK_FILE__CREATE 0x00000008UL
+#define SOCK_FILE__GETATTR 0x00000010UL
+#define SOCK_FILE__SETATTR 0x00000020UL
+#define SOCK_FILE__LOCK 0x00000040UL
+#define SOCK_FILE__RELABELFROM 0x00000080UL
+#define SOCK_FILE__RELABELTO 0x00000100UL
+#define SOCK_FILE__APPEND 0x00000200UL
+#define SOCK_FILE__UNLINK 0x00000400UL
+#define SOCK_FILE__LINK 0x00000800UL
+#define SOCK_FILE__RENAME 0x00001000UL
+#define SOCK_FILE__EXECUTE 0x00002000UL
+#define SOCK_FILE__SWAPON 0x00004000UL
+#define SOCK_FILE__QUOTAON 0x00008000UL
+#define SOCK_FILE__MOUNTON 0x00010000UL
+#define FIFO_FILE__IOCTL 0x00000001UL
+#define FIFO_FILE__READ 0x00000002UL
+#define FIFO_FILE__WRITE 0x00000004UL
+#define FIFO_FILE__CREATE 0x00000008UL
+#define FIFO_FILE__GETATTR 0x00000010UL
+#define FIFO_FILE__SETATTR 0x00000020UL
+#define FIFO_FILE__LOCK 0x00000040UL
+#define FIFO_FILE__RELABELFROM 0x00000080UL
+#define FIFO_FILE__RELABELTO 0x00000100UL
+#define FIFO_FILE__APPEND 0x00000200UL
+#define FIFO_FILE__UNLINK 0x00000400UL
+#define FIFO_FILE__LINK 0x00000800UL
+#define FIFO_FILE__RENAME 0x00001000UL
+#define FIFO_FILE__EXECUTE 0x00002000UL
+#define FIFO_FILE__SWAPON 0x00004000UL
+#define FIFO_FILE__QUOTAON 0x00008000UL
+#define FIFO_FILE__MOUNTON 0x00010000UL
+#define FIFO_FILE__OPEN 0x00020000UL
+#define FD__USE 0x00000001UL
+#define SOCKET__IOCTL 0x00000001UL
+#define SOCKET__READ 0x00000002UL
+#define SOCKET__WRITE 0x00000004UL
+#define SOCKET__CREATE 0x00000008UL
+#define SOCKET__GETATTR 0x00000010UL
+#define SOCKET__SETATTR 0x00000020UL
+#define SOCKET__LOCK 0x00000040UL
+#define SOCKET__RELABELFROM 0x00000080UL
+#define SOCKET__RELABELTO 0x00000100UL
+#define SOCKET__APPEND 0x00000200UL
+#define SOCKET__BIND 0x00000400UL
+#define SOCKET__CONNECT 0x00000800UL
+#define SOCKET__LISTEN 0x00001000UL
+#define SOCKET__ACCEPT 0x00002000UL
+#define SOCKET__GETOPT 0x00004000UL
+#define SOCKET__SETOPT 0x00008000UL
+#define SOCKET__SHUTDOWN 0x00010000UL
+#define SOCKET__RECVFROM 0x00020000UL
+#define SOCKET__SENDTO 0x00040000UL
+#define SOCKET__RECV_MSG 0x00080000UL
+#define SOCKET__SEND_MSG 0x00100000UL
+#define SOCKET__NAME_BIND 0x00200000UL
+#define TCP_SOCKET__IOCTL 0x00000001UL
+#define TCP_SOCKET__READ 0x00000002UL
+#define TCP_SOCKET__WRITE 0x00000004UL
+#define TCP_SOCKET__CREATE 0x00000008UL
+#define TCP_SOCKET__GETATTR 0x00000010UL
+#define TCP_SOCKET__SETATTR 0x00000020UL
+#define TCP_SOCKET__LOCK 0x00000040UL
+#define TCP_SOCKET__RELABELFROM 0x00000080UL
+#define TCP_SOCKET__RELABELTO 0x00000100UL
+#define TCP_SOCKET__APPEND 0x00000200UL
+#define TCP_SOCKET__BIND 0x00000400UL
+#define TCP_SOCKET__CONNECT 0x00000800UL
+#define TCP_SOCKET__LISTEN 0x00001000UL
+#define TCP_SOCKET__ACCEPT 0x00002000UL
+#define TCP_SOCKET__GETOPT 0x00004000UL
+#define TCP_SOCKET__SETOPT 0x00008000UL
+#define TCP_SOCKET__SHUTDOWN 0x00010000UL
+#define TCP_SOCKET__RECVFROM 0x00020000UL
+#define TCP_SOCKET__SENDTO 0x00040000UL
+#define TCP_SOCKET__RECV_MSG 0x00080000UL
+#define TCP_SOCKET__SEND_MSG 0x00100000UL
+#define TCP_SOCKET__NAME_BIND 0x00200000UL
+#define TCP_SOCKET__CONNECTTO 0x00400000UL
+#define TCP_SOCKET__NEWCONN 0x00800000UL
+#define TCP_SOCKET__ACCEPTFROM 0x01000000UL
+#define TCP_SOCKET__NODE_BIND 0x02000000UL
+#define TCP_SOCKET__NAME_CONNECT 0x04000000UL
+#define UDP_SOCKET__IOCTL 0x00000001UL
+#define UDP_SOCKET__READ 0x00000002UL
+#define UDP_SOCKET__WRITE 0x00000004UL
+#define UDP_SOCKET__CREATE 0x00000008UL
+#define UDP_SOCKET__GETATTR 0x00000010UL
+#define UDP_SOCKET__SETATTR 0x00000020UL
+#define UDP_SOCKET__LOCK 0x00000040UL
+#define UDP_SOCKET__RELABELFROM 0x00000080UL
+#define UDP_SOCKET__RELABELTO 0x00000100UL
+#define UDP_SOCKET__APPEND 0x00000200UL
+#define UDP_SOCKET__BIND 0x00000400UL
+#define UDP_SOCKET__CONNECT 0x00000800UL
+#define UDP_SOCKET__LISTEN 0x00001000UL
+#define UDP_SOCKET__ACCEPT 0x00002000UL
+#define UDP_SOCKET__GETOPT 0x00004000UL
+#define UDP_SOCKET__SETOPT 0x00008000UL
+#define UDP_SOCKET__SHUTDOWN 0x00010000UL
+#define UDP_SOCKET__RECVFROM 0x00020000UL
+#define UDP_SOCKET__SENDTO 0x00040000UL
+#define UDP_SOCKET__RECV_MSG 0x00080000UL
+#define UDP_SOCKET__SEND_MSG 0x00100000UL
+#define UDP_SOCKET__NAME_BIND 0x00200000UL
+#define UDP_SOCKET__NODE_BIND 0x00400000UL
+#define RAWIP_SOCKET__IOCTL 0x00000001UL
+#define RAWIP_SOCKET__READ 0x00000002UL
+#define RAWIP_SOCKET__WRITE 0x00000004UL
+#define RAWIP_SOCKET__CREATE 0x00000008UL
+#define RAWIP_SOCKET__GETATTR 0x00000010UL
+#define RAWIP_SOCKET__SETATTR 0x00000020UL
+#define RAWIP_SOCKET__LOCK 0x00000040UL
+#define RAWIP_SOCKET__RELABELFROM 0x00000080UL
+#define RAWIP_SOCKET__RELABELTO 0x00000100UL
+#define RAWIP_SOCKET__APPEND 0x00000200UL
+#define RAWIP_SOCKET__BIND 0x00000400UL
+#define RAWIP_SOCKET__CONNECT 0x00000800UL
+#define RAWIP_SOCKET__LISTEN 0x00001000UL
+#define RAWIP_SOCKET__ACCEPT 0x00002000UL
+#define RAWIP_SOCKET__GETOPT 0x00004000UL
+#define RAWIP_SOCKET__SETOPT 0x00008000UL
+#define RAWIP_SOCKET__SHUTDOWN 0x00010000UL
+#define RAWIP_SOCKET__RECVFROM 0x00020000UL
+#define RAWIP_SOCKET__SENDTO 0x00040000UL
+#define RAWIP_SOCKET__RECV_MSG 0x00080000UL
+#define RAWIP_SOCKET__SEND_MSG 0x00100000UL
+#define RAWIP_SOCKET__NAME_BIND 0x00200000UL
+#define RAWIP_SOCKET__NODE_BIND 0x00400000UL
+#define NODE__TCP_RECV 0x00000001UL
+#define NODE__TCP_SEND 0x00000002UL
+#define NODE__UDP_RECV 0x00000004UL
+#define NODE__UDP_SEND 0x00000008UL
+#define NODE__RAWIP_RECV 0x00000010UL
+#define NODE__RAWIP_SEND 0x00000020UL
+#define NODE__ENFORCE_DEST 0x00000040UL
+#define NODE__DCCP_RECV 0x00000080UL
+#define NODE__DCCP_SEND 0x00000100UL
+#define NODE__RECVFROM 0x00000200UL
+#define NODE__SENDTO 0x00000400UL
+#define NETIF__TCP_RECV 0x00000001UL
+#define NETIF__TCP_SEND 0x00000002UL
+#define NETIF__UDP_RECV 0x00000004UL
+#define NETIF__UDP_SEND 0x00000008UL
+#define NETIF__RAWIP_RECV 0x00000010UL
+#define NETIF__RAWIP_SEND 0x00000020UL
+#define NETIF__DCCP_RECV 0x00000040UL
+#define NETIF__DCCP_SEND 0x00000080UL
+#define NETIF__INGRESS 0x00000100UL
+#define NETIF__EGRESS 0x00000200UL
+#define NETLINK_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_SOCKET__READ 0x00000002UL
+#define NETLINK_SOCKET__WRITE 0x00000004UL
+#define NETLINK_SOCKET__CREATE 0x00000008UL
+#define NETLINK_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_SOCKET__LOCK 0x00000040UL
+#define NETLINK_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_SOCKET__APPEND 0x00000200UL
+#define NETLINK_SOCKET__BIND 0x00000400UL
+#define NETLINK_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_SOCKET__NAME_BIND 0x00200000UL
+#define PACKET_SOCKET__IOCTL 0x00000001UL
+#define PACKET_SOCKET__READ 0x00000002UL
+#define PACKET_SOCKET__WRITE 0x00000004UL
+#define PACKET_SOCKET__CREATE 0x00000008UL
+#define PACKET_SOCKET__GETATTR 0x00000010UL
+#define PACKET_SOCKET__SETATTR 0x00000020UL
+#define PACKET_SOCKET__LOCK 0x00000040UL
+#define PACKET_SOCKET__RELABELFROM 0x00000080UL
+#define PACKET_SOCKET__RELABELTO 0x00000100UL
+#define PACKET_SOCKET__APPEND 0x00000200UL
+#define PACKET_SOCKET__BIND 0x00000400UL
+#define PACKET_SOCKET__CONNECT 0x00000800UL
+#define PACKET_SOCKET__LISTEN 0x00001000UL
+#define PACKET_SOCKET__ACCEPT 0x00002000UL
+#define PACKET_SOCKET__GETOPT 0x00004000UL
+#define PACKET_SOCKET__SETOPT 0x00008000UL
+#define PACKET_SOCKET__SHUTDOWN 0x00010000UL
+#define PACKET_SOCKET__RECVFROM 0x00020000UL
+#define PACKET_SOCKET__SENDTO 0x00040000UL
+#define PACKET_SOCKET__RECV_MSG 0x00080000UL
+#define PACKET_SOCKET__SEND_MSG 0x00100000UL
+#define PACKET_SOCKET__NAME_BIND 0x00200000UL
+#define KEY_SOCKET__IOCTL 0x00000001UL
+#define KEY_SOCKET__READ 0x00000002UL
+#define KEY_SOCKET__WRITE 0x00000004UL
+#define KEY_SOCKET__CREATE 0x00000008UL
+#define KEY_SOCKET__GETATTR 0x00000010UL
+#define KEY_SOCKET__SETATTR 0x00000020UL
+#define KEY_SOCKET__LOCK 0x00000040UL
+#define KEY_SOCKET__RELABELFROM 0x00000080UL
+#define KEY_SOCKET__RELABELTO 0x00000100UL
+#define KEY_SOCKET__APPEND 0x00000200UL
+#define KEY_SOCKET__BIND 0x00000400UL
+#define KEY_SOCKET__CONNECT 0x00000800UL
+#define KEY_SOCKET__LISTEN 0x00001000UL
+#define KEY_SOCKET__ACCEPT 0x00002000UL
+#define KEY_SOCKET__GETOPT 0x00004000UL
+#define KEY_SOCKET__SETOPT 0x00008000UL
+#define KEY_SOCKET__SHUTDOWN 0x00010000UL
+#define KEY_SOCKET__RECVFROM 0x00020000UL
+#define KEY_SOCKET__SENDTO 0x00040000UL
+#define KEY_SOCKET__RECV_MSG 0x00080000UL
+#define KEY_SOCKET__SEND_MSG 0x00100000UL
+#define KEY_SOCKET__NAME_BIND 0x00200000UL
+#define UNIX_STREAM_SOCKET__IOCTL 0x00000001UL
+#define UNIX_STREAM_SOCKET__READ 0x00000002UL
+#define UNIX_STREAM_SOCKET__WRITE 0x00000004UL
+#define UNIX_STREAM_SOCKET__CREATE 0x00000008UL
+#define UNIX_STREAM_SOCKET__GETATTR 0x00000010UL
+#define UNIX_STREAM_SOCKET__SETATTR 0x00000020UL
+#define UNIX_STREAM_SOCKET__LOCK 0x00000040UL
+#define UNIX_STREAM_SOCKET__RELABELFROM 0x00000080UL
+#define UNIX_STREAM_SOCKET__RELABELTO 0x00000100UL
+#define UNIX_STREAM_SOCKET__APPEND 0x00000200UL
+#define UNIX_STREAM_SOCKET__BIND 0x00000400UL
+#define UNIX_STREAM_SOCKET__CONNECT 0x00000800UL
+#define UNIX_STREAM_SOCKET__LISTEN 0x00001000UL
+#define UNIX_STREAM_SOCKET__ACCEPT 0x00002000UL
+#define UNIX_STREAM_SOCKET__GETOPT 0x00004000UL
+#define UNIX_STREAM_SOCKET__SETOPT 0x00008000UL
+#define UNIX_STREAM_SOCKET__SHUTDOWN 0x00010000UL
+#define UNIX_STREAM_SOCKET__RECVFROM 0x00020000UL
+#define UNIX_STREAM_SOCKET__SENDTO 0x00040000UL
+#define UNIX_STREAM_SOCKET__RECV_MSG 0x00080000UL
+#define UNIX_STREAM_SOCKET__SEND_MSG 0x00100000UL
+#define UNIX_STREAM_SOCKET__NAME_BIND 0x00200000UL
+#define UNIX_STREAM_SOCKET__CONNECTTO 0x00400000UL
+#define UNIX_STREAM_SOCKET__NEWCONN 0x00800000UL
+#define UNIX_STREAM_SOCKET__ACCEPTFROM 0x01000000UL
+#define UNIX_DGRAM_SOCKET__IOCTL 0x00000001UL
+#define UNIX_DGRAM_SOCKET__READ 0x00000002UL
+#define UNIX_DGRAM_SOCKET__WRITE 0x00000004UL
+#define UNIX_DGRAM_SOCKET__CREATE 0x00000008UL
+#define UNIX_DGRAM_SOCKET__GETATTR 0x00000010UL
+#define UNIX_DGRAM_SOCKET__SETATTR 0x00000020UL
+#define UNIX_DGRAM_SOCKET__LOCK 0x00000040UL
+#define UNIX_DGRAM_SOCKET__RELABELFROM 0x00000080UL
+#define UNIX_DGRAM_SOCKET__RELABELTO 0x00000100UL
+#define UNIX_DGRAM_SOCKET__APPEND 0x00000200UL
+#define UNIX_DGRAM_SOCKET__BIND 0x00000400UL
+#define UNIX_DGRAM_SOCKET__CONNECT 0x00000800UL
+#define UNIX_DGRAM_SOCKET__LISTEN 0x00001000UL
+#define UNIX_DGRAM_SOCKET__ACCEPT 0x00002000UL
+#define UNIX_DGRAM_SOCKET__GETOPT 0x00004000UL
+#define UNIX_DGRAM_SOCKET__SETOPT 0x00008000UL
+#define UNIX_DGRAM_SOCKET__SHUTDOWN 0x00010000UL
+#define UNIX_DGRAM_SOCKET__RECVFROM 0x00020000UL
+#define UNIX_DGRAM_SOCKET__SENDTO 0x00040000UL
+#define UNIX_DGRAM_SOCKET__RECV_MSG 0x00080000UL
+#define UNIX_DGRAM_SOCKET__SEND_MSG 0x00100000UL
+#define UNIX_DGRAM_SOCKET__NAME_BIND 0x00200000UL
+#define PROCESS__FORK 0x00000001UL
+#define PROCESS__TRANSITION 0x00000002UL
+#define PROCESS__SIGCHLD 0x00000004UL
+#define PROCESS__SIGKILL 0x00000008UL
+#define PROCESS__SIGSTOP 0x00000010UL
+#define PROCESS__SIGNULL 0x00000020UL
+#define PROCESS__SIGNAL 0x00000040UL
+#define PROCESS__PTRACE 0x00000080UL
+#define PROCESS__GETSCHED 0x00000100UL
+#define PROCESS__SETSCHED 0x00000200UL
+#define PROCESS__GETSESSION 0x00000400UL
+#define PROCESS__GETPGID 0x00000800UL
+#define PROCESS__SETPGID 0x00001000UL
+#define PROCESS__GETCAP 0x00002000UL
+#define PROCESS__SETCAP 0x00004000UL
+#define PROCESS__SHARE 0x00008000UL
+#define PROCESS__GETATTR 0x00010000UL
+#define PROCESS__SETEXEC 0x00020000UL
+#define PROCESS__SETFSCREATE 0x00040000UL
+#define PROCESS__NOATSECURE 0x00080000UL
+#define PROCESS__SIGINH 0x00100000UL
+#define PROCESS__SETRLIMIT 0x00200000UL
+#define PROCESS__RLIMITINH 0x00400000UL
+#define PROCESS__DYNTRANSITION 0x00800000UL
+#define PROCESS__SETCURRENT 0x01000000UL
+#define PROCESS__EXECMEM 0x02000000UL
+#define PROCESS__EXECSTACK 0x04000000UL
+#define PROCESS__EXECHEAP 0x08000000UL
+#define PROCESS__SETKEYCREATE 0x10000000UL
+#define PROCESS__SETSOCKCREATE 0x20000000UL
+#define IPC__CREATE 0x00000001UL
+#define IPC__DESTROY 0x00000002UL
+#define IPC__GETATTR 0x00000004UL
+#define IPC__SETATTR 0x00000008UL
+#define IPC__READ 0x00000010UL
+#define IPC__WRITE 0x00000020UL
+#define IPC__ASSOCIATE 0x00000040UL
+#define IPC__UNIX_READ 0x00000080UL
+#define IPC__UNIX_WRITE 0x00000100UL
+#define SEM__CREATE 0x00000001UL
+#define SEM__DESTROY 0x00000002UL
+#define SEM__GETATTR 0x00000004UL
+#define SEM__SETATTR 0x00000008UL
+#define SEM__READ 0x00000010UL
+#define SEM__WRITE 0x00000020UL
+#define SEM__ASSOCIATE 0x00000040UL
+#define SEM__UNIX_READ 0x00000080UL
+#define SEM__UNIX_WRITE 0x00000100UL
+#define MSGQ__CREATE 0x00000001UL
+#define MSGQ__DESTROY 0x00000002UL
+#define MSGQ__GETATTR 0x00000004UL
+#define MSGQ__SETATTR 0x00000008UL
+#define MSGQ__READ 0x00000010UL
+#define MSGQ__WRITE 0x00000020UL
+#define MSGQ__ASSOCIATE 0x00000040UL
+#define MSGQ__UNIX_READ 0x00000080UL
+#define MSGQ__UNIX_WRITE 0x00000100UL
+#define MSGQ__ENQUEUE 0x00000200UL
+#define MSG__SEND 0x00000001UL
+#define MSG__RECEIVE 0x00000002UL
+#define SHM__CREATE 0x00000001UL
+#define SHM__DESTROY 0x00000002UL
+#define SHM__GETATTR 0x00000004UL
+#define SHM__SETATTR 0x00000008UL
+#define SHM__READ 0x00000010UL
+#define SHM__WRITE 0x00000020UL
+#define SHM__ASSOCIATE 0x00000040UL
+#define SHM__UNIX_READ 0x00000080UL
+#define SHM__UNIX_WRITE 0x00000100UL
+#define SHM__LOCK 0x00000200UL
+#define SECURITY__COMPUTE_AV 0x00000001UL
+#define SECURITY__COMPUTE_CREATE 0x00000002UL
+#define SECURITY__COMPUTE_MEMBER 0x00000004UL
+#define SECURITY__CHECK_CONTEXT 0x00000008UL
+#define SECURITY__LOAD_POLICY 0x00000010UL
+#define SECURITY__COMPUTE_RELABEL 0x00000020UL
+#define SECURITY__COMPUTE_USER 0x00000040UL
+#define SECURITY__SETENFORCE 0x00000080UL
+#define SECURITY__SETBOOL 0x00000100UL
+#define SECURITY__SETSECPARAM 0x00000200UL
+#define SECURITY__SETCHECKREQPROT 0x00000400UL
+#define SYSTEM__IPC_INFO 0x00000001UL
+#define SYSTEM__SYSLOG_READ 0x00000002UL
+#define SYSTEM__SYSLOG_MOD 0x00000004UL
+#define SYSTEM__SYSLOG_CONSOLE 0x00000008UL
+#define CAPABILITY__CHOWN 0x00000001UL
+#define CAPABILITY__DAC_OVERRIDE 0x00000002UL
+#define CAPABILITY__DAC_READ_SEARCH 0x00000004UL
+#define CAPABILITY__FOWNER 0x00000008UL
+#define CAPABILITY__FSETID 0x00000010UL
+#define CAPABILITY__KILL 0x00000020UL
+#define CAPABILITY__SETGID 0x00000040UL
+#define CAPABILITY__SETUID 0x00000080UL
+#define CAPABILITY__SETPCAP 0x00000100UL
+#define CAPABILITY__LINUX_IMMUTABLE 0x00000200UL
+#define CAPABILITY__NET_BIND_SERVICE 0x00000400UL
+#define CAPABILITY__NET_BROADCAST 0x00000800UL
+#define CAPABILITY__NET_ADMIN 0x00001000UL
+#define CAPABILITY__NET_RAW 0x00002000UL
+#define CAPABILITY__IPC_LOCK 0x00004000UL
+#define CAPABILITY__IPC_OWNER 0x00008000UL
+#define CAPABILITY__SYS_MODULE 0x00010000UL
+#define CAPABILITY__SYS_RAWIO 0x00020000UL
+#define CAPABILITY__SYS_CHROOT 0x00040000UL
+#define CAPABILITY__SYS_PTRACE 0x00080000UL
+#define CAPABILITY__SYS_PACCT 0x00100000UL
+#define CAPABILITY__SYS_ADMIN 0x00200000UL
+#define CAPABILITY__SYS_BOOT 0x00400000UL
+#define CAPABILITY__SYS_NICE 0x00800000UL
+#define CAPABILITY__SYS_RESOURCE 0x01000000UL
+#define CAPABILITY__SYS_TIME 0x02000000UL
+#define CAPABILITY__SYS_TTY_CONFIG 0x04000000UL
+#define CAPABILITY__MKNOD 0x08000000UL
+#define CAPABILITY__LEASE 0x10000000UL
+#define CAPABILITY__AUDIT_WRITE 0x20000000UL
+#define CAPABILITY__AUDIT_CONTROL 0x40000000UL
+#define CAPABILITY__SETFCAP 0x80000000UL
+#define CAPABILITY2__MAC_OVERRIDE 0x00000001UL
+#define CAPABILITY2__MAC_ADMIN 0x00000002UL
+#define NETLINK_ROUTE_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_ROUTE_SOCKET__READ 0x00000002UL
+#define NETLINK_ROUTE_SOCKET__WRITE 0x00000004UL
+#define NETLINK_ROUTE_SOCKET__CREATE 0x00000008UL
+#define NETLINK_ROUTE_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_ROUTE_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_ROUTE_SOCKET__LOCK 0x00000040UL
+#define NETLINK_ROUTE_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_ROUTE_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_ROUTE_SOCKET__APPEND 0x00000200UL
+#define NETLINK_ROUTE_SOCKET__BIND 0x00000400UL
+#define NETLINK_ROUTE_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_ROUTE_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_ROUTE_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_ROUTE_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_ROUTE_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_ROUTE_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_ROUTE_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_ROUTE_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_ROUTE_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_ROUTE_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_ROUTE_SOCKET__NAME_BIND 0x00200000UL
+#define NETLINK_ROUTE_SOCKET__NLMSG_READ 0x00400000UL
+#define NETLINK_ROUTE_SOCKET__NLMSG_WRITE 0x00800000UL
+#define NETLINK_FIREWALL_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_FIREWALL_SOCKET__READ 0x00000002UL
+#define NETLINK_FIREWALL_SOCKET__WRITE 0x00000004UL
+#define NETLINK_FIREWALL_SOCKET__CREATE 0x00000008UL
+#define NETLINK_FIREWALL_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_FIREWALL_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_FIREWALL_SOCKET__LOCK 0x00000040UL
+#define NETLINK_FIREWALL_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_FIREWALL_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_FIREWALL_SOCKET__APPEND 0x00000200UL
+#define NETLINK_FIREWALL_SOCKET__BIND 0x00000400UL
+#define NETLINK_FIREWALL_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_FIREWALL_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_FIREWALL_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_FIREWALL_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_FIREWALL_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_FIREWALL_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_FIREWALL_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_FIREWALL_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_FIREWALL_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_FIREWALL_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_FIREWALL_SOCKET__NAME_BIND 0x00200000UL
+#define NETLINK_FIREWALL_SOCKET__NLMSG_READ 0x00400000UL
+#define NETLINK_FIREWALL_SOCKET__NLMSG_WRITE 0x00800000UL
+#define NETLINK_TCPDIAG_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_TCPDIAG_SOCKET__READ 0x00000002UL
+#define NETLINK_TCPDIAG_SOCKET__WRITE 0x00000004UL
+#define NETLINK_TCPDIAG_SOCKET__CREATE 0x00000008UL
+#define NETLINK_TCPDIAG_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_TCPDIAG_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_TCPDIAG_SOCKET__LOCK 0x00000040UL
+#define NETLINK_TCPDIAG_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_TCPDIAG_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_TCPDIAG_SOCKET__APPEND 0x00000200UL
+#define NETLINK_TCPDIAG_SOCKET__BIND 0x00000400UL
+#define NETLINK_TCPDIAG_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_TCPDIAG_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_TCPDIAG_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_TCPDIAG_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_TCPDIAG_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_TCPDIAG_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_TCPDIAG_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_TCPDIAG_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_TCPDIAG_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_TCPDIAG_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_TCPDIAG_SOCKET__NAME_BIND 0x00200000UL
+#define NETLINK_TCPDIAG_SOCKET__NLMSG_READ 0x00400000UL
+#define NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE 0x00800000UL
+#define NETLINK_NFLOG_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_NFLOG_SOCKET__READ 0x00000002UL
+#define NETLINK_NFLOG_SOCKET__WRITE 0x00000004UL
+#define NETLINK_NFLOG_SOCKET__CREATE 0x00000008UL
+#define NETLINK_NFLOG_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_NFLOG_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_NFLOG_SOCKET__LOCK 0x00000040UL
+#define NETLINK_NFLOG_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_NFLOG_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_NFLOG_SOCKET__APPEND 0x00000200UL
+#define NETLINK_NFLOG_SOCKET__BIND 0x00000400UL
+#define NETLINK_NFLOG_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_NFLOG_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_NFLOG_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_NFLOG_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_NFLOG_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_NFLOG_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_NFLOG_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_NFLOG_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_NFLOG_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_NFLOG_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_NFLOG_SOCKET__NAME_BIND 0x00200000UL
+#define NETLINK_XFRM_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_XFRM_SOCKET__READ 0x00000002UL
+#define NETLINK_XFRM_SOCKET__WRITE 0x00000004UL
+#define NETLINK_XFRM_SOCKET__CREATE 0x00000008UL
+#define NETLINK_XFRM_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_XFRM_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_XFRM_SOCKET__LOCK 0x00000040UL
+#define NETLINK_XFRM_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_XFRM_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_XFRM_SOCKET__APPEND 0x00000200UL
+#define NETLINK_XFRM_SOCKET__BIND 0x00000400UL
+#define NETLINK_XFRM_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_XFRM_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_XFRM_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_XFRM_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_XFRM_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_XFRM_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_XFRM_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_XFRM_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_XFRM_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_XFRM_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_XFRM_SOCKET__NAME_BIND 0x00200000UL
+#define NETLINK_XFRM_SOCKET__NLMSG_READ 0x00400000UL
+#define NETLINK_XFRM_SOCKET__NLMSG_WRITE 0x00800000UL
+#define NETLINK_SELINUX_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_SELINUX_SOCKET__READ 0x00000002UL
+#define NETLINK_SELINUX_SOCKET__WRITE 0x00000004UL
+#define NETLINK_SELINUX_SOCKET__CREATE 0x00000008UL
+#define NETLINK_SELINUX_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_SELINUX_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_SELINUX_SOCKET__LOCK 0x00000040UL
+#define NETLINK_SELINUX_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_SELINUX_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_SELINUX_SOCKET__APPEND 0x00000200UL
+#define NETLINK_SELINUX_SOCKET__BIND 0x00000400UL
+#define NETLINK_SELINUX_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_SELINUX_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_SELINUX_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_SELINUX_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_SELINUX_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_SELINUX_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_SELINUX_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_SELINUX_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_SELINUX_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_SELINUX_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_SELINUX_SOCKET__NAME_BIND 0x00200000UL
+#define NETLINK_AUDIT_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_AUDIT_SOCKET__READ 0x00000002UL
+#define NETLINK_AUDIT_SOCKET__WRITE 0x00000004UL
+#define NETLINK_AUDIT_SOCKET__CREATE 0x00000008UL
+#define NETLINK_AUDIT_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_AUDIT_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_AUDIT_SOCKET__LOCK 0x00000040UL
+#define NETLINK_AUDIT_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_AUDIT_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_AUDIT_SOCKET__APPEND 0x00000200UL
+#define NETLINK_AUDIT_SOCKET__BIND 0x00000400UL
+#define NETLINK_AUDIT_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_AUDIT_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_AUDIT_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_AUDIT_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_AUDIT_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_AUDIT_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_AUDIT_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_AUDIT_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_AUDIT_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_AUDIT_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_AUDIT_SOCKET__NAME_BIND 0x00200000UL
+#define NETLINK_AUDIT_SOCKET__NLMSG_READ 0x00400000UL
+#define NETLINK_AUDIT_SOCKET__NLMSG_WRITE 0x00800000UL
+#define NETLINK_AUDIT_SOCKET__NLMSG_RELAY 0x01000000UL
+#define NETLINK_AUDIT_SOCKET__NLMSG_READPRIV 0x02000000UL
+#define NETLINK_IP6FW_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_IP6FW_SOCKET__READ 0x00000002UL
+#define NETLINK_IP6FW_SOCKET__WRITE 0x00000004UL
+#define NETLINK_IP6FW_SOCKET__CREATE 0x00000008UL
+#define NETLINK_IP6FW_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_IP6FW_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_IP6FW_SOCKET__LOCK 0x00000040UL
+#define NETLINK_IP6FW_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_IP6FW_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_IP6FW_SOCKET__APPEND 0x00000200UL
+#define NETLINK_IP6FW_SOCKET__BIND 0x00000400UL
+#define NETLINK_IP6FW_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_IP6FW_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_IP6FW_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_IP6FW_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_IP6FW_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_IP6FW_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_IP6FW_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_IP6FW_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_IP6FW_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_IP6FW_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_IP6FW_SOCKET__NAME_BIND 0x00200000UL
+#define NETLINK_IP6FW_SOCKET__NLMSG_READ 0x00400000UL
+#define NETLINK_IP6FW_SOCKET__NLMSG_WRITE 0x00800000UL
+#define NETLINK_DNRT_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_DNRT_SOCKET__READ 0x00000002UL
+#define NETLINK_DNRT_SOCKET__WRITE 0x00000004UL
+#define NETLINK_DNRT_SOCKET__CREATE 0x00000008UL
+#define NETLINK_DNRT_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_DNRT_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_DNRT_SOCKET__LOCK 0x00000040UL
+#define NETLINK_DNRT_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_DNRT_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_DNRT_SOCKET__APPEND 0x00000200UL
+#define NETLINK_DNRT_SOCKET__BIND 0x00000400UL
+#define NETLINK_DNRT_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_DNRT_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_DNRT_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_DNRT_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_DNRT_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_DNRT_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_DNRT_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_DNRT_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_DNRT_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_DNRT_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_DNRT_SOCKET__NAME_BIND 0x00200000UL
+#define ASSOCIATION__SENDTO 0x00000001UL
+#define ASSOCIATION__RECVFROM 0x00000002UL
+#define ASSOCIATION__SETCONTEXT 0x00000004UL
+#define ASSOCIATION__POLMATCH 0x00000008UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__IOCTL 0x00000001UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__READ 0x00000002UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__WRITE 0x00000004UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__CREATE 0x00000008UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__GETATTR 0x00000010UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__SETATTR 0x00000020UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__LOCK 0x00000040UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__RELABELFROM 0x00000080UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__RELABELTO 0x00000100UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__APPEND 0x00000200UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__BIND 0x00000400UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__CONNECT 0x00000800UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__LISTEN 0x00001000UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__ACCEPT 0x00002000UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__GETOPT 0x00004000UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__SETOPT 0x00008000UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__SHUTDOWN 0x00010000UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__RECVFROM 0x00020000UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__SENDTO 0x00040000UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__RECV_MSG 0x00080000UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__SEND_MSG 0x00100000UL
+#define NETLINK_KOBJECT_UEVENT_SOCKET__NAME_BIND 0x00200000UL
+#define APPLETALK_SOCKET__IOCTL 0x00000001UL
+#define APPLETALK_SOCKET__READ 0x00000002UL
+#define APPLETALK_SOCKET__WRITE 0x00000004UL
+#define APPLETALK_SOCKET__CREATE 0x00000008UL
+#define APPLETALK_SOCKET__GETATTR 0x00000010UL
+#define APPLETALK_SOCKET__SETATTR 0x00000020UL
+#define APPLETALK_SOCKET__LOCK 0x00000040UL
+#define APPLETALK_SOCKET__RELABELFROM 0x00000080UL
+#define APPLETALK_SOCKET__RELABELTO 0x00000100UL
+#define APPLETALK_SOCKET__APPEND 0x00000200UL
+#define APPLETALK_SOCKET__BIND 0x00000400UL
+#define APPLETALK_SOCKET__CONNECT 0x00000800UL
+#define APPLETALK_SOCKET__LISTEN 0x00001000UL
+#define APPLETALK_SOCKET__ACCEPT 0x00002000UL
+#define APPLETALK_SOCKET__GETOPT 0x00004000UL
+#define APPLETALK_SOCKET__SETOPT 0x00008000UL
+#define APPLETALK_SOCKET__SHUTDOWN 0x00010000UL
+#define APPLETALK_SOCKET__RECVFROM 0x00020000UL
+#define APPLETALK_SOCKET__SENDTO 0x00040000UL
+#define APPLETALK_SOCKET__RECV_MSG 0x00080000UL
+#define APPLETALK_SOCKET__SEND_MSG 0x00100000UL
+#define APPLETALK_SOCKET__NAME_BIND 0x00200000UL
+#define PACKET__SEND 0x00000001UL
+#define PACKET__RECV 0x00000002UL
+#define PACKET__RELABELTO 0x00000004UL
+#define PACKET__FLOW_IN 0x00000008UL
+#define PACKET__FLOW_OUT 0x00000010UL
+#define PACKET__FORWARD_IN 0x00000020UL
+#define PACKET__FORWARD_OUT 0x00000040UL
+#define KEY__VIEW 0x00000001UL
+#define KEY__READ 0x00000002UL
+#define KEY__WRITE 0x00000004UL
+#define KEY__SEARCH 0x00000008UL
+#define KEY__LINK 0x00000010UL
+#define KEY__SETATTR 0x00000020UL
+#define KEY__CREATE 0x00000040UL
+#define DCCP_SOCKET__IOCTL 0x00000001UL
+#define DCCP_SOCKET__READ 0x00000002UL
+#define DCCP_SOCKET__WRITE 0x00000004UL
+#define DCCP_SOCKET__CREATE 0x00000008UL
+#define DCCP_SOCKET__GETATTR 0x00000010UL
+#define DCCP_SOCKET__SETATTR 0x00000020UL
+#define DCCP_SOCKET__LOCK 0x00000040UL
+#define DCCP_SOCKET__RELABELFROM 0x00000080UL
+#define DCCP_SOCKET__RELABELTO 0x00000100UL
+#define DCCP_SOCKET__APPEND 0x00000200UL
+#define DCCP_SOCKET__BIND 0x00000400UL
+#define DCCP_SOCKET__CONNECT 0x00000800UL
+#define DCCP_SOCKET__LISTEN 0x00001000UL
+#define DCCP_SOCKET__ACCEPT 0x00002000UL
+#define DCCP_SOCKET__GETOPT 0x00004000UL
+#define DCCP_SOCKET__SETOPT 0x00008000UL
+#define DCCP_SOCKET__SHUTDOWN 0x00010000UL
+#define DCCP_SOCKET__RECVFROM 0x00020000UL
+#define DCCP_SOCKET__SENDTO 0x00040000UL
+#define DCCP_SOCKET__RECV_MSG 0x00080000UL
+#define DCCP_SOCKET__SEND_MSG 0x00100000UL
+#define DCCP_SOCKET__NAME_BIND 0x00200000UL
+#define DCCP_SOCKET__NODE_BIND 0x00400000UL
+#define DCCP_SOCKET__NAME_CONNECT 0x00800000UL
+#define MEMPROTECT__MMAP_ZERO 0x00000001UL
+#define PEER__RECV 0x00000001UL
diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h
new file mode 100644
index 0000000..d12ff1a
--- /dev/null
+++ b/security/selinux/include/avc.h
@@ -0,0 +1,142 @@
+/*
+ * Access vector cache interface for object managers.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SELINUX_AVC_H_
+#define _SELINUX_AVC_H_
+
+#include <linux/stddef.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/kdev_t.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/audit.h>
+#include <linux/in6.h>
+#include <linux/path.h>
+#include <asm/system.h>
+#include "flask.h"
+#include "av_permissions.h"
+#include "security.h"
+
+#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
+extern int selinux_enforcing;
+#else
+#define selinux_enforcing 1
+#endif
+
+/*
+ * An entry in the AVC.
+ */
+struct avc_entry;
+
+struct task_struct;
+struct inode;
+struct sock;
+struct sk_buff;
+
+/* Auxiliary data to use in generating the audit record. */
+struct avc_audit_data {
+ char type;
+#define AVC_AUDIT_DATA_FS 1
+#define AVC_AUDIT_DATA_NET 2
+#define AVC_AUDIT_DATA_CAP 3
+#define AVC_AUDIT_DATA_IPC 4
+ struct task_struct *tsk;
+ union {
+ struct {
+ struct path path;
+ struct inode *inode;
+ } fs;
+ struct {
+ int netif;
+ struct sock *sk;
+ u16 family;
+ __be16 dport;
+ __be16 sport;
+ union {
+ struct {
+ __be32 daddr;
+ __be32 saddr;
+ } v4;
+ struct {
+ struct in6_addr daddr;
+ struct in6_addr saddr;
+ } v6;
+ } fam;
+ } net;
+ int cap;
+ int ipc_id;
+ } u;
+};
+
+#define v4info fam.v4
+#define v6info fam.v6
+
+/* Initialize an AVC audit data structure. */
+#define AVC_AUDIT_DATA_INIT(_d,_t) \
+ { memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; }
+
+/*
+ * AVC statistics
+ */
+struct avc_cache_stats {
+ unsigned int lookups;
+ unsigned int hits;
+ unsigned int misses;
+ unsigned int allocations;
+ unsigned int reclaims;
+ unsigned int frees;
+};
+
+/*
+ * AVC operations
+ */
+
+void __init avc_init(void);
+
+void avc_audit(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct av_decision *avd, int result, struct avc_audit_data *auditdata);
+
+#define AVC_STRICT 1 /* Ignore permissive mode. */
+int avc_has_perm_noaudit(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ unsigned flags,
+ struct av_decision *avd);
+
+int avc_has_perm(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct avc_audit_data *auditdata);
+
+u32 avc_policy_seqno(void);
+
+#define AVC_CALLBACK_GRANT 1
+#define AVC_CALLBACK_TRY_REVOKE 2
+#define AVC_CALLBACK_REVOKE 4
+#define AVC_CALLBACK_RESET 8
+#define AVC_CALLBACK_AUDITALLOW_ENABLE 16
+#define AVC_CALLBACK_AUDITALLOW_DISABLE 32
+#define AVC_CALLBACK_AUDITDENY_ENABLE 64
+#define AVC_CALLBACK_AUDITDENY_DISABLE 128
+
+int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
+ u16 tclass, u32 perms,
+ u32 *out_retained),
+ u32 events, u32 ssid, u32 tsid,
+ u16 tclass, u32 perms);
+
+/* Shows permission in human readable form */
+void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av);
+
+/* Exported to selinuxfs */
+int avc_get_hash_stats(char *page);
+extern unsigned int avc_cache_threshold;
+
+#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
+DECLARE_PER_CPU(struct avc_cache_stats, avc_cache_stats);
+#endif
+
+#endif /* _SELINUX_AVC_H_ */
+
diff --git a/security/selinux/include/avc_ss.h b/security/selinux/include/avc_ss.h
new file mode 100644
index 0000000..c0d314d
--- /dev/null
+++ b/security/selinux/include/avc_ss.h
@@ -0,0 +1,35 @@
+/*
+ * Access vector cache interface for the security server.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SELINUX_AVC_SS_H_
+#define _SELINUX_AVC_SS_H_
+
+#include "flask.h"
+
+int avc_ss_reset(u32 seqno);
+
+struct av_perm_to_string {
+ u16 tclass;
+ u32 value;
+ const char *name;
+};
+
+struct av_inherit {
+ u16 tclass;
+ const char **common_pts;
+ u32 common_base;
+};
+
+struct selinux_class_perm {
+ const struct av_perm_to_string *av_perm_to_string;
+ u32 av_pts_len;
+ const char **class_to_string;
+ u32 cts_len;
+ const struct av_inherit *av_inherit;
+ u32 av_inherit_len;
+};
+
+#endif /* _SELINUX_AVC_SS_H_ */
+
diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h
new file mode 100644
index 0000000..bd813c3
--- /dev/null
+++ b/security/selinux/include/class_to_string.h
@@ -0,0 +1,74 @@
+/* This file is automatically generated. Do not edit. */
+/*
+ * Security object class definitions
+ */
+ S_(NULL)
+ S_("security")
+ S_("process")
+ S_("system")
+ S_("capability")
+ S_("filesystem")
+ S_("file")
+ S_("dir")
+ S_("fd")
+ S_("lnk_file")
+ S_("chr_file")
+ S_("blk_file")
+ S_("sock_file")
+ S_("fifo_file")
+ S_("socket")
+ S_("tcp_socket")
+ S_("udp_socket")
+ S_("rawip_socket")
+ S_("node")
+ S_("netif")
+ S_("netlink_socket")
+ S_("packet_socket")
+ S_("key_socket")
+ S_("unix_stream_socket")
+ S_("unix_dgram_socket")
+ S_("sem")
+ S_("msg")
+ S_("msgq")
+ S_("shm")
+ S_("ipc")
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_("netlink_route_socket")
+ S_("netlink_firewall_socket")
+ S_("netlink_tcpdiag_socket")
+ S_("netlink_nflog_socket")
+ S_("netlink_xfrm_socket")
+ S_("netlink_selinux_socket")
+ S_("netlink_audit_socket")
+ S_("netlink_ip6fw_socket")
+ S_("netlink_dnrt_socket")
+ S_(NULL)
+ S_(NULL)
+ S_("association")
+ S_("netlink_kobject_uevent_socket")
+ S_("appletalk_socket")
+ S_("packet")
+ S_("key")
+ S_(NULL)
+ S_("dccp_socket")
+ S_("memprotect")
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_(NULL)
+ S_("peer")
+ S_("capability2")
diff --git a/security/selinux/include/common_perm_to_string.h b/security/selinux/include/common_perm_to_string.h
new file mode 100644
index 0000000..ce5b6e2
--- /dev/null
+++ b/security/selinux/include/common_perm_to_string.h
@@ -0,0 +1,58 @@
+/* This file is automatically generated. Do not edit. */
+TB_(common_file_perm_to_string)
+ S_("ioctl")
+ S_("read")
+ S_("write")
+ S_("create")
+ S_("getattr")
+ S_("setattr")
+ S_("lock")
+ S_("relabelfrom")
+ S_("relabelto")
+ S_("append")
+ S_("unlink")
+ S_("link")
+ S_("rename")
+ S_("execute")
+ S_("swapon")
+ S_("quotaon")
+ S_("mounton")
+TE_(common_file_perm_to_string)
+
+TB_(common_socket_perm_to_string)
+ S_("ioctl")
+ S_("read")
+ S_("write")
+ S_("create")
+ S_("getattr")
+ S_("setattr")
+ S_("lock")
+ S_("relabelfrom")
+ S_("relabelto")
+ S_("append")
+ S_("bind")
+ S_("connect")
+ S_("listen")
+ S_("accept")
+ S_("getopt")
+ S_("setopt")
+ S_("shutdown")
+ S_("recvfrom")
+ S_("sendto")
+ S_("recv_msg")
+ S_("send_msg")
+ S_("name_bind")
+TE_(common_socket_perm_to_string)
+
+TB_(common_ipc_perm_to_string)
+ S_("create")
+ S_("destroy")
+ S_("getattr")
+ S_("setattr")
+ S_("read")
+ S_("write")
+ S_("associate")
+ S_("unix_read")
+ S_("unix_write")
+TE_(common_ipc_perm_to_string)
+
diff --git a/security/selinux/include/conditional.h b/security/selinux/include/conditional.h
new file mode 100644
index 0000000..67ce7a8
--- /dev/null
+++ b/security/selinux/include/conditional.h
@@ -0,0 +1,22 @@
+/*
+ * Interface to booleans in the security server. This is exported
+ * for the selinuxfs.
+ *
+ * Author: Karl MacMillan <kmacmillan@tresys.com>
+ *
+ * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#ifndef _SELINUX_CONDITIONAL_H_
+#define _SELINUX_CONDITIONAL_H_
+
+int security_get_bools(int *len, char ***names, int **values);
+
+int security_set_bools(int len, int *values);
+
+int security_get_bool_value(int bool);
+
+#endif
diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h
new file mode 100644
index 0000000..febf886
--- /dev/null
+++ b/security/selinux/include/flask.h
@@ -0,0 +1,89 @@
+/* This file is automatically generated. Do not edit. */
+#ifndef _SELINUX_FLASK_H_
+#define _SELINUX_FLASK_H_
+
+/*
+ * Security object class definitions
+ */
+#define SECCLASS_SECURITY 1
+#define SECCLASS_PROCESS 2
+#define SECCLASS_SYSTEM 3
+#define SECCLASS_CAPABILITY 4
+#define SECCLASS_FILESYSTEM 5
+#define SECCLASS_FILE 6
+#define SECCLASS_DIR 7
+#define SECCLASS_FD 8
+#define SECCLASS_LNK_FILE 9
+#define SECCLASS_CHR_FILE 10
+#define SECCLASS_BLK_FILE 11
+#define SECCLASS_SOCK_FILE 12
+#define SECCLASS_FIFO_FILE 13
+#define SECCLASS_SOCKET 14
+#define SECCLASS_TCP_SOCKET 15
+#define SECCLASS_UDP_SOCKET 16
+#define SECCLASS_RAWIP_SOCKET 17
+#define SECCLASS_NODE 18
+#define SECCLASS_NETIF 19
+#define SECCLASS_NETLINK_SOCKET 20
+#define SECCLASS_PACKET_SOCKET 21
+#define SECCLASS_KEY_SOCKET 22
+#define SECCLASS_UNIX_STREAM_SOCKET 23
+#define SECCLASS_UNIX_DGRAM_SOCKET 24
+#define SECCLASS_SEM 25
+#define SECCLASS_MSG 26
+#define SECCLASS_MSGQ 27
+#define SECCLASS_SHM 28
+#define SECCLASS_IPC 29
+#define SECCLASS_NETLINK_ROUTE_SOCKET 43
+#define SECCLASS_NETLINK_FIREWALL_SOCKET 44
+#define SECCLASS_NETLINK_TCPDIAG_SOCKET 45
+#define SECCLASS_NETLINK_NFLOG_SOCKET 46
+#define SECCLASS_NETLINK_XFRM_SOCKET 47
+#define SECCLASS_NETLINK_SELINUX_SOCKET 48
+#define SECCLASS_NETLINK_AUDIT_SOCKET 49
+#define SECCLASS_NETLINK_IP6FW_SOCKET 50
+#define SECCLASS_NETLINK_DNRT_SOCKET 51
+#define SECCLASS_ASSOCIATION 54
+#define SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET 55
+#define SECCLASS_APPLETALK_SOCKET 56
+#define SECCLASS_PACKET 57
+#define SECCLASS_KEY 58
+#define SECCLASS_DCCP_SOCKET 60
+#define SECCLASS_MEMPROTECT 61
+#define SECCLASS_PEER 68
+#define SECCLASS_CAPABILITY2 69
+
+/*
+ * Security identifier indices for initial entities
+ */
+#define SECINITSID_KERNEL 1
+#define SECINITSID_SECURITY 2
+#define SECINITSID_UNLABELED 3
+#define SECINITSID_FS 4
+#define SECINITSID_FILE 5
+#define SECINITSID_FILE_LABELS 6
+#define SECINITSID_INIT 7
+#define SECINITSID_ANY_SOCKET 8
+#define SECINITSID_PORT 9
+#define SECINITSID_NETIF 10
+#define SECINITSID_NETMSG 11
+#define SECINITSID_NODE 12
+#define SECINITSID_IGMP_PACKET 13
+#define SECINITSID_ICMP_SOCKET 14
+#define SECINITSID_TCP_SOCKET 15
+#define SECINITSID_SYSCTL_MODPROBE 16
+#define SECINITSID_SYSCTL 17
+#define SECINITSID_SYSCTL_FS 18
+#define SECINITSID_SYSCTL_KERNEL 19
+#define SECINITSID_SYSCTL_NET 20
+#define SECINITSID_SYSCTL_NET_UNIX 21
+#define SECINITSID_SYSCTL_VM 22
+#define SECINITSID_SYSCTL_DEV 23
+#define SECINITSID_KMOD 24
+#define SECINITSID_POLICY 25
+#define SECINITSID_SCMP_PACKET 26
+#define SECINITSID_DEVNULL 27
+
+#define SECINITSID_NUM 27
+
+#endif
diff --git a/security/selinux/include/initial_sid_to_string.h b/security/selinux/include/initial_sid_to_string.h
new file mode 100644
index 0000000..d4fac82
--- /dev/null
+++ b/security/selinux/include/initial_sid_to_string.h
@@ -0,0 +1,33 @@
+/* This file is automatically generated. Do not edit. */
+static char *initial_sid_to_string[] =
+{
+ "null",
+ "kernel",
+ "security",
+ "unlabeled",
+ "fs",
+ "file",
+ "file_labels",
+ "init",
+ "any_socket",
+ "port",
+ "netif",
+ "netmsg",
+ "node",
+ "igmp_packet",
+ "icmp_socket",
+ "tcp_socket",
+ "sysctl_modprobe",
+ "sysctl",
+ "sysctl_fs",
+ "sysctl_kernel",
+ "sysctl_net",
+ "sysctl_net_unix",
+ "sysctl_vm",
+ "sysctl_dev",
+ "kmod",
+ "policy",
+ "scmp_packet",
+ "devnull",
+};
+
diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h
new file mode 100644
index 0000000..ce23edd
--- /dev/null
+++ b/security/selinux/include/netif.h
@@ -0,0 +1,23 @@
+/*
+ * Network interface table.
+ *
+ * Network interfaces (devices) do not have a security field, so we
+ * maintain a table associating each interface with a SID.
+ *
+ * Author: James Morris <jmorris@redhat.com>
+ *
+ * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
+ * Paul Moore, <paul.moore@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+#ifndef _SELINUX_NETIF_H_
+#define _SELINUX_NETIF_H_
+
+int sel_netif_sid(int ifindex, u32 *sid);
+
+#endif /* _SELINUX_NETIF_H_ */
+
diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h
new file mode 100644
index 0000000..b913c8d
--- /dev/null
+++ b/security/selinux/include/netlabel.h
@@ -0,0 +1,150 @@
+/*
+ * SELinux interface to the NetLabel subsystem
+ *
+ * Author : Paul Moore <paul.moore@hp.com>
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _SELINUX_NETLABEL_H_
+#define _SELINUX_NETLABEL_H_
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+
+#include "avc.h"
+#include "objsec.h"
+
+#ifdef CONFIG_NETLABEL
+void selinux_netlbl_cache_invalidate(void);
+
+void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway);
+
+void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec);
+void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
+ int family);
+
+int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
+ u16 family,
+ u32 *type,
+ u32 *sid);
+int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
+ u16 family,
+ u32 sid);
+
+void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family);
+int selinux_netlbl_socket_post_create(struct socket *sock);
+int selinux_netlbl_inode_permission(struct inode *inode, int mask);
+int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
+ struct sk_buff *skb,
+ u16 family,
+ struct avc_audit_data *ad);
+int selinux_netlbl_socket_setsockopt(struct socket *sock,
+ int level,
+ int optname);
+int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr);
+
+#else
+static inline void selinux_netlbl_cache_invalidate(void)
+{
+ return;
+}
+
+static inline void selinux_netlbl_err(struct sk_buff *skb,
+ int error,
+ int gateway)
+{
+ return;
+}
+
+static inline void selinux_netlbl_sk_security_free(
+ struct sk_security_struct *ssec)
+{
+ return;
+}
+
+static inline void selinux_netlbl_sk_security_reset(
+ struct sk_security_struct *ssec,
+ int family)
+{
+ return;
+}
+
+static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
+ u16 family,
+ u32 *type,
+ u32 *sid)
+{
+ *type = NETLBL_NLTYPE_NONE;
+ *sid = SECSID_NULL;
+ return 0;
+}
+static inline int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
+ u16 family,
+ u32 sid)
+{
+ return 0;
+}
+
+static inline int selinux_netlbl_conn_setsid(struct sock *sk,
+ struct sockaddr *addr)
+{
+ return 0;
+}
+
+static inline void selinux_netlbl_inet_conn_established(struct sock *sk,
+ u16 family)
+{
+ return;
+}
+static inline int selinux_netlbl_socket_post_create(struct socket *sock)
+{
+ return 0;
+}
+static inline int selinux_netlbl_inode_permission(struct inode *inode,
+ int mask)
+{
+ return 0;
+}
+static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
+ struct sk_buff *skb,
+ u16 family,
+ struct avc_audit_data *ad)
+{
+ return 0;
+}
+static inline int selinux_netlbl_socket_setsockopt(struct socket *sock,
+ int level,
+ int optname)
+{
+ return 0;
+}
+static inline int selinux_netlbl_socket_connect(struct sock *sk,
+ struct sockaddr *addr)
+{
+ return 0;
+}
+#endif /* CONFIG_NETLABEL */
+
+#endif
diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h
new file mode 100644
index 0000000..1b94450
--- /dev/null
+++ b/security/selinux/include/netnode.h
@@ -0,0 +1,32 @@
+/*
+ * Network node table
+ *
+ * SELinux must keep a mapping of network nodes to labels/SIDs. This
+ * mapping is maintained as part of the normal policy but a fast cache is
+ * needed to reduce the lookup overhead since most of these queries happen on
+ * a per-packet basis.
+ *
+ * Author: Paul Moore <paul.moore@hp.com>
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _SELINUX_NETNODE_H
+#define _SELINUX_NETNODE_H
+
+int sel_netnode_sid(void *addr, u16 family, u32 *sid);
+
+#endif
diff --git a/security/selinux/include/netport.h b/security/selinux/include/netport.h
new file mode 100644
index 0000000..8991752
--- /dev/null
+++ b/security/selinux/include/netport.h
@@ -0,0 +1,31 @@
+/*
+ * Network port table
+ *
+ * SELinux must keep a mapping of network ports to labels/SIDs. This
+ * mapping is maintained as part of the normal policy but a fast cache is
+ * needed to reduce the lookup overhead.
+ *
+ * Author: Paul Moore <paul.moore@hp.com>
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2008
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _SELINUX_NETPORT_H
+#define _SELINUX_NETPORT_H
+
+int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid);
+
+#endif
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
new file mode 100644
index 0000000..f8be8d7
--- /dev/null
+++ b/security/selinux/include/objsec.h
@@ -0,0 +1,133 @@
+/*
+ * NSA Security-Enhanced Linux (SELinux) security module
+ *
+ * This file contains the SELinux security data structures for kernel objects.
+ *
+ * Author(s): Stephen Smalley, <sds@epoch.ncsc.mil>
+ * Chris Vance, <cvance@nai.com>
+ * Wayne Salamon, <wsalamon@nai.com>
+ * James Morris <jmorris@redhat.com>
+ *
+ * Copyright (C) 2001,2002 Networks Associates Technology, Inc.
+ * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+#ifndef _SELINUX_OBJSEC_H_
+#define _SELINUX_OBJSEC_H_
+
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/binfmts.h>
+#include <linux/in.h>
+#include <linux/spinlock.h>
+#include "flask.h"
+#include "avc.h"
+
+struct task_security_struct {
+ u32 osid; /* SID prior to last execve */
+ u32 sid; /* current SID */
+ u32 exec_sid; /* exec SID */
+ u32 create_sid; /* fscreate SID */
+ u32 keycreate_sid; /* keycreate SID */
+ u32 sockcreate_sid; /* fscreate SID */
+};
+
+struct inode_security_struct {
+ struct inode *inode; /* back pointer to inode object */
+ struct list_head list; /* list of inode_security_struct */
+ u32 task_sid; /* SID of creating task */
+ u32 sid; /* SID of this object */
+ u16 sclass; /* security class of this object */
+ unsigned char initialized; /* initialization flag */
+ struct mutex lock;
+};
+
+struct file_security_struct {
+ u32 sid; /* SID of open file description */
+ u32 fown_sid; /* SID of file owner (for SIGIO) */
+ u32 isid; /* SID of inode at the time of file open */
+ u32 pseqno; /* Policy seqno at the time of file open */
+};
+
+struct superblock_security_struct {
+ struct super_block *sb; /* back pointer to sb object */
+ struct list_head list; /* list of superblock_security_struct */
+ u32 sid; /* SID of file system superblock */
+ u32 def_sid; /* default SID for labeling */
+ u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
+ unsigned int behavior; /* labeling behavior */
+ unsigned char initialized; /* initialization flag */
+ unsigned char flags; /* which mount options were specified */
+ unsigned char proc; /* proc fs */
+ struct mutex lock;
+ struct list_head isec_head;
+ spinlock_t isec_lock;
+};
+
+struct msg_security_struct {
+ u32 sid; /* SID of message */
+};
+
+struct ipc_security_struct {
+ u16 sclass; /* security class of this object */
+ u32 sid; /* SID of IPC resource */
+};
+
+struct bprm_security_struct {
+ u32 sid; /* SID for transformed process */
+ unsigned char set;
+
+ /*
+ * unsafe is used to share failure information from bprm_apply_creds()
+ * to bprm_post_apply_creds().
+ */
+ char unsafe;
+};
+
+struct netif_security_struct {
+ int ifindex; /* device index */
+ u32 sid; /* SID for this interface */
+};
+
+struct netnode_security_struct {
+ union {
+ __be32 ipv4; /* IPv4 node address */
+ struct in6_addr ipv6; /* IPv6 node address */
+ } addr;
+ u32 sid; /* SID for this node */
+ u16 family; /* address family */
+};
+
+struct netport_security_struct {
+ u32 sid; /* SID for this node */
+ u16 port; /* port number */
+ u8 protocol; /* transport protocol */
+};
+
+struct sk_security_struct {
+#ifdef CONFIG_NETLABEL
+ enum { /* NetLabel state */
+ NLBL_UNSET = 0,
+ NLBL_REQUIRE,
+ NLBL_LABELED,
+ NLBL_REQSKB,
+ NLBL_CONNLABELED,
+ } nlbl_state;
+ struct netlbl_lsm_secattr *nlbl_secattr; /* NetLabel sec attributes */
+#endif
+ u32 sid; /* SID of this object */
+ u32 peer_sid; /* SID of peer */
+ u16 sclass; /* sock security class */
+};
+
+struct key_security_struct {
+ u32 sid; /* SID of key */
+};
+
+extern unsigned int selinux_checkreqprot;
+
+#endif /* _SELINUX_OBJSEC_H_ */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
new file mode 100644
index 0000000..7244737
--- /dev/null
+++ b/security/selinux/include/security.h
@@ -0,0 +1,181 @@
+/*
+ * Security server interface.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ *
+ */
+
+#ifndef _SELINUX_SECURITY_H_
+#define _SELINUX_SECURITY_H_
+
+#include "flask.h"
+
+#define SECSID_NULL 0x00000000 /* unspecified SID */
+#define SECSID_WILD 0xffffffff /* wildcard SID */
+#define SECCLASS_NULL 0x0000 /* no class */
+
+#define SELINUX_MAGIC 0xf97cff8c
+
+/* Identify specific policy version changes */
+#define POLICYDB_VERSION_BASE 15
+#define POLICYDB_VERSION_BOOL 16
+#define POLICYDB_VERSION_IPV6 17
+#define POLICYDB_VERSION_NLCLASS 18
+#define POLICYDB_VERSION_VALIDATETRANS 19
+#define POLICYDB_VERSION_MLS 19
+#define POLICYDB_VERSION_AVTAB 20
+#define POLICYDB_VERSION_RANGETRANS 21
+#define POLICYDB_VERSION_POLCAP 22
+#define POLICYDB_VERSION_PERMISSIVE 23
+#define POLICYDB_VERSION_BOUNDARY 24
+
+/* Range of policy versions we understand*/
+#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
+#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
+#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
+#else
+#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY
+#endif
+
+#define CONTEXT_MNT 0x01
+#define FSCONTEXT_MNT 0x02
+#define ROOTCONTEXT_MNT 0x04
+#define DEFCONTEXT_MNT 0x08
+
+#define CONTEXT_STR "context="
+#define FSCONTEXT_STR "fscontext="
+#define ROOTCONTEXT_STR "rootcontext="
+#define DEFCONTEXT_STR "defcontext="
+
+struct netlbl_lsm_secattr;
+
+extern int selinux_enabled;
+extern int selinux_mls_enabled;
+
+/* Policy capabilities */
+enum {
+ POLICYDB_CAPABILITY_NETPEER,
+ POLICYDB_CAPABILITY_OPENPERM,
+ __POLICYDB_CAPABILITY_MAX
+};
+#define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
+
+extern int selinux_policycap_netpeer;
+extern int selinux_policycap_openperm;
+
+/*
+ * type_datum properties
+ * available at the kernel policy version >= POLICYDB_VERSION_BOUNDARY
+ */
+#define TYPEDATUM_PROPERTY_PRIMARY 0x0001
+#define TYPEDATUM_PROPERTY_ATTRIBUTE 0x0002
+
+/* limitation of boundary depth */
+#define POLICYDB_BOUNDS_MAXDEPTH 4
+
+int security_load_policy(void *data, size_t len);
+
+int security_policycap_supported(unsigned int req_cap);
+
+#define SEL_VEC_MAX 32
+struct av_decision {
+ u32 allowed;
+ u32 decided;
+ u32 auditallow;
+ u32 auditdeny;
+ u32 seqno;
+};
+
+int security_permissive_sid(u32 sid);
+
+int security_compute_av(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct av_decision *avd);
+
+int security_transition_sid(u32 ssid, u32 tsid,
+ u16 tclass, u32 *out_sid);
+
+int security_member_sid(u32 ssid, u32 tsid,
+ u16 tclass, u32 *out_sid);
+
+int security_change_sid(u32 ssid, u32 tsid,
+ u16 tclass, u32 *out_sid);
+
+int security_sid_to_context(u32 sid, char **scontext,
+ u32 *scontext_len);
+
+int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len);
+
+int security_context_to_sid(const char *scontext, u32 scontext_len,
+ u32 *out_sid);
+
+int security_context_to_sid_default(const char *scontext, u32 scontext_len,
+ u32 *out_sid, u32 def_sid, gfp_t gfp_flags);
+
+int security_context_to_sid_force(const char *scontext, u32 scontext_len,
+ u32 *sid);
+
+int security_get_user_sids(u32 callsid, char *username,
+ u32 **sids, u32 *nel);
+
+int security_port_sid(u8 protocol, u16 port, u32 *out_sid);
+
+int security_netif_sid(char *name, u32 *if_sid);
+
+int security_node_sid(u16 domain, void *addr, u32 addrlen,
+ u32 *out_sid);
+
+int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
+ u16 tclass);
+
+int security_bounded_transition(u32 oldsid, u32 newsid);
+
+int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid);
+
+int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
+ u32 xfrm_sid,
+ u32 *peer_sid);
+
+int security_get_classes(char ***classes, int *nclasses);
+int security_get_permissions(char *class, char ***perms, int *nperms);
+int security_get_reject_unknown(void);
+int security_get_allow_unknown(void);
+
+#define SECURITY_FS_USE_XATTR 1 /* use xattr */
+#define SECURITY_FS_USE_TRANS 2 /* use transition SIDs, e.g. devpts/tmpfs */
+#define SECURITY_FS_USE_TASK 3 /* use task SIDs, e.g. pipefs/sockfs */
+#define SECURITY_FS_USE_GENFS 4 /* use the genfs support */
+#define SECURITY_FS_USE_NONE 5 /* no labeling support */
+#define SECURITY_FS_USE_MNTPOINT 6 /* use mountpoint labeling */
+
+int security_fs_use(const char *fstype, unsigned int *behavior,
+ u32 *sid);
+
+int security_genfs_sid(const char *fstype, char *name, u16 sclass,
+ u32 *sid);
+
+#ifdef CONFIG_NETLABEL
+int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
+ u32 *sid);
+
+int security_netlbl_sid_to_secattr(u32 sid,
+ struct netlbl_lsm_secattr *secattr);
+#else
+static inline int security_netlbl_secattr_to_sid(
+ struct netlbl_lsm_secattr *secattr,
+ u32 *sid)
+{
+ return -EIDRM;
+}
+
+static inline int security_netlbl_sid_to_secattr(u32 sid,
+ struct netlbl_lsm_secattr *secattr)
+{
+ return -ENOENT;
+}
+#endif /* CONFIG_NETLABEL */
+
+const char *security_get_initial_sid_context(u32 sid);
+
+#endif /* _SELINUX_SECURITY_H_ */
+
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
new file mode 100644
index 0000000..289e24b
--- /dev/null
+++ b/security/selinux/include/xfrm.h
@@ -0,0 +1,88 @@
+/*
+ * SELinux support for the XFRM LSM hooks
+ *
+ * Author : Trent Jaeger, <jaegert@us.ibm.com>
+ * Updated : Venkat Yekkirala, <vyekkirala@TrustedCS.com>
+ */
+#ifndef _SELINUX_XFRM_H_
+#define _SELINUX_XFRM_H_
+
+int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
+ struct xfrm_user_sec_ctx *sec_ctx);
+int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
+ struct xfrm_sec_ctx **new_ctxp);
+void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx);
+int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx);
+int selinux_xfrm_state_alloc(struct xfrm_state *x,
+ struct xfrm_user_sec_ctx *sec_ctx, u32 secid);
+void selinux_xfrm_state_free(struct xfrm_state *x);
+int selinux_xfrm_state_delete(struct xfrm_state *x);
+int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir);
+int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
+ struct xfrm_policy *xp, struct flowi *fl);
+
+/*
+ * Extract the security blob from the sock (it's actually on the socket)
+ */
+static inline struct inode_security_struct *get_sock_isec(struct sock *sk)
+{
+ if (!sk->sk_socket)
+ return NULL;
+
+ return SOCK_INODE(sk->sk_socket)->i_security;
+}
+
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+extern atomic_t selinux_xfrm_refcount;
+
+static inline int selinux_xfrm_enabled(void)
+{
+ return (atomic_read(&selinux_xfrm_refcount) > 0);
+}
+
+int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
+ struct avc_audit_data *ad);
+int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
+ struct avc_audit_data *ad, u8 proto);
+int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
+
+static inline void selinux_xfrm_notify_policyload(void)
+{
+ atomic_inc(&flow_cache_genid);
+}
+#else
+static inline int selinux_xfrm_enabled(void)
+{
+ return 0;
+}
+
+static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
+ struct avc_audit_data *ad)
+{
+ return 0;
+}
+
+static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
+ struct avc_audit_data *ad, u8 proto)
+{
+ return 0;
+}
+
+static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
+{
+ *sid = SECSID_NULL;
+ return 0;
+}
+
+static inline void selinux_xfrm_notify_policyload(void)
+{
+}
+#endif
+
+static inline void selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid)
+{
+ int err = selinux_xfrm_decode_session(skb, sid, 0);
+ BUG_ON(err);
+}
+
+#endif /* _SELINUX_XFRM_H_ */
diff --git a/security/selinux/netif.c b/security/selinux/netif.c
new file mode 100644
index 0000000..b4e14bc
--- /dev/null
+++ b/security/selinux/netif.c
@@ -0,0 +1,319 @@
+/*
+ * Network interface table.
+ *
+ * Network interfaces (devices) do not have a security field, so we
+ * maintain a table associating each interface with a SID.
+ *
+ * Author: James Morris <jmorris@redhat.com>
+ *
+ * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
+ * Paul Moore <paul.moore@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/rcupdate.h>
+#include <net/net_namespace.h>
+
+#include "security.h"
+#include "objsec.h"
+#include "netif.h"
+
+#define SEL_NETIF_HASH_SIZE 64
+#define SEL_NETIF_HASH_MAX 1024
+
+struct sel_netif {
+ struct list_head list;
+ struct netif_security_struct nsec;
+ struct rcu_head rcu_head;
+};
+
+static u32 sel_netif_total;
+static LIST_HEAD(sel_netif_list);
+static DEFINE_SPINLOCK(sel_netif_lock);
+static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE];
+
+/**
+ * sel_netif_hashfn - Hashing function for the interface table
+ * @ifindex: the network interface
+ *
+ * Description:
+ * This is the hashing function for the network interface table, it returns the
+ * bucket number for the given interface.
+ *
+ */
+static inline u32 sel_netif_hashfn(int ifindex)
+{
+ return (ifindex & (SEL_NETIF_HASH_SIZE - 1));
+}
+
+/**
+ * sel_netif_find - Search for an interface record
+ * @ifindex: the network interface
+ *
+ * Description:
+ * Search the network interface table and return the record matching @ifindex.
+ * If an entry can not be found in the table return NULL.
+ *
+ */
+static inline struct sel_netif *sel_netif_find(int ifindex)
+{
+ int idx = sel_netif_hashfn(ifindex);
+ struct sel_netif *netif;
+
+ list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list)
+ /* all of the devices should normally fit in the hash, so we
+ * optimize for that case */
+ if (likely(netif->nsec.ifindex == ifindex))
+ return netif;
+
+ return NULL;
+}
+
+/**
+ * sel_netif_insert - Insert a new interface into the table
+ * @netif: the new interface record
+ *
+ * Description:
+ * Add a new interface record to the network interface hash table. Returns
+ * zero on success, negative values on failure.
+ *
+ */
+static int sel_netif_insert(struct sel_netif *netif)
+{
+ int idx;
+
+ if (sel_netif_total >= SEL_NETIF_HASH_MAX)
+ return -ENOSPC;
+
+ idx = sel_netif_hashfn(netif->nsec.ifindex);
+ list_add_rcu(&netif->list, &sel_netif_hash[idx]);
+ sel_netif_total++;
+
+ return 0;
+}
+
+/**
+ * sel_netif_free - Frees an interface entry
+ * @p: the entry's RCU field
+ *
+ * Description:
+ * This function is designed to be used as a callback to the call_rcu()
+ * function so that memory allocated to a hash table interface entry can be
+ * released safely.
+ *
+ */
+static void sel_netif_free(struct rcu_head *p)
+{
+ struct sel_netif *netif = container_of(p, struct sel_netif, rcu_head);
+ kfree(netif);
+}
+
+/**
+ * sel_netif_destroy - Remove an interface record from the table
+ * @netif: the existing interface record
+ *
+ * Description:
+ * Remove an existing interface record from the network interface table.
+ *
+ */
+static void sel_netif_destroy(struct sel_netif *netif)
+{
+ list_del_rcu(&netif->list);
+ sel_netif_total--;
+ call_rcu(&netif->rcu_head, sel_netif_free);
+}
+
+/**
+ * sel_netif_sid_slow - Lookup the SID of a network interface using the policy
+ * @ifindex: the network interface
+ * @sid: interface SID
+ *
+ * Description:
+ * This function determines the SID of a network interface by quering the
+ * security policy. The result is added to the network interface table to
+ * speedup future queries. Returns zero on success, negative values on
+ * failure.
+ *
+ */
+static int sel_netif_sid_slow(int ifindex, u32 *sid)
+{
+ int ret;
+ struct sel_netif *netif;
+ struct sel_netif *new = NULL;
+ struct net_device *dev;
+
+ /* NOTE: we always use init's network namespace since we don't
+ * currently support containers */
+
+ dev = dev_get_by_index(&init_net, ifindex);
+ if (unlikely(dev == NULL)) {
+ printk(KERN_WARNING
+ "SELinux: failure in sel_netif_sid_slow(),"
+ " invalid network interface (%d)\n", ifindex);
+ return -ENOENT;
+ }
+
+ spin_lock_bh(&sel_netif_lock);
+ netif = sel_netif_find(ifindex);
+ if (netif != NULL) {
+ *sid = netif->nsec.sid;
+ ret = 0;
+ goto out;
+ }
+ new = kzalloc(sizeof(*new), GFP_ATOMIC);
+ if (new == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = security_netif_sid(dev->name, &new->nsec.sid);
+ if (ret != 0)
+ goto out;
+ new->nsec.ifindex = ifindex;
+ ret = sel_netif_insert(new);
+ if (ret != 0)
+ goto out;
+ *sid = new->nsec.sid;
+
+out:
+ spin_unlock_bh(&sel_netif_lock);
+ dev_put(dev);
+ if (unlikely(ret)) {
+ printk(KERN_WARNING
+ "SELinux: failure in sel_netif_sid_slow(),"
+ " unable to determine network interface label (%d)\n",
+ ifindex);
+ kfree(new);
+ }
+ return ret;
+}
+
+/**
+ * sel_netif_sid - Lookup the SID of a network interface
+ * @ifindex: the network interface
+ * @sid: interface SID
+ *
+ * Description:
+ * This function determines the SID of a network interface using the fastest
+ * method possible. First the interface table is queried, but if an entry
+ * can't be found then the policy is queried and the result is added to the
+ * table to speedup future queries. Returns zero on success, negative values
+ * on failure.
+ *
+ */
+int sel_netif_sid(int ifindex, u32 *sid)
+{
+ struct sel_netif *netif;
+
+ rcu_read_lock();
+ netif = sel_netif_find(ifindex);
+ if (likely(netif != NULL)) {
+ *sid = netif->nsec.sid;
+ rcu_read_unlock();
+ return 0;
+ }
+ rcu_read_unlock();
+
+ return sel_netif_sid_slow(ifindex, sid);
+}
+
+/**
+ * sel_netif_kill - Remove an entry from the network interface table
+ * @ifindex: the network interface
+ *
+ * Description:
+ * This function removes the entry matching @ifindex from the network interface
+ * table if it exists.
+ *
+ */
+static void sel_netif_kill(int ifindex)
+{
+ struct sel_netif *netif;
+
+ rcu_read_lock();
+ spin_lock_bh(&sel_netif_lock);
+ netif = sel_netif_find(ifindex);
+ if (netif)
+ sel_netif_destroy(netif);
+ spin_unlock_bh(&sel_netif_lock);
+ rcu_read_unlock();
+}
+
+/**
+ * sel_netif_flush - Flush the entire network interface table
+ *
+ * Description:
+ * Remove all entries from the network interface table.
+ *
+ */
+static void sel_netif_flush(void)
+{
+ int idx;
+ struct sel_netif *netif;
+
+ spin_lock_bh(&sel_netif_lock);
+ for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++)
+ list_for_each_entry(netif, &sel_netif_hash[idx], list)
+ sel_netif_destroy(netif);
+ spin_unlock_bh(&sel_netif_lock);
+}
+
+static int sel_netif_avc_callback(u32 event, u32 ssid, u32 tsid,
+ u16 class, u32 perms, u32 *retained)
+{
+ if (event == AVC_CALLBACK_RESET) {
+ sel_netif_flush();
+ synchronize_net();
+ }
+ return 0;
+}
+
+static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = ptr;
+
+ if (dev_net(dev) != &init_net)
+ return NOTIFY_DONE;
+
+ if (event == NETDEV_DOWN)
+ sel_netif_kill(dev->ifindex);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block sel_netif_netdev_notifier = {
+ .notifier_call = sel_netif_netdev_notifier_handler,
+};
+
+static __init int sel_netif_init(void)
+{
+ int i, err;
+
+ if (!selinux_enabled)
+ return 0;
+
+ for (i = 0; i < SEL_NETIF_HASH_SIZE; i++)
+ INIT_LIST_HEAD(&sel_netif_hash[i]);
+
+ register_netdevice_notifier(&sel_netif_netdev_notifier);
+
+ err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET,
+ SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+ if (err)
+ panic("avc_add_callback() failed, error %d\n", err);
+
+ return err;
+}
+
+__initcall(sel_netif_init);
+
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
new file mode 100644
index 0000000..350794a
--- /dev/null
+++ b/security/selinux/netlabel.c
@@ -0,0 +1,549 @@
+/*
+ * SELinux NetLabel Support
+ *
+ * This file provides the necessary glue to tie NetLabel into the SELinux
+ * subsystem.
+ *
+ * Author: Paul Moore <paul.moore@hp.com>
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/sock.h>
+#include <net/netlabel.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+
+#include "objsec.h"
+#include "security.h"
+#include "netlabel.h"
+
+/**
+ * selinux_netlbl_sidlookup_cached - Cache a SID lookup
+ * @skb: the packet
+ * @secattr: the NetLabel security attributes
+ * @sid: the SID
+ *
+ * Description:
+ * Query the SELinux security server to lookup the correct SID for the given
+ * security attributes. If the query is successful, cache the result to speed
+ * up future lookups. Returns zero on success, negative values on failure.
+ *
+ */
+static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
+ struct netlbl_lsm_secattr *secattr,
+ u32 *sid)
+{
+ int rc;
+
+ rc = security_netlbl_secattr_to_sid(secattr, sid);
+ if (rc == 0 &&
+ (secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
+ (secattr->flags & NETLBL_SECATTR_CACHE))
+ netlbl_cache_add(skb, secattr);
+
+ return rc;
+}
+
+/**
+ * selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr
+ * @sk: the socket
+ *
+ * Description:
+ * Generate the NetLabel security attributes for a socket, making full use of
+ * the socket's attribute cache. Returns a pointer to the security attributes
+ * on success, NULL on failure.
+ *
+ */
+static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk)
+{
+ int rc;
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct netlbl_lsm_secattr *secattr;
+
+ if (sksec->nlbl_secattr != NULL)
+ return sksec->nlbl_secattr;
+
+ secattr = netlbl_secattr_alloc(GFP_ATOMIC);
+ if (secattr == NULL)
+ return NULL;
+ rc = security_netlbl_sid_to_secattr(sksec->sid, secattr);
+ if (rc != 0) {
+ netlbl_secattr_free(secattr);
+ return NULL;
+ }
+ sksec->nlbl_secattr = secattr;
+
+ return secattr;
+}
+
+/**
+ * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism
+ * @sk: the socket to label
+ *
+ * Description:
+ * Attempt to label a socket using the NetLabel mechanism. Returns zero values
+ * on success, negative values on failure.
+ *
+ */
+static int selinux_netlbl_sock_setsid(struct sock *sk)
+{
+ int rc;
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct netlbl_lsm_secattr *secattr;
+
+ if (sksec->nlbl_state != NLBL_REQUIRE)
+ return 0;
+
+ secattr = selinux_netlbl_sock_genattr(sk);
+ if (secattr == NULL)
+ return -ENOMEM;
+ rc = netlbl_sock_setattr(sk, secattr);
+ switch (rc) {
+ case 0:
+ sksec->nlbl_state = NLBL_LABELED;
+ break;
+ case -EDESTADDRREQ:
+ sksec->nlbl_state = NLBL_REQSKB;
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache
+ *
+ * Description:
+ * Invalidate the NetLabel security attribute mapping cache.
+ *
+ */
+void selinux_netlbl_cache_invalidate(void)
+{
+ netlbl_cache_invalidate();
+}
+
+/**
+ * selinux_netlbl_err - Handle a NetLabel packet error
+ * @skb: the packet
+ * @error: the error code
+ * @gateway: true if host is acting as a gateway, false otherwise
+ *
+ * Description:
+ * When a packet is dropped due to a call to avc_has_perm() pass the error
+ * code to the NetLabel subsystem so any protocol specific processing can be
+ * done. This is safe to call even if you are unsure if NetLabel labeling is
+ * present on the packet, NetLabel is smart enough to only act when it should.
+ *
+ */
+void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway)
+{
+ netlbl_skbuff_err(skb, error, gateway);
+}
+
+/**
+ * selinux_netlbl_sk_security_free - Free the NetLabel fields
+ * @sssec: the sk_security_struct
+ *
+ * Description:
+ * Free all of the memory in the NetLabel fields of a sk_security_struct.
+ *
+ */
+void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec)
+{
+ if (ssec->nlbl_secattr != NULL)
+ netlbl_secattr_free(ssec->nlbl_secattr);
+}
+
+/**
+ * selinux_netlbl_sk_security_reset - Reset the NetLabel fields
+ * @ssec: the sk_security_struct
+ * @family: the socket family
+ *
+ * Description:
+ * Called when the NetLabel state of a sk_security_struct needs to be reset.
+ * The caller is responsibile for all the NetLabel sk_security_struct locking.
+ *
+ */
+void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec,
+ int family)
+{
+ if (family == PF_INET)
+ ssec->nlbl_state = NLBL_REQUIRE;
+ else
+ ssec->nlbl_state = NLBL_UNSET;
+}
+
+/**
+ * selinux_netlbl_skbuff_getsid - Get the sid of a packet using NetLabel
+ * @skb: the packet
+ * @family: protocol family
+ * @type: NetLabel labeling protocol type
+ * @sid: the SID
+ *
+ * Description:
+ * Call the NetLabel mechanism to get the security attributes of the given
+ * packet and use those attributes to determine the correct context/SID to
+ * assign to the packet. Returns zero on success, negative values on failure.
+ *
+ */
+int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
+ u16 family,
+ u32 *type,
+ u32 *sid)
+{
+ int rc;
+ struct netlbl_lsm_secattr secattr;
+
+ if (!netlbl_enabled()) {
+ *sid = SECSID_NULL;
+ return 0;
+ }
+
+ netlbl_secattr_init(&secattr);
+ rc = netlbl_skbuff_getattr(skb, family, &secattr);
+ if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
+ rc = selinux_netlbl_sidlookup_cached(skb, &secattr, sid);
+ else
+ *sid = SECSID_NULL;
+ *type = secattr.type;
+ netlbl_secattr_destroy(&secattr);
+
+ return rc;
+}
+
+/**
+ * selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid
+ * @skb: the packet
+ * @family: protocol family
+ * @sid: the SID
+ *
+ * Description
+ * Call the NetLabel mechanism to set the label of a packet using @sid.
+ * Returns zero on auccess, negative values on failure.
+ *
+ */
+int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
+ u16 family,
+ u32 sid)
+{
+ int rc;
+ struct netlbl_lsm_secattr secattr_storage;
+ struct netlbl_lsm_secattr *secattr = NULL;
+ struct sock *sk;
+
+ /* if this is a locally generated packet check to see if it is already
+ * being labeled by it's parent socket, if it is just exit */
+ sk = skb->sk;
+ if (sk != NULL) {
+ struct sk_security_struct *sksec = sk->sk_security;
+ if (sksec->nlbl_state != NLBL_REQSKB)
+ return 0;
+ secattr = sksec->nlbl_secattr;
+ }
+ if (secattr == NULL) {
+ secattr = &secattr_storage;
+ netlbl_secattr_init(secattr);
+ rc = security_netlbl_sid_to_secattr(sid, secattr);
+ if (rc != 0)
+ goto skbuff_setsid_return;
+ }
+
+ rc = netlbl_skbuff_setattr(skb, family, secattr);
+
+skbuff_setsid_return:
+ if (secattr == &secattr_storage)
+ netlbl_secattr_destroy(secattr);
+ return rc;
+}
+
+/**
+ * selinux_netlbl_inet_conn_established - Netlabel the newly accepted connection
+ * @sk: the new connection
+ *
+ * Description:
+ * A new connection has been established on @sk so make sure it is labeled
+ * correctly with the NetLabel susbsystem.
+ *
+ */
+void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family)
+{
+ int rc;
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct netlbl_lsm_secattr *secattr;
+ struct inet_sock *sk_inet = inet_sk(sk);
+ struct sockaddr_in addr;
+
+ if (sksec->nlbl_state != NLBL_REQUIRE)
+ return;
+
+ secattr = selinux_netlbl_sock_genattr(sk);
+ if (secattr == NULL)
+ return;
+
+ rc = netlbl_sock_setattr(sk, secattr);
+ switch (rc) {
+ case 0:
+ sksec->nlbl_state = NLBL_LABELED;
+ break;
+ case -EDESTADDRREQ:
+ /* no PF_INET6 support yet because we don't support any IPv6
+ * labeling protocols */
+ if (family != PF_INET) {
+ sksec->nlbl_state = NLBL_UNSET;
+ return;
+ }
+
+ addr.sin_family = family;
+ addr.sin_addr.s_addr = sk_inet->daddr;
+ if (netlbl_conn_setattr(sk, (struct sockaddr *)&addr,
+ secattr) != 0) {
+ /* we failed to label the connected socket (could be
+ * for a variety of reasons, the actual "why" isn't
+ * important here) so we have to go to our backup plan,
+ * labeling the packets individually in the netfilter
+ * local output hook. this is okay but we need to
+ * adjust the MSS of the connection to take into
+ * account any labeling overhead, since we don't know
+ * the exact overhead at this point we'll use the worst
+ * case value which is 40 bytes for IPv4 */
+ struct inet_connection_sock *sk_conn = inet_csk(sk);
+ sk_conn->icsk_ext_hdr_len += 40 -
+ (sk_inet->opt ? sk_inet->opt->optlen : 0);
+ sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
+
+ sksec->nlbl_state = NLBL_REQSKB;
+ } else
+ sksec->nlbl_state = NLBL_CONNLABELED;
+ break;
+ default:
+ /* note that we are failing to label the socket which could be
+ * a bad thing since it means traffic could leave the system
+ * without the desired labeling, however, all is not lost as
+ * we have a check in selinux_netlbl_inode_permission() to
+ * pick up the pieces that we might drop here because we can't
+ * return an error code */
+ break;
+ }
+}
+
+/**
+ * selinux_netlbl_socket_post_create - Label a socket using NetLabel
+ * @sock: the socket to label
+ *
+ * Description:
+ * Attempt to label a socket using the NetLabel mechanism using the given
+ * SID. Returns zero values on success, negative values on failure.
+ *
+ */
+int selinux_netlbl_socket_post_create(struct socket *sock)
+{
+ return selinux_netlbl_sock_setsid(sock->sk);
+}
+
+/**
+ * selinux_netlbl_inode_permission - Verify the socket is NetLabel labeled
+ * @inode: the file descriptor's inode
+ * @mask: the permission mask
+ *
+ * Description:
+ * Looks at a file's inode and if it is marked as a socket protected by
+ * NetLabel then verify that the socket has been labeled, if not try to label
+ * the socket now with the inode's SID. Returns zero on success, negative
+ * values on failure.
+ *
+ */
+int selinux_netlbl_inode_permission(struct inode *inode, int mask)
+{
+ int rc;
+ struct sock *sk;
+ struct socket *sock;
+ struct sk_security_struct *sksec;
+
+ if (!S_ISSOCK(inode->i_mode) ||
+ ((mask & (MAY_WRITE | MAY_APPEND)) == 0))
+ return 0;
+ sock = SOCKET_I(inode);
+ sk = sock->sk;
+ if (sk == NULL)
+ return 0;
+ sksec = sk->sk_security;
+ if (sksec == NULL || sksec->nlbl_state != NLBL_REQUIRE)
+ return 0;
+
+ local_bh_disable();
+ bh_lock_sock_nested(sk);
+ if (likely(sksec->nlbl_state == NLBL_REQUIRE))
+ rc = selinux_netlbl_sock_setsid(sk);
+ else
+ rc = 0;
+ bh_unlock_sock(sk);
+ local_bh_enable();
+
+ return rc;
+}
+
+/**
+ * selinux_netlbl_sock_rcv_skb - Do an inbound access check using NetLabel
+ * @sksec: the sock's sk_security_struct
+ * @skb: the packet
+ * @family: protocol family
+ * @ad: the audit data
+ *
+ * Description:
+ * Fetch the NetLabel security attributes from @skb and perform an access check
+ * against the receiving socket. Returns zero on success, negative values on
+ * error.
+ *
+ */
+int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
+ struct sk_buff *skb,
+ u16 family,
+ struct avc_audit_data *ad)
+{
+ int rc;
+ u32 nlbl_sid;
+ u32 perm;
+ struct netlbl_lsm_secattr secattr;
+
+ if (!netlbl_enabled())
+ return 0;
+
+ netlbl_secattr_init(&secattr);
+ rc = netlbl_skbuff_getattr(skb, family, &secattr);
+ if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE)
+ rc = selinux_netlbl_sidlookup_cached(skb, &secattr, &nlbl_sid);
+ else
+ nlbl_sid = SECINITSID_UNLABELED;
+ netlbl_secattr_destroy(&secattr);
+ if (rc != 0)
+ return rc;
+
+ switch (sksec->sclass) {
+ case SECCLASS_UDP_SOCKET:
+ perm = UDP_SOCKET__RECVFROM;
+ break;
+ case SECCLASS_TCP_SOCKET:
+ perm = TCP_SOCKET__RECVFROM;
+ break;
+ default:
+ perm = RAWIP_SOCKET__RECVFROM;
+ }
+
+ rc = avc_has_perm(sksec->sid, nlbl_sid, sksec->sclass, perm, ad);
+ if (rc == 0)
+ return 0;
+
+ if (nlbl_sid != SECINITSID_UNLABELED)
+ netlbl_skbuff_err(skb, rc, 0);
+ return rc;
+}
+
+/**
+ * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel
+ * @sock: the socket
+ * @level: the socket level or protocol
+ * @optname: the socket option name
+ *
+ * Description:
+ * Check the setsockopt() call and if the user is trying to replace the IP
+ * options on a socket and a NetLabel is in place for the socket deny the
+ * access; otherwise allow the access. Returns zero when the access is
+ * allowed, -EACCES when denied, and other negative values on error.
+ *
+ */
+int selinux_netlbl_socket_setsockopt(struct socket *sock,
+ int level,
+ int optname)
+{
+ int rc = 0;
+ struct sock *sk = sock->sk;
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct netlbl_lsm_secattr secattr;
+
+ if (level == IPPROTO_IP && optname == IP_OPTIONS &&
+ (sksec->nlbl_state == NLBL_LABELED ||
+ sksec->nlbl_state == NLBL_CONNLABELED)) {
+ netlbl_secattr_init(&secattr);
+ lock_sock(sk);
+ rc = netlbl_sock_getattr(sk, &secattr);
+ release_sock(sk);
+ if (rc == 0)
+ rc = -EACCES;
+ else if (rc == -ENOMSG)
+ rc = 0;
+ netlbl_secattr_destroy(&secattr);
+ }
+
+ return rc;
+}
+
+/**
+ * selinux_netlbl_socket_connect - Label a client-side socket on connect
+ * @sk: the socket to label
+ * @addr: the destination address
+ *
+ * Description:
+ * Attempt to label a connected socket with NetLabel using the given address.
+ * Returns zero values on success, negative values on failure.
+ *
+ */
+int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr)
+{
+ int rc;
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct netlbl_lsm_secattr *secattr;
+
+ if (sksec->nlbl_state != NLBL_REQSKB &&
+ sksec->nlbl_state != NLBL_CONNLABELED)
+ return 0;
+
+ local_bh_disable();
+ bh_lock_sock_nested(sk);
+
+ /* connected sockets are allowed to disconnect when the address family
+ * is set to AF_UNSPEC, if that is what is happening we want to reset
+ * the socket */
+ if (addr->sa_family == AF_UNSPEC) {
+ netlbl_sock_delattr(sk);
+ sksec->nlbl_state = NLBL_REQSKB;
+ rc = 0;
+ goto socket_connect_return;
+ }
+ secattr = selinux_netlbl_sock_genattr(sk);
+ if (secattr == NULL) {
+ rc = -ENOMEM;
+ goto socket_connect_return;
+ }
+ rc = netlbl_conn_setattr(sk, addr, secattr);
+ if (rc == 0)
+ sksec->nlbl_state = NLBL_CONNLABELED;
+
+socket_connect_return:
+ bh_unlock_sock(sk);
+ local_bh_enable();
+ return rc;
+}
diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c
new file mode 100644
index 0000000..1ae5564
--- /dev/null
+++ b/security/selinux/netlink.c
@@ -0,0 +1,116 @@
+/*
+ * Netlink event notifications for SELinux.
+ *
+ * Author: James Morris <jmorris@redhat.com>
+ *
+ * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/selinux_netlink.h>
+#include <net/net_namespace.h>
+
+static struct sock *selnl;
+
+static int selnl_msglen(int msgtype)
+{
+ int ret = 0;
+
+ switch (msgtype) {
+ case SELNL_MSG_SETENFORCE:
+ ret = sizeof(struct selnl_msg_setenforce);
+ break;
+
+ case SELNL_MSG_POLICYLOAD:
+ ret = sizeof(struct selnl_msg_policyload);
+ break;
+
+ default:
+ BUG();
+ }
+ return ret;
+}
+
+static void selnl_add_payload(struct nlmsghdr *nlh, int len, int msgtype, void *data)
+{
+ switch (msgtype) {
+ case SELNL_MSG_SETENFORCE: {
+ struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
+
+ memset(msg, 0, len);
+ msg->val = *((int *)data);
+ break;
+ }
+
+ case SELNL_MSG_POLICYLOAD: {
+ struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
+
+ memset(msg, 0, len);
+ msg->seqno = *((u32 *)data);
+ break;
+ }
+
+ default:
+ BUG();
+ }
+}
+
+static void selnl_notify(int msgtype, void *data)
+{
+ int len;
+ sk_buff_data_t tmp;
+ struct sk_buff *skb;
+ struct nlmsghdr *nlh;
+
+ len = selnl_msglen(msgtype);
+
+ skb = alloc_skb(NLMSG_SPACE(len), GFP_USER);
+ if (!skb)
+ goto oom;
+
+ tmp = skb->tail;
+ nlh = NLMSG_PUT(skb, 0, 0, msgtype, len);
+ selnl_add_payload(nlh, len, msgtype, data);
+ nlh->nlmsg_len = skb->tail - tmp;
+ NETLINK_CB(skb).dst_group = SELNLGRP_AVC;
+ netlink_broadcast(selnl, skb, 0, SELNLGRP_AVC, GFP_USER);
+out:
+ return;
+
+nlmsg_failure:
+ kfree_skb(skb);
+oom:
+ printk(KERN_ERR "SELinux: OOM in %s\n", __func__);
+ goto out;
+}
+
+void selnl_notify_setenforce(int val)
+{
+ selnl_notify(SELNL_MSG_SETENFORCE, &val);
+}
+
+void selnl_notify_policyload(u32 seqno)
+{
+ selnl_notify(SELNL_MSG_POLICYLOAD, &seqno);
+}
+
+static int __init selnl_init(void)
+{
+ selnl = netlink_kernel_create(&init_net, NETLINK_SELINUX,
+ SELNLGRP_MAX, NULL, NULL, THIS_MODULE);
+ if (selnl == NULL)
+ panic("SELinux: Cannot create netlink socket.");
+ netlink_set_nonroot(NETLINK_SELINUX, NL_NONROOT_RECV);
+ return 0;
+}
+
+__initcall(selnl_init);
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
new file mode 100644
index 0000000..7100072
--- /dev/null
+++ b/security/selinux/netnode.c
@@ -0,0 +1,347 @@
+/*
+ * Network node table
+ *
+ * SELinux must keep a mapping of network nodes to labels/SIDs. This
+ * mapping is maintained as part of the normal policy but a fast cache is
+ * needed to reduce the lookup overhead since most of these queries happen on
+ * a per-packet basis.
+ *
+ * Author: Paul Moore <paul.moore@hp.com>
+ *
+ * This code is heavily based on the "netif" concept originally developed by
+ * James Morris <jmorris@redhat.com>
+ * (see security/selinux/netif.c for more information)
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2007
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+
+#include "netnode.h"
+#include "objsec.h"
+
+#define SEL_NETNODE_HASH_SIZE 256
+#define SEL_NETNODE_HASH_BKT_LIMIT 16
+
+struct sel_netnode_bkt {
+ unsigned int size;
+ struct list_head list;
+};
+
+struct sel_netnode {
+ struct netnode_security_struct nsec;
+
+ struct list_head list;
+ struct rcu_head rcu;
+};
+
+/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
+ * for this is that I suspect most users will not make heavy use of both
+ * address families at the same time so one table will usually end up wasted,
+ * if this becomes a problem we can always add a hash table for each address
+ * family later */
+
+static LIST_HEAD(sel_netnode_list);
+static DEFINE_SPINLOCK(sel_netnode_lock);
+static struct sel_netnode_bkt sel_netnode_hash[SEL_NETNODE_HASH_SIZE];
+
+/**
+ * sel_netnode_free - Frees a node entry
+ * @p: the entry's RCU field
+ *
+ * Description:
+ * This function is designed to be used as a callback to the call_rcu()
+ * function so that memory allocated to a hash table node entry can be
+ * released safely.
+ *
+ */
+static void sel_netnode_free(struct rcu_head *p)
+{
+ struct sel_netnode *node = container_of(p, struct sel_netnode, rcu);
+ kfree(node);
+}
+
+/**
+ * sel_netnode_hashfn_ipv4 - IPv4 hashing function for the node table
+ * @addr: IPv4 address
+ *
+ * Description:
+ * This is the IPv4 hashing function for the node interface table, it returns
+ * the bucket number for the given IP address.
+ *
+ */
+static unsigned int sel_netnode_hashfn_ipv4(__be32 addr)
+{
+ /* at some point we should determine if the mismatch in byte order
+ * affects the hash function dramatically */
+ return (addr & (SEL_NETNODE_HASH_SIZE - 1));
+}
+
+/**
+ * sel_netnode_hashfn_ipv6 - IPv6 hashing function for the node table
+ * @addr: IPv6 address
+ *
+ * Description:
+ * This is the IPv6 hashing function for the node interface table, it returns
+ * the bucket number for the given IP address.
+ *
+ */
+static unsigned int sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
+{
+ /* just hash the least significant 32 bits to keep things fast (they
+ * are the most likely to be different anyway), we can revisit this
+ * later if needed */
+ return (addr->s6_addr32[3] & (SEL_NETNODE_HASH_SIZE - 1));
+}
+
+/**
+ * sel_netnode_find - Search for a node record
+ * @addr: IP address
+ * @family: address family
+ *
+ * Description:
+ * Search the network node table and return the record matching @addr. If an
+ * entry can not be found in the table return NULL.
+ *
+ */
+static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
+{
+ unsigned int idx;
+ struct sel_netnode *node;
+
+ switch (family) {
+ case PF_INET:
+ idx = sel_netnode_hashfn_ipv4(*(__be32 *)addr);
+ break;
+ case PF_INET6:
+ idx = sel_netnode_hashfn_ipv6(addr);
+ break;
+ default:
+ BUG();
+ }
+
+ list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list)
+ if (node->nsec.family == family)
+ switch (family) {
+ case PF_INET:
+ if (node->nsec.addr.ipv4 == *(__be32 *)addr)
+ return node;
+ break;
+ case PF_INET6:
+ if (ipv6_addr_equal(&node->nsec.addr.ipv6,
+ addr))
+ return node;
+ break;
+ }
+
+ return NULL;
+}
+
+/**
+ * sel_netnode_insert - Insert a new node into the table
+ * @node: the new node record
+ *
+ * Description:
+ * Add a new node record to the network address hash table.
+ *
+ */
+static void sel_netnode_insert(struct sel_netnode *node)
+{
+ unsigned int idx;
+
+ switch (node->nsec.family) {
+ case PF_INET:
+ idx = sel_netnode_hashfn_ipv4(node->nsec.addr.ipv4);
+ break;
+ case PF_INET6:
+ idx = sel_netnode_hashfn_ipv6(&node->nsec.addr.ipv6);
+ break;
+ default:
+ BUG();
+ }
+
+ INIT_RCU_HEAD(&node->rcu);
+
+ /* we need to impose a limit on the growth of the hash table so check
+ * this bucket to make sure it is within the specified bounds */
+ list_add_rcu(&node->list, &sel_netnode_hash[idx].list);
+ if (sel_netnode_hash[idx].size == SEL_NETNODE_HASH_BKT_LIMIT) {
+ struct sel_netnode *tail;
+ tail = list_entry(
+ rcu_dereference(sel_netnode_hash[idx].list.prev),
+ struct sel_netnode, list);
+ list_del_rcu(&tail->list);
+ call_rcu(&tail->rcu, sel_netnode_free);
+ } else
+ sel_netnode_hash[idx].size++;
+}
+
+/**
+ * sel_netnode_sid_slow - Lookup the SID of a network address using the policy
+ * @addr: the IP address
+ * @family: the address family
+ * @sid: node SID
+ *
+ * Description:
+ * This function determines the SID of a network address by quering the
+ * security policy. The result is added to the network address table to
+ * speedup future queries. Returns zero on success, negative values on
+ * failure.
+ *
+ */
+static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
+{
+ int ret = -ENOMEM;
+ struct sel_netnode *node;
+ struct sel_netnode *new = NULL;
+
+ spin_lock_bh(&sel_netnode_lock);
+ node = sel_netnode_find(addr, family);
+ if (node != NULL) {
+ *sid = node->nsec.sid;
+ spin_unlock_bh(&sel_netnode_lock);
+ return 0;
+ }
+ new = kzalloc(sizeof(*new), GFP_ATOMIC);
+ if (new == NULL)
+ goto out;
+ switch (family) {
+ case PF_INET:
+ ret = security_node_sid(PF_INET,
+ addr, sizeof(struct in_addr), sid);
+ new->nsec.addr.ipv4 = *(__be32 *)addr;
+ break;
+ case PF_INET6:
+ ret = security_node_sid(PF_INET6,
+ addr, sizeof(struct in6_addr), sid);
+ ipv6_addr_copy(&new->nsec.addr.ipv6, addr);
+ break;
+ default:
+ BUG();
+ }
+ if (ret != 0)
+ goto out;
+
+ new->nsec.family = family;
+ new->nsec.sid = *sid;
+ sel_netnode_insert(new);
+
+out:
+ spin_unlock_bh(&sel_netnode_lock);
+ if (unlikely(ret)) {
+ printk(KERN_WARNING
+ "SELinux: failure in sel_netnode_sid_slow(),"
+ " unable to determine network node label\n");
+ kfree(new);
+ }
+ return ret;
+}
+
+/**
+ * sel_netnode_sid - Lookup the SID of a network address
+ * @addr: the IP address
+ * @family: the address family
+ * @sid: node SID
+ *
+ * Description:
+ * This function determines the SID of a network address using the fastest
+ * method possible. First the address table is queried, but if an entry
+ * can't be found then the policy is queried and the result is added to the
+ * table to speedup future queries. Returns zero on success, negative values
+ * on failure.
+ *
+ */
+int sel_netnode_sid(void *addr, u16 family, u32 *sid)
+{
+ struct sel_netnode *node;
+
+ rcu_read_lock();
+ node = sel_netnode_find(addr, family);
+ if (node != NULL) {
+ *sid = node->nsec.sid;
+ rcu_read_unlock();
+ return 0;
+ }
+ rcu_read_unlock();
+
+ return sel_netnode_sid_slow(addr, family, sid);
+}
+
+/**
+ * sel_netnode_flush - Flush the entire network address table
+ *
+ * Description:
+ * Remove all entries from the network address table.
+ *
+ */
+static void sel_netnode_flush(void)
+{
+ unsigned int idx;
+ struct sel_netnode *node, *node_tmp;
+
+ spin_lock_bh(&sel_netnode_lock);
+ for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) {
+ list_for_each_entry_safe(node, node_tmp,
+ &sel_netnode_hash[idx].list, list) {
+ list_del_rcu(&node->list);
+ call_rcu(&node->rcu, sel_netnode_free);
+ }
+ sel_netnode_hash[idx].size = 0;
+ }
+ spin_unlock_bh(&sel_netnode_lock);
+}
+
+static int sel_netnode_avc_callback(u32 event, u32 ssid, u32 tsid,
+ u16 class, u32 perms, u32 *retained)
+{
+ if (event == AVC_CALLBACK_RESET) {
+ sel_netnode_flush();
+ synchronize_net();
+ }
+ return 0;
+}
+
+static __init int sel_netnode_init(void)
+{
+ int iter;
+ int ret;
+
+ if (!selinux_enabled)
+ return 0;
+
+ for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) {
+ INIT_LIST_HEAD(&sel_netnode_hash[iter].list);
+ sel_netnode_hash[iter].size = 0;
+ }
+
+ ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET,
+ SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+ if (ret != 0)
+ panic("avc_add_callback() failed, error %d\n", ret);
+
+ return ret;
+}
+
+__initcall(sel_netnode_init);
diff --git a/security/selinux/netport.c b/security/selinux/netport.c
new file mode 100644
index 0000000..fe7fba6
--- /dev/null
+++ b/security/selinux/netport.c
@@ -0,0 +1,281 @@
+/*
+ * Network port table
+ *
+ * SELinux must keep a mapping of network ports to labels/SIDs. This
+ * mapping is maintained as part of the normal policy but a fast cache is
+ * needed to reduce the lookup overhead.
+ *
+ * Author: Paul Moore <paul.moore@hp.com>
+ *
+ * This code is heavily based on the "netif" concept originally developed by
+ * James Morris <jmorris@redhat.com>
+ * (see security/selinux/netif.c for more information)
+ *
+ */
+
+/*
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2008
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+
+#include "netport.h"
+#include "objsec.h"
+
+#define SEL_NETPORT_HASH_SIZE 256
+#define SEL_NETPORT_HASH_BKT_LIMIT 16
+
+struct sel_netport_bkt {
+ int size;
+ struct list_head list;
+};
+
+struct sel_netport {
+ struct netport_security_struct psec;
+
+ struct list_head list;
+ struct rcu_head rcu;
+};
+
+/* NOTE: we are using a combined hash table for both IPv4 and IPv6, the reason
+ * for this is that I suspect most users will not make heavy use of both
+ * address families at the same time so one table will usually end up wasted,
+ * if this becomes a problem we can always add a hash table for each address
+ * family later */
+
+static LIST_HEAD(sel_netport_list);
+static DEFINE_SPINLOCK(sel_netport_lock);
+static struct sel_netport_bkt sel_netport_hash[SEL_NETPORT_HASH_SIZE];
+
+/**
+ * sel_netport_free - Frees a port entry
+ * @p: the entry's RCU field
+ *
+ * Description:
+ * This function is designed to be used as a callback to the call_rcu()
+ * function so that memory allocated to a hash table port entry can be
+ * released safely.
+ *
+ */
+static void sel_netport_free(struct rcu_head *p)
+{
+ struct sel_netport *port = container_of(p, struct sel_netport, rcu);
+ kfree(port);
+}
+
+/**
+ * sel_netport_hashfn - Hashing function for the port table
+ * @pnum: port number
+ *
+ * Description:
+ * This is the hashing function for the port table, it returns the bucket
+ * number for the given port.
+ *
+ */
+static unsigned int sel_netport_hashfn(u16 pnum)
+{
+ return (pnum & (SEL_NETPORT_HASH_SIZE - 1));
+}
+
+/**
+ * sel_netport_find - Search for a port record
+ * @protocol: protocol
+ * @port: pnum
+ *
+ * Description:
+ * Search the network port table and return the matching record. If an entry
+ * can not be found in the table return NULL.
+ *
+ */
+static struct sel_netport *sel_netport_find(u8 protocol, u16 pnum)
+{
+ unsigned int idx;
+ struct sel_netport *port;
+
+ idx = sel_netport_hashfn(pnum);
+ list_for_each_entry_rcu(port, &sel_netport_hash[idx].list, list)
+ if (port->psec.port == pnum && port->psec.protocol == protocol)
+ return port;
+
+ return NULL;
+}
+
+/**
+ * sel_netport_insert - Insert a new port into the table
+ * @port: the new port record
+ *
+ * Description:
+ * Add a new port record to the network address hash table.
+ *
+ */
+static void sel_netport_insert(struct sel_netport *port)
+{
+ unsigned int idx;
+
+ /* we need to impose a limit on the growth of the hash table so check
+ * this bucket to make sure it is within the specified bounds */
+ idx = sel_netport_hashfn(port->psec.port);
+ list_add_rcu(&port->list, &sel_netport_hash[idx].list);
+ if (sel_netport_hash[idx].size == SEL_NETPORT_HASH_BKT_LIMIT) {
+ struct sel_netport *tail;
+ tail = list_entry(
+ rcu_dereference(sel_netport_hash[idx].list.prev),
+ struct sel_netport, list);
+ list_del_rcu(&tail->list);
+ call_rcu(&tail->rcu, sel_netport_free);
+ } else
+ sel_netport_hash[idx].size++;
+}
+
+/**
+ * sel_netport_sid_slow - Lookup the SID of a network address using the policy
+ * @protocol: protocol
+ * @pnum: port
+ * @sid: port SID
+ *
+ * Description:
+ * This function determines the SID of a network port by quering the security
+ * policy. The result is added to the network port table to speedup future
+ * queries. Returns zero on success, negative values on failure.
+ *
+ */
+static int sel_netport_sid_slow(u8 protocol, u16 pnum, u32 *sid)
+{
+ int ret = -ENOMEM;
+ struct sel_netport *port;
+ struct sel_netport *new = NULL;
+
+ spin_lock_bh(&sel_netport_lock);
+ port = sel_netport_find(protocol, pnum);
+ if (port != NULL) {
+ *sid = port->psec.sid;
+ spin_unlock_bh(&sel_netport_lock);
+ return 0;
+ }
+ new = kzalloc(sizeof(*new), GFP_ATOMIC);
+ if (new == NULL)
+ goto out;
+ ret = security_port_sid(protocol, pnum, sid);
+ if (ret != 0)
+ goto out;
+
+ new->psec.port = pnum;
+ new->psec.protocol = protocol;
+ new->psec.sid = *sid;
+ sel_netport_insert(new);
+
+out:
+ spin_unlock_bh(&sel_netport_lock);
+ if (unlikely(ret)) {
+ printk(KERN_WARNING
+ "SELinux: failure in sel_netport_sid_slow(),"
+ " unable to determine network port label\n");
+ kfree(new);
+ }
+ return ret;
+}
+
+/**
+ * sel_netport_sid - Lookup the SID of a network port
+ * @protocol: protocol
+ * @pnum: port
+ * @sid: port SID
+ *
+ * Description:
+ * This function determines the SID of a network port using the fastest method
+ * possible. First the port table is queried, but if an entry can't be found
+ * then the policy is queried and the result is added to the table to speedup
+ * future queries. Returns zero on success, negative values on failure.
+ *
+ */
+int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid)
+{
+ struct sel_netport *port;
+
+ rcu_read_lock();
+ port = sel_netport_find(protocol, pnum);
+ if (port != NULL) {
+ *sid = port->psec.sid;
+ rcu_read_unlock();
+ return 0;
+ }
+ rcu_read_unlock();
+
+ return sel_netport_sid_slow(protocol, pnum, sid);
+}
+
+/**
+ * sel_netport_flush - Flush the entire network port table
+ *
+ * Description:
+ * Remove all entries from the network address table.
+ *
+ */
+static void sel_netport_flush(void)
+{
+ unsigned int idx;
+ struct sel_netport *port, *port_tmp;
+
+ spin_lock_bh(&sel_netport_lock);
+ for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) {
+ list_for_each_entry_safe(port, port_tmp,
+ &sel_netport_hash[idx].list, list) {
+ list_del_rcu(&port->list);
+ call_rcu(&port->rcu, sel_netport_free);
+ }
+ sel_netport_hash[idx].size = 0;
+ }
+ spin_unlock_bh(&sel_netport_lock);
+}
+
+static int sel_netport_avc_callback(u32 event, u32 ssid, u32 tsid,
+ u16 class, u32 perms, u32 *retained)
+{
+ if (event == AVC_CALLBACK_RESET) {
+ sel_netport_flush();
+ synchronize_net();
+ }
+ return 0;
+}
+
+static __init int sel_netport_init(void)
+{
+ int iter;
+ int ret;
+
+ if (!selinux_enabled)
+ return 0;
+
+ for (iter = 0; iter < SEL_NETPORT_HASH_SIZE; iter++) {
+ INIT_LIST_HEAD(&sel_netport_hash[iter].list);
+ sel_netport_hash[iter].size = 0;
+ }
+
+ ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET,
+ SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+ if (ret != 0)
+ panic("avc_add_callback() failed, error %d\n", ret);
+
+ return ret;
+}
+
+__initcall(sel_netport_init);
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
new file mode 100644
index 0000000..ff59c0c
--- /dev/null
+++ b/security/selinux/nlmsgtab.c
@@ -0,0 +1,176 @@
+/*
+ * Netlink message type permission tables, for user generated messages.
+ *
+ * Author: James Morris <jmorris@redhat.com>
+ *
+ * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if.h>
+#include <linux/netfilter_ipv4/ip_queue.h>
+#include <linux/inet_diag.h>
+#include <linux/xfrm.h>
+#include <linux/audit.h>
+
+#include "flask.h"
+#include "av_permissions.h"
+
+struct nlmsg_perm {
+ u16 nlmsg_type;
+ u32 perm;
+};
+
+static struct nlmsg_perm nlmsg_route_perms[] =
+{
+ { RTM_NEWLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETLINK, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_SETLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_NEWADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELADDR, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETADDR, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELROUTE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETROUTE, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETNEIGH, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELRULE, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETRULE, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELQDISC, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETQDISC, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETTCLASS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETTFILTER, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_DELACTION, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETACTION, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWPREFIX, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+ { RTM_GETMULTICAST, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_GETANYCAST, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_GETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_SETNEIGHTBL, NETLINK_ROUTE_SOCKET__NLMSG_WRITE },
+};
+
+static struct nlmsg_perm nlmsg_firewall_perms[] =
+{
+ { IPQM_MODE, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE },
+ { IPQM_VERDICT, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE },
+};
+
+static struct nlmsg_perm nlmsg_tcpdiag_perms[] =
+{
+ { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
+ { DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ },
+};
+
+static struct nlmsg_perm nlmsg_xfrm_perms[] =
+{
+ { XFRM_MSG_NEWSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_DELSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETSA, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_NEWPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_DELPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETPOLICY, NETLINK_XFRM_SOCKET__NLMSG_READ },
+ { XFRM_MSG_ALLOCSPI, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_ACQUIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_EXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_UPDPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_UPDSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_POLEXPIRE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_FLUSHSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_FLUSHPOLICY, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_NEWAE, NETLINK_XFRM_SOCKET__NLMSG_WRITE },
+ { XFRM_MSG_GETAE, NETLINK_XFRM_SOCKET__NLMSG_READ },
+};
+
+static struct nlmsg_perm nlmsg_audit_perms[] =
+{
+ { AUDIT_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
+ { AUDIT_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV },
+ { AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_LIST_RULES, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV },
+ { AUDIT_ADD_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_DEL_RULE, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY },
+ { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ },
+ { AUDIT_TTY_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ },
+ { AUDIT_TTY_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+};
+
+
+static int nlmsg_perm(u16 nlmsg_type, u32 *perm, struct nlmsg_perm *tab, size_t tabsize)
+{
+ int i, err = -EINVAL;
+
+ for (i = 0; i < tabsize/sizeof(struct nlmsg_perm); i++)
+ if (nlmsg_type == tab[i].nlmsg_type) {
+ *perm = tab[i].perm;
+ err = 0;
+ break;
+ }
+
+ return err;
+}
+
+int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
+{
+ int err = 0;
+
+ switch (sclass) {
+ case SECCLASS_NETLINK_ROUTE_SOCKET:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
+ sizeof(nlmsg_route_perms));
+ break;
+
+ case SECCLASS_NETLINK_FIREWALL_SOCKET:
+ case SECCLASS_NETLINK_IP6FW_SOCKET:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_firewall_perms,
+ sizeof(nlmsg_firewall_perms));
+ break;
+
+ case SECCLASS_NETLINK_TCPDIAG_SOCKET:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_tcpdiag_perms,
+ sizeof(nlmsg_tcpdiag_perms));
+ break;
+
+ case SECCLASS_NETLINK_XFRM_SOCKET:
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_xfrm_perms,
+ sizeof(nlmsg_xfrm_perms));
+ break;
+
+ case SECCLASS_NETLINK_AUDIT_SOCKET:
+ if ((nlmsg_type >= AUDIT_FIRST_USER_MSG &&
+ nlmsg_type <= AUDIT_LAST_USER_MSG) ||
+ (nlmsg_type >= AUDIT_FIRST_USER_MSG2 &&
+ nlmsg_type <= AUDIT_LAST_USER_MSG2)) {
+ *perm = NETLINK_AUDIT_SOCKET__NLMSG_RELAY;
+ } else {
+ err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms,
+ sizeof(nlmsg_audit_perms));
+ }
+ break;
+
+ /* No messaging from userspace, or class unknown/unhandled */
+ default:
+ err = -ENOENT;
+ break;
+ }
+
+ return err;
+}
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
new file mode 100644
index 0000000..69c9dcc
--- /dev/null
+++ b/security/selinux/selinuxfs.c
@@ -0,0 +1,1809 @@
+/* Updated: Karl MacMillan <kmacmillan@tresys.com>
+ *
+ * Added conditional policy language extensions
+ *
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ * Added support for the policy capability bitmap
+ *
+ * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/security.h>
+#include <linux/major.h>
+#include <linux/seq_file.h>
+#include <linux/percpu.h>
+#include <linux/audit.h>
+#include <linux/uaccess.h>
+
+/* selinuxfs pseudo filesystem for exporting the security policy API.
+ Based on the proc code and the fs/nfsd/nfsctl.c code. */
+
+#include "flask.h"
+#include "avc.h"
+#include "avc_ss.h"
+#include "security.h"
+#include "objsec.h"
+#include "conditional.h"
+
+/* Policy capability filenames */
+static char *policycap_names[] = {
+ "network_peer_controls",
+ "open_perms"
+};
+
+unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE;
+
+#ifdef CONFIG_SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT
+#define SELINUX_COMPAT_NET_VALUE 0
+#else
+#define SELINUX_COMPAT_NET_VALUE 1
+#endif
+
+int selinux_compat_net = SELINUX_COMPAT_NET_VALUE;
+
+static int __init checkreqprot_setup(char *str)
+{
+ unsigned long checkreqprot;
+ if (!strict_strtoul(str, 0, &checkreqprot))
+ selinux_checkreqprot = checkreqprot ? 1 : 0;
+ return 1;
+}
+__setup("checkreqprot=", checkreqprot_setup);
+
+static int __init selinux_compat_net_setup(char *str)
+{
+ unsigned long compat_net;
+ if (!strict_strtoul(str, 0, &compat_net))
+ selinux_compat_net = compat_net ? 1 : 0;
+ return 1;
+}
+__setup("selinux_compat_net=", selinux_compat_net_setup);
+
+
+static DEFINE_MUTEX(sel_mutex);
+
+/* global data for booleans */
+static struct dentry *bool_dir;
+static int bool_num;
+static char **bool_pending_names;
+static int *bool_pending_values;
+
+/* global data for classes */
+static struct dentry *class_dir;
+static unsigned long last_class_ino;
+
+/* global data for policy capabilities */
+static struct dentry *policycap_dir;
+
+extern void selnl_notify_setenforce(int val);
+
+/* Check whether a task is allowed to use a security operation. */
+static int task_has_security(struct task_struct *tsk,
+ u32 perms)
+{
+ struct task_security_struct *tsec;
+
+ tsec = tsk->security;
+ if (!tsec)
+ return -EACCES;
+
+ return avc_has_perm(tsec->sid, SECINITSID_SECURITY,
+ SECCLASS_SECURITY, perms, NULL);
+}
+
+enum sel_inos {
+ SEL_ROOT_INO = 2,
+ SEL_LOAD, /* load policy */
+ SEL_ENFORCE, /* get or set enforcing status */
+ SEL_CONTEXT, /* validate context */
+ SEL_ACCESS, /* compute access decision */
+ SEL_CREATE, /* compute create labeling decision */
+ SEL_RELABEL, /* compute relabeling decision */
+ SEL_USER, /* compute reachable user contexts */
+ SEL_POLICYVERS, /* return policy version for this kernel */
+ SEL_COMMIT_BOOLS, /* commit new boolean values */
+ SEL_MLS, /* return if MLS policy is enabled */
+ SEL_DISABLE, /* disable SELinux until next reboot */
+ SEL_MEMBER, /* compute polyinstantiation membership decision */
+ SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */
+ SEL_COMPAT_NET, /* whether to use old compat network packet controls */
+ SEL_REJECT_UNKNOWN, /* export unknown reject handling to userspace */
+ SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */
+ SEL_INO_NEXT, /* The next inode number to use */
+};
+
+static unsigned long sel_last_ino = SEL_INO_NEXT - 1;
+
+#define SEL_INITCON_INO_OFFSET 0x01000000
+#define SEL_BOOL_INO_OFFSET 0x02000000
+#define SEL_CLASS_INO_OFFSET 0x04000000
+#define SEL_POLICYCAP_INO_OFFSET 0x08000000
+#define SEL_INO_MASK 0x00ffffff
+
+#define TMPBUFLEN 12
+static ssize_t sel_read_enforce(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_enforcing);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
+static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+
+{
+ char *page;
+ ssize_t length;
+ int new_value;
+
+ if (count >= PAGE_SIZE)
+ return -ENOMEM;
+ if (*ppos != 0) {
+ /* No partial writes. */
+ return -EINVAL;
+ }
+ page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ length = -EFAULT;
+ if (copy_from_user(page, buf, count))
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(page, "%d", &new_value) != 1)
+ goto out;
+
+ if (new_value != selinux_enforcing) {
+ length = task_has_security(current, SECURITY__SETENFORCE);
+ if (length)
+ goto out;
+ audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
+ "enforcing=%d old_enforcing=%d auid=%u ses=%u",
+ new_value, selinux_enforcing,
+ audit_get_loginuid(current),
+ audit_get_sessionid(current));
+ selinux_enforcing = new_value;
+ if (selinux_enforcing)
+ avc_ss_reset(0);
+ selnl_notify_setenforce(selinux_enforcing);
+ }
+ length = count;
+out:
+ free_page((unsigned long) page);
+ return length;
+}
+#else
+#define sel_write_enforce NULL
+#endif
+
+static const struct file_operations sel_enforce_ops = {
+ .read = sel_read_enforce,
+ .write = sel_write_enforce,
+};
+
+static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+ ino_t ino = filp->f_path.dentry->d_inode->i_ino;
+ int handle_unknown = (ino == SEL_REJECT_UNKNOWN) ?
+ security_get_reject_unknown() : !security_get_allow_unknown();
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%d", handle_unknown);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static const struct file_operations sel_handle_unknown_ops = {
+ .read = sel_read_handle_unknown,
+};
+
+#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+static ssize_t sel_write_disable(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+
+{
+ char *page;
+ ssize_t length;
+ int new_value;
+ extern int selinux_disable(void);
+
+ if (count >= PAGE_SIZE)
+ return -ENOMEM;
+ if (*ppos != 0) {
+ /* No partial writes. */
+ return -EINVAL;
+ }
+ page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ length = -EFAULT;
+ if (copy_from_user(page, buf, count))
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(page, "%d", &new_value) != 1)
+ goto out;
+
+ if (new_value) {
+ length = selinux_disable();
+ if (length < 0)
+ goto out;
+ audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_STATUS,
+ "selinux=0 auid=%u ses=%u",
+ audit_get_loginuid(current),
+ audit_get_sessionid(current));
+ }
+
+ length = count;
+out:
+ free_page((unsigned long) page);
+ return length;
+}
+#else
+#define sel_write_disable NULL
+#endif
+
+static const struct file_operations sel_disable_ops = {
+ .write = sel_write_disable,
+};
+
+static ssize_t sel_read_policyvers(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%u", POLICYDB_VERSION_MAX);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static const struct file_operations sel_policyvers_ops = {
+ .read = sel_read_policyvers,
+};
+
+/* declaration for sel_write_load */
+static int sel_make_bools(void);
+static int sel_make_classes(void);
+static int sel_make_policycap(void);
+
+/* declaration for sel_make_class_dirs */
+static int sel_make_dir(struct inode *dir, struct dentry *dentry,
+ unsigned long *ino);
+
+static ssize_t sel_read_mls(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_mls_enabled);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static const struct file_operations sel_mls_ops = {
+ .read = sel_read_mls,
+};
+
+static ssize_t sel_write_load(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+
+{
+ int ret;
+ ssize_t length;
+ void *data = NULL;
+
+ mutex_lock(&sel_mutex);
+
+ length = task_has_security(current, SECURITY__LOAD_POLICY);
+ if (length)
+ goto out;
+
+ if (*ppos != 0) {
+ /* No partial writes. */
+ length = -EINVAL;
+ goto out;
+ }
+
+ if ((count > 64 * 1024 * 1024)
+ || (data = vmalloc(count)) == NULL) {
+ length = -ENOMEM;
+ goto out;
+ }
+
+ length = -EFAULT;
+ if (copy_from_user(data, buf, count) != 0)
+ goto out;
+
+ length = security_load_policy(data, count);
+ if (length)
+ goto out;
+
+ ret = sel_make_bools();
+ if (ret) {
+ length = ret;
+ goto out1;
+ }
+
+ ret = sel_make_classes();
+ if (ret) {
+ length = ret;
+ goto out1;
+ }
+
+ ret = sel_make_policycap();
+ if (ret)
+ length = ret;
+ else
+ length = count;
+
+out1:
+ audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
+ "policy loaded auid=%u ses=%u",
+ audit_get_loginuid(current),
+ audit_get_sessionid(current));
+out:
+ mutex_unlock(&sel_mutex);
+ vfree(data);
+ return length;
+}
+
+static const struct file_operations sel_load_ops = {
+ .write = sel_write_load,
+};
+
+static ssize_t sel_write_context(struct file *file, char *buf, size_t size)
+{
+ char *canon;
+ u32 sid, len;
+ ssize_t length;
+
+ length = task_has_security(current, SECURITY__CHECK_CONTEXT);
+ if (length)
+ return length;
+
+ length = security_context_to_sid(buf, size, &sid);
+ if (length < 0)
+ return length;
+
+ length = security_sid_to_context(sid, &canon, &len);
+ if (length < 0)
+ return length;
+
+ if (len > SIMPLE_TRANSACTION_LIMIT) {
+ printk(KERN_ERR "SELinux: %s: context size (%u) exceeds "
+ "payload max\n", __func__, len);
+ length = -ERANGE;
+ goto out;
+ }
+
+ memcpy(buf, canon, len);
+ length = len;
+out:
+ kfree(canon);
+ return length;
+}
+
+static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%u", selinux_checkreqprot);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page;
+ ssize_t length;
+ unsigned int new_value;
+
+ length = task_has_security(current, SECURITY__SETCHECKREQPROT);
+ if (length)
+ return length;
+
+ if (count >= PAGE_SIZE)
+ return -ENOMEM;
+ if (*ppos != 0) {
+ /* No partial writes. */
+ return -EINVAL;
+ }
+ page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ length = -EFAULT;
+ if (copy_from_user(page, buf, count))
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(page, "%u", &new_value) != 1)
+ goto out;
+
+ selinux_checkreqprot = new_value ? 1 : 0;
+ length = count;
+out:
+ free_page((unsigned long) page);
+ return length;
+}
+static const struct file_operations sel_checkreqprot_ops = {
+ .read = sel_read_checkreqprot,
+ .write = sel_write_checkreqprot,
+};
+
+static ssize_t sel_read_compat_net(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_compat_net);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static ssize_t sel_write_compat_net(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page;
+ ssize_t length;
+ int new_value;
+
+ length = task_has_security(current, SECURITY__LOAD_POLICY);
+ if (length)
+ return length;
+
+ if (count >= PAGE_SIZE)
+ return -ENOMEM;
+ if (*ppos != 0) {
+ /* No partial writes. */
+ return -EINVAL;
+ }
+ page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+ length = -EFAULT;
+ if (copy_from_user(page, buf, count))
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(page, "%d", &new_value) != 1)
+ goto out;
+
+ selinux_compat_net = new_value ? 1 : 0;
+ length = count;
+out:
+ free_page((unsigned long) page);
+ return length;
+}
+static const struct file_operations sel_compat_net_ops = {
+ .read = sel_read_compat_net,
+ .write = sel_write_compat_net,
+};
+
+/*
+ * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c
+ */
+static ssize_t sel_write_access(struct file *file, char *buf, size_t size);
+static ssize_t sel_write_create(struct file *file, char *buf, size_t size);
+static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size);
+static ssize_t sel_write_user(struct file *file, char *buf, size_t size);
+static ssize_t sel_write_member(struct file *file, char *buf, size_t size);
+
+static ssize_t (*write_op[])(struct file *, char *, size_t) = {
+ [SEL_ACCESS] = sel_write_access,
+ [SEL_CREATE] = sel_write_create,
+ [SEL_RELABEL] = sel_write_relabel,
+ [SEL_USER] = sel_write_user,
+ [SEL_MEMBER] = sel_write_member,
+ [SEL_CONTEXT] = sel_write_context,
+};
+
+static ssize_t selinux_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
+{
+ ino_t ino = file->f_path.dentry->d_inode->i_ino;
+ char *data;
+ ssize_t rv;
+
+ if (ino >= ARRAY_SIZE(write_op) || !write_op[ino])
+ return -EINVAL;
+
+ data = simple_transaction_get(file, buf, size);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ rv = write_op[ino](file, data, size);
+ if (rv > 0) {
+ simple_transaction_set(file, rv);
+ rv = size;
+ }
+ return rv;
+}
+
+static const struct file_operations transaction_ops = {
+ .write = selinux_transaction_write,
+ .read = simple_transaction_read,
+ .release = simple_transaction_release,
+};
+
+/*
+ * payload - write methods
+ * If the method has a response, the response should be put in buf,
+ * and the length returned. Otherwise return 0 or and -error.
+ */
+
+static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
+{
+ char *scon, *tcon;
+ u32 ssid, tsid;
+ u16 tclass;
+ u32 req;
+ struct av_decision avd;
+ ssize_t length;
+
+ length = task_has_security(current, SECURITY__COMPUTE_AV);
+ if (length)
+ return length;
+
+ length = -ENOMEM;
+ scon = kzalloc(size+1, GFP_KERNEL);
+ if (!scon)
+ return length;
+
+ tcon = kzalloc(size+1, GFP_KERNEL);
+ if (!tcon)
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4)
+ goto out2;
+
+ length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
+ if (length < 0)
+ goto out2;
+ length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_compute_av(ssid, tsid, tclass, req, &avd);
+ if (length < 0)
+ goto out2;
+
+ length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
+ "%x %x %x %x %u",
+ avd.allowed, avd.decided,
+ avd.auditallow, avd.auditdeny,
+ avd.seqno);
+out2:
+ kfree(tcon);
+out:
+ kfree(scon);
+ return length;
+}
+
+static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
+{
+ char *scon, *tcon;
+ u32 ssid, tsid, newsid;
+ u16 tclass;
+ ssize_t length;
+ char *newcon;
+ u32 len;
+
+ length = task_has_security(current, SECURITY__COMPUTE_CREATE);
+ if (length)
+ return length;
+
+ length = -ENOMEM;
+ scon = kzalloc(size+1, GFP_KERNEL);
+ if (!scon)
+ return length;
+
+ tcon = kzalloc(size+1, GFP_KERNEL);
+ if (!tcon)
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
+ goto out2;
+
+ length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
+ if (length < 0)
+ goto out2;
+ length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_transition_sid(ssid, tsid, tclass, &newsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_sid_to_context(newsid, &newcon, &len);
+ if (length < 0)
+ goto out2;
+
+ if (len > SIMPLE_TRANSACTION_LIMIT) {
+ printk(KERN_ERR "SELinux: %s: context size (%u) exceeds "
+ "payload max\n", __func__, len);
+ length = -ERANGE;
+ goto out3;
+ }
+
+ memcpy(buf, newcon, len);
+ length = len;
+out3:
+ kfree(newcon);
+out2:
+ kfree(tcon);
+out:
+ kfree(scon);
+ return length;
+}
+
+static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size)
+{
+ char *scon, *tcon;
+ u32 ssid, tsid, newsid;
+ u16 tclass;
+ ssize_t length;
+ char *newcon;
+ u32 len;
+
+ length = task_has_security(current, SECURITY__COMPUTE_RELABEL);
+ if (length)
+ return length;
+
+ length = -ENOMEM;
+ scon = kzalloc(size+1, GFP_KERNEL);
+ if (!scon)
+ return length;
+
+ tcon = kzalloc(size+1, GFP_KERNEL);
+ if (!tcon)
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
+ goto out2;
+
+ length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
+ if (length < 0)
+ goto out2;
+ length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_change_sid(ssid, tsid, tclass, &newsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_sid_to_context(newsid, &newcon, &len);
+ if (length < 0)
+ goto out2;
+
+ if (len > SIMPLE_TRANSACTION_LIMIT) {
+ length = -ERANGE;
+ goto out3;
+ }
+
+ memcpy(buf, newcon, len);
+ length = len;
+out3:
+ kfree(newcon);
+out2:
+ kfree(tcon);
+out:
+ kfree(scon);
+ return length;
+}
+
+static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
+{
+ char *con, *user, *ptr;
+ u32 sid, *sids;
+ ssize_t length;
+ char *newcon;
+ int i, rc;
+ u32 len, nsids;
+
+ length = task_has_security(current, SECURITY__COMPUTE_USER);
+ if (length)
+ return length;
+
+ length = -ENOMEM;
+ con = kzalloc(size+1, GFP_KERNEL);
+ if (!con)
+ return length;
+
+ user = kzalloc(size+1, GFP_KERNEL);
+ if (!user)
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(buf, "%s %s", con, user) != 2)
+ goto out2;
+
+ length = security_context_to_sid(con, strlen(con)+1, &sid);
+ if (length < 0)
+ goto out2;
+
+ length = security_get_user_sids(sid, user, &sids, &nsids);
+ if (length < 0)
+ goto out2;
+
+ length = sprintf(buf, "%u", nsids) + 1;
+ ptr = buf + length;
+ for (i = 0; i < nsids; i++) {
+ rc = security_sid_to_context(sids[i], &newcon, &len);
+ if (rc) {
+ length = rc;
+ goto out3;
+ }
+ if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) {
+ kfree(newcon);
+ length = -ERANGE;
+ goto out3;
+ }
+ memcpy(ptr, newcon, len);
+ kfree(newcon);
+ ptr += len;
+ length += len;
+ }
+out3:
+ kfree(sids);
+out2:
+ kfree(user);
+out:
+ kfree(con);
+ return length;
+}
+
+static ssize_t sel_write_member(struct file *file, char *buf, size_t size)
+{
+ char *scon, *tcon;
+ u32 ssid, tsid, newsid;
+ u16 tclass;
+ ssize_t length;
+ char *newcon;
+ u32 len;
+
+ length = task_has_security(current, SECURITY__COMPUTE_MEMBER);
+ if (length)
+ return length;
+
+ length = -ENOMEM;
+ scon = kzalloc(size+1, GFP_KERNEL);
+ if (!scon)
+ return length;
+
+ tcon = kzalloc(size+1, GFP_KERNEL);
+ if (!tcon)
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
+ goto out2;
+
+ length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
+ if (length < 0)
+ goto out2;
+ length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_member_sid(ssid, tsid, tclass, &newsid);
+ if (length < 0)
+ goto out2;
+
+ length = security_sid_to_context(newsid, &newcon, &len);
+ if (length < 0)
+ goto out2;
+
+ if (len > SIMPLE_TRANSACTION_LIMIT) {
+ printk(KERN_ERR "SELinux: %s: context size (%u) exceeds "
+ "payload max\n", __func__, len);
+ length = -ERANGE;
+ goto out3;
+ }
+
+ memcpy(buf, newcon, len);
+ length = len;
+out3:
+ kfree(newcon);
+out2:
+ kfree(tcon);
+out:
+ kfree(scon);
+ return length;
+}
+
+static struct inode *sel_make_inode(struct super_block *sb, int mode)
+{
+ struct inode *ret = new_inode(sb);
+
+ if (ret) {
+ ret->i_mode = mode;
+ ret->i_uid = ret->i_gid = 0;
+ ret->i_blocks = 0;
+ ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
+ }
+ return ret;
+}
+
+static ssize_t sel_read_bool(struct file *filep, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page = NULL;
+ ssize_t length;
+ ssize_t ret;
+ int cur_enforcing;
+ struct inode *inode = filep->f_path.dentry->d_inode;
+ unsigned index = inode->i_ino & SEL_INO_MASK;
+ const char *name = filep->f_path.dentry->d_name.name;
+
+ mutex_lock(&sel_mutex);
+
+ if (index >= bool_num || strcmp(name, bool_pending_names[index])) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (count > PAGE_SIZE) {
+ ret = -EINVAL;
+ goto out;
+ }
+ page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!page) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cur_enforcing = security_get_bool_value(index);
+ if (cur_enforcing < 0) {
+ ret = cur_enforcing;
+ goto out;
+ }
+ length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing,
+ bool_pending_values[index]);
+ ret = simple_read_from_buffer(buf, count, ppos, page, length);
+out:
+ mutex_unlock(&sel_mutex);
+ if (page)
+ free_page((unsigned long)page);
+ return ret;
+}
+
+static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page = NULL;
+ ssize_t length;
+ int new_value;
+ struct inode *inode = filep->f_path.dentry->d_inode;
+ unsigned index = inode->i_ino & SEL_INO_MASK;
+ const char *name = filep->f_path.dentry->d_name.name;
+
+ mutex_lock(&sel_mutex);
+
+ length = task_has_security(current, SECURITY__SETBOOL);
+ if (length)
+ goto out;
+
+ if (index >= bool_num || strcmp(name, bool_pending_names[index])) {
+ length = -EINVAL;
+ goto out;
+ }
+
+ if (count >= PAGE_SIZE) {
+ length = -ENOMEM;
+ goto out;
+ }
+
+ if (*ppos != 0) {
+ /* No partial writes. */
+ length = -EINVAL;
+ goto out;
+ }
+ page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!page) {
+ length = -ENOMEM;
+ goto out;
+ }
+
+ length = -EFAULT;
+ if (copy_from_user(page, buf, count))
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(page, "%d", &new_value) != 1)
+ goto out;
+
+ if (new_value)
+ new_value = 1;
+
+ bool_pending_values[index] = new_value;
+ length = count;
+
+out:
+ mutex_unlock(&sel_mutex);
+ if (page)
+ free_page((unsigned long) page);
+ return length;
+}
+
+static const struct file_operations sel_bool_ops = {
+ .read = sel_read_bool,
+ .write = sel_write_bool,
+};
+
+static ssize_t sel_commit_bools_write(struct file *filep,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page = NULL;
+ ssize_t length;
+ int new_value;
+
+ mutex_lock(&sel_mutex);
+
+ length = task_has_security(current, SECURITY__SETBOOL);
+ if (length)
+ goto out;
+
+ if (count >= PAGE_SIZE) {
+ length = -ENOMEM;
+ goto out;
+ }
+ if (*ppos != 0) {
+ /* No partial writes. */
+ goto out;
+ }
+ page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!page) {
+ length = -ENOMEM;
+ goto out;
+ }
+
+ length = -EFAULT;
+ if (copy_from_user(page, buf, count))
+ goto out;
+
+ length = -EINVAL;
+ if (sscanf(page, "%d", &new_value) != 1)
+ goto out;
+
+ if (new_value && bool_pending_values)
+ security_set_bools(bool_num, bool_pending_values);
+
+ length = count;
+
+out:
+ mutex_unlock(&sel_mutex);
+ if (page)
+ free_page((unsigned long) page);
+ return length;
+}
+
+static const struct file_operations sel_commit_bools_ops = {
+ .write = sel_commit_bools_write,
+};
+
+static void sel_remove_entries(struct dentry *de)
+{
+ struct list_head *node;
+
+ spin_lock(&dcache_lock);
+ node = de->d_subdirs.next;
+ while (node != &de->d_subdirs) {
+ struct dentry *d = list_entry(node, struct dentry, d_u.d_child);
+ list_del_init(node);
+
+ if (d->d_inode) {
+ d = dget_locked(d);
+ spin_unlock(&dcache_lock);
+ d_delete(d);
+ simple_unlink(de->d_inode, d);
+ dput(d);
+ spin_lock(&dcache_lock);
+ }
+ node = de->d_subdirs.next;
+ }
+
+ spin_unlock(&dcache_lock);
+}
+
+#define BOOL_DIR_NAME "booleans"
+
+static int sel_make_bools(void)
+{
+ int i, ret = 0;
+ ssize_t len;
+ struct dentry *dentry = NULL;
+ struct dentry *dir = bool_dir;
+ struct inode *inode = NULL;
+ struct inode_security_struct *isec;
+ char **names = NULL, *page;
+ int num;
+ int *values = NULL;
+ u32 sid;
+
+ /* remove any existing files */
+ kfree(bool_pending_names);
+ kfree(bool_pending_values);
+ bool_pending_names = NULL;
+ bool_pending_values = NULL;
+
+ sel_remove_entries(dir);
+
+ page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!page)
+ return -ENOMEM;
+
+ ret = security_get_bools(&num, &names, &values);
+ if (ret != 0)
+ goto out;
+
+ for (i = 0; i < num; i++) {
+ dentry = d_alloc_name(dir, names[i]);
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ inode = sel_make_inode(dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR);
+ if (!inode) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]);
+ if (len < 0) {
+ ret = -EINVAL;
+ goto err;
+ } else if (len >= PAGE_SIZE) {
+ ret = -ENAMETOOLONG;
+ goto err;
+ }
+ isec = (struct inode_security_struct *)inode->i_security;
+ ret = security_genfs_sid("selinuxfs", page, SECCLASS_FILE, &sid);
+ if (ret)
+ goto err;
+ isec->sid = sid;
+ isec->initialized = 1;
+ inode->i_fop = &sel_bool_ops;
+ inode->i_ino = i|SEL_BOOL_INO_OFFSET;
+ d_add(dentry, inode);
+ }
+ bool_num = num;
+ bool_pending_names = names;
+ bool_pending_values = values;
+out:
+ free_page((unsigned long)page);
+ return ret;
+err:
+ if (names) {
+ for (i = 0; i < num; i++)
+ kfree(names[i]);
+ kfree(names);
+ }
+ kfree(values);
+ sel_remove_entries(dir);
+ ret = -ENOMEM;
+ goto out;
+}
+
+#define NULL_FILE_NAME "null"
+
+struct dentry *selinux_null;
+
+static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%u", avc_cache_threshold);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static ssize_t sel_write_avc_cache_threshold(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+
+{
+ char *page;
+ ssize_t ret;
+ int new_value;
+
+ if (count >= PAGE_SIZE) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (*ppos != 0) {
+ /* No partial writes. */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ page = (char *)get_zeroed_page(GFP_KERNEL);
+ if (!page) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (copy_from_user(page, buf, count)) {
+ ret = -EFAULT;
+ goto out_free;
+ }
+
+ if (sscanf(page, "%u", &new_value) != 1) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (new_value != avc_cache_threshold) {
+ ret = task_has_security(current, SECURITY__SETSECPARAM);
+ if (ret)
+ goto out_free;
+ avc_cache_threshold = new_value;
+ }
+ ret = count;
+out_free:
+ free_page((unsigned long)page);
+out:
+ return ret;
+}
+
+static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char *page;
+ ssize_t ret = 0;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = avc_get_hash_stats(page);
+ if (ret >= 0)
+ ret = simple_read_from_buffer(buf, count, ppos, page, ret);
+ free_page((unsigned long)page);
+out:
+ return ret;
+}
+
+static const struct file_operations sel_avc_cache_threshold_ops = {
+ .read = sel_read_avc_cache_threshold,
+ .write = sel_write_avc_cache_threshold,
+};
+
+static const struct file_operations sel_avc_hash_stats_ops = {
+ .read = sel_read_avc_hash_stats,
+};
+
+#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
+static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx)
+{
+ int cpu;
+
+ for (cpu = *idx; cpu < NR_CPUS; ++cpu) {
+ if (!cpu_possible(cpu))
+ continue;
+ *idx = cpu + 1;
+ return &per_cpu(avc_cache_stats, cpu);
+ }
+ return NULL;
+}
+
+static void *sel_avc_stats_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ loff_t n = *pos - 1;
+
+ if (*pos == 0)
+ return SEQ_START_TOKEN;
+
+ return sel_avc_get_stat_idx(&n);
+}
+
+static void *sel_avc_stats_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ return sel_avc_get_stat_idx(pos);
+}
+
+static int sel_avc_stats_seq_show(struct seq_file *seq, void *v)
+{
+ struct avc_cache_stats *st = v;
+
+ if (v == SEQ_START_TOKEN)
+ seq_printf(seq, "lookups hits misses allocations reclaims "
+ "frees\n");
+ else
+ seq_printf(seq, "%u %u %u %u %u %u\n", st->lookups,
+ st->hits, st->misses, st->allocations,
+ st->reclaims, st->frees);
+ return 0;
+}
+
+static void sel_avc_stats_seq_stop(struct seq_file *seq, void *v)
+{ }
+
+static const struct seq_operations sel_avc_cache_stats_seq_ops = {
+ .start = sel_avc_stats_seq_start,
+ .next = sel_avc_stats_seq_next,
+ .show = sel_avc_stats_seq_show,
+ .stop = sel_avc_stats_seq_stop,
+};
+
+static int sel_open_avc_cache_stats(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &sel_avc_cache_stats_seq_ops);
+}
+
+static const struct file_operations sel_avc_cache_stats_ops = {
+ .open = sel_open_avc_cache_stats,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+#endif
+
+static int sel_make_avc_files(struct dentry *dir)
+{
+ int i, ret = 0;
+ static struct tree_descr files[] = {
+ { "cache_threshold",
+ &sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR },
+ { "hash_stats", &sel_avc_hash_stats_ops, S_IRUGO },
+#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
+ { "cache_stats", &sel_avc_cache_stats_ops, S_IRUGO },
+#endif
+ };
+
+ for (i = 0; i < ARRAY_SIZE(files); i++) {
+ struct inode *inode;
+ struct dentry *dentry;
+
+ dentry = d_alloc_name(dir, files[i].name);
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode);
+ if (!inode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ inode->i_fop = files[i].ops;
+ inode->i_ino = ++sel_last_ino;
+ d_add(dentry, inode);
+ }
+out:
+ return ret;
+}
+
+static ssize_t sel_read_initcon(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct inode *inode;
+ char *con;
+ u32 sid, len;
+ ssize_t ret;
+
+ inode = file->f_path.dentry->d_inode;
+ sid = inode->i_ino&SEL_INO_MASK;
+ ret = security_sid_to_context(sid, &con, &len);
+ if (ret < 0)
+ return ret;
+
+ ret = simple_read_from_buffer(buf, count, ppos, con, len);
+ kfree(con);
+ return ret;
+}
+
+static const struct file_operations sel_initcon_ops = {
+ .read = sel_read_initcon,
+};
+
+static int sel_make_initcon_files(struct dentry *dir)
+{
+ int i, ret = 0;
+
+ for (i = 1; i <= SECINITSID_NUM; i++) {
+ struct inode *inode;
+ struct dentry *dentry;
+ dentry = d_alloc_name(dir, security_get_initial_sid_context(i));
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
+ if (!inode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ inode->i_fop = &sel_initcon_ops;
+ inode->i_ino = i|SEL_INITCON_INO_OFFSET;
+ d_add(dentry, inode);
+ }
+out:
+ return ret;
+}
+
+static inline unsigned int sel_div(unsigned long a, unsigned long b)
+{
+ return a / b - (a % b < 0);
+}
+
+static inline unsigned long sel_class_to_ino(u16 class)
+{
+ return (class * (SEL_VEC_MAX + 1)) | SEL_CLASS_INO_OFFSET;
+}
+
+static inline u16 sel_ino_to_class(unsigned long ino)
+{
+ return sel_div(ino & SEL_INO_MASK, SEL_VEC_MAX + 1);
+}
+
+static inline unsigned long sel_perm_to_ino(u16 class, u32 perm)
+{
+ return (class * (SEL_VEC_MAX + 1) + perm) | SEL_CLASS_INO_OFFSET;
+}
+
+static inline u32 sel_ino_to_perm(unsigned long ino)
+{
+ return (ino & SEL_INO_MASK) % (SEL_VEC_MAX + 1);
+}
+
+static ssize_t sel_read_class(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t rc, len;
+ char *page;
+ unsigned long ino = file->f_path.dentry->d_inode->i_ino;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ len = snprintf(page, PAGE_SIZE, "%d", sel_ino_to_class(ino));
+ rc = simple_read_from_buffer(buf, count, ppos, page, len);
+ free_page((unsigned long)page);
+out:
+ return rc;
+}
+
+static const struct file_operations sel_class_ops = {
+ .read = sel_read_class,
+};
+
+static ssize_t sel_read_perm(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t rc, len;
+ char *page;
+ unsigned long ino = file->f_path.dentry->d_inode->i_ino;
+
+ page = (char *)__get_free_page(GFP_KERNEL);
+ if (!page) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ len = snprintf(page, PAGE_SIZE, "%d", sel_ino_to_perm(ino));
+ rc = simple_read_from_buffer(buf, count, ppos, page, len);
+ free_page((unsigned long)page);
+out:
+ return rc;
+}
+
+static const struct file_operations sel_perm_ops = {
+ .read = sel_read_perm,
+};
+
+static ssize_t sel_read_policycap(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int value;
+ char tmpbuf[TMPBUFLEN];
+ ssize_t length;
+ unsigned long i_ino = file->f_path.dentry->d_inode->i_ino;
+
+ value = security_policycap_supported(i_ino & SEL_INO_MASK);
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%d", value);
+
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static const struct file_operations sel_policycap_ops = {
+ .read = sel_read_policycap,
+};
+
+static int sel_make_perm_files(char *objclass, int classvalue,
+ struct dentry *dir)
+{
+ int i, rc = 0, nperms;
+ char **perms;
+
+ rc = security_get_permissions(objclass, &perms, &nperms);
+ if (rc)
+ goto out;
+
+ for (i = 0; i < nperms; i++) {
+ struct inode *inode;
+ struct dentry *dentry;
+
+ dentry = d_alloc_name(dir, perms[i]);
+ if (!dentry) {
+ rc = -ENOMEM;
+ goto out1;
+ }
+
+ inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
+ if (!inode) {
+ rc = -ENOMEM;
+ goto out1;
+ }
+ inode->i_fop = &sel_perm_ops;
+ /* i+1 since perm values are 1-indexed */
+ inode->i_ino = sel_perm_to_ino(classvalue, i+1);
+ d_add(dentry, inode);
+ }
+
+out1:
+ for (i = 0; i < nperms; i++)
+ kfree(perms[i]);
+ kfree(perms);
+out:
+ return rc;
+}
+
+static int sel_make_class_dir_entries(char *classname, int index,
+ struct dentry *dir)
+{
+ struct dentry *dentry = NULL;
+ struct inode *inode = NULL;
+ int rc;
+
+ dentry = d_alloc_name(dir, "index");
+ if (!dentry) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
+ if (!inode) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ inode->i_fop = &sel_class_ops;
+ inode->i_ino = sel_class_to_ino(index);
+ d_add(dentry, inode);
+
+ dentry = d_alloc_name(dir, "perms");
+ if (!dentry) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = sel_make_dir(dir->d_inode, dentry, &last_class_ino);
+ if (rc)
+ goto out;
+
+ rc = sel_make_perm_files(classname, index, dentry);
+
+out:
+ return rc;
+}
+
+static void sel_remove_classes(void)
+{
+ struct list_head *class_node;
+
+ list_for_each(class_node, &class_dir->d_subdirs) {
+ struct dentry *class_subdir = list_entry(class_node,
+ struct dentry, d_u.d_child);
+ struct list_head *class_subdir_node;
+
+ list_for_each(class_subdir_node, &class_subdir->d_subdirs) {
+ struct dentry *d = list_entry(class_subdir_node,
+ struct dentry, d_u.d_child);
+
+ if (d->d_inode)
+ if (d->d_inode->i_mode & S_IFDIR)
+ sel_remove_entries(d);
+ }
+
+ sel_remove_entries(class_subdir);
+ }
+
+ sel_remove_entries(class_dir);
+}
+
+static int sel_make_classes(void)
+{
+ int rc = 0, nclasses, i;
+ char **classes;
+
+ /* delete any existing entries */
+ sel_remove_classes();
+
+ rc = security_get_classes(&classes, &nclasses);
+ if (rc < 0)
+ goto out;
+
+ /* +2 since classes are 1-indexed */
+ last_class_ino = sel_class_to_ino(nclasses+2);
+
+ for (i = 0; i < nclasses; i++) {
+ struct dentry *class_name_dir;
+
+ class_name_dir = d_alloc_name(class_dir, classes[i]);
+ if (!class_name_dir) {
+ rc = -ENOMEM;
+ goto out1;
+ }
+
+ rc = sel_make_dir(class_dir->d_inode, class_name_dir,
+ &last_class_ino);
+ if (rc)
+ goto out1;
+
+ /* i+1 since class values are 1-indexed */
+ rc = sel_make_class_dir_entries(classes[i], i+1,
+ class_name_dir);
+ if (rc)
+ goto out1;
+ }
+
+out1:
+ for (i = 0; i < nclasses; i++)
+ kfree(classes[i]);
+ kfree(classes);
+out:
+ return rc;
+}
+
+static int sel_make_policycap(void)
+{
+ unsigned int iter;
+ struct dentry *dentry = NULL;
+ struct inode *inode = NULL;
+
+ sel_remove_entries(policycap_dir);
+
+ for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) {
+ if (iter < ARRAY_SIZE(policycap_names))
+ dentry = d_alloc_name(policycap_dir,
+ policycap_names[iter]);
+ else
+ dentry = d_alloc_name(policycap_dir, "unknown");
+
+ if (dentry == NULL)
+ return -ENOMEM;
+
+ inode = sel_make_inode(policycap_dir->d_sb, S_IFREG | S_IRUGO);
+ if (inode == NULL)
+ return -ENOMEM;
+
+ inode->i_fop = &sel_policycap_ops;
+ inode->i_ino = iter | SEL_POLICYCAP_INO_OFFSET;
+ d_add(dentry, inode);
+ }
+
+ return 0;
+}
+
+static int sel_make_dir(struct inode *dir, struct dentry *dentry,
+ unsigned long *ino)
+{
+ int ret = 0;
+ struct inode *inode;
+
+ inode = sel_make_inode(dir->i_sb, S_IFDIR | S_IRUGO | S_IXUGO);
+ if (!inode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ inode->i_op = &simple_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+ inode->i_ino = ++(*ino);
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inc_nlink(inode);
+ d_add(dentry, inode);
+ /* bump link count on parent directory, too */
+ inc_nlink(dir);
+out:
+ return ret;
+}
+
+static int sel_fill_super(struct super_block *sb, void *data, int silent)
+{
+ int ret;
+ struct dentry *dentry;
+ struct inode *inode, *root_inode;
+ struct inode_security_struct *isec;
+
+ static struct tree_descr selinux_files[] = {
+ [SEL_LOAD] = {"load", &sel_load_ops, S_IRUSR|S_IWUSR},
+ [SEL_ENFORCE] = {"enforce", &sel_enforce_ops, S_IRUGO|S_IWUSR},
+ [SEL_CONTEXT] = {"context", &transaction_ops, S_IRUGO|S_IWUGO},
+ [SEL_ACCESS] = {"access", &transaction_ops, S_IRUGO|S_IWUGO},
+ [SEL_CREATE] = {"create", &transaction_ops, S_IRUGO|S_IWUGO},
+ [SEL_RELABEL] = {"relabel", &transaction_ops, S_IRUGO|S_IWUGO},
+ [SEL_USER] = {"user", &transaction_ops, S_IRUGO|S_IWUGO},
+ [SEL_POLICYVERS] = {"policyvers", &sel_policyvers_ops, S_IRUGO},
+ [SEL_COMMIT_BOOLS] = {"commit_pending_bools", &sel_commit_bools_ops, S_IWUSR},
+ [SEL_MLS] = {"mls", &sel_mls_ops, S_IRUGO},
+ [SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR},
+ [SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO},
+ [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR},
+ [SEL_COMPAT_NET] = {"compat_net", &sel_compat_net_ops, S_IRUGO|S_IWUSR},
+ [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO},
+ [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO},
+ /* last one */ {""}
+ };
+ ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
+ if (ret)
+ goto err;
+
+ root_inode = sb->s_root->d_inode;
+
+ dentry = d_alloc_name(sb->s_root, BOOL_DIR_NAME);
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
+ if (ret)
+ goto err;
+
+ bool_dir = dentry;
+
+ dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME);
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO);
+ if (!inode) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ inode->i_ino = ++sel_last_ino;
+ isec = (struct inode_security_struct *)inode->i_security;
+ isec->sid = SECINITSID_DEVNULL;
+ isec->sclass = SECCLASS_CHR_FILE;
+ isec->initialized = 1;
+
+ init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3));
+ d_add(dentry, inode);
+ selinux_null = dentry;
+
+ dentry = d_alloc_name(sb->s_root, "avc");
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
+ if (ret)
+ goto err;
+
+ ret = sel_make_avc_files(dentry);
+ if (ret)
+ goto err;
+
+ dentry = d_alloc_name(sb->s_root, "initial_contexts");
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
+ if (ret)
+ goto err;
+
+ ret = sel_make_initcon_files(dentry);
+ if (ret)
+ goto err;
+
+ dentry = d_alloc_name(sb->s_root, "class");
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
+ if (ret)
+ goto err;
+
+ class_dir = dentry;
+
+ dentry = d_alloc_name(sb->s_root, "policy_capabilities");
+ if (!dentry) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = sel_make_dir(root_inode, dentry, &sel_last_ino);
+ if (ret)
+ goto err;
+
+ policycap_dir = dentry;
+
+out:
+ return ret;
+err:
+ printk(KERN_ERR "SELinux: %s: failed while creating inodes\n",
+ __func__);
+ goto out;
+}
+
+static int sel_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data,
+ struct vfsmount *mnt)
+{
+ return get_sb_single(fs_type, flags, data, sel_fill_super, mnt);
+}
+
+static struct file_system_type sel_fs_type = {
+ .name = "selinuxfs",
+ .get_sb = sel_get_sb,
+ .kill_sb = kill_litter_super,
+};
+
+struct vfsmount *selinuxfs_mount;
+
+static int __init init_sel_fs(void)
+{
+ int err;
+
+ if (!selinux_enabled)
+ return 0;
+ err = register_filesystem(&sel_fs_type);
+ if (!err) {
+ selinuxfs_mount = kern_mount(&sel_fs_type);
+ if (IS_ERR(selinuxfs_mount)) {
+ printk(KERN_ERR "selinuxfs: could not mount!\n");
+ err = PTR_ERR(selinuxfs_mount);
+ selinuxfs_mount = NULL;
+ }
+ }
+ return err;
+}
+
+__initcall(init_sel_fs);
+
+#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+void exit_sel_fs(void)
+{
+ unregister_filesystem(&sel_fs_type);
+}
+#endif
diff --git a/security/selinux/ss/Makefile b/security/selinux/ss/Makefile
new file mode 100644
index 0000000..bad7877
--- /dev/null
+++ b/security/selinux/ss/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for building the SELinux security server as part of the kernel tree.
+#
+
+EXTRA_CFLAGS += -Isecurity/selinux/include
+obj-y := ss.o
+
+ss-y := ebitmap.o hashtab.o symtab.o sidtab.o avtab.o policydb.o services.o conditional.o mls.o
+
diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c
new file mode 100644
index 0000000..1215b8e
--- /dev/null
+++ b/security/selinux/ss/avtab.c
@@ -0,0 +1,515 @@
+/*
+ * Implementation of the access vector table type.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+
+/* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
+ *
+ * Added conditional policy language extensions
+ *
+ * Copyright (C) 2003 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp>
+ * Tuned number of hash slots for avtab to reduce memory usage
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include "avtab.h"
+#include "policydb.h"
+
+static struct kmem_cache *avtab_node_cachep;
+
+static inline int avtab_hash(struct avtab_key *keyp, u16 mask)
+{
+ return ((keyp->target_class + (keyp->target_type << 2) +
+ (keyp->source_type << 9)) & mask);
+}
+
+static struct avtab_node*
+avtab_insert_node(struct avtab *h, int hvalue,
+ struct avtab_node *prev, struct avtab_node *cur,
+ struct avtab_key *key, struct avtab_datum *datum)
+{
+ struct avtab_node *newnode;
+ newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
+ if (newnode == NULL)
+ return NULL;
+ newnode->key = *key;
+ newnode->datum = *datum;
+ if (prev) {
+ newnode->next = prev->next;
+ prev->next = newnode;
+ } else {
+ newnode->next = h->htable[hvalue];
+ h->htable[hvalue] = newnode;
+ }
+
+ h->nel++;
+ return newnode;
+}
+
+static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum)
+{
+ int hvalue;
+ struct avtab_node *prev, *cur, *newnode;
+ u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
+
+ if (!h || !h->htable)
+ return -EINVAL;
+
+ hvalue = avtab_hash(key, h->mask);
+ for (prev = NULL, cur = h->htable[hvalue];
+ cur;
+ prev = cur, cur = cur->next) {
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class == cur->key.target_class &&
+ (specified & cur->key.specified))
+ return -EEXIST;
+ if (key->source_type < cur->key.source_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type < cur->key.target_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class < cur->key.target_class)
+ break;
+ }
+
+ newnode = avtab_insert_node(h, hvalue, prev, cur, key, datum);
+ if (!newnode)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/* Unlike avtab_insert(), this function allow multiple insertions of the same
+ * key/specified mask into the table, as needed by the conditional avtab.
+ * It also returns a pointer to the node inserted.
+ */
+struct avtab_node *
+avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datum *datum)
+{
+ int hvalue;
+ struct avtab_node *prev, *cur;
+ u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
+
+ if (!h || !h->htable)
+ return NULL;
+ hvalue = avtab_hash(key, h->mask);
+ for (prev = NULL, cur = h->htable[hvalue];
+ cur;
+ prev = cur, cur = cur->next) {
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class == cur->key.target_class &&
+ (specified & cur->key.specified))
+ break;
+ if (key->source_type < cur->key.source_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type < cur->key.target_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class < cur->key.target_class)
+ break;
+ }
+ return avtab_insert_node(h, hvalue, prev, cur, key, datum);
+}
+
+struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key)
+{
+ int hvalue;
+ struct avtab_node *cur;
+ u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
+
+ if (!h || !h->htable)
+ return NULL;
+
+ hvalue = avtab_hash(key, h->mask);
+ for (cur = h->htable[hvalue]; cur; cur = cur->next) {
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class == cur->key.target_class &&
+ (specified & cur->key.specified))
+ return &cur->datum;
+
+ if (key->source_type < cur->key.source_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type < cur->key.target_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class < cur->key.target_class)
+ break;
+ }
+
+ return NULL;
+}
+
+/* This search function returns a node pointer, and can be used in
+ * conjunction with avtab_search_next_node()
+ */
+struct avtab_node*
+avtab_search_node(struct avtab *h, struct avtab_key *key)
+{
+ int hvalue;
+ struct avtab_node *cur;
+ u16 specified = key->specified & ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
+
+ if (!h || !h->htable)
+ return NULL;
+
+ hvalue = avtab_hash(key, h->mask);
+ for (cur = h->htable[hvalue]; cur; cur = cur->next) {
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class == cur->key.target_class &&
+ (specified & cur->key.specified))
+ return cur;
+
+ if (key->source_type < cur->key.source_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type < cur->key.target_type)
+ break;
+ if (key->source_type == cur->key.source_type &&
+ key->target_type == cur->key.target_type &&
+ key->target_class < cur->key.target_class)
+ break;
+ }
+ return NULL;
+}
+
+struct avtab_node*
+avtab_search_node_next(struct avtab_node *node, int specified)
+{
+ struct avtab_node *cur;
+
+ if (!node)
+ return NULL;
+
+ specified &= ~(AVTAB_ENABLED|AVTAB_ENABLED_OLD);
+ for (cur = node->next; cur; cur = cur->next) {
+ if (node->key.source_type == cur->key.source_type &&
+ node->key.target_type == cur->key.target_type &&
+ node->key.target_class == cur->key.target_class &&
+ (specified & cur->key.specified))
+ return cur;
+
+ if (node->key.source_type < cur->key.source_type)
+ break;
+ if (node->key.source_type == cur->key.source_type &&
+ node->key.target_type < cur->key.target_type)
+ break;
+ if (node->key.source_type == cur->key.source_type &&
+ node->key.target_type == cur->key.target_type &&
+ node->key.target_class < cur->key.target_class)
+ break;
+ }
+ return NULL;
+}
+
+void avtab_destroy(struct avtab *h)
+{
+ int i;
+ struct avtab_node *cur, *temp;
+
+ if (!h || !h->htable)
+ return;
+
+ for (i = 0; i < h->nslot; i++) {
+ cur = h->htable[i];
+ while (cur) {
+ temp = cur;
+ cur = cur->next;
+ kmem_cache_free(avtab_node_cachep, temp);
+ }
+ h->htable[i] = NULL;
+ }
+ kfree(h->htable);
+ h->htable = NULL;
+ h->nslot = 0;
+ h->mask = 0;
+}
+
+int avtab_init(struct avtab *h)
+{
+ h->htable = NULL;
+ h->nel = 0;
+ return 0;
+}
+
+int avtab_alloc(struct avtab *h, u32 nrules)
+{
+ u16 mask = 0;
+ u32 shift = 0;
+ u32 work = nrules;
+ u32 nslot = 0;
+
+ if (nrules == 0)
+ goto avtab_alloc_out;
+
+ while (work) {
+ work = work >> 1;
+ shift++;
+ }
+ if (shift > 2)
+ shift = shift - 2;
+ nslot = 1 << shift;
+ if (nslot > MAX_AVTAB_SIZE)
+ nslot = MAX_AVTAB_SIZE;
+ mask = nslot - 1;
+
+ h->htable = kcalloc(nslot, sizeof(*(h->htable)), GFP_KERNEL);
+ if (!h->htable)
+ return -ENOMEM;
+
+ avtab_alloc_out:
+ h->nel = 0;
+ h->nslot = nslot;
+ h->mask = mask;
+ printk(KERN_DEBUG "SELinux: %d avtab hash slots, %d rules.\n",
+ h->nslot, nrules);
+ return 0;
+}
+
+void avtab_hash_eval(struct avtab *h, char *tag)
+{
+ int i, chain_len, slots_used, max_chain_len;
+ unsigned long long chain2_len_sum;
+ struct avtab_node *cur;
+
+ slots_used = 0;
+ max_chain_len = 0;
+ chain2_len_sum = 0;
+ for (i = 0; i < h->nslot; i++) {
+ cur = h->htable[i];
+ if (cur) {
+ slots_used++;
+ chain_len = 0;
+ while (cur) {
+ chain_len++;
+ cur = cur->next;
+ }
+
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ chain2_len_sum += chain_len * chain_len;
+ }
+ }
+
+ printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, "
+ "longest chain length %d sum of chain length^2 %llu\n",
+ tag, h->nel, slots_used, h->nslot, max_chain_len,
+ chain2_len_sum);
+}
+
+static uint16_t spec_order[] = {
+ AVTAB_ALLOWED,
+ AVTAB_AUDITDENY,
+ AVTAB_AUDITALLOW,
+ AVTAB_TRANSITION,
+ AVTAB_CHANGE,
+ AVTAB_MEMBER
+};
+
+int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
+ int (*insertf)(struct avtab *a, struct avtab_key *k,
+ struct avtab_datum *d, void *p),
+ void *p)
+{
+ __le16 buf16[4];
+ u16 enabled;
+ __le32 buf32[7];
+ u32 items, items2, val, vers = pol->policyvers;
+ struct avtab_key key;
+ struct avtab_datum datum;
+ int i, rc;
+ unsigned set;
+
+ memset(&key, 0, sizeof(struct avtab_key));
+ memset(&datum, 0, sizeof(struct avtab_datum));
+
+ if (vers < POLICYDB_VERSION_AVTAB) {
+ rc = next_entry(buf32, fp, sizeof(u32));
+ if (rc < 0) {
+ printk(KERN_ERR "SELinux: avtab: truncated entry\n");
+ return -1;
+ }
+ items2 = le32_to_cpu(buf32[0]);
+ if (items2 > ARRAY_SIZE(buf32)) {
+ printk(KERN_ERR "SELinux: avtab: entry overflow\n");
+ return -1;
+
+ }
+ rc = next_entry(buf32, fp, sizeof(u32)*items2);
+ if (rc < 0) {
+ printk(KERN_ERR "SELinux: avtab: truncated entry\n");
+ return -1;
+ }
+ items = 0;
+
+ val = le32_to_cpu(buf32[items++]);
+ key.source_type = (u16)val;
+ if (key.source_type != val) {
+ printk(KERN_ERR "SELinux: avtab: truncated source type\n");
+ return -1;
+ }
+ val = le32_to_cpu(buf32[items++]);
+ key.target_type = (u16)val;
+ if (key.target_type != val) {
+ printk(KERN_ERR "SELinux: avtab: truncated target type\n");
+ return -1;
+ }
+ val = le32_to_cpu(buf32[items++]);
+ key.target_class = (u16)val;
+ if (key.target_class != val) {
+ printk(KERN_ERR "SELinux: avtab: truncated target class\n");
+ return -1;
+ }
+
+ val = le32_to_cpu(buf32[items++]);
+ enabled = (val & AVTAB_ENABLED_OLD) ? AVTAB_ENABLED : 0;
+
+ if (!(val & (AVTAB_AV | AVTAB_TYPE))) {
+ printk(KERN_ERR "SELinux: avtab: null entry\n");
+ return -1;
+ }
+ if ((val & AVTAB_AV) &&
+ (val & AVTAB_TYPE)) {
+ printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n");
+ return -1;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
+ if (val & spec_order[i]) {
+ key.specified = spec_order[i] | enabled;
+ datum.data = le32_to_cpu(buf32[items++]);
+ rc = insertf(a, &key, &datum, p);
+ if (rc)
+ return rc;
+ }
+ }
+
+ if (items != items2) {
+ printk(KERN_ERR "SELinux: avtab: entry only had %d items, expected %d\n", items2, items);
+ return -1;
+ }
+ return 0;
+ }
+
+ rc = next_entry(buf16, fp, sizeof(u16)*4);
+ if (rc < 0) {
+ printk(KERN_ERR "SELinux: avtab: truncated entry\n");
+ return -1;
+ }
+
+ items = 0;
+ key.source_type = le16_to_cpu(buf16[items++]);
+ key.target_type = le16_to_cpu(buf16[items++]);
+ key.target_class = le16_to_cpu(buf16[items++]);
+ key.specified = le16_to_cpu(buf16[items++]);
+
+ if (!policydb_type_isvalid(pol, key.source_type) ||
+ !policydb_type_isvalid(pol, key.target_type) ||
+ !policydb_class_isvalid(pol, key.target_class)) {
+ printk(KERN_ERR "SELinux: avtab: invalid type or class\n");
+ return -1;
+ }
+
+ set = 0;
+ for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
+ if (key.specified & spec_order[i])
+ set++;
+ }
+ if (!set || set > 1) {
+ printk(KERN_ERR "SELinux: avtab: more than one specifier\n");
+ return -1;
+ }
+
+ rc = next_entry(buf32, fp, sizeof(u32));
+ if (rc < 0) {
+ printk(KERN_ERR "SELinux: avtab: truncated entry\n");
+ return -1;
+ }
+ datum.data = le32_to_cpu(*buf32);
+ if ((key.specified & AVTAB_TYPE) &&
+ !policydb_type_isvalid(pol, datum.data)) {
+ printk(KERN_ERR "SELinux: avtab: invalid type\n");
+ return -1;
+ }
+ return insertf(a, &key, &datum, p);
+}
+
+static int avtab_insertf(struct avtab *a, struct avtab_key *k,
+ struct avtab_datum *d, void *p)
+{
+ return avtab_insert(a, k, d);
+}
+
+int avtab_read(struct avtab *a, void *fp, struct policydb *pol)
+{
+ int rc;
+ __le32 buf[1];
+ u32 nel, i;
+
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0) {
+ printk(KERN_ERR "SELinux: avtab: truncated table\n");
+ goto bad;
+ }
+ nel = le32_to_cpu(buf[0]);
+ if (!nel) {
+ printk(KERN_ERR "SELinux: avtab: table is empty\n");
+ rc = -EINVAL;
+ goto bad;
+ }
+
+ rc = avtab_alloc(a, nel);
+ if (rc)
+ goto bad;
+
+ for (i = 0; i < nel; i++) {
+ rc = avtab_read_item(a, fp, pol, avtab_insertf, NULL);
+ if (rc) {
+ if (rc == -ENOMEM)
+ printk(KERN_ERR "SELinux: avtab: out of memory\n");
+ else if (rc == -EEXIST)
+ printk(KERN_ERR "SELinux: avtab: duplicate entry\n");
+ else
+ rc = -EINVAL;
+ goto bad;
+ }
+ }
+
+ rc = 0;
+out:
+ return rc;
+
+bad:
+ avtab_destroy(a);
+ goto out;
+}
+
+void avtab_cache_init(void)
+{
+ avtab_node_cachep = kmem_cache_create("avtab_node",
+ sizeof(struct avtab_node),
+ 0, SLAB_PANIC, NULL);
+}
+
+void avtab_cache_destroy(void)
+{
+ kmem_cache_destroy(avtab_node_cachep);
+}
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
new file mode 100644
index 0000000..8da6a84
--- /dev/null
+++ b/security/selinux/ss/avtab.h
@@ -0,0 +1,91 @@
+/*
+ * An access vector table (avtab) is a hash table
+ * of access vectors and transition types indexed
+ * by a type pair and a class. An access vector
+ * table is used to represent the type enforcement
+ * tables.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+
+/* Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
+ *
+ * Added conditional policy language extensions
+ *
+ * Copyright (C) 2003 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * Updated: Yuichi Nakamura <ynakam@hitachisoft.jp>
+ * Tuned number of hash slots for avtab to reduce memory usage
+ */
+#ifndef _SS_AVTAB_H_
+#define _SS_AVTAB_H_
+
+struct avtab_key {
+ u16 source_type; /* source type */
+ u16 target_type; /* target type */
+ u16 target_class; /* target object class */
+#define AVTAB_ALLOWED 1
+#define AVTAB_AUDITALLOW 2
+#define AVTAB_AUDITDENY 4
+#define AVTAB_AV (AVTAB_ALLOWED | AVTAB_AUDITALLOW | AVTAB_AUDITDENY)
+#define AVTAB_TRANSITION 16
+#define AVTAB_MEMBER 32
+#define AVTAB_CHANGE 64
+#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
+#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
+#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
+ u16 specified; /* what field is specified */
+};
+
+struct avtab_datum {
+ u32 data; /* access vector or type value */
+};
+
+struct avtab_node {
+ struct avtab_key key;
+ struct avtab_datum datum;
+ struct avtab_node *next;
+};
+
+struct avtab {
+ struct avtab_node **htable;
+ u32 nel; /* number of elements */
+ u32 nslot; /* number of hash slots */
+ u16 mask; /* mask to compute hash func */
+
+};
+
+int avtab_init(struct avtab *);
+int avtab_alloc(struct avtab *, u32);
+struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *k);
+void avtab_destroy(struct avtab *h);
+void avtab_hash_eval(struct avtab *h, char *tag);
+
+struct policydb;
+int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
+ int (*insert)(struct avtab *a, struct avtab_key *k,
+ struct avtab_datum *d, void *p),
+ void *p);
+
+int avtab_read(struct avtab *a, void *fp, struct policydb *pol);
+
+struct avtab_node *avtab_insert_nonunique(struct avtab *h, struct avtab_key *key,
+ struct avtab_datum *datum);
+
+struct avtab_node *avtab_search_node(struct avtab *h, struct avtab_key *key);
+
+struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified);
+
+void avtab_cache_init(void);
+void avtab_cache_destroy(void);
+
+#define MAX_AVTAB_HASH_BITS 13
+#define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS)
+#define MAX_AVTAB_HASH_MASK (MAX_AVTAB_HASH_BUCKETS-1)
+#define MAX_AVTAB_SIZE MAX_AVTAB_HASH_BUCKETS
+
+#endif /* _SS_AVTAB_H_ */
+
diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c
new file mode 100644
index 0000000..4a4e35c
--- /dev/null
+++ b/security/selinux/ss/conditional.c
@@ -0,0 +1,506 @@
+/* Authors: Karl MacMillan <kmacmillan@tresys.com>
+ * Frank Mayer <mayerf@tresys.com>
+ *
+ * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+#include "security.h"
+#include "conditional.h"
+
+/*
+ * cond_evaluate_expr evaluates a conditional expr
+ * in reverse polish notation. It returns true (1), false (0),
+ * or undefined (-1). Undefined occurs when the expression
+ * exceeds the stack depth of COND_EXPR_MAXDEPTH.
+ */
+static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr)
+{
+
+ struct cond_expr *cur;
+ int s[COND_EXPR_MAXDEPTH];
+ int sp = -1;
+
+ for (cur = expr; cur; cur = cur->next) {
+ switch (cur->expr_type) {
+ case COND_BOOL:
+ if (sp == (COND_EXPR_MAXDEPTH - 1))
+ return -1;
+ sp++;
+ s[sp] = p->bool_val_to_struct[cur->bool - 1]->state;
+ break;
+ case COND_NOT:
+ if (sp < 0)
+ return -1;
+ s[sp] = !s[sp];
+ break;
+ case COND_OR:
+ if (sp < 1)
+ return -1;
+ sp--;
+ s[sp] |= s[sp + 1];
+ break;
+ case COND_AND:
+ if (sp < 1)
+ return -1;
+ sp--;
+ s[sp] &= s[sp + 1];
+ break;
+ case COND_XOR:
+ if (sp < 1)
+ return -1;
+ sp--;
+ s[sp] ^= s[sp + 1];
+ break;
+ case COND_EQ:
+ if (sp < 1)
+ return -1;
+ sp--;
+ s[sp] = (s[sp] == s[sp + 1]);
+ break;
+ case COND_NEQ:
+ if (sp < 1)
+ return -1;
+ sp--;
+ s[sp] = (s[sp] != s[sp + 1]);
+ break;
+ default:
+ return -1;
+ }
+ }
+ return s[0];
+}
+
+/*
+ * evaluate_cond_node evaluates the conditional stored in
+ * a struct cond_node and if the result is different than the
+ * current state of the node it sets the rules in the true/false
+ * list appropriately. If the result of the expression is undefined
+ * all of the rules are disabled for safety.
+ */
+int evaluate_cond_node(struct policydb *p, struct cond_node *node)
+{
+ int new_state;
+ struct cond_av_list *cur;
+
+ new_state = cond_evaluate_expr(p, node->expr);
+ if (new_state != node->cur_state) {
+ node->cur_state = new_state;
+ if (new_state == -1)
+ printk(KERN_ERR "SELinux: expression result was undefined - disabling all rules.\n");
+ /* turn the rules on or off */
+ for (cur = node->true_list; cur; cur = cur->next) {
+ if (new_state <= 0)
+ cur->node->key.specified &= ~AVTAB_ENABLED;
+ else
+ cur->node->key.specified |= AVTAB_ENABLED;
+ }
+
+ for (cur = node->false_list; cur; cur = cur->next) {
+ /* -1 or 1 */
+ if (new_state)
+ cur->node->key.specified &= ~AVTAB_ENABLED;
+ else
+ cur->node->key.specified |= AVTAB_ENABLED;
+ }
+ }
+ return 0;
+}
+
+int cond_policydb_init(struct policydb *p)
+{
+ p->bool_val_to_struct = NULL;
+ p->cond_list = NULL;
+ if (avtab_init(&p->te_cond_avtab))
+ return -1;
+
+ return 0;
+}
+
+static void cond_av_list_destroy(struct cond_av_list *list)
+{
+ struct cond_av_list *cur, *next;
+ for (cur = list; cur; cur = next) {
+ next = cur->next;
+ /* the avtab_ptr_t node is destroy by the avtab */
+ kfree(cur);
+ }
+}
+
+static void cond_node_destroy(struct cond_node *node)
+{
+ struct cond_expr *cur_expr, *next_expr;
+
+ for (cur_expr = node->expr; cur_expr; cur_expr = next_expr) {
+ next_expr = cur_expr->next;
+ kfree(cur_expr);
+ }
+ cond_av_list_destroy(node->true_list);
+ cond_av_list_destroy(node->false_list);
+ kfree(node);
+}
+
+static void cond_list_destroy(struct cond_node *list)
+{
+ struct cond_node *next, *cur;
+
+ if (list == NULL)
+ return;
+
+ for (cur = list; cur; cur = next) {
+ next = cur->next;
+ cond_node_destroy(cur);
+ }
+}
+
+void cond_policydb_destroy(struct policydb *p)
+{
+ kfree(p->bool_val_to_struct);
+ avtab_destroy(&p->te_cond_avtab);
+ cond_list_destroy(p->cond_list);
+}
+
+int cond_init_bool_indexes(struct policydb *p)
+{
+ kfree(p->bool_val_to_struct);
+ p->bool_val_to_struct = (struct cond_bool_datum **)
+ kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum *), GFP_KERNEL);
+ if (!p->bool_val_to_struct)
+ return -1;
+ return 0;
+}
+
+int cond_destroy_bool(void *key, void *datum, void *p)
+{
+ kfree(key);
+ kfree(datum);
+ return 0;
+}
+
+int cond_index_bool(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct cond_bool_datum *booldatum;
+
+ booldatum = datum;
+ p = datap;
+
+ if (!booldatum->value || booldatum->value > p->p_bools.nprim)
+ return -EINVAL;
+
+ p->p_bool_val_to_name[booldatum->value - 1] = key;
+ p->bool_val_to_struct[booldatum->value - 1] = booldatum;
+
+ return 0;
+}
+
+static int bool_isvalid(struct cond_bool_datum *b)
+{
+ if (!(b->state == 0 || b->state == 1))
+ return 0;
+ return 1;
+}
+
+int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct cond_bool_datum *booldatum;
+ __le32 buf[3];
+ u32 len;
+ int rc;
+
+ booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL);
+ if (!booldatum)
+ return -1;
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto err;
+
+ booldatum->value = le32_to_cpu(buf[0]);
+ booldatum->state = le32_to_cpu(buf[1]);
+
+ if (!bool_isvalid(booldatum))
+ goto err;
+
+ len = le32_to_cpu(buf[2]);
+
+ key = kmalloc(len + 1, GFP_KERNEL);
+ if (!key)
+ goto err;
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto err;
+ key[len] = '\0';
+ if (hashtab_insert(h, key, booldatum))
+ goto err;
+
+ return 0;
+err:
+ cond_destroy_bool(key, booldatum, NULL);
+ return -1;
+}
+
+struct cond_insertf_data {
+ struct policydb *p;
+ struct cond_av_list *other;
+ struct cond_av_list *head;
+ struct cond_av_list *tail;
+};
+
+static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum *d, void *ptr)
+{
+ struct cond_insertf_data *data = ptr;
+ struct policydb *p = data->p;
+ struct cond_av_list *other = data->other, *list, *cur;
+ struct avtab_node *node_ptr;
+ u8 found;
+
+
+ /*
+ * For type rules we have to make certain there aren't any
+ * conflicting rules by searching the te_avtab and the
+ * cond_te_avtab.
+ */
+ if (k->specified & AVTAB_TYPE) {
+ if (avtab_search(&p->te_avtab, k)) {
+ printk(KERN_ERR "SELinux: type rule already exists outside of a conditional.\n");
+ goto err;
+ }
+ /*
+ * If we are reading the false list other will be a pointer to
+ * the true list. We can have duplicate entries if there is only
+ * 1 other entry and it is in our true list.
+ *
+ * If we are reading the true list (other == NULL) there shouldn't
+ * be any other entries.
+ */
+ if (other) {
+ node_ptr = avtab_search_node(&p->te_cond_avtab, k);
+ if (node_ptr) {
+ if (avtab_search_node_next(node_ptr, k->specified)) {
+ printk(KERN_ERR "SELinux: too many conflicting type rules.\n");
+ goto err;
+ }
+ found = 0;
+ for (cur = other; cur; cur = cur->next) {
+ if (cur->node == node_ptr) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ printk(KERN_ERR "SELinux: conflicting type rules.\n");
+ goto err;
+ }
+ }
+ } else {
+ if (avtab_search(&p->te_cond_avtab, k)) {
+ printk(KERN_ERR "SELinux: conflicting type rules when adding type rule for true.\n");
+ goto err;
+ }
+ }
+ }
+
+ node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d);
+ if (!node_ptr) {
+ printk(KERN_ERR "SELinux: could not insert rule.\n");
+ goto err;
+ }
+
+ list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL);
+ if (!list)
+ goto err;
+
+ list->node = node_ptr;
+ if (!data->head)
+ data->head = list;
+ else
+ data->tail->next = list;
+ data->tail = list;
+ return 0;
+
+err:
+ cond_av_list_destroy(data->head);
+ data->head = NULL;
+ return -1;
+}
+
+static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other)
+{
+ int i, rc;
+ __le32 buf[1];
+ u32 len;
+ struct cond_insertf_data data;
+
+ *ret_list = NULL;
+
+ len = 0;
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ return -1;
+
+ len = le32_to_cpu(buf[0]);
+ if (len == 0)
+ return 0;
+
+ data.p = p;
+ data.other = other;
+ data.head = NULL;
+ data.tail = NULL;
+ for (i = 0; i < len; i++) {
+ rc = avtab_read_item(&p->te_cond_avtab, fp, p, cond_insertf,
+ &data);
+ if (rc)
+ return rc;
+
+ }
+
+ *ret_list = data.head;
+ return 0;
+}
+
+static int expr_isvalid(struct policydb *p, struct cond_expr *expr)
+{
+ if (expr->expr_type <= 0 || expr->expr_type > COND_LAST) {
+ printk(KERN_ERR "SELinux: conditional expressions uses unknown operator.\n");
+ return 0;
+ }
+
+ if (expr->bool > p->p_bools.nprim) {
+ printk(KERN_ERR "SELinux: conditional expressions uses unknown bool.\n");
+ return 0;
+ }
+ return 1;
+}
+
+static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp)
+{
+ __le32 buf[2];
+ u32 len, i;
+ int rc;
+ struct cond_expr *expr = NULL, *last = NULL;
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ return -1;
+
+ node->cur_state = le32_to_cpu(buf[0]);
+
+ len = 0;
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ return -1;
+
+ /* expr */
+ len = le32_to_cpu(buf[0]);
+
+ for (i = 0; i < len; i++) {
+ rc = next_entry(buf, fp, sizeof(u32) * 2);
+ if (rc < 0)
+ goto err;
+
+ expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL);
+ if (!expr)
+ goto err;
+
+ expr->expr_type = le32_to_cpu(buf[0]);
+ expr->bool = le32_to_cpu(buf[1]);
+
+ if (!expr_isvalid(p, expr)) {
+ kfree(expr);
+ goto err;
+ }
+
+ if (i == 0)
+ node->expr = expr;
+ else
+ last->next = expr;
+ last = expr;
+ }
+
+ if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0)
+ goto err;
+ if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0)
+ goto err;
+ return 0;
+err:
+ cond_node_destroy(node);
+ return -1;
+}
+
+int cond_read_list(struct policydb *p, void *fp)
+{
+ struct cond_node *node, *last = NULL;
+ __le32 buf[1];
+ u32 i, len;
+ int rc;
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ return -1;
+
+ len = le32_to_cpu(buf[0]);
+
+ rc = avtab_alloc(&(p->te_cond_avtab), p->te_avtab.nel);
+ if (rc)
+ goto err;
+
+ for (i = 0; i < len; i++) {
+ node = kzalloc(sizeof(struct cond_node), GFP_KERNEL);
+ if (!node)
+ goto err;
+
+ if (cond_read_node(p, node, fp) != 0)
+ goto err;
+
+ if (i == 0)
+ p->cond_list = node;
+ else
+ last->next = node;
+ last = node;
+ }
+ return 0;
+err:
+ cond_list_destroy(p->cond_list);
+ p->cond_list = NULL;
+ return -1;
+}
+
+/* Determine whether additional permissions are granted by the conditional
+ * av table, and if so, add them to the result
+ */
+void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd)
+{
+ struct avtab_node *node;
+
+ if (!ctab || !key || !avd)
+ return;
+
+ for (node = avtab_search_node(ctab, key); node;
+ node = avtab_search_node_next(node, key->specified)) {
+ if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) ==
+ (node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))
+ avd->allowed |= node->datum.data;
+ if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) ==
+ (node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED)))
+ /* Since a '0' in an auditdeny mask represents a
+ * permission we do NOT want to audit (dontaudit), we use
+ * the '&' operand to ensure that all '0's in the mask
+ * are retained (much unlike the allow and auditallow cases).
+ */
+ avd->auditdeny &= node->datum.data;
+ if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
+ (node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
+ avd->auditallow |= node->datum.data;
+ }
+ return;
+}
diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h
new file mode 100644
index 0000000..53ddb01
--- /dev/null
+++ b/security/selinux/ss/conditional.h
@@ -0,0 +1,77 @@
+/* Authors: Karl MacMillan <kmacmillan@tresys.com>
+ * Frank Mayer <mayerf@tresys.com>
+ *
+ * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#ifndef _CONDITIONAL_H_
+#define _CONDITIONAL_H_
+
+#include "avtab.h"
+#include "symtab.h"
+#include "policydb.h"
+
+#define COND_EXPR_MAXDEPTH 10
+
+/*
+ * A conditional expression is a list of operators and operands
+ * in reverse polish notation.
+ */
+struct cond_expr {
+#define COND_BOOL 1 /* plain bool */
+#define COND_NOT 2 /* !bool */
+#define COND_OR 3 /* bool || bool */
+#define COND_AND 4 /* bool && bool */
+#define COND_XOR 5 /* bool ^ bool */
+#define COND_EQ 6 /* bool == bool */
+#define COND_NEQ 7 /* bool != bool */
+#define COND_LAST COND_NEQ
+ __u32 expr_type;
+ __u32 bool;
+ struct cond_expr *next;
+};
+
+/*
+ * Each cond_node contains a list of rules to be enabled/disabled
+ * depending on the current value of the conditional expression. This
+ * struct is for that list.
+ */
+struct cond_av_list {
+ struct avtab_node *node;
+ struct cond_av_list *next;
+};
+
+/*
+ * A cond node represents a conditional block in a policy. It
+ * contains a conditional expression, the current state of the expression,
+ * two lists of rules to enable/disable depending on the value of the
+ * expression (the true list corresponds to if and the false list corresponds
+ * to else)..
+ */
+struct cond_node {
+ int cur_state;
+ struct cond_expr *expr;
+ struct cond_av_list *true_list;
+ struct cond_av_list *false_list;
+ struct cond_node *next;
+};
+
+int cond_policydb_init(struct policydb *p);
+void cond_policydb_destroy(struct policydb *p);
+
+int cond_init_bool_indexes(struct policydb *p);
+int cond_destroy_bool(void *key, void *datum, void *p);
+
+int cond_index_bool(void *key, void *datum, void *datap);
+
+int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp);
+int cond_read_list(struct policydb *p, void *fp);
+
+void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd);
+
+int evaluate_cond_node(struct policydb *p, struct cond_node *node);
+
+#endif /* _CONDITIONAL_H_ */
diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h
new file mode 100644
index 0000000..149dda7
--- /dev/null
+++ b/security/selinux/ss/constraint.h
@@ -0,0 +1,61 @@
+/*
+ * A constraint is a condition that must be satisfied in
+ * order for one or more permissions to be granted.
+ * Constraints are used to impose additional restrictions
+ * beyond the type-based rules in `te' or the role-based
+ * transition rules in `rbac'. Constraints are typically
+ * used to prevent a process from transitioning to a new user
+ * identity or role unless it is in a privileged type.
+ * Constraints are likewise typically used to prevent a
+ * process from labeling an object with a different user
+ * identity.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_CONSTRAINT_H_
+#define _SS_CONSTRAINT_H_
+
+#include "ebitmap.h"
+
+#define CEXPR_MAXDEPTH 5
+
+struct constraint_expr {
+#define CEXPR_NOT 1 /* not expr */
+#define CEXPR_AND 2 /* expr and expr */
+#define CEXPR_OR 3 /* expr or expr */
+#define CEXPR_ATTR 4 /* attr op attr */
+#define CEXPR_NAMES 5 /* attr op names */
+ u32 expr_type; /* expression type */
+
+#define CEXPR_USER 1 /* user */
+#define CEXPR_ROLE 2 /* role */
+#define CEXPR_TYPE 4 /* type */
+#define CEXPR_TARGET 8 /* target if set, source otherwise */
+#define CEXPR_XTARGET 16 /* special 3rd target for validatetrans rule */
+#define CEXPR_L1L2 32 /* low level 1 vs. low level 2 */
+#define CEXPR_L1H2 64 /* low level 1 vs. high level 2 */
+#define CEXPR_H1L2 128 /* high level 1 vs. low level 2 */
+#define CEXPR_H1H2 256 /* high level 1 vs. high level 2 */
+#define CEXPR_L1H1 512 /* low level 1 vs. high level 1 */
+#define CEXPR_L2H2 1024 /* low level 2 vs. high level 2 */
+ u32 attr; /* attribute */
+
+#define CEXPR_EQ 1 /* == or eq */
+#define CEXPR_NEQ 2 /* != */
+#define CEXPR_DOM 3 /* dom */
+#define CEXPR_DOMBY 4 /* domby */
+#define CEXPR_INCOMP 5 /* incomp */
+ u32 op; /* operator */
+
+ struct ebitmap names; /* names */
+
+ struct constraint_expr *next; /* next expression */
+};
+
+struct constraint_node {
+ u32 permissions; /* constrained permissions */
+ struct constraint_expr *expr; /* constraint on permissions */
+ struct constraint_node *next; /* next constraint */
+};
+
+#endif /* _SS_CONSTRAINT_H_ */
diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h
new file mode 100644
index 0000000..658c2bd
--- /dev/null
+++ b/security/selinux/ss/context.h
@@ -0,0 +1,155 @@
+/*
+ * A security context is a set of security attributes
+ * associated with each subject and object controlled
+ * by the security policy. Security contexts are
+ * externally represented as variable-length strings
+ * that can be interpreted by a user or application
+ * with an understanding of the security policy.
+ * Internally, the security server uses a simple
+ * structure. This structure is private to the
+ * security server and can be changed without affecting
+ * clients of the security server.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_CONTEXT_H_
+#define _SS_CONTEXT_H_
+
+#include "ebitmap.h"
+#include "mls_types.h"
+#include "security.h"
+
+/*
+ * A security context consists of an authenticated user
+ * identity, a role, a type and a MLS range.
+ */
+struct context {
+ u32 user;
+ u32 role;
+ u32 type;
+ struct mls_range range;
+ char *str; /* string representation if context cannot be mapped. */
+ u32 len; /* length of string in bytes */
+};
+
+static inline void mls_context_init(struct context *c)
+{
+ memset(&c->range, 0, sizeof(c->range));
+}
+
+static inline int mls_context_cpy(struct context *dst, struct context *src)
+{
+ int rc;
+
+ if (!selinux_mls_enabled)
+ return 0;
+
+ dst->range.level[0].sens = src->range.level[0].sens;
+ rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat);
+ if (rc)
+ goto out;
+
+ dst->range.level[1].sens = src->range.level[1].sens;
+ rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[1].cat);
+ if (rc)
+ ebitmap_destroy(&dst->range.level[0].cat);
+out:
+ return rc;
+}
+
+/*
+ * Sets both levels in the MLS range of 'dst' to the low level of 'src'.
+ */
+static inline int mls_context_cpy_low(struct context *dst, struct context *src)
+{
+ int rc;
+
+ if (!selinux_mls_enabled)
+ return 0;
+
+ dst->range.level[0].sens = src->range.level[0].sens;
+ rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat);
+ if (rc)
+ goto out;
+
+ dst->range.level[1].sens = src->range.level[0].sens;
+ rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[0].cat);
+ if (rc)
+ ebitmap_destroy(&dst->range.level[0].cat);
+out:
+ return rc;
+}
+
+static inline int mls_context_cmp(struct context *c1, struct context *c2)
+{
+ if (!selinux_mls_enabled)
+ return 1;
+
+ return ((c1->range.level[0].sens == c2->range.level[0].sens) &&
+ ebitmap_cmp(&c1->range.level[0].cat, &c2->range.level[0].cat) &&
+ (c1->range.level[1].sens == c2->range.level[1].sens) &&
+ ebitmap_cmp(&c1->range.level[1].cat, &c2->range.level[1].cat));
+}
+
+static inline void mls_context_destroy(struct context *c)
+{
+ if (!selinux_mls_enabled)
+ return;
+
+ ebitmap_destroy(&c->range.level[0].cat);
+ ebitmap_destroy(&c->range.level[1].cat);
+ mls_context_init(c);
+}
+
+static inline void context_init(struct context *c)
+{
+ memset(c, 0, sizeof(*c));
+}
+
+static inline int context_cpy(struct context *dst, struct context *src)
+{
+ int rc;
+
+ dst->user = src->user;
+ dst->role = src->role;
+ dst->type = src->type;
+ if (src->str) {
+ dst->str = kstrdup(src->str, GFP_ATOMIC);
+ if (!dst->str)
+ return -ENOMEM;
+ dst->len = src->len;
+ } else {
+ dst->str = NULL;
+ dst->len = 0;
+ }
+ rc = mls_context_cpy(dst, src);
+ if (rc) {
+ kfree(dst->str);
+ return rc;
+ }
+ return 0;
+}
+
+static inline void context_destroy(struct context *c)
+{
+ c->user = c->role = c->type = 0;
+ kfree(c->str);
+ c->str = NULL;
+ c->len = 0;
+ mls_context_destroy(c);
+}
+
+static inline int context_cmp(struct context *c1, struct context *c2)
+{
+ if (c1->len && c2->len)
+ return (c1->len == c2->len && !strcmp(c1->str, c2->str));
+ if (c1->len || c2->len)
+ return 0;
+ return ((c1->user == c2->user) &&
+ (c1->role == c2->role) &&
+ (c1->type == c2->type) &&
+ mls_context_cmp(c1, c2));
+}
+
+#endif /* _SS_CONTEXT_H_ */
+
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
new file mode 100644
index 0000000..68c7348
--- /dev/null
+++ b/security/selinux/ss/ebitmap.c
@@ -0,0 +1,448 @@
+/*
+ * Implementation of the extensible bitmap type.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+/*
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ * Added support to import/export the NetLabel category bitmap
+ *
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ */
+/*
+ * Updated: KaiGai Kohei <kaigai@ak.jp.nec.com>
+ * Applied standard bit operations to improve bitmap scanning.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <net/netlabel.h>
+#include "ebitmap.h"
+#include "policydb.h"
+
+int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2)
+{
+ struct ebitmap_node *n1, *n2;
+
+ if (e1->highbit != e2->highbit)
+ return 0;
+
+ n1 = e1->node;
+ n2 = e2->node;
+ while (n1 && n2 &&
+ (n1->startbit == n2->startbit) &&
+ !memcmp(n1->maps, n2->maps, EBITMAP_SIZE / 8)) {
+ n1 = n1->next;
+ n2 = n2->next;
+ }
+
+ if (n1 || n2)
+ return 0;
+
+ return 1;
+}
+
+int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src)
+{
+ struct ebitmap_node *n, *new, *prev;
+
+ ebitmap_init(dst);
+ n = src->node;
+ prev = NULL;
+ while (n) {
+ new = kzalloc(sizeof(*new), GFP_ATOMIC);
+ if (!new) {
+ ebitmap_destroy(dst);
+ return -ENOMEM;
+ }
+ new->startbit = n->startbit;
+ memcpy(new->maps, n->maps, EBITMAP_SIZE / 8);
+ new->next = NULL;
+ if (prev)
+ prev->next = new;
+ else
+ dst->node = new;
+ prev = new;
+ n = n->next;
+ }
+
+ dst->highbit = src->highbit;
+ return 0;
+}
+
+#ifdef CONFIG_NETLABEL
+/**
+ * ebitmap_netlbl_export - Export an ebitmap into a NetLabel category bitmap
+ * @ebmap: the ebitmap to export
+ * @catmap: the NetLabel category bitmap
+ *
+ * Description:
+ * Export a SELinux extensibile bitmap into a NetLabel category bitmap.
+ * Returns zero on success, negative values on error.
+ *
+ */
+int ebitmap_netlbl_export(struct ebitmap *ebmap,
+ struct netlbl_lsm_secattr_catmap **catmap)
+{
+ struct ebitmap_node *e_iter = ebmap->node;
+ struct netlbl_lsm_secattr_catmap *c_iter;
+ u32 cmap_idx, cmap_sft;
+ int i;
+
+ /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64,
+ * however, it is not always compatible with an array of unsigned long
+ * in ebitmap_node.
+ * In addition, you should pay attention the following implementation
+ * assumes unsigned long has a width equal with or less than 64-bit.
+ */
+
+ if (e_iter == NULL) {
+ *catmap = NULL;
+ return 0;
+ }
+
+ c_iter = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
+ if (c_iter == NULL)
+ return -ENOMEM;
+ *catmap = c_iter;
+ c_iter->startbit = e_iter->startbit & ~(NETLBL_CATMAP_SIZE - 1);
+
+ while (e_iter) {
+ for (i = 0; i < EBITMAP_UNIT_NUMS; i++) {
+ unsigned int delta, e_startbit, c_endbit;
+
+ e_startbit = e_iter->startbit + i * EBITMAP_UNIT_SIZE;
+ c_endbit = c_iter->startbit + NETLBL_CATMAP_SIZE;
+ if (e_startbit >= c_endbit) {
+ c_iter->next
+ = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
+ if (c_iter->next == NULL)
+ goto netlbl_export_failure;
+ c_iter = c_iter->next;
+ c_iter->startbit
+ = e_startbit & ~(NETLBL_CATMAP_SIZE - 1);
+ }
+ delta = e_startbit - c_iter->startbit;
+ cmap_idx = delta / NETLBL_CATMAP_MAPSIZE;
+ cmap_sft = delta % NETLBL_CATMAP_MAPSIZE;
+ c_iter->bitmap[cmap_idx]
+ |= e_iter->maps[cmap_idx] << cmap_sft;
+ }
+ e_iter = e_iter->next;
+ }
+
+ return 0;
+
+netlbl_export_failure:
+ netlbl_secattr_catmap_free(*catmap);
+ return -ENOMEM;
+}
+
+/**
+ * ebitmap_netlbl_import - Import a NetLabel category bitmap into an ebitmap
+ * @ebmap: the ebitmap to import
+ * @catmap: the NetLabel category bitmap
+ *
+ * Description:
+ * Import a NetLabel category bitmap into a SELinux extensibile bitmap.
+ * Returns zero on success, negative values on error.
+ *
+ */
+int ebitmap_netlbl_import(struct ebitmap *ebmap,
+ struct netlbl_lsm_secattr_catmap *catmap)
+{
+ struct ebitmap_node *e_iter = NULL;
+ struct ebitmap_node *emap_prev = NULL;
+ struct netlbl_lsm_secattr_catmap *c_iter = catmap;
+ u32 c_idx, c_pos, e_idx, e_sft;
+
+ /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64,
+ * however, it is not always compatible with an array of unsigned long
+ * in ebitmap_node.
+ * In addition, you should pay attention the following implementation
+ * assumes unsigned long has a width equal with or less than 64-bit.
+ */
+
+ do {
+ for (c_idx = 0; c_idx < NETLBL_CATMAP_MAPCNT; c_idx++) {
+ unsigned int delta;
+ u64 map = c_iter->bitmap[c_idx];
+
+ if (!map)
+ continue;
+
+ c_pos = c_iter->startbit
+ + c_idx * NETLBL_CATMAP_MAPSIZE;
+ if (!e_iter
+ || c_pos >= e_iter->startbit + EBITMAP_SIZE) {
+ e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC);
+ if (!e_iter)
+ goto netlbl_import_failure;
+ e_iter->startbit
+ = c_pos - (c_pos % EBITMAP_SIZE);
+ if (emap_prev == NULL)
+ ebmap->node = e_iter;
+ else
+ emap_prev->next = e_iter;
+ emap_prev = e_iter;
+ }
+ delta = c_pos - e_iter->startbit;
+ e_idx = delta / EBITMAP_UNIT_SIZE;
+ e_sft = delta % EBITMAP_UNIT_SIZE;
+ while (map) {
+ e_iter->maps[e_idx++] |= map & (-1UL);
+ map = EBITMAP_SHIFT_UNIT_SIZE(map);
+ }
+ }
+ c_iter = c_iter->next;
+ } while (c_iter);
+ if (e_iter != NULL)
+ ebmap->highbit = e_iter->startbit + EBITMAP_SIZE;
+ else
+ ebitmap_destroy(ebmap);
+
+ return 0;
+
+netlbl_import_failure:
+ ebitmap_destroy(ebmap);
+ return -ENOMEM;
+}
+#endif /* CONFIG_NETLABEL */
+
+int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2)
+{
+ struct ebitmap_node *n1, *n2;
+ int i;
+
+ if (e1->highbit < e2->highbit)
+ return 0;
+
+ n1 = e1->node;
+ n2 = e2->node;
+ while (n1 && n2 && (n1->startbit <= n2->startbit)) {
+ if (n1->startbit < n2->startbit) {
+ n1 = n1->next;
+ continue;
+ }
+ for (i = 0; i < EBITMAP_UNIT_NUMS; i++) {
+ if ((n1->maps[i] & n2->maps[i]) != n2->maps[i])
+ return 0;
+ }
+
+ n1 = n1->next;
+ n2 = n2->next;
+ }
+
+ if (n2)
+ return 0;
+
+ return 1;
+}
+
+int ebitmap_get_bit(struct ebitmap *e, unsigned long bit)
+{
+ struct ebitmap_node *n;
+
+ if (e->highbit < bit)
+ return 0;
+
+ n = e->node;
+ while (n && (n->startbit <= bit)) {
+ if ((n->startbit + EBITMAP_SIZE) > bit)
+ return ebitmap_node_get_bit(n, bit);
+ n = n->next;
+ }
+
+ return 0;
+}
+
+int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value)
+{
+ struct ebitmap_node *n, *prev, *new;
+
+ prev = NULL;
+ n = e->node;
+ while (n && n->startbit <= bit) {
+ if ((n->startbit + EBITMAP_SIZE) > bit) {
+ if (value) {
+ ebitmap_node_set_bit(n, bit);
+ } else {
+ unsigned int s;
+
+ ebitmap_node_clr_bit(n, bit);
+
+ s = find_first_bit(n->maps, EBITMAP_SIZE);
+ if (s < EBITMAP_SIZE)
+ return 0;
+
+ /* drop this node from the bitmap */
+ if (!n->next) {
+ /*
+ * this was the highest map
+ * within the bitmap
+ */
+ if (prev)
+ e->highbit = prev->startbit
+ + EBITMAP_SIZE;
+ else
+ e->highbit = 0;
+ }
+ if (prev)
+ prev->next = n->next;
+ else
+ e->node = n->next;
+ kfree(n);
+ }
+ return 0;
+ }
+ prev = n;
+ n = n->next;
+ }
+
+ if (!value)
+ return 0;
+
+ new = kzalloc(sizeof(*new), GFP_ATOMIC);
+ if (!new)
+ return -ENOMEM;
+
+ new->startbit = bit - (bit % EBITMAP_SIZE);
+ ebitmap_node_set_bit(new, bit);
+
+ if (!n)
+ /* this node will be the highest map within the bitmap */
+ e->highbit = new->startbit + EBITMAP_SIZE;
+
+ if (prev) {
+ new->next = prev->next;
+ prev->next = new;
+ } else {
+ new->next = e->node;
+ e->node = new;
+ }
+
+ return 0;
+}
+
+void ebitmap_destroy(struct ebitmap *e)
+{
+ struct ebitmap_node *n, *temp;
+
+ if (!e)
+ return;
+
+ n = e->node;
+ while (n) {
+ temp = n;
+ n = n->next;
+ kfree(temp);
+ }
+
+ e->highbit = 0;
+ e->node = NULL;
+ return;
+}
+
+int ebitmap_read(struct ebitmap *e, void *fp)
+{
+ struct ebitmap_node *n = NULL;
+ u32 mapunit, count, startbit, index;
+ u64 map;
+ __le32 buf[3];
+ int rc, i;
+
+ ebitmap_init(e);
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto out;
+
+ mapunit = le32_to_cpu(buf[0]);
+ e->highbit = le32_to_cpu(buf[1]);
+ count = le32_to_cpu(buf[2]);
+
+ if (mapunit != sizeof(u64) * 8) {
+ printk(KERN_ERR "SELinux: ebitmap: map size %u does not "
+ "match my size %Zd (high bit was %d)\n",
+ mapunit, sizeof(u64) * 8, e->highbit);
+ goto bad;
+ }
+
+ /* round up e->highbit */
+ e->highbit += EBITMAP_SIZE - 1;
+ e->highbit -= (e->highbit % EBITMAP_SIZE);
+
+ if (!e->highbit) {
+ e->node = NULL;
+ goto ok;
+ }
+
+ for (i = 0; i < count; i++) {
+ rc = next_entry(&startbit, fp, sizeof(u32));
+ if (rc < 0) {
+ printk(KERN_ERR "SELinux: ebitmap: truncated map\n");
+ goto bad;
+ }
+ startbit = le32_to_cpu(startbit);
+
+ if (startbit & (mapunit - 1)) {
+ printk(KERN_ERR "SELinux: ebitmap start bit (%d) is "
+ "not a multiple of the map unit size (%u)\n",
+ startbit, mapunit);
+ goto bad;
+ }
+ if (startbit > e->highbit - mapunit) {
+ printk(KERN_ERR "SELinux: ebitmap start bit (%d) is "
+ "beyond the end of the bitmap (%u)\n",
+ startbit, (e->highbit - mapunit));
+ goto bad;
+ }
+
+ if (!n || startbit >= n->startbit + EBITMAP_SIZE) {
+ struct ebitmap_node *tmp;
+ tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp) {
+ printk(KERN_ERR
+ "SELinux: ebitmap: out of memory\n");
+ rc = -ENOMEM;
+ goto bad;
+ }
+ /* round down */
+ tmp->startbit = startbit - (startbit % EBITMAP_SIZE);
+ if (n)
+ n->next = tmp;
+ else
+ e->node = tmp;
+ n = tmp;
+ } else if (startbit <= n->startbit) {
+ printk(KERN_ERR "SELinux: ebitmap: start bit %d"
+ " comes after start bit %d\n",
+ startbit, n->startbit);
+ goto bad;
+ }
+
+ rc = next_entry(&map, fp, sizeof(u64));
+ if (rc < 0) {
+ printk(KERN_ERR "SELinux: ebitmap: truncated map\n");
+ goto bad;
+ }
+ map = le64_to_cpu(map);
+
+ index = (startbit - n->startbit) / EBITMAP_UNIT_SIZE;
+ while (map) {
+ n->maps[index++] = map & (-1UL);
+ map = EBITMAP_SHIFT_UNIT_SIZE(map);
+ }
+ }
+ok:
+ rc = 0;
+out:
+ return rc;
+bad:
+ if (!rc)
+ rc = -EINVAL;
+ ebitmap_destroy(e);
+ goto out;
+}
diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h
new file mode 100644
index 0000000..f283b43
--- /dev/null
+++ b/security/selinux/ss/ebitmap.h
@@ -0,0 +1,145 @@
+/*
+ * An extensible bitmap is a bitmap that supports an
+ * arbitrary number of bits. Extensible bitmaps are
+ * used to represent sets of values, such as types,
+ * roles, categories, and classes.
+ *
+ * Each extensible bitmap is implemented as a linked
+ * list of bitmap nodes, where each bitmap node has
+ * an explicitly specified starting bit position within
+ * the total bitmap.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_EBITMAP_H_
+#define _SS_EBITMAP_H_
+
+#include <net/netlabel.h>
+
+#define EBITMAP_UNIT_NUMS ((32 - sizeof(void *) - sizeof(u32)) \
+ / sizeof(unsigned long))
+#define EBITMAP_UNIT_SIZE BITS_PER_LONG
+#define EBITMAP_SIZE (EBITMAP_UNIT_NUMS * EBITMAP_UNIT_SIZE)
+#define EBITMAP_BIT 1ULL
+#define EBITMAP_SHIFT_UNIT_SIZE(x) \
+ (((x) >> EBITMAP_UNIT_SIZE / 2) >> EBITMAP_UNIT_SIZE / 2)
+
+struct ebitmap_node {
+ struct ebitmap_node *next;
+ unsigned long maps[EBITMAP_UNIT_NUMS];
+ u32 startbit;
+};
+
+struct ebitmap {
+ struct ebitmap_node *node; /* first node in the bitmap */
+ u32 highbit; /* highest position in the total bitmap */
+};
+
+#define ebitmap_length(e) ((e)->highbit)
+#define ebitmap_startbit(e) ((e)->node ? (e)->node->startbit : 0)
+
+static inline unsigned int ebitmap_start_positive(struct ebitmap *e,
+ struct ebitmap_node **n)
+{
+ unsigned int ofs;
+
+ for (*n = e->node; *n; *n = (*n)->next) {
+ ofs = find_first_bit((*n)->maps, EBITMAP_SIZE);
+ if (ofs < EBITMAP_SIZE)
+ return (*n)->startbit + ofs;
+ }
+ return ebitmap_length(e);
+}
+
+static inline void ebitmap_init(struct ebitmap *e)
+{
+ memset(e, 0, sizeof(*e));
+}
+
+static inline unsigned int ebitmap_next_positive(struct ebitmap *e,
+ struct ebitmap_node **n,
+ unsigned int bit)
+{
+ unsigned int ofs;
+
+ ofs = find_next_bit((*n)->maps, EBITMAP_SIZE, bit - (*n)->startbit + 1);
+ if (ofs < EBITMAP_SIZE)
+ return ofs + (*n)->startbit;
+
+ for (*n = (*n)->next; *n; *n = (*n)->next) {
+ ofs = find_first_bit((*n)->maps, EBITMAP_SIZE);
+ if (ofs < EBITMAP_SIZE)
+ return ofs + (*n)->startbit;
+ }
+ return ebitmap_length(e);
+}
+
+#define EBITMAP_NODE_INDEX(node, bit) \
+ (((bit) - (node)->startbit) / EBITMAP_UNIT_SIZE)
+#define EBITMAP_NODE_OFFSET(node, bit) \
+ (((bit) - (node)->startbit) % EBITMAP_UNIT_SIZE)
+
+static inline int ebitmap_node_get_bit(struct ebitmap_node *n,
+ unsigned int bit)
+{
+ unsigned int index = EBITMAP_NODE_INDEX(n, bit);
+ unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit);
+
+ BUG_ON(index >= EBITMAP_UNIT_NUMS);
+ if ((n->maps[index] & (EBITMAP_BIT << ofs)))
+ return 1;
+ return 0;
+}
+
+static inline void ebitmap_node_set_bit(struct ebitmap_node *n,
+ unsigned int bit)
+{
+ unsigned int index = EBITMAP_NODE_INDEX(n, bit);
+ unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit);
+
+ BUG_ON(index >= EBITMAP_UNIT_NUMS);
+ n->maps[index] |= (EBITMAP_BIT << ofs);
+}
+
+static inline void ebitmap_node_clr_bit(struct ebitmap_node *n,
+ unsigned int bit)
+{
+ unsigned int index = EBITMAP_NODE_INDEX(n, bit);
+ unsigned int ofs = EBITMAP_NODE_OFFSET(n, bit);
+
+ BUG_ON(index >= EBITMAP_UNIT_NUMS);
+ n->maps[index] &= ~(EBITMAP_BIT << ofs);
+}
+
+#define ebitmap_for_each_positive_bit(e, n, bit) \
+ for (bit = ebitmap_start_positive(e, &n); \
+ bit < ebitmap_length(e); \
+ bit = ebitmap_next_positive(e, &n, bit)) \
+
+int ebitmap_cmp(struct ebitmap *e1, struct ebitmap *e2);
+int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src);
+int ebitmap_contains(struct ebitmap *e1, struct ebitmap *e2);
+int ebitmap_get_bit(struct ebitmap *e, unsigned long bit);
+int ebitmap_set_bit(struct ebitmap *e, unsigned long bit, int value);
+void ebitmap_destroy(struct ebitmap *e);
+int ebitmap_read(struct ebitmap *e, void *fp);
+
+#ifdef CONFIG_NETLABEL
+int ebitmap_netlbl_export(struct ebitmap *ebmap,
+ struct netlbl_lsm_secattr_catmap **catmap);
+int ebitmap_netlbl_import(struct ebitmap *ebmap,
+ struct netlbl_lsm_secattr_catmap *catmap);
+#else
+static inline int ebitmap_netlbl_export(struct ebitmap *ebmap,
+ struct netlbl_lsm_secattr_catmap **catmap)
+{
+ return -ENOMEM;
+}
+static inline int ebitmap_netlbl_import(struct ebitmap *ebmap,
+ struct netlbl_lsm_secattr_catmap *catmap)
+{
+ return -ENOMEM;
+}
+#endif
+
+#endif /* _SS_EBITMAP_H_ */
diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c
new file mode 100644
index 0000000..933e735
--- /dev/null
+++ b/security/selinux/ss/hashtab.c
@@ -0,0 +1,165 @@
+/*
+ * Implementation of the hash table type.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include "hashtab.h"
+
+struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key),
+ int (*keycmp)(struct hashtab *h, const void *key1, const void *key2),
+ u32 size)
+{
+ struct hashtab *p;
+ u32 i;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL)
+ return p;
+
+ p->size = size;
+ p->nel = 0;
+ p->hash_value = hash_value;
+ p->keycmp = keycmp;
+ p->htable = kmalloc(sizeof(*(p->htable)) * size, GFP_KERNEL);
+ if (p->htable == NULL) {
+ kfree(p);
+ return NULL;
+ }
+
+ for (i = 0; i < size; i++)
+ p->htable[i] = NULL;
+
+ return p;
+}
+
+int hashtab_insert(struct hashtab *h, void *key, void *datum)
+{
+ u32 hvalue;
+ struct hashtab_node *prev, *cur, *newnode;
+
+ if (!h || h->nel == HASHTAB_MAX_NODES)
+ return -EINVAL;
+
+ hvalue = h->hash_value(h, key);
+ prev = NULL;
+ cur = h->htable[hvalue];
+ while (cur && h->keycmp(h, key, cur->key) > 0) {
+ prev = cur;
+ cur = cur->next;
+ }
+
+ if (cur && (h->keycmp(h, key, cur->key) == 0))
+ return -EEXIST;
+
+ newnode = kzalloc(sizeof(*newnode), GFP_KERNEL);
+ if (newnode == NULL)
+ return -ENOMEM;
+ newnode->key = key;
+ newnode->datum = datum;
+ if (prev) {
+ newnode->next = prev->next;
+ prev->next = newnode;
+ } else {
+ newnode->next = h->htable[hvalue];
+ h->htable[hvalue] = newnode;
+ }
+
+ h->nel++;
+ return 0;
+}
+
+void *hashtab_search(struct hashtab *h, const void *key)
+{
+ u32 hvalue;
+ struct hashtab_node *cur;
+
+ if (!h)
+ return NULL;
+
+ hvalue = h->hash_value(h, key);
+ cur = h->htable[hvalue];
+ while (cur && h->keycmp(h, key, cur->key) > 0)
+ cur = cur->next;
+
+ if (cur == NULL || (h->keycmp(h, key, cur->key) != 0))
+ return NULL;
+
+ return cur->datum;
+}
+
+void hashtab_destroy(struct hashtab *h)
+{
+ u32 i;
+ struct hashtab_node *cur, *temp;
+
+ if (!h)
+ return;
+
+ for (i = 0; i < h->size; i++) {
+ cur = h->htable[i];
+ while (cur) {
+ temp = cur;
+ cur = cur->next;
+ kfree(temp);
+ }
+ h->htable[i] = NULL;
+ }
+
+ kfree(h->htable);
+ h->htable = NULL;
+
+ kfree(h);
+}
+
+int hashtab_map(struct hashtab *h,
+ int (*apply)(void *k, void *d, void *args),
+ void *args)
+{
+ u32 i;
+ int ret;
+ struct hashtab_node *cur;
+
+ if (!h)
+ return 0;
+
+ for (i = 0; i < h->size; i++) {
+ cur = h->htable[i];
+ while (cur) {
+ ret = apply(cur->key, cur->datum, args);
+ if (ret)
+ return ret;
+ cur = cur->next;
+ }
+ }
+ return 0;
+}
+
+
+void hashtab_stat(struct hashtab *h, struct hashtab_info *info)
+{
+ u32 i, chain_len, slots_used, max_chain_len;
+ struct hashtab_node *cur;
+
+ slots_used = 0;
+ max_chain_len = 0;
+ for (slots_used = max_chain_len = i = 0; i < h->size; i++) {
+ cur = h->htable[i];
+ if (cur) {
+ slots_used++;
+ chain_len = 0;
+ while (cur) {
+ chain_len++;
+ cur = cur->next;
+ }
+
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ }
+ }
+
+ info->slots_used = slots_used;
+ info->max_chain_len = max_chain_len;
+}
diff --git a/security/selinux/ss/hashtab.h b/security/selinux/ss/hashtab.h
new file mode 100644
index 0000000..953872c
--- /dev/null
+++ b/security/selinux/ss/hashtab.h
@@ -0,0 +1,87 @@
+/*
+ * A hash table (hashtab) maintains associations between
+ * key values and datum values. The type of the key values
+ * and the type of the datum values is arbitrary. The
+ * functions for hash computation and key comparison are
+ * provided by the creator of the table.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_HASHTAB_H_
+#define _SS_HASHTAB_H_
+
+#define HASHTAB_MAX_NODES 0xffffffff
+
+struct hashtab_node {
+ void *key;
+ void *datum;
+ struct hashtab_node *next;
+};
+
+struct hashtab {
+ struct hashtab_node **htable; /* hash table */
+ u32 size; /* number of slots in hash table */
+ u32 nel; /* number of elements in hash table */
+ u32 (*hash_value)(struct hashtab *h, const void *key);
+ /* hash function */
+ int (*keycmp)(struct hashtab *h, const void *key1, const void *key2);
+ /* key comparison function */
+};
+
+struct hashtab_info {
+ u32 slots_used;
+ u32 max_chain_len;
+};
+
+/*
+ * Creates a new hash table with the specified characteristics.
+ *
+ * Returns NULL if insufficent space is available or
+ * the new hash table otherwise.
+ */
+struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void *key),
+ int (*keycmp)(struct hashtab *h, const void *key1, const void *key2),
+ u32 size);
+
+/*
+ * Inserts the specified (key, datum) pair into the specified hash table.
+ *
+ * Returns -ENOMEM on memory allocation error,
+ * -EEXIST if there is already an entry with the same key,
+ * -EINVAL for general errors or
+ 0 otherwise.
+ */
+int hashtab_insert(struct hashtab *h, void *k, void *d);
+
+/*
+ * Searches for the entry with the specified key in the hash table.
+ *
+ * Returns NULL if no entry has the specified key or
+ * the datum of the entry otherwise.
+ */
+void *hashtab_search(struct hashtab *h, const void *k);
+
+/*
+ * Destroys the specified hash table.
+ */
+void hashtab_destroy(struct hashtab *h);
+
+/*
+ * Applies the specified apply function to (key,datum,args)
+ * for each entry in the specified hash table.
+ *
+ * The order in which the function is applied to the entries
+ * is dependent upon the internal structure of the hash table.
+ *
+ * If apply returns a non-zero status, then hashtab_map will cease
+ * iterating through the hash table and will propagate the error
+ * return to its caller.
+ */
+int hashtab_map(struct hashtab *h,
+ int (*apply)(void *k, void *d, void *args),
+ void *args);
+
+/* Fill info with some hash table statistics */
+void hashtab_stat(struct hashtab *h, struct hashtab_info *info);
+
+#endif /* _SS_HASHTAB_H */
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
new file mode 100644
index 0000000..b5407f1
--- /dev/null
+++ b/security/selinux/ss/mls.c
@@ -0,0 +1,654 @@
+/*
+ * Implementation of the multi-level security (MLS) policy.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+/*
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ *
+ * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
+ */
+/*
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ * Added support to import/export the MLS label from NetLabel
+ *
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <net/netlabel.h>
+#include "sidtab.h"
+#include "mls.h"
+#include "policydb.h"
+#include "services.h"
+
+/*
+ * Return the length in bytes for the MLS fields of the
+ * security context string representation of `context'.
+ */
+int mls_compute_context_len(struct context *context)
+{
+ int i, l, len, head, prev;
+ char *nm;
+ struct ebitmap *e;
+ struct ebitmap_node *node;
+
+ if (!selinux_mls_enabled)
+ return 0;
+
+ len = 1; /* for the beginning ":" */
+ for (l = 0; l < 2; l++) {
+ int index_sens = context->range.level[l].sens;
+ len += strlen(policydb.p_sens_val_to_name[index_sens - 1]);
+
+ /* categories */
+ head = -2;
+ prev = -2;
+ e = &context->range.level[l].cat;
+ ebitmap_for_each_positive_bit(e, node, i) {
+ if (i - prev > 1) {
+ /* one or more negative bits are skipped */
+ if (head != prev) {
+ nm = policydb.p_cat_val_to_name[prev];
+ len += strlen(nm) + 1;
+ }
+ nm = policydb.p_cat_val_to_name[i];
+ len += strlen(nm) + 1;
+ head = i;
+ }
+ prev = i;
+ }
+ if (prev != head) {
+ nm = policydb.p_cat_val_to_name[prev];
+ len += strlen(nm) + 1;
+ }
+ if (l == 0) {
+ if (mls_level_eq(&context->range.level[0],
+ &context->range.level[1]))
+ break;
+ else
+ len++;
+ }
+ }
+
+ return len;
+}
+
+/*
+ * Write the security context string representation of
+ * the MLS fields of `context' into the string `*scontext'.
+ * Update `*scontext' to point to the end of the MLS fields.
+ */
+void mls_sid_to_context(struct context *context,
+ char **scontext)
+{
+ char *scontextp, *nm;
+ int i, l, head, prev;
+ struct ebitmap *e;
+ struct ebitmap_node *node;
+
+ if (!selinux_mls_enabled)
+ return;
+
+ scontextp = *scontext;
+
+ *scontextp = ':';
+ scontextp++;
+
+ for (l = 0; l < 2; l++) {
+ strcpy(scontextp,
+ policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
+ scontextp += strlen(scontextp);
+
+ /* categories */
+ head = -2;
+ prev = -2;
+ e = &context->range.level[l].cat;
+ ebitmap_for_each_positive_bit(e, node, i) {
+ if (i - prev > 1) {
+ /* one or more negative bits are skipped */
+ if (prev != head) {
+ if (prev - head > 1)
+ *scontextp++ = '.';
+ else
+ *scontextp++ = ',';
+ nm = policydb.p_cat_val_to_name[prev];
+ strcpy(scontextp, nm);
+ scontextp += strlen(nm);
+ }
+ if (prev < 0)
+ *scontextp++ = ':';
+ else
+ *scontextp++ = ',';
+ nm = policydb.p_cat_val_to_name[i];
+ strcpy(scontextp, nm);
+ scontextp += strlen(nm);
+ head = i;
+ }
+ prev = i;
+ }
+
+ if (prev != head) {
+ if (prev - head > 1)
+ *scontextp++ = '.';
+ else
+ *scontextp++ = ',';
+ nm = policydb.p_cat_val_to_name[prev];
+ strcpy(scontextp, nm);
+ scontextp += strlen(nm);
+ }
+
+ if (l == 0) {
+ if (mls_level_eq(&context->range.level[0],
+ &context->range.level[1]))
+ break;
+ else
+ *scontextp++ = '-';
+ }
+ }
+
+ *scontext = scontextp;
+ return;
+}
+
+int mls_level_isvalid(struct policydb *p, struct mls_level *l)
+{
+ struct level_datum *levdatum;
+ struct ebitmap_node *node;
+ int i;
+
+ if (!l->sens || l->sens > p->p_levels.nprim)
+ return 0;
+ levdatum = hashtab_search(p->p_levels.table,
+ p->p_sens_val_to_name[l->sens - 1]);
+ if (!levdatum)
+ return 0;
+
+ ebitmap_for_each_positive_bit(&l->cat, node, i) {
+ if (i > p->p_cats.nprim)
+ return 0;
+ if (!ebitmap_get_bit(&levdatum->level->cat, i)) {
+ /*
+ * Category may not be associated with
+ * sensitivity.
+ */
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int mls_range_isvalid(struct policydb *p, struct mls_range *r)
+{
+ return (mls_level_isvalid(p, &r->level[0]) &&
+ mls_level_isvalid(p, &r->level[1]) &&
+ mls_level_dom(&r->level[1], &r->level[0]));
+}
+
+/*
+ * Return 1 if the MLS fields in the security context
+ * structure `c' are valid. Return 0 otherwise.
+ */
+int mls_context_isvalid(struct policydb *p, struct context *c)
+{
+ struct user_datum *usrdatum;
+
+ if (!selinux_mls_enabled)
+ return 1;
+
+ if (!mls_range_isvalid(p, &c->range))
+ return 0;
+
+ if (c->role == OBJECT_R_VAL)
+ return 1;
+
+ /*
+ * User must be authorized for the MLS range.
+ */
+ if (!c->user || c->user > p->p_users.nprim)
+ return 0;
+ usrdatum = p->user_val_to_struct[c->user - 1];
+ if (!mls_range_contains(usrdatum->range, c->range))
+ return 0; /* user may not be associated with range */
+
+ return 1;
+}
+
+/*
+ * Set the MLS fields in the security context structure
+ * `context' based on the string representation in
+ * the string `*scontext'. Update `*scontext' to
+ * point to the end of the string representation of
+ * the MLS fields.
+ *
+ * This function modifies the string in place, inserting
+ * NULL characters to terminate the MLS fields.
+ *
+ * If a def_sid is provided and no MLS field is present,
+ * copy the MLS field of the associated default context.
+ * Used for upgraded to MLS systems where objects may lack
+ * MLS fields.
+ *
+ * Policy read-lock must be held for sidtab lookup.
+ *
+ */
+int mls_context_to_sid(struct policydb *pol,
+ char oldc,
+ char **scontext,
+ struct context *context,
+ struct sidtab *s,
+ u32 def_sid)
+{
+
+ char delim;
+ char *scontextp, *p, *rngptr;
+ struct level_datum *levdatum;
+ struct cat_datum *catdatum, *rngdatum;
+ int l, rc = -EINVAL;
+
+ if (!selinux_mls_enabled) {
+ if (def_sid != SECSID_NULL && oldc)
+ *scontext += strlen(*scontext)+1;
+ return 0;
+ }
+
+ /*
+ * No MLS component to the security context, try and map to
+ * default if provided.
+ */
+ if (!oldc) {
+ struct context *defcon;
+
+ if (def_sid == SECSID_NULL)
+ goto out;
+
+ defcon = sidtab_search(s, def_sid);
+ if (!defcon)
+ goto out;
+
+ rc = mls_context_cpy(context, defcon);
+ goto out;
+ }
+
+ /* Extract low sensitivity. */
+ scontextp = p = *scontext;
+ while (*p && *p != ':' && *p != '-')
+ p++;
+
+ delim = *p;
+ if (delim != '\0')
+ *p++ = '\0';
+
+ for (l = 0; l < 2; l++) {
+ levdatum = hashtab_search(pol->p_levels.table, scontextp);
+ if (!levdatum) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ context->range.level[l].sens = levdatum->level->sens;
+
+ if (delim == ':') {
+ /* Extract category set. */
+ while (1) {
+ scontextp = p;
+ while (*p && *p != ',' && *p != '-')
+ p++;
+ delim = *p;
+ if (delim != '\0')
+ *p++ = '\0';
+
+ /* Separate into range if exists */
+ rngptr = strchr(scontextp, '.');
+ if (rngptr != NULL) {
+ /* Remove '.' */
+ *rngptr++ = '\0';
+ }
+
+ catdatum = hashtab_search(pol->p_cats.table,
+ scontextp);
+ if (!catdatum) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = ebitmap_set_bit(&context->range.level[l].cat,
+ catdatum->value - 1, 1);
+ if (rc)
+ goto out;
+
+ /* If range, set all categories in range */
+ if (rngptr) {
+ int i;
+
+ rngdatum = hashtab_search(pol->p_cats.table, rngptr);
+ if (!rngdatum) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (catdatum->value >= rngdatum->value) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ for (i = catdatum->value; i < rngdatum->value; i++) {
+ rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
+ if (rc)
+ goto out;
+ }
+ }
+
+ if (delim != ',')
+ break;
+ }
+ }
+ if (delim == '-') {
+ /* Extract high sensitivity. */
+ scontextp = p;
+ while (*p && *p != ':')
+ p++;
+
+ delim = *p;
+ if (delim != '\0')
+ *p++ = '\0';
+ } else
+ break;
+ }
+
+ if (l == 0) {
+ context->range.level[1].sens = context->range.level[0].sens;
+ rc = ebitmap_cpy(&context->range.level[1].cat,
+ &context->range.level[0].cat);
+ if (rc)
+ goto out;
+ }
+ *scontext = ++p;
+ rc = 0;
+out:
+ return rc;
+}
+
+/*
+ * Set the MLS fields in the security context structure
+ * `context' based on the string representation in
+ * the string `str'. This function will allocate temporary memory with the
+ * given constraints of gfp_mask.
+ */
+int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
+{
+ char *tmpstr, *freestr;
+ int rc;
+
+ if (!selinux_mls_enabled)
+ return -EINVAL;
+
+ /* we need freestr because mls_context_to_sid will change
+ the value of tmpstr */
+ tmpstr = freestr = kstrdup(str, gfp_mask);
+ if (!tmpstr) {
+ rc = -ENOMEM;
+ } else {
+ rc = mls_context_to_sid(&policydb, ':', &tmpstr, context,
+ NULL, SECSID_NULL);
+ kfree(freestr);
+ }
+
+ return rc;
+}
+
+/*
+ * Copies the MLS range `range' into `context'.
+ */
+static inline int mls_range_set(struct context *context,
+ struct mls_range *range)
+{
+ int l, rc = 0;
+
+ /* Copy the MLS range into the context */
+ for (l = 0; l < 2; l++) {
+ context->range.level[l].sens = range->level[l].sens;
+ rc = ebitmap_cpy(&context->range.level[l].cat,
+ &range->level[l].cat);
+ if (rc)
+ break;
+ }
+
+ return rc;
+}
+
+int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
+ struct context *usercon)
+{
+ if (selinux_mls_enabled) {
+ struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
+ struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
+ struct mls_level *user_low = &(user->range.level[0]);
+ struct mls_level *user_clr = &(user->range.level[1]);
+ struct mls_level *user_def = &(user->dfltlevel);
+ struct mls_level *usercon_sen = &(usercon->range.level[0]);
+ struct mls_level *usercon_clr = &(usercon->range.level[1]);
+
+ /* Honor the user's default level if we can */
+ if (mls_level_between(user_def, fromcon_sen, fromcon_clr))
+ *usercon_sen = *user_def;
+ else if (mls_level_between(fromcon_sen, user_def, user_clr))
+ *usercon_sen = *fromcon_sen;
+ else if (mls_level_between(fromcon_clr, user_low, user_def))
+ *usercon_sen = *user_low;
+ else
+ return -EINVAL;
+
+ /* Lower the clearance of available contexts
+ if the clearance of "fromcon" is lower than
+ that of the user's default clearance (but
+ only if the "fromcon" clearance dominates
+ the user's computed sensitivity level) */
+ if (mls_level_dom(user_clr, fromcon_clr))
+ *usercon_clr = *fromcon_clr;
+ else if (mls_level_dom(fromcon_clr, user_clr))
+ *usercon_clr = *user_clr;
+ else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Convert the MLS fields in the security context
+ * structure `c' from the values specified in the
+ * policy `oldp' to the values specified in the policy `newp'.
+ */
+int mls_convert_context(struct policydb *oldp,
+ struct policydb *newp,
+ struct context *c)
+{
+ struct level_datum *levdatum;
+ struct cat_datum *catdatum;
+ struct ebitmap bitmap;
+ struct ebitmap_node *node;
+ int l, i;
+
+ if (!selinux_mls_enabled)
+ return 0;
+
+ for (l = 0; l < 2; l++) {
+ levdatum = hashtab_search(newp->p_levels.table,
+ oldp->p_sens_val_to_name[c->range.level[l].sens - 1]);
+
+ if (!levdatum)
+ return -EINVAL;
+ c->range.level[l].sens = levdatum->level->sens;
+
+ ebitmap_init(&bitmap);
+ ebitmap_for_each_positive_bit(&c->range.level[l].cat, node, i) {
+ int rc;
+
+ catdatum = hashtab_search(newp->p_cats.table,
+ oldp->p_cat_val_to_name[i]);
+ if (!catdatum)
+ return -EINVAL;
+ rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
+ if (rc)
+ return rc;
+ }
+ ebitmap_destroy(&c->range.level[l].cat);
+ c->range.level[l].cat = bitmap;
+ }
+
+ return 0;
+}
+
+int mls_compute_sid(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ u32 specified,
+ struct context *newcontext)
+{
+ struct range_trans *rtr;
+
+ if (!selinux_mls_enabled)
+ return 0;
+
+ switch (specified) {
+ case AVTAB_TRANSITION:
+ /* Look for a range transition rule. */
+ for (rtr = policydb.range_tr; rtr; rtr = rtr->next) {
+ if (rtr->source_type == scontext->type &&
+ rtr->target_type == tcontext->type &&
+ rtr->target_class == tclass) {
+ /* Set the range from the rule */
+ return mls_range_set(newcontext,
+ &rtr->target_range);
+ }
+ }
+ /* Fallthrough */
+ case AVTAB_CHANGE:
+ if (tclass == SECCLASS_PROCESS)
+ /* Use the process MLS attributes. */
+ return mls_context_cpy(newcontext, scontext);
+ else
+ /* Use the process effective MLS attributes. */
+ return mls_context_cpy_low(newcontext, scontext);
+ case AVTAB_MEMBER:
+ /* Use the process effective MLS attributes. */
+ return mls_context_cpy_low(newcontext, scontext);
+ default:
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+#ifdef CONFIG_NETLABEL
+/**
+ * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel
+ * @context: the security context
+ * @secattr: the NetLabel security attributes
+ *
+ * Description:
+ * Given the security context copy the low MLS sensitivity level into the
+ * NetLabel MLS sensitivity level field.
+ *
+ */
+void mls_export_netlbl_lvl(struct context *context,
+ struct netlbl_lsm_secattr *secattr)
+{
+ if (!selinux_mls_enabled)
+ return;
+
+ secattr->attr.mls.lvl = context->range.level[0].sens - 1;
+ secattr->flags |= NETLBL_SECATTR_MLS_LVL;
+}
+
+/**
+ * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels
+ * @context: the security context
+ * @secattr: the NetLabel security attributes
+ *
+ * Description:
+ * Given the security context and the NetLabel security attributes, copy the
+ * NetLabel MLS sensitivity level into the context.
+ *
+ */
+void mls_import_netlbl_lvl(struct context *context,
+ struct netlbl_lsm_secattr *secattr)
+{
+ if (!selinux_mls_enabled)
+ return;
+
+ context->range.level[0].sens = secattr->attr.mls.lvl + 1;
+ context->range.level[1].sens = context->range.level[0].sens;
+}
+
+/**
+ * mls_export_netlbl_cat - Export the MLS categories to NetLabel
+ * @context: the security context
+ * @secattr: the NetLabel security attributes
+ *
+ * Description:
+ * Given the security context copy the low MLS categories into the NetLabel
+ * MLS category field. Returns zero on success, negative values on failure.
+ *
+ */
+int mls_export_netlbl_cat(struct context *context,
+ struct netlbl_lsm_secattr *secattr)
+{
+ int rc;
+
+ if (!selinux_mls_enabled)
+ return 0;
+
+ rc = ebitmap_netlbl_export(&context->range.level[0].cat,
+ &secattr->attr.mls.cat);
+ if (rc == 0 && secattr->attr.mls.cat != NULL)
+ secattr->flags |= NETLBL_SECATTR_MLS_CAT;
+
+ return rc;
+}
+
+/**
+ * mls_import_netlbl_cat - Import the MLS categories from NetLabel
+ * @context: the security context
+ * @secattr: the NetLabel security attributes
+ *
+ * Description:
+ * Copy the NetLabel security attributes into the SELinux context; since the
+ * NetLabel security attribute only contains a single MLS category use it for
+ * both the low and high categories of the context. Returns zero on success,
+ * negative values on failure.
+ *
+ */
+int mls_import_netlbl_cat(struct context *context,
+ struct netlbl_lsm_secattr *secattr)
+{
+ int rc;
+
+ if (!selinux_mls_enabled)
+ return 0;
+
+ rc = ebitmap_netlbl_import(&context->range.level[0].cat,
+ secattr->attr.mls.cat);
+ if (rc != 0)
+ goto import_netlbl_cat_failure;
+
+ rc = ebitmap_cpy(&context->range.level[1].cat,
+ &context->range.level[0].cat);
+ if (rc != 0)
+ goto import_netlbl_cat_failure;
+
+ return 0;
+
+import_netlbl_cat_failure:
+ ebitmap_destroy(&context->range.level[0].cat);
+ ebitmap_destroy(&context->range.level[1].cat);
+ return rc;
+}
+#endif /* CONFIG_NETLABEL */
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
new file mode 100644
index 0000000..1276715
--- /dev/null
+++ b/security/selinux/ss/mls.h
@@ -0,0 +1,88 @@
+/*
+ * Multi-level security (MLS) policy operations.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+/*
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ *
+ * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
+ */
+/*
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ * Added support to import/export the MLS label from NetLabel
+ *
+ * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
+ */
+
+#ifndef _SS_MLS_H_
+#define _SS_MLS_H_
+
+#include "context.h"
+#include "policydb.h"
+
+int mls_compute_context_len(struct context *context);
+void mls_sid_to_context(struct context *context, char **scontext);
+int mls_context_isvalid(struct policydb *p, struct context *c);
+int mls_range_isvalid(struct policydb *p, struct mls_range *r);
+int mls_level_isvalid(struct policydb *p, struct mls_level *l);
+
+int mls_context_to_sid(struct policydb *p,
+ char oldc,
+ char **scontext,
+ struct context *context,
+ struct sidtab *s,
+ u32 def_sid);
+
+int mls_from_string(char *str, struct context *context, gfp_t gfp_mask);
+
+int mls_convert_context(struct policydb *oldp,
+ struct policydb *newp,
+ struct context *context);
+
+int mls_compute_sid(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ u32 specified,
+ struct context *newcontext);
+
+int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
+ struct context *usercon);
+
+#ifdef CONFIG_NETLABEL
+void mls_export_netlbl_lvl(struct context *context,
+ struct netlbl_lsm_secattr *secattr);
+void mls_import_netlbl_lvl(struct context *context,
+ struct netlbl_lsm_secattr *secattr);
+int mls_export_netlbl_cat(struct context *context,
+ struct netlbl_lsm_secattr *secattr);
+int mls_import_netlbl_cat(struct context *context,
+ struct netlbl_lsm_secattr *secattr);
+#else
+static inline void mls_export_netlbl_lvl(struct context *context,
+ struct netlbl_lsm_secattr *secattr)
+{
+ return;
+}
+static inline void mls_import_netlbl_lvl(struct context *context,
+ struct netlbl_lsm_secattr *secattr)
+{
+ return;
+}
+static inline int mls_export_netlbl_cat(struct context *context,
+ struct netlbl_lsm_secattr *secattr)
+{
+ return -ENOMEM;
+}
+static inline int mls_import_netlbl_cat(struct context *context,
+ struct netlbl_lsm_secattr *secattr)
+{
+ return -ENOMEM;
+}
+#endif
+
+#endif /* _SS_MLS_H */
+
diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h
new file mode 100644
index 0000000..b6e943a
--- /dev/null
+++ b/security/selinux/ss/mls_types.h
@@ -0,0 +1,56 @@
+/*
+ * Type definitions for the multi-level security (MLS) policy.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+/*
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ *
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ */
+
+#ifndef _SS_MLS_TYPES_H_
+#define _SS_MLS_TYPES_H_
+
+#include "security.h"
+
+struct mls_level {
+ u32 sens; /* sensitivity */
+ struct ebitmap cat; /* category set */
+};
+
+struct mls_range {
+ struct mls_level level[2]; /* low == level[0], high == level[1] */
+};
+
+static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2)
+{
+ if (!selinux_mls_enabled)
+ return 1;
+
+ return ((l1->sens == l2->sens) &&
+ ebitmap_cmp(&l1->cat, &l2->cat));
+}
+
+static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2)
+{
+ if (!selinux_mls_enabled)
+ return 1;
+
+ return ((l1->sens >= l2->sens) &&
+ ebitmap_contains(&l1->cat, &l2->cat));
+}
+
+#define mls_level_incomp(l1, l2) \
+(!mls_level_dom((l1), (l2)) && !mls_level_dom((l2), (l1)))
+
+#define mls_level_between(l1, l2, l3) \
+(mls_level_dom((l1), (l2)) && mls_level_dom((l3), (l1)))
+
+#define mls_range_contains(r1, r2) \
+(mls_level_dom(&(r2).level[0], &(r1).level[0]) && \
+ mls_level_dom(&(r1).level[1], &(r2).level[1]))
+
+#endif /* _SS_MLS_TYPES_H_ */
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
new file mode 100644
index 0000000..72e4a54
--- /dev/null
+++ b/security/selinux/ss/policydb.c
@@ -0,0 +1,2151 @@
+/*
+ * Implementation of the policy database.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+
+/*
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ *
+ * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
+ *
+ * Added conditional policy language extensions
+ *
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ * Added support for the policy capability bitmap
+ *
+ * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/audit.h>
+#include "security.h"
+
+#include "policydb.h"
+#include "conditional.h"
+#include "mls.h"
+
+#define _DEBUG_HASHES
+
+#ifdef DEBUG_HASHES
+static char *symtab_name[SYM_NUM] = {
+ "common prefixes",
+ "classes",
+ "roles",
+ "types",
+ "users",
+ "bools",
+ "levels",
+ "categories",
+};
+#endif
+
+int selinux_mls_enabled;
+
+static unsigned int symtab_sizes[SYM_NUM] = {
+ 2,
+ 32,
+ 16,
+ 512,
+ 128,
+ 16,
+ 16,
+ 16,
+};
+
+struct policydb_compat_info {
+ int version;
+ int sym_num;
+ int ocon_num;
+};
+
+/* These need to be updated if SYM_NUM or OCON_NUM changes */
+static struct policydb_compat_info policydb_compat[] = {
+ {
+ .version = POLICYDB_VERSION_BASE,
+ .sym_num = SYM_NUM - 3,
+ .ocon_num = OCON_NUM - 1,
+ },
+ {
+ .version = POLICYDB_VERSION_BOOL,
+ .sym_num = SYM_NUM - 2,
+ .ocon_num = OCON_NUM - 1,
+ },
+ {
+ .version = POLICYDB_VERSION_IPV6,
+ .sym_num = SYM_NUM - 2,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_NLCLASS,
+ .sym_num = SYM_NUM - 2,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_MLS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_AVTAB,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_RANGETRANS,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_POLCAP,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_PERMISSIVE,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+ {
+ .version = POLICYDB_VERSION_BOUNDARY,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
+};
+
+static struct policydb_compat_info *policydb_lookup_compat(int version)
+{
+ int i;
+ struct policydb_compat_info *info = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(policydb_compat); i++) {
+ if (policydb_compat[i].version == version) {
+ info = &policydb_compat[i];
+ break;
+ }
+ }
+ return info;
+}
+
+/*
+ * Initialize the role table.
+ */
+static int roles_init(struct policydb *p)
+{
+ char *key = NULL;
+ int rc;
+ struct role_datum *role;
+
+ role = kzalloc(sizeof(*role), GFP_KERNEL);
+ if (!role) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ role->value = ++p->p_roles.nprim;
+ if (role->value != OBJECT_R_VAL) {
+ rc = -EINVAL;
+ goto out_free_role;
+ }
+ key = kmalloc(strlen(OBJECT_R)+1, GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto out_free_role;
+ }
+ strcpy(key, OBJECT_R);
+ rc = hashtab_insert(p->p_roles.table, key, role);
+ if (rc)
+ goto out_free_key;
+out:
+ return rc;
+
+out_free_key:
+ kfree(key);
+out_free_role:
+ kfree(role);
+ goto out;
+}
+
+/*
+ * Initialize a policy database structure.
+ */
+static int policydb_init(struct policydb *p)
+{
+ int i, rc;
+
+ memset(p, 0, sizeof(*p));
+
+ for (i = 0; i < SYM_NUM; i++) {
+ rc = symtab_init(&p->symtab[i], symtab_sizes[i]);
+ if (rc)
+ goto out_free_symtab;
+ }
+
+ rc = avtab_init(&p->te_avtab);
+ if (rc)
+ goto out_free_symtab;
+
+ rc = roles_init(p);
+ if (rc)
+ goto out_free_symtab;
+
+ rc = cond_policydb_init(p);
+ if (rc)
+ goto out_free_symtab;
+
+ ebitmap_init(&p->policycaps);
+ ebitmap_init(&p->permissive_map);
+
+out:
+ return rc;
+
+out_free_symtab:
+ for (i = 0; i < SYM_NUM; i++)
+ hashtab_destroy(p->symtab[i].table);
+ goto out;
+}
+
+/*
+ * The following *_index functions are used to
+ * define the val_to_name and val_to_struct arrays
+ * in a policy database structure. The val_to_name
+ * arrays are used when converting security context
+ * structures into string representations. The
+ * val_to_struct arrays are used when the attributes
+ * of a class, role, or user are needed.
+ */
+
+static int common_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct common_datum *comdatum;
+
+ comdatum = datum;
+ p = datap;
+ if (!comdatum->value || comdatum->value > p->p_commons.nprim)
+ return -EINVAL;
+ p->p_common_val_to_name[comdatum->value - 1] = key;
+ return 0;
+}
+
+static int class_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct class_datum *cladatum;
+
+ cladatum = datum;
+ p = datap;
+ if (!cladatum->value || cladatum->value > p->p_classes.nprim)
+ return -EINVAL;
+ p->p_class_val_to_name[cladatum->value - 1] = key;
+ p->class_val_to_struct[cladatum->value - 1] = cladatum;
+ return 0;
+}
+
+static int role_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct role_datum *role;
+
+ role = datum;
+ p = datap;
+ if (!role->value
+ || role->value > p->p_roles.nprim
+ || role->bounds > p->p_roles.nprim)
+ return -EINVAL;
+ p->p_role_val_to_name[role->value - 1] = key;
+ p->role_val_to_struct[role->value - 1] = role;
+ return 0;
+}
+
+static int type_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct type_datum *typdatum;
+
+ typdatum = datum;
+ p = datap;
+
+ if (typdatum->primary) {
+ if (!typdatum->value
+ || typdatum->value > p->p_types.nprim
+ || typdatum->bounds > p->p_types.nprim)
+ return -EINVAL;
+ p->p_type_val_to_name[typdatum->value - 1] = key;
+ p->type_val_to_struct[typdatum->value - 1] = typdatum;
+ }
+
+ return 0;
+}
+
+static int user_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct user_datum *usrdatum;
+
+ usrdatum = datum;
+ p = datap;
+ if (!usrdatum->value
+ || usrdatum->value > p->p_users.nprim
+ || usrdatum->bounds > p->p_users.nprim)
+ return -EINVAL;
+ p->p_user_val_to_name[usrdatum->value - 1] = key;
+ p->user_val_to_struct[usrdatum->value - 1] = usrdatum;
+ return 0;
+}
+
+static int sens_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct level_datum *levdatum;
+
+ levdatum = datum;
+ p = datap;
+
+ if (!levdatum->isalias) {
+ if (!levdatum->level->sens ||
+ levdatum->level->sens > p->p_levels.nprim)
+ return -EINVAL;
+ p->p_sens_val_to_name[levdatum->level->sens - 1] = key;
+ }
+
+ return 0;
+}
+
+static int cat_index(void *key, void *datum, void *datap)
+{
+ struct policydb *p;
+ struct cat_datum *catdatum;
+
+ catdatum = datum;
+ p = datap;
+
+ if (!catdatum->isalias) {
+ if (!catdatum->value || catdatum->value > p->p_cats.nprim)
+ return -EINVAL;
+ p->p_cat_val_to_name[catdatum->value - 1] = key;
+ }
+
+ return 0;
+}
+
+static int (*index_f[SYM_NUM]) (void *key, void *datum, void *datap) =
+{
+ common_index,
+ class_index,
+ role_index,
+ type_index,
+ user_index,
+ cond_index_bool,
+ sens_index,
+ cat_index,
+};
+
+/*
+ * Define the common val_to_name array and the class
+ * val_to_name and val_to_struct arrays in a policy
+ * database structure.
+ *
+ * Caller must clean up upon failure.
+ */
+static int policydb_index_classes(struct policydb *p)
+{
+ int rc;
+
+ p->p_common_val_to_name =
+ kmalloc(p->p_commons.nprim * sizeof(char *), GFP_KERNEL);
+ if (!p->p_common_val_to_name) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = hashtab_map(p->p_commons.table, common_index, p);
+ if (rc)
+ goto out;
+
+ p->class_val_to_struct =
+ kmalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)), GFP_KERNEL);
+ if (!p->class_val_to_struct) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ p->p_class_val_to_name =
+ kmalloc(p->p_classes.nprim * sizeof(char *), GFP_KERNEL);
+ if (!p->p_class_val_to_name) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = hashtab_map(p->p_classes.table, class_index, p);
+out:
+ return rc;
+}
+
+#ifdef DEBUG_HASHES
+static void symtab_hash_eval(struct symtab *s)
+{
+ int i;
+
+ for (i = 0; i < SYM_NUM; i++) {
+ struct hashtab *h = s[i].table;
+ struct hashtab_info info;
+
+ hashtab_stat(h, &info);
+ printk(KERN_DEBUG "SELinux: %s: %d entries and %d/%d buckets used, "
+ "longest chain length %d\n", symtab_name[i], h->nel,
+ info.slots_used, h->size, info.max_chain_len);
+ }
+}
+#endif
+
+/*
+ * Define the other val_to_name and val_to_struct arrays
+ * in a policy database structure.
+ *
+ * Caller must clean up on failure.
+ */
+static int policydb_index_others(struct policydb *p)
+{
+ int i, rc = 0;
+
+ printk(KERN_DEBUG "SELinux: %d users, %d roles, %d types, %d bools",
+ p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim, p->p_bools.nprim);
+ if (selinux_mls_enabled)
+ printk(", %d sens, %d cats", p->p_levels.nprim,
+ p->p_cats.nprim);
+ printk("\n");
+
+ printk(KERN_DEBUG "SELinux: %d classes, %d rules\n",
+ p->p_classes.nprim, p->te_avtab.nel);
+
+#ifdef DEBUG_HASHES
+ avtab_hash_eval(&p->te_avtab, "rules");
+ symtab_hash_eval(p->symtab);
+#endif
+
+ p->role_val_to_struct =
+ kmalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)),
+ GFP_KERNEL);
+ if (!p->role_val_to_struct) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ p->user_val_to_struct =
+ kmalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)),
+ GFP_KERNEL);
+ if (!p->user_val_to_struct) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ p->type_val_to_struct =
+ kmalloc(p->p_types.nprim * sizeof(*(p->type_val_to_struct)),
+ GFP_KERNEL);
+ if (!p->type_val_to_struct) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (cond_init_bool_indexes(p)) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ for (i = SYM_ROLES; i < SYM_NUM; i++) {
+ p->sym_val_to_name[i] =
+ kmalloc(p->symtab[i].nprim * sizeof(char *), GFP_KERNEL);
+ if (!p->sym_val_to_name[i]) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ rc = hashtab_map(p->symtab[i].table, index_f[i], p);
+ if (rc)
+ goto out;
+ }
+
+out:
+ return rc;
+}
+
+/*
+ * The following *_destroy functions are used to
+ * free any memory allocated for each kind of
+ * symbol data in the policy database.
+ */
+
+static int perm_destroy(void *key, void *datum, void *p)
+{
+ kfree(key);
+ kfree(datum);
+ return 0;
+}
+
+static int common_destroy(void *key, void *datum, void *p)
+{
+ struct common_datum *comdatum;
+
+ kfree(key);
+ comdatum = datum;
+ hashtab_map(comdatum->permissions.table, perm_destroy, NULL);
+ hashtab_destroy(comdatum->permissions.table);
+ kfree(datum);
+ return 0;
+}
+
+static int cls_destroy(void *key, void *datum, void *p)
+{
+ struct class_datum *cladatum;
+ struct constraint_node *constraint, *ctemp;
+ struct constraint_expr *e, *etmp;
+
+ kfree(key);
+ cladatum = datum;
+ hashtab_map(cladatum->permissions.table, perm_destroy, NULL);
+ hashtab_destroy(cladatum->permissions.table);
+ constraint = cladatum->constraints;
+ while (constraint) {
+ e = constraint->expr;
+ while (e) {
+ ebitmap_destroy(&e->names);
+ etmp = e;
+ e = e->next;
+ kfree(etmp);
+ }
+ ctemp = constraint;
+ constraint = constraint->next;
+ kfree(ctemp);
+ }
+
+ constraint = cladatum->validatetrans;
+ while (constraint) {
+ e = constraint->expr;
+ while (e) {
+ ebitmap_destroy(&e->names);
+ etmp = e;
+ e = e->next;
+ kfree(etmp);
+ }
+ ctemp = constraint;
+ constraint = constraint->next;
+ kfree(ctemp);
+ }
+
+ kfree(cladatum->comkey);
+ kfree(datum);
+ return 0;
+}
+
+static int role_destroy(void *key, void *datum, void *p)
+{
+ struct role_datum *role;
+
+ kfree(key);
+ role = datum;
+ ebitmap_destroy(&role->dominates);
+ ebitmap_destroy(&role->types);
+ kfree(datum);
+ return 0;
+}
+
+static int type_destroy(void *key, void *datum, void *p)
+{
+ kfree(key);
+ kfree(datum);
+ return 0;
+}
+
+static int user_destroy(void *key, void *datum, void *p)
+{
+ struct user_datum *usrdatum;
+
+ kfree(key);
+ usrdatum = datum;
+ ebitmap_destroy(&usrdatum->roles);
+ ebitmap_destroy(&usrdatum->range.level[0].cat);
+ ebitmap_destroy(&usrdatum->range.level[1].cat);
+ ebitmap_destroy(&usrdatum->dfltlevel.cat);
+ kfree(datum);
+ return 0;
+}
+
+static int sens_destroy(void *key, void *datum, void *p)
+{
+ struct level_datum *levdatum;
+
+ kfree(key);
+ levdatum = datum;
+ ebitmap_destroy(&levdatum->level->cat);
+ kfree(levdatum->level);
+ kfree(datum);
+ return 0;
+}
+
+static int cat_destroy(void *key, void *datum, void *p)
+{
+ kfree(key);
+ kfree(datum);
+ return 0;
+}
+
+static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =
+{
+ common_destroy,
+ cls_destroy,
+ role_destroy,
+ type_destroy,
+ user_destroy,
+ cond_destroy_bool,
+ sens_destroy,
+ cat_destroy,
+};
+
+static void ocontext_destroy(struct ocontext *c, int i)
+{
+ context_destroy(&c->context[0]);
+ context_destroy(&c->context[1]);
+ if (i == OCON_ISID || i == OCON_FS ||
+ i == OCON_NETIF || i == OCON_FSUSE)
+ kfree(c->u.name);
+ kfree(c);
+}
+
+/*
+ * Free any memory allocated by a policy database structure.
+ */
+void policydb_destroy(struct policydb *p)
+{
+ struct ocontext *c, *ctmp;
+ struct genfs *g, *gtmp;
+ int i;
+ struct role_allow *ra, *lra = NULL;
+ struct role_trans *tr, *ltr = NULL;
+ struct range_trans *rt, *lrt = NULL;
+
+ for (i = 0; i < SYM_NUM; i++) {
+ cond_resched();
+ hashtab_map(p->symtab[i].table, destroy_f[i], NULL);
+ hashtab_destroy(p->symtab[i].table);
+ }
+
+ for (i = 0; i < SYM_NUM; i++)
+ kfree(p->sym_val_to_name[i]);
+
+ kfree(p->class_val_to_struct);
+ kfree(p->role_val_to_struct);
+ kfree(p->user_val_to_struct);
+ kfree(p->type_val_to_struct);
+
+ avtab_destroy(&p->te_avtab);
+
+ for (i = 0; i < OCON_NUM; i++) {
+ cond_resched();
+ c = p->ocontexts[i];
+ while (c) {
+ ctmp = c;
+ c = c->next;
+ ocontext_destroy(ctmp, i);
+ }
+ p->ocontexts[i] = NULL;
+ }
+
+ g = p->genfs;
+ while (g) {
+ cond_resched();
+ kfree(g->fstype);
+ c = g->head;
+ while (c) {
+ ctmp = c;
+ c = c->next;
+ ocontext_destroy(ctmp, OCON_FSUSE);
+ }
+ gtmp = g;
+ g = g->next;
+ kfree(gtmp);
+ }
+ p->genfs = NULL;
+
+ cond_policydb_destroy(p);
+
+ for (tr = p->role_tr; tr; tr = tr->next) {
+ cond_resched();
+ kfree(ltr);
+ ltr = tr;
+ }
+ kfree(ltr);
+
+ for (ra = p->role_allow; ra; ra = ra->next) {
+ cond_resched();
+ kfree(lra);
+ lra = ra;
+ }
+ kfree(lra);
+
+ for (rt = p->range_tr; rt; rt = rt->next) {
+ cond_resched();
+ if (lrt) {
+ ebitmap_destroy(&lrt->target_range.level[0].cat);
+ ebitmap_destroy(&lrt->target_range.level[1].cat);
+ kfree(lrt);
+ }
+ lrt = rt;
+ }
+ if (lrt) {
+ ebitmap_destroy(&lrt->target_range.level[0].cat);
+ ebitmap_destroy(&lrt->target_range.level[1].cat);
+ kfree(lrt);
+ }
+
+ if (p->type_attr_map) {
+ for (i = 0; i < p->p_types.nprim; i++)
+ ebitmap_destroy(&p->type_attr_map[i]);
+ }
+ kfree(p->type_attr_map);
+ kfree(p->undefined_perms);
+ ebitmap_destroy(&p->policycaps);
+ ebitmap_destroy(&p->permissive_map);
+
+ return;
+}
+
+/*
+ * Load the initial SIDs specified in a policy database
+ * structure into a SID table.
+ */
+int policydb_load_isids(struct policydb *p, struct sidtab *s)
+{
+ struct ocontext *head, *c;
+ int rc;
+
+ rc = sidtab_init(s);
+ if (rc) {
+ printk(KERN_ERR "SELinux: out of memory on SID table init\n");
+ goto out;
+ }
+
+ head = p->ocontexts[OCON_ISID];
+ for (c = head; c; c = c->next) {
+ if (!c->context[0].user) {
+ printk(KERN_ERR "SELinux: SID %s was never "
+ "defined.\n", c->u.name);
+ rc = -EINVAL;
+ goto out;
+ }
+ if (sidtab_insert(s, c->sid[0], &c->context[0])) {
+ printk(KERN_ERR "SELinux: unable to load initial "
+ "SID %s.\n", c->u.name);
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+out:
+ return rc;
+}
+
+int policydb_class_isvalid(struct policydb *p, unsigned int class)
+{
+ if (!class || class > p->p_classes.nprim)
+ return 0;
+ return 1;
+}
+
+int policydb_role_isvalid(struct policydb *p, unsigned int role)
+{
+ if (!role || role > p->p_roles.nprim)
+ return 0;
+ return 1;
+}
+
+int policydb_type_isvalid(struct policydb *p, unsigned int type)
+{
+ if (!type || type > p->p_types.nprim)
+ return 0;
+ return 1;
+}
+
+/*
+ * Return 1 if the fields in the security context
+ * structure `c' are valid. Return 0 otherwise.
+ */
+int policydb_context_isvalid(struct policydb *p, struct context *c)
+{
+ struct role_datum *role;
+ struct user_datum *usrdatum;
+
+ if (!c->role || c->role > p->p_roles.nprim)
+ return 0;
+
+ if (!c->user || c->user > p->p_users.nprim)
+ return 0;
+
+ if (!c->type || c->type > p->p_types.nprim)
+ return 0;
+
+ if (c->role != OBJECT_R_VAL) {
+ /*
+ * Role must be authorized for the type.
+ */
+ role = p->role_val_to_struct[c->role - 1];
+ if (!ebitmap_get_bit(&role->types,
+ c->type - 1))
+ /* role may not be associated with type */
+ return 0;
+
+ /*
+ * User must be authorized for the role.
+ */
+ usrdatum = p->user_val_to_struct[c->user - 1];
+ if (!usrdatum)
+ return 0;
+
+ if (!ebitmap_get_bit(&usrdatum->roles,
+ c->role - 1))
+ /* user may not be associated with role */
+ return 0;
+ }
+
+ if (!mls_context_isvalid(p, c))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Read a MLS range structure from a policydb binary
+ * representation file.
+ */
+static int mls_read_range_helper(struct mls_range *r, void *fp)
+{
+ __le32 buf[2];
+ u32 items;
+ int rc;
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto out;
+
+ items = le32_to_cpu(buf[0]);
+ if (items > ARRAY_SIZE(buf)) {
+ printk(KERN_ERR "SELinux: mls: range overflow\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = next_entry(buf, fp, sizeof(u32) * items);
+ if (rc < 0) {
+ printk(KERN_ERR "SELinux: mls: truncated range\n");
+ goto out;
+ }
+ r->level[0].sens = le32_to_cpu(buf[0]);
+ if (items > 1)
+ r->level[1].sens = le32_to_cpu(buf[1]);
+ else
+ r->level[1].sens = r->level[0].sens;
+
+ rc = ebitmap_read(&r->level[0].cat, fp);
+ if (rc) {
+ printk(KERN_ERR "SELinux: mls: error reading low "
+ "categories\n");
+ goto out;
+ }
+ if (items > 1) {
+ rc = ebitmap_read(&r->level[1].cat, fp);
+ if (rc) {
+ printk(KERN_ERR "SELinux: mls: error reading high "
+ "categories\n");
+ goto bad_high;
+ }
+ } else {
+ rc = ebitmap_cpy(&r->level[1].cat, &r->level[0].cat);
+ if (rc) {
+ printk(KERN_ERR "SELinux: mls: out of memory\n");
+ goto bad_high;
+ }
+ }
+
+ rc = 0;
+out:
+ return rc;
+bad_high:
+ ebitmap_destroy(&r->level[0].cat);
+ goto out;
+}
+
+/*
+ * Read and validate a security context structure
+ * from a policydb binary representation file.
+ */
+static int context_read_and_validate(struct context *c,
+ struct policydb *p,
+ void *fp)
+{
+ __le32 buf[3];
+ int rc;
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0) {
+ printk(KERN_ERR "SELinux: context truncated\n");
+ goto out;
+ }
+ c->user = le32_to_cpu(buf[0]);
+ c->role = le32_to_cpu(buf[1]);
+ c->type = le32_to_cpu(buf[2]);
+ if (p->policyvers >= POLICYDB_VERSION_MLS) {
+ if (mls_read_range_helper(&c->range, fp)) {
+ printk(KERN_ERR "SELinux: error reading MLS range of "
+ "context\n");
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (!policydb_context_isvalid(p, c)) {
+ printk(KERN_ERR "SELinux: invalid security context\n");
+ context_destroy(c);
+ rc = -EINVAL;
+ }
+out:
+ return rc;
+}
+
+/*
+ * The following *_read functions are used to
+ * read the symbol data from a policy database
+ * binary representation file.
+ */
+
+static int perm_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct perm_datum *perdatum;
+ int rc;
+ __le32 buf[2];
+ u32 len;
+
+ perdatum = kzalloc(sizeof(*perdatum), GFP_KERNEL);
+ if (!perdatum) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ perdatum->value = le32_to_cpu(buf[1]);
+
+ key = kmalloc(len + 1, GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = '\0';
+
+ rc = hashtab_insert(h, key, perdatum);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+bad:
+ perm_destroy(key, perdatum, NULL);
+ goto out;
+}
+
+static int common_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct common_datum *comdatum;
+ __le32 buf[4];
+ u32 len, nel;
+ int i, rc;
+
+ comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL);
+ if (!comdatum) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ comdatum->value = le32_to_cpu(buf[1]);
+
+ rc = symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE);
+ if (rc)
+ goto bad;
+ comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+ nel = le32_to_cpu(buf[3]);
+
+ key = kmalloc(len + 1, GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = '\0';
+
+ for (i = 0; i < nel; i++) {
+ rc = perm_read(p, comdatum->permissions.table, fp);
+ if (rc)
+ goto bad;
+ }
+
+ rc = hashtab_insert(h, key, comdatum);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+bad:
+ common_destroy(key, comdatum, NULL);
+ goto out;
+}
+
+static int read_cons_helper(struct constraint_node **nodep, int ncons,
+ int allowxtarget, void *fp)
+{
+ struct constraint_node *c, *lc;
+ struct constraint_expr *e, *le;
+ __le32 buf[3];
+ u32 nexpr;
+ int rc, i, j, depth;
+
+ lc = NULL;
+ for (i = 0; i < ncons; i++) {
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c)
+ return -ENOMEM;
+
+ if (lc)
+ lc->next = c;
+ else
+ *nodep = c;
+
+ rc = next_entry(buf, fp, (sizeof(u32) * 2));
+ if (rc < 0)
+ return rc;
+ c->permissions = le32_to_cpu(buf[0]);
+ nexpr = le32_to_cpu(buf[1]);
+ le = NULL;
+ depth = -1;
+ for (j = 0; j < nexpr; j++) {
+ e = kzalloc(sizeof(*e), GFP_KERNEL);
+ if (!e)
+ return -ENOMEM;
+
+ if (le)
+ le->next = e;
+ else
+ c->expr = e;
+
+ rc = next_entry(buf, fp, (sizeof(u32) * 3));
+ if (rc < 0)
+ return rc;
+ e->expr_type = le32_to_cpu(buf[0]);
+ e->attr = le32_to_cpu(buf[1]);
+ e->op = le32_to_cpu(buf[2]);
+
+ switch (e->expr_type) {
+ case CEXPR_NOT:
+ if (depth < 0)
+ return -EINVAL;
+ break;
+ case CEXPR_AND:
+ case CEXPR_OR:
+ if (depth < 1)
+ return -EINVAL;
+ depth--;
+ break;
+ case CEXPR_ATTR:
+ if (depth == (CEXPR_MAXDEPTH - 1))
+ return -EINVAL;
+ depth++;
+ break;
+ case CEXPR_NAMES:
+ if (!allowxtarget && (e->attr & CEXPR_XTARGET))
+ return -EINVAL;
+ if (depth == (CEXPR_MAXDEPTH - 1))
+ return -EINVAL;
+ depth++;
+ if (ebitmap_read(&e->names, fp))
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ le = e;
+ }
+ if (depth != 0)
+ return -EINVAL;
+ lc = c;
+ }
+
+ return 0;
+}
+
+static int class_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct class_datum *cladatum;
+ __le32 buf[6];
+ u32 len, len2, ncons, nel;
+ int i, rc;
+
+ cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL);
+ if (!cladatum) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = next_entry(buf, fp, sizeof(u32)*6);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ len2 = le32_to_cpu(buf[1]);
+ cladatum->value = le32_to_cpu(buf[2]);
+
+ rc = symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE);
+ if (rc)
+ goto bad;
+ cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+ nel = le32_to_cpu(buf[4]);
+
+ ncons = le32_to_cpu(buf[5]);
+
+ key = kmalloc(len + 1, GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = '\0';
+
+ if (len2) {
+ cladatum->comkey = kmalloc(len2 + 1, GFP_KERNEL);
+ if (!cladatum->comkey) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(cladatum->comkey, fp, len2);
+ if (rc < 0)
+ goto bad;
+ cladatum->comkey[len2] = '\0';
+
+ cladatum->comdatum = hashtab_search(p->p_commons.table,
+ cladatum->comkey);
+ if (!cladatum->comdatum) {
+ printk(KERN_ERR "SELinux: unknown common %s\n",
+ cladatum->comkey);
+ rc = -EINVAL;
+ goto bad;
+ }
+ }
+ for (i = 0; i < nel; i++) {
+ rc = perm_read(p, cladatum->permissions.table, fp);
+ if (rc)
+ goto bad;
+ }
+
+ rc = read_cons_helper(&cladatum->constraints, ncons, 0, fp);
+ if (rc)
+ goto bad;
+
+ if (p->policyvers >= POLICYDB_VERSION_VALIDATETRANS) {
+ /* grab the validatetrans rules */
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ ncons = le32_to_cpu(buf[0]);
+ rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp);
+ if (rc)
+ goto bad;
+ }
+
+ rc = hashtab_insert(h, key, cladatum);
+ if (rc)
+ goto bad;
+
+ rc = 0;
+out:
+ return rc;
+bad:
+ cls_destroy(key, cladatum, NULL);
+ goto out;
+}
+
+static int role_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct role_datum *role;
+ int rc, to_read = 2;
+ __le32 buf[3];
+ u32 len;
+
+ role = kzalloc(sizeof(*role), GFP_KERNEL);
+ if (!role) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ to_read = 3;
+
+ rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ role->value = le32_to_cpu(buf[1]);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ role->bounds = le32_to_cpu(buf[2]);
+
+ key = kmalloc(len + 1, GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = '\0';
+
+ rc = ebitmap_read(&role->dominates, fp);
+ if (rc)
+ goto bad;
+
+ rc = ebitmap_read(&role->types, fp);
+ if (rc)
+ goto bad;
+
+ if (strcmp(key, OBJECT_R) == 0) {
+ if (role->value != OBJECT_R_VAL) {
+ printk(KERN_ERR "SELinux: Role %s has wrong value %d\n",
+ OBJECT_R, role->value);
+ rc = -EINVAL;
+ goto bad;
+ }
+ rc = 0;
+ goto bad;
+ }
+
+ rc = hashtab_insert(h, key, role);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+bad:
+ role_destroy(key, role, NULL);
+ goto out;
+}
+
+static int type_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct type_datum *typdatum;
+ int rc, to_read = 3;
+ __le32 buf[4];
+ u32 len;
+
+ typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL);
+ if (!typdatum) {
+ rc = -ENOMEM;
+ return rc;
+ }
+
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ to_read = 4;
+
+ rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ typdatum->value = le32_to_cpu(buf[1]);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) {
+ u32 prop = le32_to_cpu(buf[2]);
+
+ if (prop & TYPEDATUM_PROPERTY_PRIMARY)
+ typdatum->primary = 1;
+ if (prop & TYPEDATUM_PROPERTY_ATTRIBUTE)
+ typdatum->attribute = 1;
+
+ typdatum->bounds = le32_to_cpu(buf[3]);
+ } else {
+ typdatum->primary = le32_to_cpu(buf[2]);
+ }
+
+ key = kmalloc(len + 1, GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = '\0';
+
+ rc = hashtab_insert(h, key, typdatum);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+bad:
+ type_destroy(key, typdatum, NULL);
+ goto out;
+}
+
+
+/*
+ * Read a MLS level structure from a policydb binary
+ * representation file.
+ */
+static int mls_read_level(struct mls_level *lp, void *fp)
+{
+ __le32 buf[1];
+ int rc;
+
+ memset(lp, 0, sizeof(*lp));
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0) {
+ printk(KERN_ERR "SELinux: mls: truncated level\n");
+ goto bad;
+ }
+ lp->sens = le32_to_cpu(buf[0]);
+
+ if (ebitmap_read(&lp->cat, fp)) {
+ printk(KERN_ERR "SELinux: mls: error reading level "
+ "categories\n");
+ goto bad;
+ }
+
+ return 0;
+
+bad:
+ return -EINVAL;
+}
+
+static int user_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct user_datum *usrdatum;
+ int rc, to_read = 2;
+ __le32 buf[3];
+ u32 len;
+
+ usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL);
+ if (!usrdatum) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ to_read = 3;
+
+ rc = next_entry(buf, fp, sizeof(buf[0]) * to_read);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ usrdatum->value = le32_to_cpu(buf[1]);
+ if (p->policyvers >= POLICYDB_VERSION_BOUNDARY)
+ usrdatum->bounds = le32_to_cpu(buf[2]);
+
+ key = kmalloc(len + 1, GFP_KERNEL);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = '\0';
+
+ rc = ebitmap_read(&usrdatum->roles, fp);
+ if (rc)
+ goto bad;
+
+ if (p->policyvers >= POLICYDB_VERSION_MLS) {
+ rc = mls_read_range_helper(&usrdatum->range, fp);
+ if (rc)
+ goto bad;
+ rc = mls_read_level(&usrdatum->dfltlevel, fp);
+ if (rc)
+ goto bad;
+ }
+
+ rc = hashtab_insert(h, key, usrdatum);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+bad:
+ user_destroy(key, usrdatum, NULL);
+ goto out;
+}
+
+static int sens_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct level_datum *levdatum;
+ int rc;
+ __le32 buf[2];
+ u32 len;
+
+ levdatum = kzalloc(sizeof(*levdatum), GFP_ATOMIC);
+ if (!levdatum) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ levdatum->isalias = le32_to_cpu(buf[1]);
+
+ key = kmalloc(len + 1, GFP_ATOMIC);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = '\0';
+
+ levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC);
+ if (!levdatum->level) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ if (mls_read_level(levdatum->level, fp)) {
+ rc = -EINVAL;
+ goto bad;
+ }
+
+ rc = hashtab_insert(h, key, levdatum);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+bad:
+ sens_destroy(key, levdatum, NULL);
+ goto out;
+}
+
+static int cat_read(struct policydb *p, struct hashtab *h, void *fp)
+{
+ char *key = NULL;
+ struct cat_datum *catdatum;
+ int rc;
+ __le32 buf[3];
+ u32 len;
+
+ catdatum = kzalloc(sizeof(*catdatum), GFP_ATOMIC);
+ if (!catdatum) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rc = next_entry(buf, fp, sizeof buf);
+ if (rc < 0)
+ goto bad;
+
+ len = le32_to_cpu(buf[0]);
+ catdatum->value = le32_to_cpu(buf[1]);
+ catdatum->isalias = le32_to_cpu(buf[2]);
+
+ key = kmalloc(len + 1, GFP_ATOMIC);
+ if (!key) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(key, fp, len);
+ if (rc < 0)
+ goto bad;
+ key[len] = '\0';
+
+ rc = hashtab_insert(h, key, catdatum);
+ if (rc)
+ goto bad;
+out:
+ return rc;
+
+bad:
+ cat_destroy(key, catdatum, NULL);
+ goto out;
+}
+
+static int (*read_f[SYM_NUM]) (struct policydb *p, struct hashtab *h, void *fp) =
+{
+ common_read,
+ class_read,
+ role_read,
+ type_read,
+ user_read,
+ cond_read_bool,
+ sens_read,
+ cat_read,
+};
+
+static int user_bounds_sanity_check(void *key, void *datum, void *datap)
+{
+ struct user_datum *upper, *user;
+ struct policydb *p = datap;
+ int depth = 0;
+
+ upper = user = datum;
+ while (upper->bounds) {
+ struct ebitmap_node *node;
+ unsigned long bit;
+
+ if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
+ printk(KERN_ERR "SELinux: user %s: "
+ "too deep or looped boundary",
+ (char *) key);
+ return -EINVAL;
+ }
+
+ upper = p->user_val_to_struct[upper->bounds - 1];
+ ebitmap_for_each_positive_bit(&user->roles, node, bit) {
+ if (ebitmap_get_bit(&upper->roles, bit))
+ continue;
+
+ printk(KERN_ERR
+ "SELinux: boundary violated policy: "
+ "user=%s role=%s bounds=%s\n",
+ p->p_user_val_to_name[user->value - 1],
+ p->p_role_val_to_name[bit],
+ p->p_user_val_to_name[upper->value - 1]);
+
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int role_bounds_sanity_check(void *key, void *datum, void *datap)
+{
+ struct role_datum *upper, *role;
+ struct policydb *p = datap;
+ int depth = 0;
+
+ upper = role = datum;
+ while (upper->bounds) {
+ struct ebitmap_node *node;
+ unsigned long bit;
+
+ if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
+ printk(KERN_ERR "SELinux: role %s: "
+ "too deep or looped bounds\n",
+ (char *) key);
+ return -EINVAL;
+ }
+
+ upper = p->role_val_to_struct[upper->bounds - 1];
+ ebitmap_for_each_positive_bit(&role->types, node, bit) {
+ if (ebitmap_get_bit(&upper->types, bit))
+ continue;
+
+ printk(KERN_ERR
+ "SELinux: boundary violated policy: "
+ "role=%s type=%s bounds=%s\n",
+ p->p_role_val_to_name[role->value - 1],
+ p->p_type_val_to_name[bit],
+ p->p_role_val_to_name[upper->value - 1]);
+
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int type_bounds_sanity_check(void *key, void *datum, void *datap)
+{
+ struct type_datum *upper, *type;
+ struct policydb *p = datap;
+ int depth = 0;
+
+ upper = type = datum;
+ while (upper->bounds) {
+ if (++depth == POLICYDB_BOUNDS_MAXDEPTH) {
+ printk(KERN_ERR "SELinux: type %s: "
+ "too deep or looped boundary\n",
+ (char *) key);
+ return -EINVAL;
+ }
+
+ upper = p->type_val_to_struct[upper->bounds - 1];
+ if (upper->attribute) {
+ printk(KERN_ERR "SELinux: type %s: "
+ "bounded by attribute %s",
+ (char *) key,
+ p->p_type_val_to_name[upper->value - 1]);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int policydb_bounds_sanity_check(struct policydb *p)
+{
+ int rc;
+
+ if (p->policyvers < POLICYDB_VERSION_BOUNDARY)
+ return 0;
+
+ rc = hashtab_map(p->p_users.table,
+ user_bounds_sanity_check, p);
+ if (rc)
+ return rc;
+
+ rc = hashtab_map(p->p_roles.table,
+ role_bounds_sanity_check, p);
+ if (rc)
+ return rc;
+
+ rc = hashtab_map(p->p_types.table,
+ type_bounds_sanity_check, p);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+extern int ss_initialized;
+
+/*
+ * Read the configuration data from a policy database binary
+ * representation file into a policy database structure.
+ */
+int policydb_read(struct policydb *p, void *fp)
+{
+ struct role_allow *ra, *lra;
+ struct role_trans *tr, *ltr;
+ struct ocontext *l, *c, *newc;
+ struct genfs *genfs_p, *genfs, *newgenfs;
+ int i, j, rc;
+ __le32 buf[4];
+ u32 nodebuf[8];
+ u32 len, len2, config, nprim, nel, nel2;
+ char *policydb_str;
+ struct policydb_compat_info *info;
+ struct range_trans *rt, *lrt;
+
+ config = 0;
+
+ rc = policydb_init(p);
+ if (rc)
+ goto out;
+
+ /* Read the magic number and string length. */
+ rc = next_entry(buf, fp, sizeof(u32) * 2);
+ if (rc < 0)
+ goto bad;
+
+ if (le32_to_cpu(buf[0]) != POLICYDB_MAGIC) {
+ printk(KERN_ERR "SELinux: policydb magic number 0x%x does "
+ "not match expected magic number 0x%x\n",
+ le32_to_cpu(buf[0]), POLICYDB_MAGIC);
+ goto bad;
+ }
+
+ len = le32_to_cpu(buf[1]);
+ if (len != strlen(POLICYDB_STRING)) {
+ printk(KERN_ERR "SELinux: policydb string length %d does not "
+ "match expected length %Zu\n",
+ len, strlen(POLICYDB_STRING));
+ goto bad;
+ }
+ policydb_str = kmalloc(len + 1, GFP_KERNEL);
+ if (!policydb_str) {
+ printk(KERN_ERR "SELinux: unable to allocate memory for policydb "
+ "string of length %d\n", len);
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(policydb_str, fp, len);
+ if (rc < 0) {
+ printk(KERN_ERR "SELinux: truncated policydb string identifier\n");
+ kfree(policydb_str);
+ goto bad;
+ }
+ policydb_str[len] = '\0';
+ if (strcmp(policydb_str, POLICYDB_STRING)) {
+ printk(KERN_ERR "SELinux: policydb string %s does not match "
+ "my string %s\n", policydb_str, POLICYDB_STRING);
+ kfree(policydb_str);
+ goto bad;
+ }
+ /* Done with policydb_str. */
+ kfree(policydb_str);
+ policydb_str = NULL;
+
+ /* Read the version, config, and table sizes. */
+ rc = next_entry(buf, fp, sizeof(u32)*4);
+ if (rc < 0)
+ goto bad;
+
+ p->policyvers = le32_to_cpu(buf[0]);
+ if (p->policyvers < POLICYDB_VERSION_MIN ||
+ p->policyvers > POLICYDB_VERSION_MAX) {
+ printk(KERN_ERR "SELinux: policydb version %d does not match "
+ "my version range %d-%d\n",
+ le32_to_cpu(buf[0]), POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX);
+ goto bad;
+ }
+
+ if ((le32_to_cpu(buf[1]) & POLICYDB_CONFIG_MLS)) {
+ if (ss_initialized && !selinux_mls_enabled) {
+ printk(KERN_ERR "SELinux: Cannot switch between non-MLS"
+ " and MLS policies\n");
+ goto bad;
+ }
+ selinux_mls_enabled = 1;
+ config |= POLICYDB_CONFIG_MLS;
+
+ if (p->policyvers < POLICYDB_VERSION_MLS) {
+ printk(KERN_ERR "SELinux: security policydb version %d "
+ "(MLS) not backwards compatible\n",
+ p->policyvers);
+ goto bad;
+ }
+ } else {
+ if (ss_initialized && selinux_mls_enabled) {
+ printk(KERN_ERR "SELinux: Cannot switch between MLS and"
+ " non-MLS policies\n");
+ goto bad;
+ }
+ }
+ p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN);
+ p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN);
+
+ if (p->policyvers >= POLICYDB_VERSION_POLCAP &&
+ ebitmap_read(&p->policycaps, fp) != 0)
+ goto bad;
+
+ if (p->policyvers >= POLICYDB_VERSION_PERMISSIVE &&
+ ebitmap_read(&p->permissive_map, fp) != 0)
+ goto bad;
+
+ info = policydb_lookup_compat(p->policyvers);
+ if (!info) {
+ printk(KERN_ERR "SELinux: unable to find policy compat info "
+ "for version %d\n", p->policyvers);
+ goto bad;
+ }
+
+ if (le32_to_cpu(buf[2]) != info->sym_num ||
+ le32_to_cpu(buf[3]) != info->ocon_num) {
+ printk(KERN_ERR "SELinux: policydb table sizes (%d,%d) do "
+ "not match mine (%d,%d)\n", le32_to_cpu(buf[2]),
+ le32_to_cpu(buf[3]),
+ info->sym_num, info->ocon_num);
+ goto bad;
+ }
+
+ for (i = 0; i < info->sym_num; i++) {
+ rc = next_entry(buf, fp, sizeof(u32)*2);
+ if (rc < 0)
+ goto bad;
+ nprim = le32_to_cpu(buf[0]);
+ nel = le32_to_cpu(buf[1]);
+ for (j = 0; j < nel; j++) {
+ rc = read_f[i](p, p->symtab[i].table, fp);
+ if (rc)
+ goto bad;
+ }
+
+ p->symtab[i].nprim = nprim;
+ }
+
+ rc = avtab_read(&p->te_avtab, fp, p);
+ if (rc)
+ goto bad;
+
+ if (p->policyvers >= POLICYDB_VERSION_BOOL) {
+ rc = cond_read_list(p, fp);
+ if (rc)
+ goto bad;
+ }
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ nel = le32_to_cpu(buf[0]);
+ ltr = NULL;
+ for (i = 0; i < nel; i++) {
+ tr = kzalloc(sizeof(*tr), GFP_KERNEL);
+ if (!tr) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ if (ltr)
+ ltr->next = tr;
+ else
+ p->role_tr = tr;
+ rc = next_entry(buf, fp, sizeof(u32)*3);
+ if (rc < 0)
+ goto bad;
+ tr->role = le32_to_cpu(buf[0]);
+ tr->type = le32_to_cpu(buf[1]);
+ tr->new_role = le32_to_cpu(buf[2]);
+ if (!policydb_role_isvalid(p, tr->role) ||
+ !policydb_type_isvalid(p, tr->type) ||
+ !policydb_role_isvalid(p, tr->new_role)) {
+ rc = -EINVAL;
+ goto bad;
+ }
+ ltr = tr;
+ }
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ nel = le32_to_cpu(buf[0]);
+ lra = NULL;
+ for (i = 0; i < nel; i++) {
+ ra = kzalloc(sizeof(*ra), GFP_KERNEL);
+ if (!ra) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ if (lra)
+ lra->next = ra;
+ else
+ p->role_allow = ra;
+ rc = next_entry(buf, fp, sizeof(u32)*2);
+ if (rc < 0)
+ goto bad;
+ ra->role = le32_to_cpu(buf[0]);
+ ra->new_role = le32_to_cpu(buf[1]);
+ if (!policydb_role_isvalid(p, ra->role) ||
+ !policydb_role_isvalid(p, ra->new_role)) {
+ rc = -EINVAL;
+ goto bad;
+ }
+ lra = ra;
+ }
+
+ rc = policydb_index_classes(p);
+ if (rc)
+ goto bad;
+
+ rc = policydb_index_others(p);
+ if (rc)
+ goto bad;
+
+ for (i = 0; i < info->ocon_num; i++) {
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ nel = le32_to_cpu(buf[0]);
+ l = NULL;
+ for (j = 0; j < nel; j++) {
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ if (l)
+ l->next = c;
+ else
+ p->ocontexts[i] = c;
+ l = c;
+ rc = -EINVAL;
+ switch (i) {
+ case OCON_ISID:
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ c->sid[0] = le32_to_cpu(buf[0]);
+ rc = context_read_and_validate(&c->context[0], p, fp);
+ if (rc)
+ goto bad;
+ break;
+ case OCON_FS:
+ case OCON_NETIF:
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ len = le32_to_cpu(buf[0]);
+ c->u.name = kmalloc(len + 1, GFP_KERNEL);
+ if (!c->u.name) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(c->u.name, fp, len);
+ if (rc < 0)
+ goto bad;
+ c->u.name[len] = 0;
+ rc = context_read_and_validate(&c->context[0], p, fp);
+ if (rc)
+ goto bad;
+ rc = context_read_and_validate(&c->context[1], p, fp);
+ if (rc)
+ goto bad;
+ break;
+ case OCON_PORT:
+ rc = next_entry(buf, fp, sizeof(u32)*3);
+ if (rc < 0)
+ goto bad;
+ c->u.port.protocol = le32_to_cpu(buf[0]);
+ c->u.port.low_port = le32_to_cpu(buf[1]);
+ c->u.port.high_port = le32_to_cpu(buf[2]);
+ rc = context_read_and_validate(&c->context[0], p, fp);
+ if (rc)
+ goto bad;
+ break;
+ case OCON_NODE:
+ rc = next_entry(nodebuf, fp, sizeof(u32) * 2);
+ if (rc < 0)
+ goto bad;
+ c->u.node.addr = nodebuf[0]; /* network order */
+ c->u.node.mask = nodebuf[1]; /* network order */
+ rc = context_read_and_validate(&c->context[0], p, fp);
+ if (rc)
+ goto bad;
+ break;
+ case OCON_FSUSE:
+ rc = next_entry(buf, fp, sizeof(u32)*2);
+ if (rc < 0)
+ goto bad;
+ c->v.behavior = le32_to_cpu(buf[0]);
+ if (c->v.behavior > SECURITY_FS_USE_NONE)
+ goto bad;
+ len = le32_to_cpu(buf[1]);
+ c->u.name = kmalloc(len + 1, GFP_KERNEL);
+ if (!c->u.name) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ rc = next_entry(c->u.name, fp, len);
+ if (rc < 0)
+ goto bad;
+ c->u.name[len] = 0;
+ rc = context_read_and_validate(&c->context[0], p, fp);
+ if (rc)
+ goto bad;
+ break;
+ case OCON_NODE6: {
+ int k;
+
+ rc = next_entry(nodebuf, fp, sizeof(u32) * 8);
+ if (rc < 0)
+ goto bad;
+ for (k = 0; k < 4; k++)
+ c->u.node6.addr[k] = nodebuf[k];
+ for (k = 0; k < 4; k++)
+ c->u.node6.mask[k] = nodebuf[k+4];
+ if (context_read_and_validate(&c->context[0], p, fp))
+ goto bad;
+ break;
+ }
+ }
+ }
+ }
+
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ nel = le32_to_cpu(buf[0]);
+ genfs_p = NULL;
+ rc = -EINVAL;
+ for (i = 0; i < nel; i++) {
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ len = le32_to_cpu(buf[0]);
+ newgenfs = kzalloc(sizeof(*newgenfs), GFP_KERNEL);
+ if (!newgenfs) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+
+ newgenfs->fstype = kmalloc(len + 1, GFP_KERNEL);
+ if (!newgenfs->fstype) {
+ rc = -ENOMEM;
+ kfree(newgenfs);
+ goto bad;
+ }
+ rc = next_entry(newgenfs->fstype, fp, len);
+ if (rc < 0) {
+ kfree(newgenfs->fstype);
+ kfree(newgenfs);
+ goto bad;
+ }
+ newgenfs->fstype[len] = 0;
+ for (genfs_p = NULL, genfs = p->genfs; genfs;
+ genfs_p = genfs, genfs = genfs->next) {
+ if (strcmp(newgenfs->fstype, genfs->fstype) == 0) {
+ printk(KERN_ERR "SELinux: dup genfs "
+ "fstype %s\n", newgenfs->fstype);
+ kfree(newgenfs->fstype);
+ kfree(newgenfs);
+ goto bad;
+ }
+ if (strcmp(newgenfs->fstype, genfs->fstype) < 0)
+ break;
+ }
+ newgenfs->next = genfs;
+ if (genfs_p)
+ genfs_p->next = newgenfs;
+ else
+ p->genfs = newgenfs;
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ nel2 = le32_to_cpu(buf[0]);
+ for (j = 0; j < nel2; j++) {
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ len = le32_to_cpu(buf[0]);
+
+ newc = kzalloc(sizeof(*newc), GFP_KERNEL);
+ if (!newc) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+
+ newc->u.name = kmalloc(len + 1, GFP_KERNEL);
+ if (!newc->u.name) {
+ rc = -ENOMEM;
+ goto bad_newc;
+ }
+ rc = next_entry(newc->u.name, fp, len);
+ if (rc < 0)
+ goto bad_newc;
+ newc->u.name[len] = 0;
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad_newc;
+ newc->v.sclass = le32_to_cpu(buf[0]);
+ if (context_read_and_validate(&newc->context[0], p, fp))
+ goto bad_newc;
+ for (l = NULL, c = newgenfs->head; c;
+ l = c, c = c->next) {
+ if (!strcmp(newc->u.name, c->u.name) &&
+ (!c->v.sclass || !newc->v.sclass ||
+ newc->v.sclass == c->v.sclass)) {
+ printk(KERN_ERR "SELinux: dup genfs "
+ "entry (%s,%s)\n",
+ newgenfs->fstype, c->u.name);
+ goto bad_newc;
+ }
+ len = strlen(newc->u.name);
+ len2 = strlen(c->u.name);
+ if (len > len2)
+ break;
+ }
+
+ newc->next = c;
+ if (l)
+ l->next = newc;
+ else
+ newgenfs->head = newc;
+ }
+ }
+
+ if (p->policyvers >= POLICYDB_VERSION_MLS) {
+ int new_rangetr = p->policyvers >= POLICYDB_VERSION_RANGETRANS;
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ nel = le32_to_cpu(buf[0]);
+ lrt = NULL;
+ for (i = 0; i < nel; i++) {
+ rt = kzalloc(sizeof(*rt), GFP_KERNEL);
+ if (!rt) {
+ rc = -ENOMEM;
+ goto bad;
+ }
+ if (lrt)
+ lrt->next = rt;
+ else
+ p->range_tr = rt;
+ rc = next_entry(buf, fp, (sizeof(u32) * 2));
+ if (rc < 0)
+ goto bad;
+ rt->source_type = le32_to_cpu(buf[0]);
+ rt->target_type = le32_to_cpu(buf[1]);
+ if (new_rangetr) {
+ rc = next_entry(buf, fp, sizeof(u32));
+ if (rc < 0)
+ goto bad;
+ rt->target_class = le32_to_cpu(buf[0]);
+ } else
+ rt->target_class = SECCLASS_PROCESS;
+ if (!policydb_type_isvalid(p, rt->source_type) ||
+ !policydb_type_isvalid(p, rt->target_type) ||
+ !policydb_class_isvalid(p, rt->target_class)) {
+ rc = -EINVAL;
+ goto bad;
+ }
+ rc = mls_read_range_helper(&rt->target_range, fp);
+ if (rc)
+ goto bad;
+ if (!mls_range_isvalid(p, &rt->target_range)) {
+ printk(KERN_WARNING "SELinux: rangetrans: invalid range\n");
+ goto bad;
+ }
+ lrt = rt;
+ }
+ }
+
+ p->type_attr_map = kmalloc(p->p_types.nprim*sizeof(struct ebitmap), GFP_KERNEL);
+ if (!p->type_attr_map)
+ goto bad;
+
+ for (i = 0; i < p->p_types.nprim; i++) {
+ ebitmap_init(&p->type_attr_map[i]);
+ if (p->policyvers >= POLICYDB_VERSION_AVTAB) {
+ if (ebitmap_read(&p->type_attr_map[i], fp))
+ goto bad;
+ }
+ /* add the type itself as the degenerate case */
+ if (ebitmap_set_bit(&p->type_attr_map[i], i, 1))
+ goto bad;
+ }
+
+ rc = policydb_bounds_sanity_check(p);
+ if (rc)
+ goto bad;
+
+ rc = 0;
+out:
+ return rc;
+bad_newc:
+ ocontext_destroy(newc, OCON_FSUSE);
+bad:
+ if (!rc)
+ rc = -EINVAL;
+ policydb_destroy(p);
+ goto out;
+}
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
new file mode 100644
index 0000000..55152d4
--- /dev/null
+++ b/security/selinux/ss/policydb.h
@@ -0,0 +1,299 @@
+/*
+ * A policy database (policydb) specifies the
+ * configuration data for the security policy.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+
+/*
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ *
+ * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
+ *
+ * Added conditional policy language extensions
+ *
+ * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
+ * Copyright (C) 2003 - 2004 Tresys Technology, LLC
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+
+#ifndef _SS_POLICYDB_H_
+#define _SS_POLICYDB_H_
+
+#include "symtab.h"
+#include "avtab.h"
+#include "sidtab.h"
+#include "context.h"
+#include "constraint.h"
+
+/*
+ * A datum type is defined for each kind of symbol
+ * in the configuration data: individual permissions,
+ * common prefixes for access vectors, classes,
+ * users, roles, types, sensitivities, categories, etc.
+ */
+
+/* Permission attributes */
+struct perm_datum {
+ u32 value; /* permission bit + 1 */
+};
+
+/* Attributes of a common prefix for access vectors */
+struct common_datum {
+ u32 value; /* internal common value */
+ struct symtab permissions; /* common permissions */
+};
+
+/* Class attributes */
+struct class_datum {
+ u32 value; /* class value */
+ char *comkey; /* common name */
+ struct common_datum *comdatum; /* common datum */
+ struct symtab permissions; /* class-specific permission symbol table */
+ struct constraint_node *constraints; /* constraints on class permissions */
+ struct constraint_node *validatetrans; /* special transition rules */
+};
+
+/* Role attributes */
+struct role_datum {
+ u32 value; /* internal role value */
+ u32 bounds; /* boundary of role */
+ struct ebitmap dominates; /* set of roles dominated by this role */
+ struct ebitmap types; /* set of authorized types for role */
+};
+
+struct role_trans {
+ u32 role; /* current role */
+ u32 type; /* program executable type */
+ u32 new_role; /* new role */
+ struct role_trans *next;
+};
+
+struct role_allow {
+ u32 role; /* current role */
+ u32 new_role; /* new role */
+ struct role_allow *next;
+};
+
+/* Type attributes */
+struct type_datum {
+ u32 value; /* internal type value */
+ u32 bounds; /* boundary of type */
+ unsigned char primary; /* primary name? */
+ unsigned char attribute;/* attribute ?*/
+};
+
+/* User attributes */
+struct user_datum {
+ u32 value; /* internal user value */
+ u32 bounds; /* bounds of user */
+ struct ebitmap roles; /* set of authorized roles for user */
+ struct mls_range range; /* MLS range (min - max) for user */
+ struct mls_level dfltlevel; /* default login MLS level for user */
+};
+
+
+/* Sensitivity attributes */
+struct level_datum {
+ struct mls_level *level; /* sensitivity and associated categories */
+ unsigned char isalias; /* is this sensitivity an alias for another? */
+};
+
+/* Category attributes */
+struct cat_datum {
+ u32 value; /* internal category bit + 1 */
+ unsigned char isalias; /* is this category an alias for another? */
+};
+
+struct range_trans {
+ u32 source_type;
+ u32 target_type;
+ u32 target_class;
+ struct mls_range target_range;
+ struct range_trans *next;
+};
+
+/* Boolean data type */
+struct cond_bool_datum {
+ __u32 value; /* internal type value */
+ int state;
+};
+
+struct cond_node;
+
+/*
+ * The configuration data includes security contexts for
+ * initial SIDs, unlabeled file systems, TCP and UDP port numbers,
+ * network interfaces, and nodes. This structure stores the
+ * relevant data for one such entry. Entries of the same kind
+ * (e.g. all initial SIDs) are linked together into a list.
+ */
+struct ocontext {
+ union {
+ char *name; /* name of initial SID, fs, netif, fstype, path */
+ struct {
+ u8 protocol;
+ u16 low_port;
+ u16 high_port;
+ } port; /* TCP or UDP port information */
+ struct {
+ u32 addr;
+ u32 mask;
+ } node; /* node information */
+ struct {
+ u32 addr[4];
+ u32 mask[4];
+ } node6; /* IPv6 node information */
+ } u;
+ union {
+ u32 sclass; /* security class for genfs */
+ u32 behavior; /* labeling behavior for fs_use */
+ } v;
+ struct context context[2]; /* security context(s) */
+ u32 sid[2]; /* SID(s) */
+ struct ocontext *next;
+};
+
+struct genfs {
+ char *fstype;
+ struct ocontext *head;
+ struct genfs *next;
+};
+
+/* symbol table array indices */
+#define SYM_COMMONS 0
+#define SYM_CLASSES 1
+#define SYM_ROLES 2
+#define SYM_TYPES 3
+#define SYM_USERS 4
+#define SYM_BOOLS 5
+#define SYM_LEVELS 6
+#define SYM_CATS 7
+#define SYM_NUM 8
+
+/* object context array indices */
+#define OCON_ISID 0 /* initial SIDs */
+#define OCON_FS 1 /* unlabeled file systems */
+#define OCON_PORT 2 /* TCP and UDP port numbers */
+#define OCON_NETIF 3 /* network interfaces */
+#define OCON_NODE 4 /* nodes */
+#define OCON_FSUSE 5 /* fs_use */
+#define OCON_NODE6 6 /* IPv6 nodes */
+#define OCON_NUM 7
+
+/* The policy database */
+struct policydb {
+ /* symbol tables */
+ struct symtab symtab[SYM_NUM];
+#define p_commons symtab[SYM_COMMONS]
+#define p_classes symtab[SYM_CLASSES]
+#define p_roles symtab[SYM_ROLES]
+#define p_types symtab[SYM_TYPES]
+#define p_users symtab[SYM_USERS]
+#define p_bools symtab[SYM_BOOLS]
+#define p_levels symtab[SYM_LEVELS]
+#define p_cats symtab[SYM_CATS]
+
+ /* symbol names indexed by (value - 1) */
+ char **sym_val_to_name[SYM_NUM];
+#define p_common_val_to_name sym_val_to_name[SYM_COMMONS]
+#define p_class_val_to_name sym_val_to_name[SYM_CLASSES]
+#define p_role_val_to_name sym_val_to_name[SYM_ROLES]
+#define p_type_val_to_name sym_val_to_name[SYM_TYPES]
+#define p_user_val_to_name sym_val_to_name[SYM_USERS]
+#define p_bool_val_to_name sym_val_to_name[SYM_BOOLS]
+#define p_sens_val_to_name sym_val_to_name[SYM_LEVELS]
+#define p_cat_val_to_name sym_val_to_name[SYM_CATS]
+
+ /* class, role, and user attributes indexed by (value - 1) */
+ struct class_datum **class_val_to_struct;
+ struct role_datum **role_val_to_struct;
+ struct user_datum **user_val_to_struct;
+ struct type_datum **type_val_to_struct;
+
+ /* type enforcement access vectors and transitions */
+ struct avtab te_avtab;
+
+ /* role transitions */
+ struct role_trans *role_tr;
+
+ /* bools indexed by (value - 1) */
+ struct cond_bool_datum **bool_val_to_struct;
+ /* type enforcement conditional access vectors and transitions */
+ struct avtab te_cond_avtab;
+ /* linked list indexing te_cond_avtab by conditional */
+ struct cond_node *cond_list;
+
+ /* role allows */
+ struct role_allow *role_allow;
+
+ /* security contexts of initial SIDs, unlabeled file systems,
+ TCP or UDP port numbers, network interfaces and nodes */
+ struct ocontext *ocontexts[OCON_NUM];
+
+ /* security contexts for files in filesystems that cannot support
+ a persistent label mapping or use another
+ fixed labeling behavior. */
+ struct genfs *genfs;
+
+ /* range transitions */
+ struct range_trans *range_tr;
+
+ /* type -> attribute reverse mapping */
+ struct ebitmap *type_attr_map;
+
+ struct ebitmap policycaps;
+
+ struct ebitmap permissive_map;
+
+ unsigned int policyvers;
+
+ unsigned int reject_unknown : 1;
+ unsigned int allow_unknown : 1;
+ u32 *undefined_perms;
+};
+
+extern void policydb_destroy(struct policydb *p);
+extern int policydb_load_isids(struct policydb *p, struct sidtab *s);
+extern int policydb_context_isvalid(struct policydb *p, struct context *c);
+extern int policydb_class_isvalid(struct policydb *p, unsigned int class);
+extern int policydb_type_isvalid(struct policydb *p, unsigned int type);
+extern int policydb_role_isvalid(struct policydb *p, unsigned int role);
+extern int policydb_read(struct policydb *p, void *fp);
+
+#define PERM_SYMTAB_SIZE 32
+
+#define POLICYDB_CONFIG_MLS 1
+
+/* the config flags related to unknown classes/perms are bits 2 and 3 */
+#define REJECT_UNKNOWN 0x00000002
+#define ALLOW_UNKNOWN 0x00000004
+
+#define OBJECT_R "object_r"
+#define OBJECT_R_VAL 1
+
+#define POLICYDB_MAGIC SELINUX_MAGIC
+#define POLICYDB_STRING "SE Linux"
+
+struct policy_file {
+ char *data;
+ size_t len;
+};
+
+static inline int next_entry(void *buf, struct policy_file *fp, size_t bytes)
+{
+ if (bytes > fp->len)
+ return -EINVAL;
+
+ memcpy(buf, fp->data, bytes);
+ fp->data += bytes;
+ fp->len -= bytes;
+ return 0;
+}
+
+#endif /* _SS_POLICYDB_H_ */
+
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
new file mode 100644
index 0000000..343c8ab
--- /dev/null
+++ b/security/selinux/ss/services.c
@@ -0,0 +1,2990 @@
+/*
+ * Implementation of the security services.
+ *
+ * Authors : Stephen Smalley, <sds@epoch.ncsc.mil>
+ * James Morris <jmorris@redhat.com>
+ *
+ * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
+ *
+ * Support for enhanced MLS infrastructure.
+ * Support for context based audit filters.
+ *
+ * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com>
+ *
+ * Added conditional policy language extensions
+ *
+ * Updated: Hewlett-Packard <paul.moore@hp.com>
+ *
+ * Added support for NetLabel
+ * Added support for the policy capability bitmap
+ *
+ * Updated: Chad Sellers <csellers@tresys.com>
+ *
+ * Added validation of kernel classes and permissions
+ *
+ * Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
+ * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC
+ * Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@redhat.com>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/sched.h>
+#include <linux/audit.h>
+#include <linux/mutex.h>
+#include <linux/selinux.h>
+#include <net/netlabel.h>
+
+#include "flask.h"
+#include "avc.h"
+#include "avc_ss.h"
+#include "security.h"
+#include "context.h"
+#include "policydb.h"
+#include "sidtab.h"
+#include "services.h"
+#include "conditional.h"
+#include "mls.h"
+#include "objsec.h"
+#include "netlabel.h"
+#include "xfrm.h"
+#include "ebitmap.h"
+#include "audit.h"
+
+extern void selnl_notify_policyload(u32 seqno);
+unsigned int policydb_loaded_version;
+
+int selinux_policycap_netpeer;
+int selinux_policycap_openperm;
+
+/*
+ * This is declared in avc.c
+ */
+extern const struct selinux_class_perm selinux_class_perm;
+
+static DEFINE_RWLOCK(policy_rwlock);
+
+static struct sidtab sidtab;
+struct policydb policydb;
+int ss_initialized;
+
+/*
+ * The largest sequence number that has been used when
+ * providing an access decision to the access vector cache.
+ * The sequence number only changes when a policy change
+ * occurs.
+ */
+static u32 latest_granting;
+
+/* Forward declaration. */
+static int context_struct_to_string(struct context *context, char **scontext,
+ u32 *scontext_len);
+
+static int context_struct_compute_av(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ u32 requested,
+ struct av_decision *avd);
+/*
+ * Return the boolean value of a constraint expression
+ * when it is applied to the specified source and target
+ * security contexts.
+ *
+ * xcontext is a special beast... It is used by the validatetrans rules
+ * only. For these rules, scontext is the context before the transition,
+ * tcontext is the context after the transition, and xcontext is the context
+ * of the process performing the transition. All other callers of
+ * constraint_expr_eval should pass in NULL for xcontext.
+ */
+static int constraint_expr_eval(struct context *scontext,
+ struct context *tcontext,
+ struct context *xcontext,
+ struct constraint_expr *cexpr)
+{
+ u32 val1, val2;
+ struct context *c;
+ struct role_datum *r1, *r2;
+ struct mls_level *l1, *l2;
+ struct constraint_expr *e;
+ int s[CEXPR_MAXDEPTH];
+ int sp = -1;
+
+ for (e = cexpr; e; e = e->next) {
+ switch (e->expr_type) {
+ case CEXPR_NOT:
+ BUG_ON(sp < 0);
+ s[sp] = !s[sp];
+ break;
+ case CEXPR_AND:
+ BUG_ON(sp < 1);
+ sp--;
+ s[sp] &= s[sp+1];
+ break;
+ case CEXPR_OR:
+ BUG_ON(sp < 1);
+ sp--;
+ s[sp] |= s[sp+1];
+ break;
+ case CEXPR_ATTR:
+ if (sp == (CEXPR_MAXDEPTH-1))
+ return 0;
+ switch (e->attr) {
+ case CEXPR_USER:
+ val1 = scontext->user;
+ val2 = tcontext->user;
+ break;
+ case CEXPR_TYPE:
+ val1 = scontext->type;
+ val2 = tcontext->type;
+ break;
+ case CEXPR_ROLE:
+ val1 = scontext->role;
+ val2 = tcontext->role;
+ r1 = policydb.role_val_to_struct[val1 - 1];
+ r2 = policydb.role_val_to_struct[val2 - 1];
+ switch (e->op) {
+ case CEXPR_DOM:
+ s[++sp] = ebitmap_get_bit(&r1->dominates,
+ val2 - 1);
+ continue;
+ case CEXPR_DOMBY:
+ s[++sp] = ebitmap_get_bit(&r2->dominates,
+ val1 - 1);
+ continue;
+ case CEXPR_INCOMP:
+ s[++sp] = (!ebitmap_get_bit(&r1->dominates,
+ val2 - 1) &&
+ !ebitmap_get_bit(&r2->dominates,
+ val1 - 1));
+ continue;
+ default:
+ break;
+ }
+ break;
+ case CEXPR_L1L2:
+ l1 = &(scontext->range.level[0]);
+ l2 = &(tcontext->range.level[0]);
+ goto mls_ops;
+ case CEXPR_L1H2:
+ l1 = &(scontext->range.level[0]);
+ l2 = &(tcontext->range.level[1]);
+ goto mls_ops;
+ case CEXPR_H1L2:
+ l1 = &(scontext->range.level[1]);
+ l2 = &(tcontext->range.level[0]);
+ goto mls_ops;
+ case CEXPR_H1H2:
+ l1 = &(scontext->range.level[1]);
+ l2 = &(tcontext->range.level[1]);
+ goto mls_ops;
+ case CEXPR_L1H1:
+ l1 = &(scontext->range.level[0]);
+ l2 = &(scontext->range.level[1]);
+ goto mls_ops;
+ case CEXPR_L2H2:
+ l1 = &(tcontext->range.level[0]);
+ l2 = &(tcontext->range.level[1]);
+ goto mls_ops;
+mls_ops:
+ switch (e->op) {
+ case CEXPR_EQ:
+ s[++sp] = mls_level_eq(l1, l2);
+ continue;
+ case CEXPR_NEQ:
+ s[++sp] = !mls_level_eq(l1, l2);
+ continue;
+ case CEXPR_DOM:
+ s[++sp] = mls_level_dom(l1, l2);
+ continue;
+ case CEXPR_DOMBY:
+ s[++sp] = mls_level_dom(l2, l1);
+ continue;
+ case CEXPR_INCOMP:
+ s[++sp] = mls_level_incomp(l2, l1);
+ continue;
+ default:
+ BUG();
+ return 0;
+ }
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+
+ switch (e->op) {
+ case CEXPR_EQ:
+ s[++sp] = (val1 == val2);
+ break;
+ case CEXPR_NEQ:
+ s[++sp] = (val1 != val2);
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+ break;
+ case CEXPR_NAMES:
+ if (sp == (CEXPR_MAXDEPTH-1))
+ return 0;
+ c = scontext;
+ if (e->attr & CEXPR_TARGET)
+ c = tcontext;
+ else if (e->attr & CEXPR_XTARGET) {
+ c = xcontext;
+ if (!c) {
+ BUG();
+ return 0;
+ }
+ }
+ if (e->attr & CEXPR_USER)
+ val1 = c->user;
+ else if (e->attr & CEXPR_ROLE)
+ val1 = c->role;
+ else if (e->attr & CEXPR_TYPE)
+ val1 = c->type;
+ else {
+ BUG();
+ return 0;
+ }
+
+ switch (e->op) {
+ case CEXPR_EQ:
+ s[++sp] = ebitmap_get_bit(&e->names, val1 - 1);
+ break;
+ case CEXPR_NEQ:
+ s[++sp] = !ebitmap_get_bit(&e->names, val1 - 1);
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+ break;
+ default:
+ BUG();
+ return 0;
+ }
+ }
+
+ BUG_ON(sp != 0);
+ return s[0];
+}
+
+/*
+ * security_boundary_permission - drops violated permissions
+ * on boundary constraint.
+ */
+static void type_attribute_bounds_av(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ u32 requested,
+ struct av_decision *avd)
+{
+ struct context lo_scontext;
+ struct context lo_tcontext;
+ struct av_decision lo_avd;
+ struct type_datum *source
+ = policydb.type_val_to_struct[scontext->type - 1];
+ struct type_datum *target
+ = policydb.type_val_to_struct[tcontext->type - 1];
+ u32 masked = 0;
+
+ if (source->bounds) {
+ memset(&lo_avd, 0, sizeof(lo_avd));
+
+ memcpy(&lo_scontext, scontext, sizeof(lo_scontext));
+ lo_scontext.type = source->bounds;
+
+ context_struct_compute_av(&lo_scontext,
+ tcontext,
+ tclass,
+ requested,
+ &lo_avd);
+ if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+ return; /* no masked permission */
+ masked = ~lo_avd.allowed & avd->allowed;
+ }
+
+ if (target->bounds) {
+ memset(&lo_avd, 0, sizeof(lo_avd));
+
+ memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext));
+ lo_tcontext.type = target->bounds;
+
+ context_struct_compute_av(scontext,
+ &lo_tcontext,
+ tclass,
+ requested,
+ &lo_avd);
+ if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+ return; /* no masked permission */
+ masked = ~lo_avd.allowed & avd->allowed;
+ }
+
+ if (source->bounds && target->bounds) {
+ memset(&lo_avd, 0, sizeof(lo_avd));
+ /*
+ * lo_scontext and lo_tcontext are already
+ * set up.
+ */
+
+ context_struct_compute_av(&lo_scontext,
+ &lo_tcontext,
+ tclass,
+ requested,
+ &lo_avd);
+ if ((lo_avd.allowed & avd->allowed) == avd->allowed)
+ return; /* no masked permission */
+ masked = ~lo_avd.allowed & avd->allowed;
+ }
+
+ if (masked) {
+ struct audit_buffer *ab;
+ char *stype_name
+ = policydb.p_type_val_to_name[source->value - 1];
+ char *ttype_name
+ = policydb.p_type_val_to_name[target->value - 1];
+ char *tclass_name
+ = policydb.p_class_val_to_name[tclass - 1];
+
+ /* mask violated permissions */
+ avd->allowed &= ~masked;
+
+ /* notice to userspace via audit message */
+ ab = audit_log_start(current->audit_context,
+ GFP_ATOMIC, AUDIT_SELINUX_ERR);
+ if (!ab)
+ return;
+
+ audit_log_format(ab, "av boundary violation: "
+ "source=%s target=%s tclass=%s",
+ stype_name, ttype_name, tclass_name);
+ avc_dump_av(ab, tclass, masked);
+ audit_log_end(ab);
+ }
+}
+
+/*
+ * Compute access vectors based on a context structure pair for
+ * the permissions in a particular class.
+ */
+static int context_struct_compute_av(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ u32 requested,
+ struct av_decision *avd)
+{
+ struct constraint_node *constraint;
+ struct role_allow *ra;
+ struct avtab_key avkey;
+ struct avtab_node *node;
+ struct class_datum *tclass_datum;
+ struct ebitmap *sattr, *tattr;
+ struct ebitmap_node *snode, *tnode;
+ const struct selinux_class_perm *kdefs = &selinux_class_perm;
+ unsigned int i, j;
+
+ /*
+ * Remap extended Netlink classes for old policy versions.
+ * Do this here rather than socket_type_to_security_class()
+ * in case a newer policy version is loaded, allowing sockets
+ * to remain in the correct class.
+ */
+ if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS)
+ if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET &&
+ tclass <= SECCLASS_NETLINK_DNRT_SOCKET)
+ tclass = SECCLASS_NETLINK_SOCKET;
+
+ /*
+ * Initialize the access vectors to the default values.
+ */
+ avd->allowed = 0;
+ avd->decided = 0xffffffff;
+ avd->auditallow = 0;
+ avd->auditdeny = 0xffffffff;
+ avd->seqno = latest_granting;
+
+ /*
+ * Check for all the invalid cases.
+ * - tclass 0
+ * - tclass > policy and > kernel
+ * - tclass > policy but is a userspace class
+ * - tclass > policy but we do not allow unknowns
+ */
+ if (unlikely(!tclass))
+ goto inval_class;
+ if (unlikely(tclass > policydb.p_classes.nprim))
+ if (tclass > kdefs->cts_len ||
+ !kdefs->class_to_string[tclass] ||
+ !policydb.allow_unknown)
+ goto inval_class;
+
+ /*
+ * Kernel class and we allow unknown so pad the allow decision
+ * the pad will be all 1 for unknown classes.
+ */
+ if (tclass <= kdefs->cts_len && policydb.allow_unknown)
+ avd->allowed = policydb.undefined_perms[tclass - 1];
+
+ /*
+ * Not in policy. Since decision is completed (all 1 or all 0) return.
+ */
+ if (unlikely(tclass > policydb.p_classes.nprim))
+ return 0;
+
+ tclass_datum = policydb.class_val_to_struct[tclass - 1];
+
+ /*
+ * If a specific type enforcement rule was defined for
+ * this permission check, then use it.
+ */
+ avkey.target_class = tclass;
+ avkey.specified = AVTAB_AV;
+ sattr = &policydb.type_attr_map[scontext->type - 1];
+ tattr = &policydb.type_attr_map[tcontext->type - 1];
+ ebitmap_for_each_positive_bit(sattr, snode, i) {
+ ebitmap_for_each_positive_bit(tattr, tnode, j) {
+ avkey.source_type = i + 1;
+ avkey.target_type = j + 1;
+ for (node = avtab_search_node(&policydb.te_avtab, &avkey);
+ node;
+ node = avtab_search_node_next(node, avkey.specified)) {
+ if (node->key.specified == AVTAB_ALLOWED)
+ avd->allowed |= node->datum.data;
+ else if (node->key.specified == AVTAB_AUDITALLOW)
+ avd->auditallow |= node->datum.data;
+ else if (node->key.specified == AVTAB_AUDITDENY)
+ avd->auditdeny &= node->datum.data;
+ }
+
+ /* Check conditional av table for additional permissions */
+ cond_compute_av(&policydb.te_cond_avtab, &avkey, avd);
+
+ }
+ }
+
+ /*
+ * Remove any permissions prohibited by a constraint (this includes
+ * the MLS policy).
+ */
+ constraint = tclass_datum->constraints;
+ while (constraint) {
+ if ((constraint->permissions & (avd->allowed)) &&
+ !constraint_expr_eval(scontext, tcontext, NULL,
+ constraint->expr)) {
+ avd->allowed = (avd->allowed) & ~(constraint->permissions);
+ }
+ constraint = constraint->next;
+ }
+
+ /*
+ * If checking process transition permission and the
+ * role is changing, then check the (current_role, new_role)
+ * pair.
+ */
+ if (tclass == SECCLASS_PROCESS &&
+ (avd->allowed & (PROCESS__TRANSITION | PROCESS__DYNTRANSITION)) &&
+ scontext->role != tcontext->role) {
+ for (ra = policydb.role_allow; ra; ra = ra->next) {
+ if (scontext->role == ra->role &&
+ tcontext->role == ra->new_role)
+ break;
+ }
+ if (!ra)
+ avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION |
+ PROCESS__DYNTRANSITION);
+ }
+
+ /*
+ * If the given source and target types have boundary
+ * constraint, lazy checks have to mask any violated
+ * permission and notice it to userspace via audit.
+ */
+ type_attribute_bounds_av(scontext, tcontext,
+ tclass, requested, avd);
+
+ return 0;
+
+inval_class:
+ if (!tclass || tclass > kdefs->cts_len ||
+ !kdefs->class_to_string[tclass]) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "SELinux: %s: unrecognized class %d\n",
+ __func__, tclass);
+ return -EINVAL;
+ }
+
+ /*
+ * Known to the kernel, but not to the policy.
+ * Handle as a denial (allowed is 0).
+ */
+ return 0;
+}
+
+/*
+ * Given a sid find if the type has the permissive flag set
+ */
+int security_permissive_sid(u32 sid)
+{
+ struct context *context;
+ u32 type;
+ int rc;
+
+ read_lock(&policy_rwlock);
+
+ context = sidtab_search(&sidtab, sid);
+ BUG_ON(!context);
+
+ type = context->type;
+ /*
+ * we are intentionally using type here, not type-1, the 0th bit may
+ * someday indicate that we are globally setting permissive in policy.
+ */
+ rc = ebitmap_get_bit(&policydb.permissive_map, type);
+
+ read_unlock(&policy_rwlock);
+ return rc;
+}
+
+static int security_validtrans_handle_fail(struct context *ocontext,
+ struct context *ncontext,
+ struct context *tcontext,
+ u16 tclass)
+{
+ char *o = NULL, *n = NULL, *t = NULL;
+ u32 olen, nlen, tlen;
+
+ if (context_struct_to_string(ocontext, &o, &olen) < 0)
+ goto out;
+ if (context_struct_to_string(ncontext, &n, &nlen) < 0)
+ goto out;
+ if (context_struct_to_string(tcontext, &t, &tlen) < 0)
+ goto out;
+ audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+ "security_validate_transition: denied for"
+ " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s",
+ o, n, t, policydb.p_class_val_to_name[tclass-1]);
+out:
+ kfree(o);
+ kfree(n);
+ kfree(t);
+
+ if (!selinux_enforcing)
+ return 0;
+ return -EPERM;
+}
+
+int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid,
+ u16 tclass)
+{
+ struct context *ocontext;
+ struct context *ncontext;
+ struct context *tcontext;
+ struct class_datum *tclass_datum;
+ struct constraint_node *constraint;
+ int rc = 0;
+
+ if (!ss_initialized)
+ return 0;
+
+ read_lock(&policy_rwlock);
+
+ /*
+ * Remap extended Netlink classes for old policy versions.
+ * Do this here rather than socket_type_to_security_class()
+ * in case a newer policy version is loaded, allowing sockets
+ * to remain in the correct class.
+ */
+ if (policydb_loaded_version < POLICYDB_VERSION_NLCLASS)
+ if (tclass >= SECCLASS_NETLINK_ROUTE_SOCKET &&
+ tclass <= SECCLASS_NETLINK_DNRT_SOCKET)
+ tclass = SECCLASS_NETLINK_SOCKET;
+
+ if (!tclass || tclass > policydb.p_classes.nprim) {
+ printk(KERN_ERR "SELinux: %s: unrecognized class %d\n",
+ __func__, tclass);
+ rc = -EINVAL;
+ goto out;
+ }
+ tclass_datum = policydb.class_val_to_struct[tclass - 1];
+
+ ocontext = sidtab_search(&sidtab, oldsid);
+ if (!ocontext) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, oldsid);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ ncontext = sidtab_search(&sidtab, newsid);
+ if (!ncontext) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, newsid);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ tcontext = sidtab_search(&sidtab, tasksid);
+ if (!tcontext) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, tasksid);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ constraint = tclass_datum->validatetrans;
+ while (constraint) {
+ if (!constraint_expr_eval(ocontext, ncontext, tcontext,
+ constraint->expr)) {
+ rc = security_validtrans_handle_fail(ocontext, ncontext,
+ tcontext, tclass);
+ goto out;
+ }
+ constraint = constraint->next;
+ }
+
+out:
+ read_unlock(&policy_rwlock);
+ return rc;
+}
+
+/*
+ * security_bounded_transition - check whether the given
+ * transition is directed to bounded, or not.
+ * It returns 0, if @newsid is bounded by @oldsid.
+ * Otherwise, it returns error code.
+ *
+ * @oldsid : current security identifier
+ * @newsid : destinated security identifier
+ */
+int security_bounded_transition(u32 old_sid, u32 new_sid)
+{
+ struct context *old_context, *new_context;
+ struct type_datum *type;
+ int index;
+ int rc = -EINVAL;
+
+ read_lock(&policy_rwlock);
+
+ old_context = sidtab_search(&sidtab, old_sid);
+ if (!old_context) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
+ __func__, old_sid);
+ goto out;
+ }
+
+ new_context = sidtab_search(&sidtab, new_sid);
+ if (!new_context) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %u\n",
+ __func__, new_sid);
+ goto out;
+ }
+
+ /* type/domain unchaned */
+ if (old_context->type == new_context->type) {
+ rc = 0;
+ goto out;
+ }
+
+ index = new_context->type;
+ while (true) {
+ type = policydb.type_val_to_struct[index - 1];
+ BUG_ON(!type);
+
+ /* not bounded anymore */
+ if (!type->bounds) {
+ rc = -EPERM;
+ break;
+ }
+
+ /* @newsid is bounded by @oldsid */
+ if (type->bounds == old_context->type) {
+ rc = 0;
+ break;
+ }
+ index = type->bounds;
+ }
+out:
+ read_unlock(&policy_rwlock);
+
+ return rc;
+}
+
+
+/**
+ * security_compute_av - Compute access vector decisions.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @requested: requested permissions
+ * @avd: access vector decisions
+ *
+ * Compute a set of access vector decisions based on the
+ * SID pair (@ssid, @tsid) for the permissions in @tclass.
+ * Return -%EINVAL if any of the parameters are invalid or %0
+ * if the access vector decisions were computed successfully.
+ */
+int security_compute_av(u32 ssid,
+ u32 tsid,
+ u16 tclass,
+ u32 requested,
+ struct av_decision *avd)
+{
+ struct context *scontext = NULL, *tcontext = NULL;
+ int rc = 0;
+
+ if (!ss_initialized) {
+ avd->allowed = 0xffffffff;
+ avd->decided = 0xffffffff;
+ avd->auditallow = 0;
+ avd->auditdeny = 0xffffffff;
+ avd->seqno = latest_granting;
+ return 0;
+ }
+
+ read_lock(&policy_rwlock);
+
+ scontext = sidtab_search(&sidtab, ssid);
+ if (!scontext) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, ssid);
+ rc = -EINVAL;
+ goto out;
+ }
+ tcontext = sidtab_search(&sidtab, tsid);
+ if (!tcontext) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, tsid);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = context_struct_compute_av(scontext, tcontext, tclass,
+ requested, avd);
+out:
+ read_unlock(&policy_rwlock);
+ return rc;
+}
+
+/*
+ * Write the security context string representation of
+ * the context structure `context' into a dynamically
+ * allocated string of the correct size. Set `*scontext'
+ * to point to this string and set `*scontext_len' to
+ * the length of the string.
+ */
+static int context_struct_to_string(struct context *context, char **scontext, u32 *scontext_len)
+{
+ char *scontextp;
+
+ *scontext = NULL;
+ *scontext_len = 0;
+
+ if (context->len) {
+ *scontext_len = context->len;
+ *scontext = kstrdup(context->str, GFP_ATOMIC);
+ if (!(*scontext))
+ return -ENOMEM;
+ return 0;
+ }
+
+ /* Compute the size of the context. */
+ *scontext_len += strlen(policydb.p_user_val_to_name[context->user - 1]) + 1;
+ *scontext_len += strlen(policydb.p_role_val_to_name[context->role - 1]) + 1;
+ *scontext_len += strlen(policydb.p_type_val_to_name[context->type - 1]) + 1;
+ *scontext_len += mls_compute_context_len(context);
+
+ /* Allocate space for the context; caller must free this space. */
+ scontextp = kmalloc(*scontext_len, GFP_ATOMIC);
+ if (!scontextp)
+ return -ENOMEM;
+ *scontext = scontextp;
+
+ /*
+ * Copy the user name, role name and type name into the context.
+ */
+ sprintf(scontextp, "%s:%s:%s",
+ policydb.p_user_val_to_name[context->user - 1],
+ policydb.p_role_val_to_name[context->role - 1],
+ policydb.p_type_val_to_name[context->type - 1]);
+ scontextp += strlen(policydb.p_user_val_to_name[context->user - 1]) +
+ 1 + strlen(policydb.p_role_val_to_name[context->role - 1]) +
+ 1 + strlen(policydb.p_type_val_to_name[context->type - 1]);
+
+ mls_sid_to_context(context, &scontextp);
+
+ *scontextp = 0;
+
+ return 0;
+}
+
+#include "initial_sid_to_string.h"
+
+const char *security_get_initial_sid_context(u32 sid)
+{
+ if (unlikely(sid > SECINITSID_NUM))
+ return NULL;
+ return initial_sid_to_string[sid];
+}
+
+static int security_sid_to_context_core(u32 sid, char **scontext,
+ u32 *scontext_len, int force)
+{
+ struct context *context;
+ int rc = 0;
+
+ *scontext = NULL;
+ *scontext_len = 0;
+
+ if (!ss_initialized) {
+ if (sid <= SECINITSID_NUM) {
+ char *scontextp;
+
+ *scontext_len = strlen(initial_sid_to_string[sid]) + 1;
+ scontextp = kmalloc(*scontext_len, GFP_ATOMIC);
+ if (!scontextp) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ strcpy(scontextp, initial_sid_to_string[sid]);
+ *scontext = scontextp;
+ goto out;
+ }
+ printk(KERN_ERR "SELinux: %s: called before initial "
+ "load_policy on unknown SID %d\n", __func__, sid);
+ rc = -EINVAL;
+ goto out;
+ }
+ read_lock(&policy_rwlock);
+ if (force)
+ context = sidtab_search_force(&sidtab, sid);
+ else
+ context = sidtab_search(&sidtab, sid);
+ if (!context) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, sid);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+ rc = context_struct_to_string(context, scontext, scontext_len);
+out_unlock:
+ read_unlock(&policy_rwlock);
+out:
+ return rc;
+
+}
+
+/**
+ * security_sid_to_context - Obtain a context for a given SID.
+ * @sid: security identifier, SID
+ * @scontext: security context
+ * @scontext_len: length in bytes
+ *
+ * Write the string representation of the context associated with @sid
+ * into a dynamically allocated string of the correct size. Set @scontext
+ * to point to this string and set @scontext_len to the length of the string.
+ */
+int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
+{
+ return security_sid_to_context_core(sid, scontext, scontext_len, 0);
+}
+
+int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len)
+{
+ return security_sid_to_context_core(sid, scontext, scontext_len, 1);
+}
+
+/*
+ * Caveat: Mutates scontext.
+ */
+static int string_to_context_struct(struct policydb *pol,
+ struct sidtab *sidtabp,
+ char *scontext,
+ u32 scontext_len,
+ struct context *ctx,
+ u32 def_sid)
+{
+ struct role_datum *role;
+ struct type_datum *typdatum;
+ struct user_datum *usrdatum;
+ char *scontextp, *p, oldc;
+ int rc = 0;
+
+ context_init(ctx);
+
+ /* Parse the security context. */
+
+ rc = -EINVAL;
+ scontextp = (char *) scontext;
+
+ /* Extract the user. */
+ p = scontextp;
+ while (*p && *p != ':')
+ p++;
+
+ if (*p == 0)
+ goto out;
+
+ *p++ = 0;
+
+ usrdatum = hashtab_search(pol->p_users.table, scontextp);
+ if (!usrdatum)
+ goto out;
+
+ ctx->user = usrdatum->value;
+
+ /* Extract role. */
+ scontextp = p;
+ while (*p && *p != ':')
+ p++;
+
+ if (*p == 0)
+ goto out;
+
+ *p++ = 0;
+
+ role = hashtab_search(pol->p_roles.table, scontextp);
+ if (!role)
+ goto out;
+ ctx->role = role->value;
+
+ /* Extract type. */
+ scontextp = p;
+ while (*p && *p != ':')
+ p++;
+ oldc = *p;
+ *p++ = 0;
+
+ typdatum = hashtab_search(pol->p_types.table, scontextp);
+ if (!typdatum || typdatum->attribute)
+ goto out;
+
+ ctx->type = typdatum->value;
+
+ rc = mls_context_to_sid(pol, oldc, &p, ctx, sidtabp, def_sid);
+ if (rc)
+ goto out;
+
+ if ((p - scontext) < scontext_len) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Check the validity of the new context. */
+ if (!policydb_context_isvalid(pol, ctx)) {
+ rc = -EINVAL;
+ goto out;
+ }
+ rc = 0;
+out:
+ if (rc)
+ context_destroy(ctx);
+ return rc;
+}
+
+static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
+ u32 *sid, u32 def_sid, gfp_t gfp_flags,
+ int force)
+{
+ char *scontext2, *str = NULL;
+ struct context context;
+ int rc = 0;
+
+ if (!ss_initialized) {
+ int i;
+
+ for (i = 1; i < SECINITSID_NUM; i++) {
+ if (!strcmp(initial_sid_to_string[i], scontext)) {
+ *sid = i;
+ return 0;
+ }
+ }
+ *sid = SECINITSID_KERNEL;
+ return 0;
+ }
+ *sid = SECSID_NULL;
+
+ /* Copy the string so that we can modify the copy as we parse it. */
+ scontext2 = kmalloc(scontext_len+1, gfp_flags);
+ if (!scontext2)
+ return -ENOMEM;
+ memcpy(scontext2, scontext, scontext_len);
+ scontext2[scontext_len] = 0;
+
+ if (force) {
+ /* Save another copy for storing in uninterpreted form */
+ str = kstrdup(scontext2, gfp_flags);
+ if (!str) {
+ kfree(scontext2);
+ return -ENOMEM;
+ }
+ }
+
+ read_lock(&policy_rwlock);
+ rc = string_to_context_struct(&policydb, &sidtab,
+ scontext2, scontext_len,
+ &context, def_sid);
+ if (rc == -EINVAL && force) {
+ context.str = str;
+ context.len = scontext_len;
+ str = NULL;
+ } else if (rc)
+ goto out;
+ rc = sidtab_context_to_sid(&sidtab, &context, sid);
+ context_destroy(&context);
+out:
+ read_unlock(&policy_rwlock);
+ kfree(scontext2);
+ kfree(str);
+ return rc;
+}
+
+/**
+ * security_context_to_sid - Obtain a SID for a given security context.
+ * @scontext: security context
+ * @scontext_len: length in bytes
+ * @sid: security identifier, SID
+ *
+ * Obtains a SID associated with the security context that
+ * has the string representation specified by @scontext.
+ * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient
+ * memory is available, or 0 on success.
+ */
+int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid)
+{
+ return security_context_to_sid_core(scontext, scontext_len,
+ sid, SECSID_NULL, GFP_KERNEL, 0);
+}
+
+/**
+ * security_context_to_sid_default - Obtain a SID for a given security context,
+ * falling back to specified default if needed.
+ *
+ * @scontext: security context
+ * @scontext_len: length in bytes
+ * @sid: security identifier, SID
+ * @def_sid: default SID to assign on error
+ *
+ * Obtains a SID associated with the security context that
+ * has the string representation specified by @scontext.
+ * The default SID is passed to the MLS layer to be used to allow
+ * kernel labeling of the MLS field if the MLS field is not present
+ * (for upgrading to MLS without full relabel).
+ * Implicitly forces adding of the context even if it cannot be mapped yet.
+ * Returns -%EINVAL if the context is invalid, -%ENOMEM if insufficient
+ * memory is available, or 0 on success.
+ */
+int security_context_to_sid_default(const char *scontext, u32 scontext_len,
+ u32 *sid, u32 def_sid, gfp_t gfp_flags)
+{
+ return security_context_to_sid_core(scontext, scontext_len,
+ sid, def_sid, gfp_flags, 1);
+}
+
+int security_context_to_sid_force(const char *scontext, u32 scontext_len,
+ u32 *sid)
+{
+ return security_context_to_sid_core(scontext, scontext_len,
+ sid, SECSID_NULL, GFP_KERNEL, 1);
+}
+
+static int compute_sid_handle_invalid_context(
+ struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ struct context *newcontext)
+{
+ char *s = NULL, *t = NULL, *n = NULL;
+ u32 slen, tlen, nlen;
+
+ if (context_struct_to_string(scontext, &s, &slen) < 0)
+ goto out;
+ if (context_struct_to_string(tcontext, &t, &tlen) < 0)
+ goto out;
+ if (context_struct_to_string(newcontext, &n, &nlen) < 0)
+ goto out;
+ audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+ "security_compute_sid: invalid context %s"
+ " for scontext=%s"
+ " tcontext=%s"
+ " tclass=%s",
+ n, s, t, policydb.p_class_val_to_name[tclass-1]);
+out:
+ kfree(s);
+ kfree(t);
+ kfree(n);
+ if (!selinux_enforcing)
+ return 0;
+ return -EACCES;
+}
+
+static int security_compute_sid(u32 ssid,
+ u32 tsid,
+ u16 tclass,
+ u32 specified,
+ u32 *out_sid)
+{
+ struct context *scontext = NULL, *tcontext = NULL, newcontext;
+ struct role_trans *roletr = NULL;
+ struct avtab_key avkey;
+ struct avtab_datum *avdatum;
+ struct avtab_node *node;
+ int rc = 0;
+
+ if (!ss_initialized) {
+ switch (tclass) {
+ case SECCLASS_PROCESS:
+ *out_sid = ssid;
+ break;
+ default:
+ *out_sid = tsid;
+ break;
+ }
+ goto out;
+ }
+
+ context_init(&newcontext);
+
+ read_lock(&policy_rwlock);
+
+ scontext = sidtab_search(&sidtab, ssid);
+ if (!scontext) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, ssid);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+ tcontext = sidtab_search(&sidtab, tsid);
+ if (!tcontext) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, tsid);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ /* Set the user identity. */
+ switch (specified) {
+ case AVTAB_TRANSITION:
+ case AVTAB_CHANGE:
+ /* Use the process user identity. */
+ newcontext.user = scontext->user;
+ break;
+ case AVTAB_MEMBER:
+ /* Use the related object owner. */
+ newcontext.user = tcontext->user;
+ break;
+ }
+
+ /* Set the role and type to default values. */
+ switch (tclass) {
+ case SECCLASS_PROCESS:
+ /* Use the current role and type of process. */
+ newcontext.role = scontext->role;
+ newcontext.type = scontext->type;
+ break;
+ default:
+ /* Use the well-defined object role. */
+ newcontext.role = OBJECT_R_VAL;
+ /* Use the type of the related object. */
+ newcontext.type = tcontext->type;
+ }
+
+ /* Look for a type transition/member/change rule. */
+ avkey.source_type = scontext->type;
+ avkey.target_type = tcontext->type;
+ avkey.target_class = tclass;
+ avkey.specified = specified;
+ avdatum = avtab_search(&policydb.te_avtab, &avkey);
+
+ /* If no permanent rule, also check for enabled conditional rules */
+ if (!avdatum) {
+ node = avtab_search_node(&policydb.te_cond_avtab, &avkey);
+ for (; node; node = avtab_search_node_next(node, specified)) {
+ if (node->key.specified & AVTAB_ENABLED) {
+ avdatum = &node->datum;
+ break;
+ }
+ }
+ }
+
+ if (avdatum) {
+ /* Use the type from the type transition/member/change rule. */
+ newcontext.type = avdatum->data;
+ }
+
+ /* Check for class-specific changes. */
+ switch (tclass) {
+ case SECCLASS_PROCESS:
+ if (specified & AVTAB_TRANSITION) {
+ /* Look for a role transition rule. */
+ for (roletr = policydb.role_tr; roletr;
+ roletr = roletr->next) {
+ if (roletr->role == scontext->role &&
+ roletr->type == tcontext->type) {
+ /* Use the role transition rule. */
+ newcontext.role = roletr->new_role;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Set the MLS attributes.
+ This is done last because it may allocate memory. */
+ rc = mls_compute_sid(scontext, tcontext, tclass, specified, &newcontext);
+ if (rc)
+ goto out_unlock;
+
+ /* Check the validity of the context. */
+ if (!policydb_context_isvalid(&policydb, &newcontext)) {
+ rc = compute_sid_handle_invalid_context(scontext,
+ tcontext,
+ tclass,
+ &newcontext);
+ if (rc)
+ goto out_unlock;
+ }
+ /* Obtain the sid for the context. */
+ rc = sidtab_context_to_sid(&sidtab, &newcontext, out_sid);
+out_unlock:
+ read_unlock(&policy_rwlock);
+ context_destroy(&newcontext);
+out:
+ return rc;
+}
+
+/**
+ * security_transition_sid - Compute the SID for a new subject/object.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @out_sid: security identifier for new subject/object
+ *
+ * Compute a SID to use for labeling a new subject or object in the
+ * class @tclass based on a SID pair (@ssid, @tsid).
+ * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM
+ * if insufficient memory is available, or %0 if the new SID was
+ * computed successfully.
+ */
+int security_transition_sid(u32 ssid,
+ u32 tsid,
+ u16 tclass,
+ u32 *out_sid)
+{
+ return security_compute_sid(ssid, tsid, tclass, AVTAB_TRANSITION, out_sid);
+}
+
+/**
+ * security_member_sid - Compute the SID for member selection.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @out_sid: security identifier for selected member
+ *
+ * Compute a SID to use when selecting a member of a polyinstantiated
+ * object of class @tclass based on a SID pair (@ssid, @tsid).
+ * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM
+ * if insufficient memory is available, or %0 if the SID was
+ * computed successfully.
+ */
+int security_member_sid(u32 ssid,
+ u32 tsid,
+ u16 tclass,
+ u32 *out_sid)
+{
+ return security_compute_sid(ssid, tsid, tclass, AVTAB_MEMBER, out_sid);
+}
+
+/**
+ * security_change_sid - Compute the SID for object relabeling.
+ * @ssid: source security identifier
+ * @tsid: target security identifier
+ * @tclass: target security class
+ * @out_sid: security identifier for selected member
+ *
+ * Compute a SID to use for relabeling an object of class @tclass
+ * based on a SID pair (@ssid, @tsid).
+ * Return -%EINVAL if any of the parameters are invalid, -%ENOMEM
+ * if insufficient memory is available, or %0 if the SID was
+ * computed successfully.
+ */
+int security_change_sid(u32 ssid,
+ u32 tsid,
+ u16 tclass,
+ u32 *out_sid)
+{
+ return security_compute_sid(ssid, tsid, tclass, AVTAB_CHANGE, out_sid);
+}
+
+/*
+ * Verify that each kernel class that is defined in the
+ * policy is correct
+ */
+static int validate_classes(struct policydb *p)
+{
+ int i, j;
+ struct class_datum *cladatum;
+ struct perm_datum *perdatum;
+ u32 nprim, tmp, common_pts_len, perm_val, pol_val;
+ u16 class_val;
+ const struct selinux_class_perm *kdefs = &selinux_class_perm;
+ const char *def_class, *def_perm, *pol_class;
+ struct symtab *perms;
+ bool print_unknown_handle = 0;
+
+ if (p->allow_unknown) {
+ u32 num_classes = kdefs->cts_len;
+ p->undefined_perms = kcalloc(num_classes, sizeof(u32), GFP_KERNEL);
+ if (!p->undefined_perms)
+ return -ENOMEM;
+ }
+
+ for (i = 1; i < kdefs->cts_len; i++) {
+ def_class = kdefs->class_to_string[i];
+ if (!def_class)
+ continue;
+ if (i > p->p_classes.nprim) {
+ printk(KERN_INFO
+ "SELinux: class %s not defined in policy\n",
+ def_class);
+ if (p->reject_unknown)
+ return -EINVAL;
+ if (p->allow_unknown)
+ p->undefined_perms[i-1] = ~0U;
+ print_unknown_handle = 1;
+ continue;
+ }
+ pol_class = p->p_class_val_to_name[i-1];
+ if (strcmp(pol_class, def_class)) {
+ printk(KERN_ERR
+ "SELinux: class %d is incorrect, found %s but should be %s\n",
+ i, pol_class, def_class);
+ return -EINVAL;
+ }
+ }
+ for (i = 0; i < kdefs->av_pts_len; i++) {
+ class_val = kdefs->av_perm_to_string[i].tclass;
+ perm_val = kdefs->av_perm_to_string[i].value;
+ def_perm = kdefs->av_perm_to_string[i].name;
+ if (class_val > p->p_classes.nprim)
+ continue;
+ pol_class = p->p_class_val_to_name[class_val-1];
+ cladatum = hashtab_search(p->p_classes.table, pol_class);
+ BUG_ON(!cladatum);
+ perms = &cladatum->permissions;
+ nprim = 1 << (perms->nprim - 1);
+ if (perm_val > nprim) {
+ printk(KERN_INFO
+ "SELinux: permission %s in class %s not defined in policy\n",
+ def_perm, pol_class);
+ if (p->reject_unknown)
+ return -EINVAL;
+ if (p->allow_unknown)
+ p->undefined_perms[class_val-1] |= perm_val;
+ print_unknown_handle = 1;
+ continue;
+ }
+ perdatum = hashtab_search(perms->table, def_perm);
+ if (perdatum == NULL) {
+ printk(KERN_ERR
+ "SELinux: permission %s in class %s not found in policy, bad policy\n",
+ def_perm, pol_class);
+ return -EINVAL;
+ }
+ pol_val = 1 << (perdatum->value - 1);
+ if (pol_val != perm_val) {
+ printk(KERN_ERR
+ "SELinux: permission %s in class %s has incorrect value\n",
+ def_perm, pol_class);
+ return -EINVAL;
+ }
+ }
+ for (i = 0; i < kdefs->av_inherit_len; i++) {
+ class_val = kdefs->av_inherit[i].tclass;
+ if (class_val > p->p_classes.nprim)
+ continue;
+ pol_class = p->p_class_val_to_name[class_val-1];
+ cladatum = hashtab_search(p->p_classes.table, pol_class);
+ BUG_ON(!cladatum);
+ if (!cladatum->comdatum) {
+ printk(KERN_ERR
+ "SELinux: class %s should have an inherits clause but does not\n",
+ pol_class);
+ return -EINVAL;
+ }
+ tmp = kdefs->av_inherit[i].common_base;
+ common_pts_len = 0;
+ while (!(tmp & 0x01)) {
+ common_pts_len++;
+ tmp >>= 1;
+ }
+ perms = &cladatum->comdatum->permissions;
+ for (j = 0; j < common_pts_len; j++) {
+ def_perm = kdefs->av_inherit[i].common_pts[j];
+ if (j >= perms->nprim) {
+ printk(KERN_INFO
+ "SELinux: permission %s in class %s not defined in policy\n",
+ def_perm, pol_class);
+ if (p->reject_unknown)
+ return -EINVAL;
+ if (p->allow_unknown)
+ p->undefined_perms[class_val-1] |= (1 << j);
+ print_unknown_handle = 1;
+ continue;
+ }
+ perdatum = hashtab_search(perms->table, def_perm);
+ if (perdatum == NULL) {
+ printk(KERN_ERR
+ "SELinux: permission %s in class %s not found in policy, bad policy\n",
+ def_perm, pol_class);
+ return -EINVAL;
+ }
+ if (perdatum->value != j + 1) {
+ printk(KERN_ERR
+ "SELinux: permission %s in class %s has incorrect value\n",
+ def_perm, pol_class);
+ return -EINVAL;
+ }
+ }
+ }
+ if (print_unknown_handle)
+ printk(KERN_INFO "SELinux: the above unknown classes and permissions will be %s\n",
+ (security_get_allow_unknown() ? "allowed" : "denied"));
+ return 0;
+}
+
+/* Clone the SID into the new SID table. */
+static int clone_sid(u32 sid,
+ struct context *context,
+ void *arg)
+{
+ struct sidtab *s = arg;
+
+ return sidtab_insert(s, sid, context);
+}
+
+static inline int convert_context_handle_invalid_context(struct context *context)
+{
+ int rc = 0;
+
+ if (selinux_enforcing) {
+ rc = -EINVAL;
+ } else {
+ char *s;
+ u32 len;
+
+ if (!context_struct_to_string(context, &s, &len)) {
+ printk(KERN_WARNING
+ "SELinux: Context %s would be invalid if enforcing\n",
+ s);
+ kfree(s);
+ }
+ }
+ return rc;
+}
+
+struct convert_context_args {
+ struct policydb *oldp;
+ struct policydb *newp;
+};
+
+/*
+ * Convert the values in the security context
+ * structure `c' from the values specified
+ * in the policy `p->oldp' to the values specified
+ * in the policy `p->newp'. Verify that the
+ * context is valid under the new policy.
+ */
+static int convert_context(u32 key,
+ struct context *c,
+ void *p)
+{
+ struct convert_context_args *args;
+ struct context oldc;
+ struct role_datum *role;
+ struct type_datum *typdatum;
+ struct user_datum *usrdatum;
+ char *s;
+ u32 len;
+ int rc;
+
+ args = p;
+
+ if (c->str) {
+ struct context ctx;
+ s = kstrdup(c->str, GFP_KERNEL);
+ if (!s) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ rc = string_to_context_struct(args->newp, NULL, s,
+ c->len, &ctx, SECSID_NULL);
+ kfree(s);
+ if (!rc) {
+ printk(KERN_INFO
+ "SELinux: Context %s became valid (mapped).\n",
+ c->str);
+ /* Replace string with mapped representation. */
+ kfree(c->str);
+ memcpy(c, &ctx, sizeof(*c));
+ goto out;
+ } else if (rc == -EINVAL) {
+ /* Retain string representation for later mapping. */
+ rc = 0;
+ goto out;
+ } else {
+ /* Other error condition, e.g. ENOMEM. */
+ printk(KERN_ERR
+ "SELinux: Unable to map context %s, rc = %d.\n",
+ c->str, -rc);
+ goto out;
+ }
+ }
+
+ rc = context_cpy(&oldc, c);
+ if (rc)
+ goto out;
+
+ rc = -EINVAL;
+
+ /* Convert the user. */
+ usrdatum = hashtab_search(args->newp->p_users.table,
+ args->oldp->p_user_val_to_name[c->user - 1]);
+ if (!usrdatum)
+ goto bad;
+ c->user = usrdatum->value;
+
+ /* Convert the role. */
+ role = hashtab_search(args->newp->p_roles.table,
+ args->oldp->p_role_val_to_name[c->role - 1]);
+ if (!role)
+ goto bad;
+ c->role = role->value;
+
+ /* Convert the type. */
+ typdatum = hashtab_search(args->newp->p_types.table,
+ args->oldp->p_type_val_to_name[c->type - 1]);
+ if (!typdatum)
+ goto bad;
+ c->type = typdatum->value;
+
+ rc = mls_convert_context(args->oldp, args->newp, c);
+ if (rc)
+ goto bad;
+
+ /* Check the validity of the new context. */
+ if (!policydb_context_isvalid(args->newp, c)) {
+ rc = convert_context_handle_invalid_context(&oldc);
+ if (rc)
+ goto bad;
+ }
+
+ context_destroy(&oldc);
+ rc = 0;
+out:
+ return rc;
+bad:
+ /* Map old representation to string and save it. */
+ if (context_struct_to_string(&oldc, &s, &len))
+ return -ENOMEM;
+ context_destroy(&oldc);
+ context_destroy(c);
+ c->str = s;
+ c->len = len;
+ printk(KERN_INFO
+ "SELinux: Context %s became invalid (unmapped).\n",
+ c->str);
+ rc = 0;
+ goto out;
+}
+
+static void security_load_policycaps(void)
+{
+ selinux_policycap_netpeer = ebitmap_get_bit(&policydb.policycaps,
+ POLICYDB_CAPABILITY_NETPEER);
+ selinux_policycap_openperm = ebitmap_get_bit(&policydb.policycaps,
+ POLICYDB_CAPABILITY_OPENPERM);
+}
+
+extern void selinux_complete_init(void);
+static int security_preserve_bools(struct policydb *p);
+
+/**
+ * security_load_policy - Load a security policy configuration.
+ * @data: binary policy data
+ * @len: length of data in bytes
+ *
+ * Load a new set of security policy configuration data,
+ * validate it and convert the SID table as necessary.
+ * This function will flush the access vector cache after
+ * loading the new policy.
+ */
+int security_load_policy(void *data, size_t len)
+{
+ struct policydb oldpolicydb, newpolicydb;
+ struct sidtab oldsidtab, newsidtab;
+ struct convert_context_args args;
+ u32 seqno;
+ int rc = 0;
+ struct policy_file file = { data, len }, *fp = &file;
+
+ if (!ss_initialized) {
+ avtab_cache_init();
+ if (policydb_read(&policydb, fp)) {
+ avtab_cache_destroy();
+ return -EINVAL;
+ }
+ if (policydb_load_isids(&policydb, &sidtab)) {
+ policydb_destroy(&policydb);
+ avtab_cache_destroy();
+ return -EINVAL;
+ }
+ /* Verify that the kernel defined classes are correct. */
+ if (validate_classes(&policydb)) {
+ printk(KERN_ERR
+ "SELinux: the definition of a class is incorrect\n");
+ sidtab_destroy(&sidtab);
+ policydb_destroy(&policydb);
+ avtab_cache_destroy();
+ return -EINVAL;
+ }
+ security_load_policycaps();
+ policydb_loaded_version = policydb.policyvers;
+ ss_initialized = 1;
+ seqno = ++latest_granting;
+ selinux_complete_init();
+ avc_ss_reset(seqno);
+ selnl_notify_policyload(seqno);
+ selinux_netlbl_cache_invalidate();
+ selinux_xfrm_notify_policyload();
+ return 0;
+ }
+
+#if 0
+ sidtab_hash_eval(&sidtab, "sids");
+#endif
+
+ if (policydb_read(&newpolicydb, fp))
+ return -EINVAL;
+
+ if (sidtab_init(&newsidtab)) {
+ policydb_destroy(&newpolicydb);
+ return -ENOMEM;
+ }
+
+ /* Verify that the kernel defined classes are correct. */
+ if (validate_classes(&newpolicydb)) {
+ printk(KERN_ERR
+ "SELinux: the definition of a class is incorrect\n");
+ rc = -EINVAL;
+ goto err;
+ }
+
+ rc = security_preserve_bools(&newpolicydb);
+ if (rc) {
+ printk(KERN_ERR "SELinux: unable to preserve booleans\n");
+ goto err;
+ }
+
+ /* Clone the SID table. */
+ sidtab_shutdown(&sidtab);
+ if (sidtab_map(&sidtab, clone_sid, &newsidtab)) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ /*
+ * Convert the internal representations of contexts
+ * in the new SID table.
+ */
+ args.oldp = &policydb;
+ args.newp = &newpolicydb;
+ rc = sidtab_map(&newsidtab, convert_context, &args);
+ if (rc)
+ goto err;
+
+ /* Save the old policydb and SID table to free later. */
+ memcpy(&oldpolicydb, &policydb, sizeof policydb);
+ sidtab_set(&oldsidtab, &sidtab);
+
+ /* Install the new policydb and SID table. */
+ write_lock_irq(&policy_rwlock);
+ memcpy(&policydb, &newpolicydb, sizeof policydb);
+ sidtab_set(&sidtab, &newsidtab);
+ security_load_policycaps();
+ seqno = ++latest_granting;
+ policydb_loaded_version = policydb.policyvers;
+ write_unlock_irq(&policy_rwlock);
+
+ /* Free the old policydb and SID table. */
+ policydb_destroy(&oldpolicydb);
+ sidtab_destroy(&oldsidtab);
+
+ avc_ss_reset(seqno);
+ selnl_notify_policyload(seqno);
+ selinux_netlbl_cache_invalidate();
+ selinux_xfrm_notify_policyload();
+
+ return 0;
+
+err:
+ sidtab_destroy(&newsidtab);
+ policydb_destroy(&newpolicydb);
+ return rc;
+
+}
+
+/**
+ * security_port_sid - Obtain the SID for a port.
+ * @protocol: protocol number
+ * @port: port number
+ * @out_sid: security identifier
+ */
+int security_port_sid(u8 protocol, u16 port, u32 *out_sid)
+{
+ struct ocontext *c;
+ int rc = 0;
+
+ read_lock(&policy_rwlock);
+
+ c = policydb.ocontexts[OCON_PORT];
+ while (c) {
+ if (c->u.port.protocol == protocol &&
+ c->u.port.low_port <= port &&
+ c->u.port.high_port >= port)
+ break;
+ c = c->next;
+ }
+
+ if (c) {
+ if (!c->sid[0]) {
+ rc = sidtab_context_to_sid(&sidtab,
+ &c->context[0],
+ &c->sid[0]);
+ if (rc)
+ goto out;
+ }
+ *out_sid = c->sid[0];
+ } else {
+ *out_sid = SECINITSID_PORT;
+ }
+
+out:
+ read_unlock(&policy_rwlock);
+ return rc;
+}
+
+/**
+ * security_netif_sid - Obtain the SID for a network interface.
+ * @name: interface name
+ * @if_sid: interface SID
+ */
+int security_netif_sid(char *name, u32 *if_sid)
+{
+ int rc = 0;
+ struct ocontext *c;
+
+ read_lock(&policy_rwlock);
+
+ c = policydb.ocontexts[OCON_NETIF];
+ while (c) {
+ if (strcmp(name, c->u.name) == 0)
+ break;
+ c = c->next;
+ }
+
+ if (c) {
+ if (!c->sid[0] || !c->sid[1]) {
+ rc = sidtab_context_to_sid(&sidtab,
+ &c->context[0],
+ &c->sid[0]);
+ if (rc)
+ goto out;
+ rc = sidtab_context_to_sid(&sidtab,
+ &c->context[1],
+ &c->sid[1]);
+ if (rc)
+ goto out;
+ }
+ *if_sid = c->sid[0];
+ } else
+ *if_sid = SECINITSID_NETIF;
+
+out:
+ read_unlock(&policy_rwlock);
+ return rc;
+}
+
+static int match_ipv6_addrmask(u32 *input, u32 *addr, u32 *mask)
+{
+ int i, fail = 0;
+
+ for (i = 0; i < 4; i++)
+ if (addr[i] != (input[i] & mask[i])) {
+ fail = 1;
+ break;
+ }
+
+ return !fail;
+}
+
+/**
+ * security_node_sid - Obtain the SID for a node (host).
+ * @domain: communication domain aka address family
+ * @addrp: address
+ * @addrlen: address length in bytes
+ * @out_sid: security identifier
+ */
+int security_node_sid(u16 domain,
+ void *addrp,
+ u32 addrlen,
+ u32 *out_sid)
+{
+ int rc = 0;
+ struct ocontext *c;
+
+ read_lock(&policy_rwlock);
+
+ switch (domain) {
+ case AF_INET: {
+ u32 addr;
+
+ if (addrlen != sizeof(u32)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ addr = *((u32 *)addrp);
+
+ c = policydb.ocontexts[OCON_NODE];
+ while (c) {
+ if (c->u.node.addr == (addr & c->u.node.mask))
+ break;
+ c = c->next;
+ }
+ break;
+ }
+
+ case AF_INET6:
+ if (addrlen != sizeof(u64) * 2) {
+ rc = -EINVAL;
+ goto out;
+ }
+ c = policydb.ocontexts[OCON_NODE6];
+ while (c) {
+ if (match_ipv6_addrmask(addrp, c->u.node6.addr,
+ c->u.node6.mask))
+ break;
+ c = c->next;
+ }
+ break;
+
+ default:
+ *out_sid = SECINITSID_NODE;
+ goto out;
+ }
+
+ if (c) {
+ if (!c->sid[0]) {
+ rc = sidtab_context_to_sid(&sidtab,
+ &c->context[0],
+ &c->sid[0]);
+ if (rc)
+ goto out;
+ }
+ *out_sid = c->sid[0];
+ } else {
+ *out_sid = SECINITSID_NODE;
+ }
+
+out:
+ read_unlock(&policy_rwlock);
+ return rc;
+}
+
+#define SIDS_NEL 25
+
+/**
+ * security_get_user_sids - Obtain reachable SIDs for a user.
+ * @fromsid: starting SID
+ * @username: username
+ * @sids: array of reachable SIDs for user
+ * @nel: number of elements in @sids
+ *
+ * Generate the set of SIDs for legal security contexts
+ * for a given user that can be reached by @fromsid.
+ * Set *@sids to point to a dynamically allocated
+ * array containing the set of SIDs. Set *@nel to the
+ * number of elements in the array.
+ */
+
+int security_get_user_sids(u32 fromsid,
+ char *username,
+ u32 **sids,
+ u32 *nel)
+{
+ struct context *fromcon, usercon;
+ u32 *mysids = NULL, *mysids2, sid;
+ u32 mynel = 0, maxnel = SIDS_NEL;
+ struct user_datum *user;
+ struct role_datum *role;
+ struct ebitmap_node *rnode, *tnode;
+ int rc = 0, i, j;
+
+ *sids = NULL;
+ *nel = 0;
+
+ if (!ss_initialized)
+ goto out;
+
+ read_lock(&policy_rwlock);
+
+ context_init(&usercon);
+
+ fromcon = sidtab_search(&sidtab, fromsid);
+ if (!fromcon) {
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ user = hashtab_search(policydb.p_users.table, username);
+ if (!user) {
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+ usercon.user = user->value;
+
+ mysids = kcalloc(maxnel, sizeof(*mysids), GFP_ATOMIC);
+ if (!mysids) {
+ rc = -ENOMEM;
+ goto out_unlock;
+ }
+
+ ebitmap_for_each_positive_bit(&user->roles, rnode, i) {
+ role = policydb.role_val_to_struct[i];
+ usercon.role = i+1;
+ ebitmap_for_each_positive_bit(&role->types, tnode, j) {
+ usercon.type = j+1;
+
+ if (mls_setup_user_range(fromcon, user, &usercon))
+ continue;
+
+ rc = sidtab_context_to_sid(&sidtab, &usercon, &sid);
+ if (rc)
+ goto out_unlock;
+ if (mynel < maxnel) {
+ mysids[mynel++] = sid;
+ } else {
+ maxnel += SIDS_NEL;
+ mysids2 = kcalloc(maxnel, sizeof(*mysids2), GFP_ATOMIC);
+ if (!mysids2) {
+ rc = -ENOMEM;
+ goto out_unlock;
+ }
+ memcpy(mysids2, mysids, mynel * sizeof(*mysids2));
+ kfree(mysids);
+ mysids = mysids2;
+ mysids[mynel++] = sid;
+ }
+ }
+ }
+
+out_unlock:
+ read_unlock(&policy_rwlock);
+ if (rc || !mynel) {
+ kfree(mysids);
+ goto out;
+ }
+
+ mysids2 = kcalloc(mynel, sizeof(*mysids2), GFP_KERNEL);
+ if (!mysids2) {
+ rc = -ENOMEM;
+ kfree(mysids);
+ goto out;
+ }
+ for (i = 0, j = 0; i < mynel; i++) {
+ rc = avc_has_perm_noaudit(fromsid, mysids[i],
+ SECCLASS_PROCESS,
+ PROCESS__TRANSITION, AVC_STRICT,
+ NULL);
+ if (!rc)
+ mysids2[j++] = mysids[i];
+ cond_resched();
+ }
+ rc = 0;
+ kfree(mysids);
+ *sids = mysids2;
+ *nel = j;
+out:
+ return rc;
+}
+
+/**
+ * security_genfs_sid - Obtain a SID for a file in a filesystem
+ * @fstype: filesystem type
+ * @path: path from root of mount
+ * @sclass: file security class
+ * @sid: SID for path
+ *
+ * Obtain a SID to use for a file in a filesystem that
+ * cannot support xattr or use a fixed labeling behavior like
+ * transition SIDs or task SIDs.
+ */
+int security_genfs_sid(const char *fstype,
+ char *path,
+ u16 sclass,
+ u32 *sid)
+{
+ int len;
+ struct genfs *genfs;
+ struct ocontext *c;
+ int rc = 0, cmp = 0;
+
+ while (path[0] == '/' && path[1] == '/')
+ path++;
+
+ read_lock(&policy_rwlock);
+
+ for (genfs = policydb.genfs; genfs; genfs = genfs->next) {
+ cmp = strcmp(fstype, genfs->fstype);
+ if (cmp <= 0)
+ break;
+ }
+
+ if (!genfs || cmp) {
+ *sid = SECINITSID_UNLABELED;
+ rc = -ENOENT;
+ goto out;
+ }
+
+ for (c = genfs->head; c; c = c->next) {
+ len = strlen(c->u.name);
+ if ((!c->v.sclass || sclass == c->v.sclass) &&
+ (strncmp(c->u.name, path, len) == 0))
+ break;
+ }
+
+ if (!c) {
+ *sid = SECINITSID_UNLABELED;
+ rc = -ENOENT;
+ goto out;
+ }
+
+ if (!c->sid[0]) {
+ rc = sidtab_context_to_sid(&sidtab,
+ &c->context[0],
+ &c->sid[0]);
+ if (rc)
+ goto out;
+ }
+
+ *sid = c->sid[0];
+out:
+ read_unlock(&policy_rwlock);
+ return rc;
+}
+
+/**
+ * security_fs_use - Determine how to handle labeling for a filesystem.
+ * @fstype: filesystem type
+ * @behavior: labeling behavior
+ * @sid: SID for filesystem (superblock)
+ */
+int security_fs_use(
+ const char *fstype,
+ unsigned int *behavior,
+ u32 *sid)
+{
+ int rc = 0;
+ struct ocontext *c;
+
+ read_lock(&policy_rwlock);
+
+ c = policydb.ocontexts[OCON_FSUSE];
+ while (c) {
+ if (strcmp(fstype, c->u.name) == 0)
+ break;
+ c = c->next;
+ }
+
+ if (c) {
+ *behavior = c->v.behavior;
+ if (!c->sid[0]) {
+ rc = sidtab_context_to_sid(&sidtab,
+ &c->context[0],
+ &c->sid[0]);
+ if (rc)
+ goto out;
+ }
+ *sid = c->sid[0];
+ } else {
+ rc = security_genfs_sid(fstype, "/", SECCLASS_DIR, sid);
+ if (rc) {
+ *behavior = SECURITY_FS_USE_NONE;
+ rc = 0;
+ } else {
+ *behavior = SECURITY_FS_USE_GENFS;
+ }
+ }
+
+out:
+ read_unlock(&policy_rwlock);
+ return rc;
+}
+
+int security_get_bools(int *len, char ***names, int **values)
+{
+ int i, rc = -ENOMEM;
+
+ read_lock(&policy_rwlock);
+ *names = NULL;
+ *values = NULL;
+
+ *len = policydb.p_bools.nprim;
+ if (!*len) {
+ rc = 0;
+ goto out;
+ }
+
+ *names = kcalloc(*len, sizeof(char *), GFP_ATOMIC);
+ if (!*names)
+ goto err;
+
+ *values = kcalloc(*len, sizeof(int), GFP_ATOMIC);
+ if (!*values)
+ goto err;
+
+ for (i = 0; i < *len; i++) {
+ size_t name_len;
+ (*values)[i] = policydb.bool_val_to_struct[i]->state;
+ name_len = strlen(policydb.p_bool_val_to_name[i]) + 1;
+ (*names)[i] = kmalloc(sizeof(char) * name_len, GFP_ATOMIC);
+ if (!(*names)[i])
+ goto err;
+ strncpy((*names)[i], policydb.p_bool_val_to_name[i], name_len);
+ (*names)[i][name_len - 1] = 0;
+ }
+ rc = 0;
+out:
+ read_unlock(&policy_rwlock);
+ return rc;
+err:
+ if (*names) {
+ for (i = 0; i < *len; i++)
+ kfree((*names)[i]);
+ }
+ kfree(*values);
+ goto out;
+}
+
+
+int security_set_bools(int len, int *values)
+{
+ int i, rc = 0;
+ int lenp, seqno = 0;
+ struct cond_node *cur;
+
+ write_lock_irq(&policy_rwlock);
+
+ lenp = policydb.p_bools.nprim;
+ if (len != lenp) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (!!values[i] != policydb.bool_val_to_struct[i]->state) {
+ audit_log(current->audit_context, GFP_ATOMIC,
+ AUDIT_MAC_CONFIG_CHANGE,
+ "bool=%s val=%d old_val=%d auid=%u ses=%u",
+ policydb.p_bool_val_to_name[i],
+ !!values[i],
+ policydb.bool_val_to_struct[i]->state,
+ audit_get_loginuid(current),
+ audit_get_sessionid(current));
+ }
+ if (values[i])
+ policydb.bool_val_to_struct[i]->state = 1;
+ else
+ policydb.bool_val_to_struct[i]->state = 0;
+ }
+
+ for (cur = policydb.cond_list; cur; cur = cur->next) {
+ rc = evaluate_cond_node(&policydb, cur);
+ if (rc)
+ goto out;
+ }
+
+ seqno = ++latest_granting;
+
+out:
+ write_unlock_irq(&policy_rwlock);
+ if (!rc) {
+ avc_ss_reset(seqno);
+ selnl_notify_policyload(seqno);
+ selinux_xfrm_notify_policyload();
+ }
+ return rc;
+}
+
+int security_get_bool_value(int bool)
+{
+ int rc = 0;
+ int len;
+
+ read_lock(&policy_rwlock);
+
+ len = policydb.p_bools.nprim;
+ if (bool >= len) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ rc = policydb.bool_val_to_struct[bool]->state;
+out:
+ read_unlock(&policy_rwlock);
+ return rc;
+}
+
+static int security_preserve_bools(struct policydb *p)
+{
+ int rc, nbools = 0, *bvalues = NULL, i;
+ char **bnames = NULL;
+ struct cond_bool_datum *booldatum;
+ struct cond_node *cur;
+
+ rc = security_get_bools(&nbools, &bnames, &bvalues);
+ if (rc)
+ goto out;
+ for (i = 0; i < nbools; i++) {
+ booldatum = hashtab_search(p->p_bools.table, bnames[i]);
+ if (booldatum)
+ booldatum->state = bvalues[i];
+ }
+ for (cur = p->cond_list; cur; cur = cur->next) {
+ rc = evaluate_cond_node(p, cur);
+ if (rc)
+ goto out;
+ }
+
+out:
+ if (bnames) {
+ for (i = 0; i < nbools; i++)
+ kfree(bnames[i]);
+ }
+ kfree(bnames);
+ kfree(bvalues);
+ return rc;
+}
+
+/*
+ * security_sid_mls_copy() - computes a new sid based on the given
+ * sid and the mls portion of mls_sid.
+ */
+int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
+{
+ struct context *context1;
+ struct context *context2;
+ struct context newcon;
+ char *s;
+ u32 len;
+ int rc = 0;
+
+ if (!ss_initialized || !selinux_mls_enabled) {
+ *new_sid = sid;
+ goto out;
+ }
+
+ context_init(&newcon);
+
+ read_lock(&policy_rwlock);
+ context1 = sidtab_search(&sidtab, sid);
+ if (!context1) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, sid);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ context2 = sidtab_search(&sidtab, mls_sid);
+ if (!context2) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, mls_sid);
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
+ newcon.user = context1->user;
+ newcon.role = context1->role;
+ newcon.type = context1->type;
+ rc = mls_context_cpy(&newcon, context2);
+ if (rc)
+ goto out_unlock;
+
+ /* Check the validity of the new context. */
+ if (!policydb_context_isvalid(&policydb, &newcon)) {
+ rc = convert_context_handle_invalid_context(&newcon);
+ if (rc)
+ goto bad;
+ }
+
+ rc = sidtab_context_to_sid(&sidtab, &newcon, new_sid);
+ goto out_unlock;
+
+bad:
+ if (!context_struct_to_string(&newcon, &s, &len)) {
+ audit_log(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+ "security_sid_mls_copy: invalid context %s", s);
+ kfree(s);
+ }
+
+out_unlock:
+ read_unlock(&policy_rwlock);
+ context_destroy(&newcon);
+out:
+ return rc;
+}
+
+/**
+ * security_net_peersid_resolve - Compare and resolve two network peer SIDs
+ * @nlbl_sid: NetLabel SID
+ * @nlbl_type: NetLabel labeling protocol type
+ * @xfrm_sid: XFRM SID
+ *
+ * Description:
+ * Compare the @nlbl_sid and @xfrm_sid values and if the two SIDs can be
+ * resolved into a single SID it is returned via @peer_sid and the function
+ * returns zero. Otherwise @peer_sid is set to SECSID_NULL and the function
+ * returns a negative value. A table summarizing the behavior is below:
+ *
+ * | function return | @sid
+ * ------------------------------+-----------------+-----------------
+ * no peer labels | 0 | SECSID_NULL
+ * single peer label | 0 | <peer_label>
+ * multiple, consistent labels | 0 | <peer_label>
+ * multiple, inconsistent labels | -<errno> | SECSID_NULL
+ *
+ */
+int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
+ u32 xfrm_sid,
+ u32 *peer_sid)
+{
+ int rc;
+ struct context *nlbl_ctx;
+ struct context *xfrm_ctx;
+
+ /* handle the common (which also happens to be the set of easy) cases
+ * right away, these two if statements catch everything involving a
+ * single or absent peer SID/label */
+ if (xfrm_sid == SECSID_NULL) {
+ *peer_sid = nlbl_sid;
+ return 0;
+ }
+ /* NOTE: an nlbl_type == NETLBL_NLTYPE_UNLABELED is a "fallback" label
+ * and is treated as if nlbl_sid == SECSID_NULL when a XFRM SID/label
+ * is present */
+ if (nlbl_sid == SECSID_NULL || nlbl_type == NETLBL_NLTYPE_UNLABELED) {
+ *peer_sid = xfrm_sid;
+ return 0;
+ }
+
+ /* we don't need to check ss_initialized here since the only way both
+ * nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the
+ * security server was initialized and ss_initialized was true */
+ if (!selinux_mls_enabled) {
+ *peer_sid = SECSID_NULL;
+ return 0;
+ }
+
+ read_lock(&policy_rwlock);
+
+ nlbl_ctx = sidtab_search(&sidtab, nlbl_sid);
+ if (!nlbl_ctx) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, nlbl_sid);
+ rc = -EINVAL;
+ goto out_slowpath;
+ }
+ xfrm_ctx = sidtab_search(&sidtab, xfrm_sid);
+ if (!xfrm_ctx) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, xfrm_sid);
+ rc = -EINVAL;
+ goto out_slowpath;
+ }
+ rc = (mls_context_cmp(nlbl_ctx, xfrm_ctx) ? 0 : -EACCES);
+
+out_slowpath:
+ read_unlock(&policy_rwlock);
+ if (rc == 0)
+ /* at present NetLabel SIDs/labels really only carry MLS
+ * information so if the MLS portion of the NetLabel SID
+ * matches the MLS portion of the labeled XFRM SID/label
+ * then pass along the XFRM SID as it is the most
+ * expressive */
+ *peer_sid = xfrm_sid;
+ else
+ *peer_sid = SECSID_NULL;
+ return rc;
+}
+
+static int get_classes_callback(void *k, void *d, void *args)
+{
+ struct class_datum *datum = d;
+ char *name = k, **classes = args;
+ int value = datum->value - 1;
+
+ classes[value] = kstrdup(name, GFP_ATOMIC);
+ if (!classes[value])
+ return -ENOMEM;
+
+ return 0;
+}
+
+int security_get_classes(char ***classes, int *nclasses)
+{
+ int rc = -ENOMEM;
+
+ read_lock(&policy_rwlock);
+
+ *nclasses = policydb.p_classes.nprim;
+ *classes = kcalloc(*nclasses, sizeof(*classes), GFP_ATOMIC);
+ if (!*classes)
+ goto out;
+
+ rc = hashtab_map(policydb.p_classes.table, get_classes_callback,
+ *classes);
+ if (rc < 0) {
+ int i;
+ for (i = 0; i < *nclasses; i++)
+ kfree((*classes)[i]);
+ kfree(*classes);
+ }
+
+out:
+ read_unlock(&policy_rwlock);
+ return rc;
+}
+
+static int get_permissions_callback(void *k, void *d, void *args)
+{
+ struct perm_datum *datum = d;
+ char *name = k, **perms = args;
+ int value = datum->value - 1;
+
+ perms[value] = kstrdup(name, GFP_ATOMIC);
+ if (!perms[value])
+ return -ENOMEM;
+
+ return 0;
+}
+
+int security_get_permissions(char *class, char ***perms, int *nperms)
+{
+ int rc = -ENOMEM, i;
+ struct class_datum *match;
+
+ read_lock(&policy_rwlock);
+
+ match = hashtab_search(policydb.p_classes.table, class);
+ if (!match) {
+ printk(KERN_ERR "SELinux: %s: unrecognized class %s\n",
+ __func__, class);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ *nperms = match->permissions.nprim;
+ *perms = kcalloc(*nperms, sizeof(*perms), GFP_ATOMIC);
+ if (!*perms)
+ goto out;
+
+ if (match->comdatum) {
+ rc = hashtab_map(match->comdatum->permissions.table,
+ get_permissions_callback, *perms);
+ if (rc < 0)
+ goto err;
+ }
+
+ rc = hashtab_map(match->permissions.table, get_permissions_callback,
+ *perms);
+ if (rc < 0)
+ goto err;
+
+out:
+ read_unlock(&policy_rwlock);
+ return rc;
+
+err:
+ read_unlock(&policy_rwlock);
+ for (i = 0; i < *nperms; i++)
+ kfree((*perms)[i]);
+ kfree(*perms);
+ return rc;
+}
+
+int security_get_reject_unknown(void)
+{
+ return policydb.reject_unknown;
+}
+
+int security_get_allow_unknown(void)
+{
+ return policydb.allow_unknown;
+}
+
+/**
+ * security_policycap_supported - Check for a specific policy capability
+ * @req_cap: capability
+ *
+ * Description:
+ * This function queries the currently loaded policy to see if it supports the
+ * capability specified by @req_cap. Returns true (1) if the capability is
+ * supported, false (0) if it isn't supported.
+ *
+ */
+int security_policycap_supported(unsigned int req_cap)
+{
+ int rc;
+
+ read_lock(&policy_rwlock);
+ rc = ebitmap_get_bit(&policydb.policycaps, req_cap);
+ read_unlock(&policy_rwlock);
+
+ return rc;
+}
+
+struct selinux_audit_rule {
+ u32 au_seqno;
+ struct context au_ctxt;
+};
+
+void selinux_audit_rule_free(void *vrule)
+{
+ struct selinux_audit_rule *rule = vrule;
+
+ if (rule) {
+ context_destroy(&rule->au_ctxt);
+ kfree(rule);
+ }
+}
+
+int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
+{
+ struct selinux_audit_rule *tmprule;
+ struct role_datum *roledatum;
+ struct type_datum *typedatum;
+ struct user_datum *userdatum;
+ struct selinux_audit_rule **rule = (struct selinux_audit_rule **)vrule;
+ int rc = 0;
+
+ *rule = NULL;
+
+ if (!ss_initialized)
+ return -EOPNOTSUPP;
+
+ switch (field) {
+ case AUDIT_SUBJ_USER:
+ case AUDIT_SUBJ_ROLE:
+ case AUDIT_SUBJ_TYPE:
+ case AUDIT_OBJ_USER:
+ case AUDIT_OBJ_ROLE:
+ case AUDIT_OBJ_TYPE:
+ /* only 'equals' and 'not equals' fit user, role, and type */
+ if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL)
+ return -EINVAL;
+ break;
+ case AUDIT_SUBJ_SEN:
+ case AUDIT_SUBJ_CLR:
+ case AUDIT_OBJ_LEV_LOW:
+ case AUDIT_OBJ_LEV_HIGH:
+ /* we do not allow a range, indicated by the presense of '-' */
+ if (strchr(rulestr, '-'))
+ return -EINVAL;
+ break;
+ default:
+ /* only the above fields are valid */
+ return -EINVAL;
+ }
+
+ tmprule = kzalloc(sizeof(struct selinux_audit_rule), GFP_KERNEL);
+ if (!tmprule)
+ return -ENOMEM;
+
+ context_init(&tmprule->au_ctxt);
+
+ read_lock(&policy_rwlock);
+
+ tmprule->au_seqno = latest_granting;
+
+ switch (field) {
+ case AUDIT_SUBJ_USER:
+ case AUDIT_OBJ_USER:
+ userdatum = hashtab_search(policydb.p_users.table, rulestr);
+ if (!userdatum)
+ rc = -EINVAL;
+ else
+ tmprule->au_ctxt.user = userdatum->value;
+ break;
+ case AUDIT_SUBJ_ROLE:
+ case AUDIT_OBJ_ROLE:
+ roledatum = hashtab_search(policydb.p_roles.table, rulestr);
+ if (!roledatum)
+ rc = -EINVAL;
+ else
+ tmprule->au_ctxt.role = roledatum->value;
+ break;
+ case AUDIT_SUBJ_TYPE:
+ case AUDIT_OBJ_TYPE:
+ typedatum = hashtab_search(policydb.p_types.table, rulestr);
+ if (!typedatum)
+ rc = -EINVAL;
+ else
+ tmprule->au_ctxt.type = typedatum->value;
+ break;
+ case AUDIT_SUBJ_SEN:
+ case AUDIT_SUBJ_CLR:
+ case AUDIT_OBJ_LEV_LOW:
+ case AUDIT_OBJ_LEV_HIGH:
+ rc = mls_from_string(rulestr, &tmprule->au_ctxt, GFP_ATOMIC);
+ break;
+ }
+
+ read_unlock(&policy_rwlock);
+
+ if (rc) {
+ selinux_audit_rule_free(tmprule);
+ tmprule = NULL;
+ }
+
+ *rule = tmprule;
+
+ return rc;
+}
+
+/* Check to see if the rule contains any selinux fields */
+int selinux_audit_rule_known(struct audit_krule *rule)
+{
+ int i;
+
+ for (i = 0; i < rule->field_count; i++) {
+ struct audit_field *f = &rule->fields[i];
+ switch (f->type) {
+ case AUDIT_SUBJ_USER:
+ case AUDIT_SUBJ_ROLE:
+ case AUDIT_SUBJ_TYPE:
+ case AUDIT_SUBJ_SEN:
+ case AUDIT_SUBJ_CLR:
+ case AUDIT_OBJ_USER:
+ case AUDIT_OBJ_ROLE:
+ case AUDIT_OBJ_TYPE:
+ case AUDIT_OBJ_LEV_LOW:
+ case AUDIT_OBJ_LEV_HIGH:
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int selinux_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule,
+ struct audit_context *actx)
+{
+ struct context *ctxt;
+ struct mls_level *level;
+ struct selinux_audit_rule *rule = vrule;
+ int match = 0;
+
+ if (!rule) {
+ audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+ "selinux_audit_rule_match: missing rule\n");
+ return -ENOENT;
+ }
+
+ read_lock(&policy_rwlock);
+
+ if (rule->au_seqno < latest_granting) {
+ audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+ "selinux_audit_rule_match: stale rule\n");
+ match = -ESTALE;
+ goto out;
+ }
+
+ ctxt = sidtab_search(&sidtab, sid);
+ if (!ctxt) {
+ audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR,
+ "selinux_audit_rule_match: unrecognized SID %d\n",
+ sid);
+ match = -ENOENT;
+ goto out;
+ }
+
+ /* a field/op pair that is not caught here will simply fall through
+ without a match */
+ switch (field) {
+ case AUDIT_SUBJ_USER:
+ case AUDIT_OBJ_USER:
+ switch (op) {
+ case AUDIT_EQUAL:
+ match = (ctxt->user == rule->au_ctxt.user);
+ break;
+ case AUDIT_NOT_EQUAL:
+ match = (ctxt->user != rule->au_ctxt.user);
+ break;
+ }
+ break;
+ case AUDIT_SUBJ_ROLE:
+ case AUDIT_OBJ_ROLE:
+ switch (op) {
+ case AUDIT_EQUAL:
+ match = (ctxt->role == rule->au_ctxt.role);
+ break;
+ case AUDIT_NOT_EQUAL:
+ match = (ctxt->role != rule->au_ctxt.role);
+ break;
+ }
+ break;
+ case AUDIT_SUBJ_TYPE:
+ case AUDIT_OBJ_TYPE:
+ switch (op) {
+ case AUDIT_EQUAL:
+ match = (ctxt->type == rule->au_ctxt.type);
+ break;
+ case AUDIT_NOT_EQUAL:
+ match = (ctxt->type != rule->au_ctxt.type);
+ break;
+ }
+ break;
+ case AUDIT_SUBJ_SEN:
+ case AUDIT_SUBJ_CLR:
+ case AUDIT_OBJ_LEV_LOW:
+ case AUDIT_OBJ_LEV_HIGH:
+ level = ((field == AUDIT_SUBJ_SEN ||
+ field == AUDIT_OBJ_LEV_LOW) ?
+ &ctxt->range.level[0] : &ctxt->range.level[1]);
+ switch (op) {
+ case AUDIT_EQUAL:
+ match = mls_level_eq(&rule->au_ctxt.range.level[0],
+ level);
+ break;
+ case AUDIT_NOT_EQUAL:
+ match = !mls_level_eq(&rule->au_ctxt.range.level[0],
+ level);
+ break;
+ case AUDIT_LESS_THAN:
+ match = (mls_level_dom(&rule->au_ctxt.range.level[0],
+ level) &&
+ !mls_level_eq(&rule->au_ctxt.range.level[0],
+ level));
+ break;
+ case AUDIT_LESS_THAN_OR_EQUAL:
+ match = mls_level_dom(&rule->au_ctxt.range.level[0],
+ level);
+ break;
+ case AUDIT_GREATER_THAN:
+ match = (mls_level_dom(level,
+ &rule->au_ctxt.range.level[0]) &&
+ !mls_level_eq(level,
+ &rule->au_ctxt.range.level[0]));
+ break;
+ case AUDIT_GREATER_THAN_OR_EQUAL:
+ match = mls_level_dom(level,
+ &rule->au_ctxt.range.level[0]);
+ break;
+ }
+ }
+
+out:
+ read_unlock(&policy_rwlock);
+ return match;
+}
+
+static int (*aurule_callback)(void) = audit_update_lsm_rules;
+
+static int aurule_avc_callback(u32 event, u32 ssid, u32 tsid,
+ u16 class, u32 perms, u32 *retained)
+{
+ int err = 0;
+
+ if (event == AVC_CALLBACK_RESET && aurule_callback)
+ err = aurule_callback();
+ return err;
+}
+
+static int __init aurule_init(void)
+{
+ int err;
+
+ err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET,
+ SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);
+ if (err)
+ panic("avc_add_callback() failed, error %d\n", err);
+
+ return err;
+}
+__initcall(aurule_init);
+
+#ifdef CONFIG_NETLABEL
+/**
+ * security_netlbl_cache_add - Add an entry to the NetLabel cache
+ * @secattr: the NetLabel packet security attributes
+ * @sid: the SELinux SID
+ *
+ * Description:
+ * Attempt to cache the context in @ctx, which was derived from the packet in
+ * @skb, in the NetLabel subsystem cache. This function assumes @secattr has
+ * already been initialized.
+ *
+ */
+static void security_netlbl_cache_add(struct netlbl_lsm_secattr *secattr,
+ u32 sid)
+{
+ u32 *sid_cache;
+
+ sid_cache = kmalloc(sizeof(*sid_cache), GFP_ATOMIC);
+ if (sid_cache == NULL)
+ return;
+ secattr->cache = netlbl_secattr_cache_alloc(GFP_ATOMIC);
+ if (secattr->cache == NULL) {
+ kfree(sid_cache);
+ return;
+ }
+
+ *sid_cache = sid;
+ secattr->cache->free = kfree;
+ secattr->cache->data = sid_cache;
+ secattr->flags |= NETLBL_SECATTR_CACHE;
+}
+
+/**
+ * security_netlbl_secattr_to_sid - Convert a NetLabel secattr to a SELinux SID
+ * @secattr: the NetLabel packet security attributes
+ * @sid: the SELinux SID
+ *
+ * Description:
+ * Convert the given NetLabel security attributes in @secattr into a
+ * SELinux SID. If the @secattr field does not contain a full SELinux
+ * SID/context then use SECINITSID_NETMSG as the foundation. If possibile the
+ * 'cache' field of @secattr is set and the CACHE flag is set; this is to
+ * allow the @secattr to be used by NetLabel to cache the secattr to SID
+ * conversion for future lookups. Returns zero on success, negative values on
+ * failure.
+ *
+ */
+int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr,
+ u32 *sid)
+{
+ int rc = -EIDRM;
+ struct context *ctx;
+ struct context ctx_new;
+
+ if (!ss_initialized) {
+ *sid = SECSID_NULL;
+ return 0;
+ }
+
+ read_lock(&policy_rwlock);
+
+ if (secattr->flags & NETLBL_SECATTR_CACHE) {
+ *sid = *(u32 *)secattr->cache->data;
+ rc = 0;
+ } else if (secattr->flags & NETLBL_SECATTR_SECID) {
+ *sid = secattr->attr.secid;
+ rc = 0;
+ } else if (secattr->flags & NETLBL_SECATTR_MLS_LVL) {
+ ctx = sidtab_search(&sidtab, SECINITSID_NETMSG);
+ if (ctx == NULL)
+ goto netlbl_secattr_to_sid_return;
+
+ context_init(&ctx_new);
+ ctx_new.user = ctx->user;
+ ctx_new.role = ctx->role;
+ ctx_new.type = ctx->type;
+ mls_import_netlbl_lvl(&ctx_new, secattr);
+ if (secattr->flags & NETLBL_SECATTR_MLS_CAT) {
+ if (ebitmap_netlbl_import(&ctx_new.range.level[0].cat,
+ secattr->attr.mls.cat) != 0)
+ goto netlbl_secattr_to_sid_return;
+ memcpy(&ctx_new.range.level[1].cat,
+ &ctx_new.range.level[0].cat,
+ sizeof(ctx_new.range.level[0].cat));
+ }
+ if (mls_context_isvalid(&policydb, &ctx_new) != 1)
+ goto netlbl_secattr_to_sid_return_cleanup;
+
+ rc = sidtab_context_to_sid(&sidtab, &ctx_new, sid);
+ if (rc != 0)
+ goto netlbl_secattr_to_sid_return_cleanup;
+
+ security_netlbl_cache_add(secattr, *sid);
+
+ ebitmap_destroy(&ctx_new.range.level[0].cat);
+ } else {
+ *sid = SECSID_NULL;
+ rc = 0;
+ }
+
+netlbl_secattr_to_sid_return:
+ read_unlock(&policy_rwlock);
+ return rc;
+netlbl_secattr_to_sid_return_cleanup:
+ ebitmap_destroy(&ctx_new.range.level[0].cat);
+ goto netlbl_secattr_to_sid_return;
+}
+
+/**
+ * security_netlbl_sid_to_secattr - Convert a SELinux SID to a NetLabel secattr
+ * @sid: the SELinux SID
+ * @secattr: the NetLabel packet security attributes
+ *
+ * Description:
+ * Convert the given SELinux SID in @sid into a NetLabel security attribute.
+ * Returns zero on success, negative values on failure.
+ *
+ */
+int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr)
+{
+ int rc;
+ struct context *ctx;
+
+ if (!ss_initialized)
+ return 0;
+
+ read_lock(&policy_rwlock);
+ ctx = sidtab_search(&sidtab, sid);
+ if (ctx == NULL) {
+ rc = -ENOENT;
+ goto netlbl_sid_to_secattr_failure;
+ }
+ secattr->domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1],
+ GFP_ATOMIC);
+ if (secattr->domain == NULL) {
+ rc = -ENOMEM;
+ goto netlbl_sid_to_secattr_failure;
+ }
+ secattr->attr.secid = sid;
+ secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY | NETLBL_SECATTR_SECID;
+ mls_export_netlbl_lvl(ctx, secattr);
+ rc = mls_export_netlbl_cat(ctx, secattr);
+ if (rc != 0)
+ goto netlbl_sid_to_secattr_failure;
+ read_unlock(&policy_rwlock);
+
+ return 0;
+
+netlbl_sid_to_secattr_failure:
+ read_unlock(&policy_rwlock);
+ return rc;
+}
+#endif /* CONFIG_NETLABEL */
diff --git a/security/selinux/ss/services.h b/security/selinux/ss/services.h
new file mode 100644
index 0000000..e8d907e
--- /dev/null
+++ b/security/selinux/ss/services.h
@@ -0,0 +1,15 @@
+/*
+ * Implementation of the security services.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_SERVICES_H_
+#define _SS_SERVICES_H_
+
+#include "policydb.h"
+#include "sidtab.h"
+
+extern struct policydb policydb;
+
+#endif /* _SS_SERVICES_H_ */
+
diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c
new file mode 100644
index 0000000..e817989
--- /dev/null
+++ b/security/selinux/ss/sidtab.c
@@ -0,0 +1,278 @@
+/*
+ * Implementation of the SID table type.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include "flask.h"
+#include "security.h"
+#include "sidtab.h"
+
+#define SIDTAB_HASH(sid) \
+(sid & SIDTAB_HASH_MASK)
+
+int sidtab_init(struct sidtab *s)
+{
+ int i;
+
+ s->htable = kmalloc(sizeof(*(s->htable)) * SIDTAB_SIZE, GFP_ATOMIC);
+ if (!s->htable)
+ return -ENOMEM;
+ for (i = 0; i < SIDTAB_SIZE; i++)
+ s->htable[i] = NULL;
+ s->nel = 0;
+ s->next_sid = 1;
+ s->shutdown = 0;
+ spin_lock_init(&s->lock);
+ return 0;
+}
+
+int sidtab_insert(struct sidtab *s, u32 sid, struct context *context)
+{
+ int hvalue, rc = 0;
+ struct sidtab_node *prev, *cur, *newnode;
+
+ if (!s) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ hvalue = SIDTAB_HASH(sid);
+ prev = NULL;
+ cur = s->htable[hvalue];
+ while (cur && sid > cur->sid) {
+ prev = cur;
+ cur = cur->next;
+ }
+
+ if (cur && sid == cur->sid) {
+ rc = -EEXIST;
+ goto out;
+ }
+
+ newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC);
+ if (newnode == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ newnode->sid = sid;
+ if (context_cpy(&newnode->context, context)) {
+ kfree(newnode);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ if (prev) {
+ newnode->next = prev->next;
+ wmb();
+ prev->next = newnode;
+ } else {
+ newnode->next = s->htable[hvalue];
+ wmb();
+ s->htable[hvalue] = newnode;
+ }
+
+ s->nel++;
+ if (sid >= s->next_sid)
+ s->next_sid = sid + 1;
+out:
+ return rc;
+}
+
+static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force)
+{
+ int hvalue;
+ struct sidtab_node *cur;
+
+ if (!s)
+ return NULL;
+
+ hvalue = SIDTAB_HASH(sid);
+ cur = s->htable[hvalue];
+ while (cur && sid > cur->sid)
+ cur = cur->next;
+
+ if (force && cur && sid == cur->sid && cur->context.len)
+ return &cur->context;
+
+ if (cur == NULL || sid != cur->sid || cur->context.len) {
+ /* Remap invalid SIDs to the unlabeled SID. */
+ sid = SECINITSID_UNLABELED;
+ hvalue = SIDTAB_HASH(sid);
+ cur = s->htable[hvalue];
+ while (cur && sid > cur->sid)
+ cur = cur->next;
+ if (!cur || sid != cur->sid)
+ return NULL;
+ }
+
+ return &cur->context;
+}
+
+struct context *sidtab_search(struct sidtab *s, u32 sid)
+{
+ return sidtab_search_core(s, sid, 0);
+}
+
+struct context *sidtab_search_force(struct sidtab *s, u32 sid)
+{
+ return sidtab_search_core(s, sid, 1);
+}
+
+int sidtab_map(struct sidtab *s,
+ int (*apply) (u32 sid,
+ struct context *context,
+ void *args),
+ void *args)
+{
+ int i, rc = 0;
+ struct sidtab_node *cur;
+
+ if (!s)
+ goto out;
+
+ for (i = 0; i < SIDTAB_SIZE; i++) {
+ cur = s->htable[i];
+ while (cur) {
+ rc = apply(cur->sid, &cur->context, args);
+ if (rc)
+ goto out;
+ cur = cur->next;
+ }
+ }
+out:
+ return rc;
+}
+
+static inline u32 sidtab_search_context(struct sidtab *s,
+ struct context *context)
+{
+ int i;
+ struct sidtab_node *cur;
+
+ for (i = 0; i < SIDTAB_SIZE; i++) {
+ cur = s->htable[i];
+ while (cur) {
+ if (context_cmp(&cur->context, context))
+ return cur->sid;
+ cur = cur->next;
+ }
+ }
+ return 0;
+}
+
+int sidtab_context_to_sid(struct sidtab *s,
+ struct context *context,
+ u32 *out_sid)
+{
+ u32 sid;
+ int ret = 0;
+ unsigned long flags;
+
+ *out_sid = SECSID_NULL;
+
+ sid = sidtab_search_context(s, context);
+ if (!sid) {
+ spin_lock_irqsave(&s->lock, flags);
+ /* Rescan now that we hold the lock. */
+ sid = sidtab_search_context(s, context);
+ if (sid)
+ goto unlock_out;
+ /* No SID exists for the context. Allocate a new one. */
+ if (s->next_sid == UINT_MAX || s->shutdown) {
+ ret = -ENOMEM;
+ goto unlock_out;
+ }
+ sid = s->next_sid++;
+ if (context->len)
+ printk(KERN_INFO
+ "SELinux: Context %s is not valid (left unmapped).\n",
+ context->str);
+ ret = sidtab_insert(s, sid, context);
+ if (ret)
+ s->next_sid--;
+unlock_out:
+ spin_unlock_irqrestore(&s->lock, flags);
+ }
+
+ if (ret)
+ return ret;
+
+ *out_sid = sid;
+ return 0;
+}
+
+void sidtab_hash_eval(struct sidtab *h, char *tag)
+{
+ int i, chain_len, slots_used, max_chain_len;
+ struct sidtab_node *cur;
+
+ slots_used = 0;
+ max_chain_len = 0;
+ for (i = 0; i < SIDTAB_SIZE; i++) {
+ cur = h->htable[i];
+ if (cur) {
+ slots_used++;
+ chain_len = 0;
+ while (cur) {
+ chain_len++;
+ cur = cur->next;
+ }
+
+ if (chain_len > max_chain_len)
+ max_chain_len = chain_len;
+ }
+ }
+
+ printk(KERN_DEBUG "%s: %d entries and %d/%d buckets used, longest "
+ "chain length %d\n", tag, h->nel, slots_used, SIDTAB_SIZE,
+ max_chain_len);
+}
+
+void sidtab_destroy(struct sidtab *s)
+{
+ int i;
+ struct sidtab_node *cur, *temp;
+
+ if (!s)
+ return;
+
+ for (i = 0; i < SIDTAB_SIZE; i++) {
+ cur = s->htable[i];
+ while (cur) {
+ temp = cur;
+ cur = cur->next;
+ context_destroy(&temp->context);
+ kfree(temp);
+ }
+ s->htable[i] = NULL;
+ }
+ kfree(s->htable);
+ s->htable = NULL;
+ s->nel = 0;
+ s->next_sid = 1;
+}
+
+void sidtab_set(struct sidtab *dst, struct sidtab *src)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&src->lock, flags);
+ dst->htable = src->htable;
+ dst->nel = src->nel;
+ dst->next_sid = src->next_sid;
+ dst->shutdown = 0;
+ spin_unlock_irqrestore(&src->lock, flags);
+}
+
+void sidtab_shutdown(struct sidtab *s)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+ s->shutdown = 1;
+ spin_unlock_irqrestore(&s->lock, flags);
+}
diff --git a/security/selinux/ss/sidtab.h b/security/selinux/ss/sidtab.h
new file mode 100644
index 0000000..64ea5b1
--- /dev/null
+++ b/security/selinux/ss/sidtab.h
@@ -0,0 +1,54 @@
+/*
+ * A security identifier table (sidtab) is a hash table
+ * of security context structures indexed by SID value.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_SIDTAB_H_
+#define _SS_SIDTAB_H_
+
+#include "context.h"
+
+struct sidtab_node {
+ u32 sid; /* security identifier */
+ struct context context; /* security context structure */
+ struct sidtab_node *next;
+};
+
+#define SIDTAB_HASH_BITS 7
+#define SIDTAB_HASH_BUCKETS (1 << SIDTAB_HASH_BITS)
+#define SIDTAB_HASH_MASK (SIDTAB_HASH_BUCKETS-1)
+
+#define SIDTAB_SIZE SIDTAB_HASH_BUCKETS
+
+struct sidtab {
+ struct sidtab_node **htable;
+ unsigned int nel; /* number of elements */
+ unsigned int next_sid; /* next SID to allocate */
+ unsigned char shutdown;
+ spinlock_t lock;
+};
+
+int sidtab_init(struct sidtab *s);
+int sidtab_insert(struct sidtab *s, u32 sid, struct context *context);
+struct context *sidtab_search(struct sidtab *s, u32 sid);
+struct context *sidtab_search_force(struct sidtab *s, u32 sid);
+
+int sidtab_map(struct sidtab *s,
+ int (*apply) (u32 sid,
+ struct context *context,
+ void *args),
+ void *args);
+
+int sidtab_context_to_sid(struct sidtab *s,
+ struct context *context,
+ u32 *sid);
+
+void sidtab_hash_eval(struct sidtab *h, char *tag);
+void sidtab_destroy(struct sidtab *s);
+void sidtab_set(struct sidtab *dst, struct sidtab *src);
+void sidtab_shutdown(struct sidtab *s);
+
+#endif /* _SS_SIDTAB_H_ */
+
+
diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c
new file mode 100644
index 0000000..837658a
--- /dev/null
+++ b/security/selinux/ss/symtab.c
@@ -0,0 +1,44 @@
+/*
+ * Implementation of the symbol table type.
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include "symtab.h"
+
+static unsigned int symhash(struct hashtab *h, const void *key)
+{
+ const char *p, *keyp;
+ unsigned int size;
+ unsigned int val;
+
+ val = 0;
+ keyp = key;
+ size = strlen(keyp);
+ for (p = keyp; (p - keyp) < size; p++)
+ val = (val << 4 | (val >> (8*sizeof(unsigned int)-4))) ^ (*p);
+ return val & (h->size - 1);
+}
+
+static int symcmp(struct hashtab *h, const void *key1, const void *key2)
+{
+ const char *keyp1, *keyp2;
+
+ keyp1 = key1;
+ keyp2 = key2;
+ return strcmp(keyp1, keyp2);
+}
+
+
+int symtab_init(struct symtab *s, unsigned int size)
+{
+ s->table = hashtab_create(symhash, symcmp, size);
+ if (!s->table)
+ return -1;
+ s->nprim = 0;
+ return 0;
+}
+
diff --git a/security/selinux/ss/symtab.h b/security/selinux/ss/symtab.h
new file mode 100644
index 0000000..ca422b4
--- /dev/null
+++ b/security/selinux/ss/symtab.h
@@ -0,0 +1,23 @@
+/*
+ * A symbol table (symtab) maintains associations between symbol
+ * strings and datum values. The type of the datum values
+ * is arbitrary. The symbol table type is implemented
+ * using the hash table type (hashtab).
+ *
+ * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
+ */
+#ifndef _SS_SYMTAB_H_
+#define _SS_SYMTAB_H_
+
+#include "hashtab.h"
+
+struct symtab {
+ struct hashtab *table; /* hash table (keyed on a string) */
+ u32 nprim; /* number of primary names in table */
+};
+
+int symtab_init(struct symtab *s, unsigned int size);
+
+#endif /* _SS_SYMTAB_H_ */
+
+
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
new file mode 100644
index 0000000..8f17f54
--- /dev/null
+++ b/security/selinux/xfrm.c
@@ -0,0 +1,489 @@
+/*
+ * NSA Security-Enhanced Linux (SELinux) security module
+ *
+ * This file contains the SELinux XFRM hook function implementations.
+ *
+ * Authors: Serge Hallyn <sergeh@us.ibm.com>
+ * Trent Jaeger <jaegert@us.ibm.com>
+ *
+ * Updated: Venkat Yekkirala <vyekkirala@TrustedCS.com>
+ *
+ * Granular IPSec Associations for use in MLS environments.
+ *
+ * Copyright (C) 2005 International Business Machines Corporation
+ * Copyright (C) 2006 Trusted Computer Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+
+/*
+ * USAGE:
+ * NOTES:
+ * 1. Make sure to enable the following options in your kernel config:
+ * CONFIG_SECURITY=y
+ * CONFIG_SECURITY_NETWORK=y
+ * CONFIG_SECURITY_NETWORK_XFRM=y
+ * CONFIG_SECURITY_SELINUX=m/y
+ * ISSUES:
+ * 1. Caching packets, so they are not dropped during negotiation
+ * 2. Emulating a reasonable SO_PEERSEC across machines
+ * 3. Testing addition of sk_policy's with security context via setsockopt
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/security.h>
+#include <linux/types.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/xfrm.h>
+#include <net/xfrm.h>
+#include <net/checksum.h>
+#include <net/udp.h>
+#include <asm/atomic.h>
+
+#include "avc.h"
+#include "objsec.h"
+#include "xfrm.h"
+
+/* Labeled XFRM instance counter */
+atomic_t selinux_xfrm_refcount = ATOMIC_INIT(0);
+
+/*
+ * Returns true if an LSM/SELinux context
+ */
+static inline int selinux_authorizable_ctx(struct xfrm_sec_ctx *ctx)
+{
+ return (ctx &&
+ (ctx->ctx_doi == XFRM_SC_DOI_LSM) &&
+ (ctx->ctx_alg == XFRM_SC_ALG_SELINUX));
+}
+
+/*
+ * Returns true if the xfrm contains a security blob for SELinux
+ */
+static inline int selinux_authorizable_xfrm(struct xfrm_state *x)
+{
+ return selinux_authorizable_ctx(x->security);
+}
+
+/*
+ * LSM hook implementation that authorizes that a flow can use
+ * a xfrm policy rule.
+ */
+int selinux_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir)
+{
+ int rc;
+ u32 sel_sid;
+
+ /* Context sid is either set to label or ANY_ASSOC */
+ if (ctx) {
+ if (!selinux_authorizable_ctx(ctx))
+ return -EINVAL;
+
+ sel_sid = ctx->ctx_sid;
+ } else
+ /*
+ * All flows should be treated as polmatch'ing an
+ * otherwise applicable "non-labeled" policy. This
+ * would prevent inadvertent "leaks".
+ */
+ return 0;
+
+ rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION,
+ ASSOCIATION__POLMATCH,
+ NULL);
+
+ if (rc == -EACCES)
+ return -ESRCH;
+
+ return rc;
+}
+
+/*
+ * LSM hook implementation that authorizes that a state matches
+ * the given policy, flow combo.
+ */
+
+int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp,
+ struct flowi *fl)
+{
+ u32 state_sid;
+ int rc;
+
+ if (!xp->security)
+ if (x->security)
+ /* unlabeled policy and labeled SA can't match */
+ return 0;
+ else
+ /* unlabeled policy and unlabeled SA match all flows */
+ return 1;
+ else
+ if (!x->security)
+ /* unlabeled SA and labeled policy can't match */
+ return 0;
+ else
+ if (!selinux_authorizable_xfrm(x))
+ /* Not a SELinux-labeled SA */
+ return 0;
+
+ state_sid = x->security->ctx_sid;
+
+ if (fl->secid != state_sid)
+ return 0;
+
+ rc = avc_has_perm(fl->secid, state_sid, SECCLASS_ASSOCIATION,
+ ASSOCIATION__SENDTO,
+ NULL)? 0:1;
+
+ /*
+ * We don't need a separate SA Vs. policy polmatch check
+ * since the SA is now of the same label as the flow and
+ * a flow Vs. policy polmatch check had already happened
+ * in selinux_xfrm_policy_lookup() above.
+ */
+
+ return rc;
+}
+
+/*
+ * LSM hook implementation that checks and/or returns the xfrm sid for the
+ * incoming packet.
+ */
+
+int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall)
+{
+ struct sec_path *sp;
+
+ *sid = SECSID_NULL;
+
+ if (skb == NULL)
+ return 0;
+
+ sp = skb->sp;
+ if (sp) {
+ int i, sid_set = 0;
+
+ for (i = sp->len-1; i >= 0; i--) {
+ struct xfrm_state *x = sp->xvec[i];
+ if (selinux_authorizable_xfrm(x)) {
+ struct xfrm_sec_ctx *ctx = x->security;
+
+ if (!sid_set) {
+ *sid = ctx->ctx_sid;
+ sid_set = 1;
+
+ if (!ckall)
+ break;
+ } else if (*sid != ctx->ctx_sid)
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Security blob allocation for xfrm_policy and xfrm_state
+ * CTX does not have a meaningful value on input
+ */
+static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
+ struct xfrm_user_sec_ctx *uctx, u32 sid)
+{
+ int rc = 0;
+ struct task_security_struct *tsec = current->security;
+ struct xfrm_sec_ctx *ctx = NULL;
+ char *ctx_str = NULL;
+ u32 str_len;
+
+ BUG_ON(uctx && sid);
+
+ if (!uctx)
+ goto not_from_user;
+
+ if (uctx->ctx_doi != XFRM_SC_ALG_SELINUX)
+ return -EINVAL;
+
+ str_len = uctx->ctx_len;
+ if (str_len >= PAGE_SIZE)
+ return -ENOMEM;
+
+ *ctxp = ctx = kmalloc(sizeof(*ctx) +
+ str_len + 1,
+ GFP_KERNEL);
+
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->ctx_doi = uctx->ctx_doi;
+ ctx->ctx_len = str_len;
+ ctx->ctx_alg = uctx->ctx_alg;
+
+ memcpy(ctx->ctx_str,
+ uctx+1,
+ str_len);
+ ctx->ctx_str[str_len] = 0;
+ rc = security_context_to_sid(ctx->ctx_str,
+ str_len,
+ &ctx->ctx_sid);
+
+ if (rc)
+ goto out;
+
+ /*
+ * Does the subject have permission to set security context?
+ */
+ rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+ SECCLASS_ASSOCIATION,
+ ASSOCIATION__SETCONTEXT, NULL);
+ if (rc)
+ goto out;
+
+ return rc;
+
+not_from_user:
+ rc = security_sid_to_context(sid, &ctx_str, &str_len);
+ if (rc)
+ goto out;
+
+ *ctxp = ctx = kmalloc(sizeof(*ctx) +
+ str_len,
+ GFP_ATOMIC);
+
+ if (!ctx) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ ctx->ctx_doi = XFRM_SC_DOI_LSM;
+ ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
+ ctx->ctx_sid = sid;
+ ctx->ctx_len = str_len;
+ memcpy(ctx->ctx_str,
+ ctx_str,
+ str_len);
+
+ goto out2;
+
+out:
+ *ctxp = NULL;
+ kfree(ctx);
+out2:
+ kfree(ctx_str);
+ return rc;
+}
+
+/*
+ * LSM hook implementation that allocs and transfers uctx spec to
+ * xfrm_policy.
+ */
+int selinux_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
+ struct xfrm_user_sec_ctx *uctx)
+{
+ int err;
+
+ BUG_ON(!uctx);
+
+ err = selinux_xfrm_sec_ctx_alloc(ctxp, uctx, 0);
+ if (err == 0)
+ atomic_inc(&selinux_xfrm_refcount);
+
+ return err;
+}
+
+
+/*
+ * LSM hook implementation that copies security data structure from old to
+ * new for policy cloning.
+ */
+int selinux_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
+ struct xfrm_sec_ctx **new_ctxp)
+{
+ struct xfrm_sec_ctx *new_ctx;
+
+ if (old_ctx) {
+ new_ctx = kmalloc(sizeof(*old_ctx) + old_ctx->ctx_len,
+ GFP_KERNEL);
+ if (!new_ctx)
+ return -ENOMEM;
+
+ memcpy(new_ctx, old_ctx, sizeof(*new_ctx));
+ memcpy(new_ctx->ctx_str, old_ctx->ctx_str, new_ctx->ctx_len);
+ *new_ctxp = new_ctx;
+ }
+ return 0;
+}
+
+/*
+ * LSM hook implementation that frees xfrm_sec_ctx security information.
+ */
+void selinux_xfrm_policy_free(struct xfrm_sec_ctx *ctx)
+{
+ kfree(ctx);
+}
+
+/*
+ * LSM hook implementation that authorizes deletion of labeled policies.
+ */
+int selinux_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
+{
+ struct task_security_struct *tsec = current->security;
+ int rc = 0;
+
+ if (ctx) {
+ rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+ SECCLASS_ASSOCIATION,
+ ASSOCIATION__SETCONTEXT, NULL);
+ if (rc == 0)
+ atomic_dec(&selinux_xfrm_refcount);
+ }
+
+ return rc;
+}
+
+/*
+ * LSM hook implementation that allocs and transfers sec_ctx spec to
+ * xfrm_state.
+ */
+int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx,
+ u32 secid)
+{
+ int err;
+
+ BUG_ON(!x);
+
+ err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, secid);
+ if (err == 0)
+ atomic_inc(&selinux_xfrm_refcount);
+ return err;
+}
+
+/*
+ * LSM hook implementation that frees xfrm_state security information.
+ */
+void selinux_xfrm_state_free(struct xfrm_state *x)
+{
+ struct xfrm_sec_ctx *ctx = x->security;
+ kfree(ctx);
+}
+
+ /*
+ * LSM hook implementation that authorizes deletion of labeled SAs.
+ */
+int selinux_xfrm_state_delete(struct xfrm_state *x)
+{
+ struct task_security_struct *tsec = current->security;
+ struct xfrm_sec_ctx *ctx = x->security;
+ int rc = 0;
+
+ if (ctx) {
+ rc = avc_has_perm(tsec->sid, ctx->ctx_sid,
+ SECCLASS_ASSOCIATION,
+ ASSOCIATION__SETCONTEXT, NULL);
+ if (rc == 0)
+ atomic_dec(&selinux_xfrm_refcount);
+ }
+
+ return rc;
+}
+
+/*
+ * LSM hook that controls access to unlabelled packets. If
+ * a xfrm_state is authorizable (defined by macro) then it was
+ * already authorized by the IPSec process. If not, then
+ * we need to check for unlabelled access since this may not have
+ * gone thru the IPSec process.
+ */
+int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
+ struct avc_audit_data *ad)
+{
+ int i, rc = 0;
+ struct sec_path *sp;
+ u32 sel_sid = SECINITSID_UNLABELED;
+
+ sp = skb->sp;
+
+ if (sp) {
+ for (i = 0; i < sp->len; i++) {
+ struct xfrm_state *x = sp->xvec[i];
+
+ if (x && selinux_authorizable_xfrm(x)) {
+ struct xfrm_sec_ctx *ctx = x->security;
+ sel_sid = ctx->ctx_sid;
+ break;
+ }
+ }
+ }
+
+ /*
+ * This check even when there's no association involved is
+ * intended, according to Trent Jaeger, to make sure a
+ * process can't engage in non-ipsec communication unless
+ * explicitly allowed by policy.
+ */
+
+ rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION,
+ ASSOCIATION__RECVFROM, ad);
+
+ return rc;
+}
+
+/*
+ * POSTROUTE_LAST hook's XFRM processing:
+ * If we have no security association, then we need to determine
+ * whether the socket is allowed to send to an unlabelled destination.
+ * If we do have a authorizable security association, then it has already been
+ * checked in the selinux_xfrm_state_pol_flow_match hook above.
+ */
+int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
+ struct avc_audit_data *ad, u8 proto)
+{
+ struct dst_entry *dst;
+ int rc = 0;
+
+ dst = skb->dst;
+
+ if (dst) {
+ struct dst_entry *dst_test;
+
+ for (dst_test = dst; dst_test != NULL;
+ dst_test = dst_test->child) {
+ struct xfrm_state *x = dst_test->xfrm;
+
+ if (x && selinux_authorizable_xfrm(x))
+ goto out;
+ }
+ }
+
+ switch (proto) {
+ case IPPROTO_AH:
+ case IPPROTO_ESP:
+ case IPPROTO_COMP:
+ /*
+ * We should have already seen this packet once before
+ * it underwent xfrm(s). No need to subject it to the
+ * unlabeled check.
+ */
+ goto out;
+ default:
+ break;
+ }
+
+ /*
+ * This check even when there's no association involved is
+ * intended, according to Trent Jaeger, to make sure a
+ * process can't engage in non-ipsec communication unless
+ * explicitly allowed by policy.
+ */
+
+ rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
+ ASSOCIATION__SENDTO, ad);
+out:
+ return rc;
+}
diff --git a/security/smack/Kconfig b/security/smack/Kconfig
new file mode 100644
index 0000000..603b087
--- /dev/null
+++ b/security/smack/Kconfig
@@ -0,0 +1,10 @@
+config SECURITY_SMACK
+ bool "Simplified Mandatory Access Control Kernel Support"
+ depends on NETLABEL && SECURITY_NETWORK
+ default n
+ help
+ This selects the Simplified Mandatory Access Control Kernel.
+ Smack is useful for sensitivity, integrity, and a variety
+ of other mandatory security schemes.
+ If you are unsure how to answer this question, answer N.
+
diff --git a/security/smack/Makefile b/security/smack/Makefile
new file mode 100644
index 0000000..67a63aa
--- /dev/null
+++ b/security/smack/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the SMACK LSM
+#
+
+obj-$(CONFIG_SECURITY_SMACK) := smack.o
+
+smack-y := smack_lsm.o smack_access.o smackfs.o
diff --git a/security/smack/smack.h b/security/smack/smack.h
new file mode 100644
index 0000000..31dce55
--- /dev/null
+++ b/security/smack/smack.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * Author:
+ * Casey Schaufler <casey@schaufler-ca.com>
+ *
+ */
+
+#ifndef _SECURITY_SMACK_H
+#define _SECURITY_SMACK_H
+
+#include <linux/capability.h>
+#include <linux/spinlock.h>
+#include <linux/security.h>
+#include <net/netlabel.h>
+
+/*
+ * Why 23? CIPSO is constrained to 30, so a 32 byte buffer is
+ * bigger than can be used, and 24 is the next lower multiple
+ * of 8, and there are too many issues if there isn't space set
+ * aside for the terminating null byte.
+ */
+#define SMK_MAXLEN 23
+#define SMK_LABELLEN (SMK_MAXLEN+1)
+
+struct superblock_smack {
+ char *smk_root;
+ char *smk_floor;
+ char *smk_hat;
+ char *smk_default;
+ int smk_initialized;
+ spinlock_t smk_sblock; /* for initialization */
+};
+
+struct socket_smack {
+ char *smk_out; /* outbound label */
+ char *smk_in; /* inbound label */
+ char smk_packet[SMK_LABELLEN]; /* TCP peer label */
+};
+
+/*
+ * Inode smack data
+ */
+struct inode_smack {
+ char *smk_inode; /* label of the fso */
+ struct mutex smk_lock; /* initialization lock */
+ int smk_flags; /* smack inode flags */
+};
+
+#define SMK_INODE_INSTANT 0x01 /* inode is instantiated */
+
+/*
+ * A label access rule.
+ */
+struct smack_rule {
+ char *smk_subject;
+ char *smk_object;
+ int smk_access;
+};
+
+/*
+ * An entry in the table of permitted label accesses.
+ */
+struct smk_list_entry {
+ struct smk_list_entry *smk_next;
+ struct smack_rule smk_rule;
+};
+
+/*
+ * An entry in the table mapping smack values to
+ * CIPSO level/category-set values.
+ */
+struct smack_cipso {
+ int smk_level;
+ char smk_catset[SMK_LABELLEN];
+};
+
+/*
+ * This is the repository for labels seen so that it is
+ * not necessary to keep allocating tiny chuncks of memory
+ * and so that they can be shared.
+ *
+ * Labels are never modified in place. Anytime a label
+ * is imported (e.g. xattrset on a file) the list is checked
+ * for it and it is added if it doesn't exist. The address
+ * is passed out in either case. Entries are added, but
+ * never deleted.
+ *
+ * Since labels are hanging around anyway it doesn't
+ * hurt to maintain a secid for those awkward situations
+ * where kernel components that ought to use LSM independent
+ * interfaces don't. The secid should go away when all of
+ * these components have been repaired.
+ *
+ * If there is a cipso value associated with the label it
+ * gets stored here, too. This will most likely be rare as
+ * the cipso direct mapping in used internally.
+ */
+struct smack_known {
+ struct smack_known *smk_next;
+ char smk_known[SMK_LABELLEN];
+ u32 smk_secid;
+ struct smack_cipso *smk_cipso;
+ spinlock_t smk_cipsolock; /* for changing cipso map */
+};
+
+/*
+ * Mount options
+ */
+#define SMK_FSDEFAULT "smackfsdef="
+#define SMK_FSFLOOR "smackfsfloor="
+#define SMK_FSHAT "smackfshat="
+#define SMK_FSROOT "smackfsroot="
+
+/*
+ * xattr names
+ */
+#define XATTR_SMACK_SUFFIX "SMACK64"
+#define XATTR_SMACK_IPIN "SMACK64IPIN"
+#define XATTR_SMACK_IPOUT "SMACK64IPOUT"
+#define XATTR_NAME_SMACK XATTR_SECURITY_PREFIX XATTR_SMACK_SUFFIX
+#define XATTR_NAME_SMACKIPIN XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN
+#define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT
+
+/*
+ * smackfs macic number
+ */
+#define SMACK_MAGIC 0x43415d53 /* "SMAC" */
+
+/*
+ * A limit on the number of entries in the lists
+ * makes some of the list administration easier.
+ */
+#define SMACK_LIST_MAX 10000
+
+/*
+ * CIPSO defaults.
+ */
+#define SMACK_CIPSO_DOI_DEFAULT 3 /* Historical */
+#define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */
+#define SMACK_CIPSO_MAXCATVAL 63 /* Bigger gets harder */
+#define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */
+#define SMACK_CIPSO_MAXCATNUM 239 /* CIPSO 2.2 standard */
+
+/*
+ * Just to make the common cases easier to deal with
+ */
+#define MAY_ANY (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+#define MAY_ANYREAD (MAY_READ | MAY_EXEC)
+#define MAY_ANYWRITE (MAY_WRITE | MAY_APPEND)
+#define MAY_READWRITE (MAY_READ | MAY_WRITE)
+#define MAY_NOT 0
+
+/*
+ * These functions are in smack_lsm.c
+ */
+struct inode_smack *new_inode_smack(char *);
+
+/*
+ * These functions are in smack_access.c
+ */
+int smk_access(char *, char *, int);
+int smk_curacc(char *, u32);
+int smack_to_cipso(const char *, struct smack_cipso *);
+void smack_from_cipso(u32, char *, char *);
+char *smack_from_secid(const u32);
+char *smk_import(const char *, int);
+struct smack_known *smk_import_entry(const char *, int);
+u32 smack_to_secid(const char *);
+
+/*
+ * Shared data.
+ */
+extern int smack_cipso_direct;
+extern int smack_net_nltype;
+extern char *smack_net_ambient;
+extern char *smack_onlycap;
+
+extern struct smack_known *smack_known;
+extern struct smack_known smack_known_floor;
+extern struct smack_known smack_known_hat;
+extern struct smack_known smack_known_huh;
+extern struct smack_known smack_known_invalid;
+extern struct smack_known smack_known_star;
+extern struct smack_known smack_known_unset;
+
+extern struct smk_list_entry *smack_list;
+extern struct security_operations smack_ops;
+
+/*
+ * Stricly for CIPSO level manipulation.
+ * Set the category bit number in a smack label sized buffer.
+ */
+static inline void smack_catset_bit(int cat, char *catsetp)
+{
+ if (cat > SMK_LABELLEN * 8)
+ return;
+
+ catsetp[(cat - 1) / 8] |= 0x80 >> ((cat - 1) % 8);
+}
+
+/*
+ * Present a pointer to the smack label in an inode blob.
+ */
+static inline char *smk_of_inode(const struct inode *isp)
+{
+ struct inode_smack *sip = isp->i_security;
+ return sip->smk_inode;
+}
+
+#endif /* _SECURITY_SMACK_H */
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
new file mode 100644
index 0000000..79ff21e
--- /dev/null
+++ b/security/smack/smack_access.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * Author:
+ * Casey Schaufler <casey@schaufler-ca.com>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include "smack.h"
+
+struct smack_known smack_known_unset = {
+ .smk_next = NULL,
+ .smk_known = "UNSET",
+ .smk_secid = 1,
+ .smk_cipso = NULL,
+};
+
+struct smack_known smack_known_huh = {
+ .smk_next = &smack_known_unset,
+ .smk_known = "?",
+ .smk_secid = 2,
+ .smk_cipso = NULL,
+};
+
+struct smack_known smack_known_hat = {
+ .smk_next = &smack_known_huh,
+ .smk_known = "^",
+ .smk_secid = 3,
+ .smk_cipso = NULL,
+};
+
+struct smack_known smack_known_star = {
+ .smk_next = &smack_known_hat,
+ .smk_known = "*",
+ .smk_secid = 4,
+ .smk_cipso = NULL,
+};
+
+struct smack_known smack_known_floor = {
+ .smk_next = &smack_known_star,
+ .smk_known = "_",
+ .smk_secid = 5,
+ .smk_cipso = NULL,
+};
+
+struct smack_known smack_known_invalid = {
+ .smk_next = &smack_known_floor,
+ .smk_known = "",
+ .smk_secid = 6,
+ .smk_cipso = NULL,
+};
+
+struct smack_known *smack_known = &smack_known_invalid;
+
+/*
+ * The initial value needs to be bigger than any of the
+ * known values above.
+ */
+static u32 smack_next_secid = 10;
+
+/**
+ * smk_access - determine if a subject has a specific access to an object
+ * @subject_label: a pointer to the subject's Smack label
+ * @object_label: a pointer to the object's Smack label
+ * @request: the access requested, in "MAY" format
+ *
+ * This function looks up the subject/object pair in the
+ * access rule list and returns 0 if the access is permitted,
+ * non zero otherwise.
+ *
+ * Even though Smack labels are usually shared on smack_list
+ * labels that come in off the network can't be imported
+ * and added to the list for locking reasons.
+ *
+ * Therefore, it is necessary to check the contents of the labels,
+ * not just the pointer values. Of course, in most cases the labels
+ * will be on the list, so checking the pointers may be a worthwhile
+ * optimization.
+ */
+int smk_access(char *subject_label, char *object_label, int request)
+{
+ u32 may = MAY_NOT;
+ struct smk_list_entry *sp;
+ struct smack_rule *srp;
+
+ /*
+ * Hardcoded comparisons.
+ *
+ * A star subject can't access any object.
+ */
+ if (subject_label == smack_known_star.smk_known ||
+ strcmp(subject_label, smack_known_star.smk_known) == 0)
+ return -EACCES;
+ /*
+ * A star object can be accessed by any subject.
+ */
+ if (object_label == smack_known_star.smk_known ||
+ strcmp(object_label, smack_known_star.smk_known) == 0)
+ return 0;
+ /*
+ * An object can be accessed in any way by a subject
+ * with the same label.
+ */
+ if (subject_label == object_label ||
+ strcmp(subject_label, object_label) == 0)
+ return 0;
+ /*
+ * A hat subject can read any object.
+ * A floor object can be read by any subject.
+ */
+ if ((request & MAY_ANYREAD) == request) {
+ if (object_label == smack_known_floor.smk_known ||
+ strcmp(object_label, smack_known_floor.smk_known) == 0)
+ return 0;
+ if (subject_label == smack_known_hat.smk_known ||
+ strcmp(subject_label, smack_known_hat.smk_known) == 0)
+ return 0;
+ }
+ /*
+ * Beyond here an explicit relationship is required.
+ * If the requested access is contained in the available
+ * access (e.g. read is included in readwrite) it's
+ * good.
+ */
+ for (sp = smack_list; sp != NULL; sp = sp->smk_next) {
+ srp = &sp->smk_rule;
+
+ if (srp->smk_subject == subject_label ||
+ strcmp(srp->smk_subject, subject_label) == 0) {
+ if (srp->smk_object == object_label ||
+ strcmp(srp->smk_object, object_label) == 0) {
+ may = srp->smk_access;
+ break;
+ }
+ }
+ }
+ /*
+ * This is a bit map operation.
+ */
+ if ((request & may) == request)
+ return 0;
+
+ return -EACCES;
+}
+
+/**
+ * smk_curacc - determine if current has a specific access to an object
+ * @object_label: a pointer to the object's Smack label
+ * @request: the access requested, in "MAY" format
+ *
+ * This function checks the current subject label/object label pair
+ * in the access rule list and returns 0 if the access is permitted,
+ * non zero otherwise. It allows that current may have the capability
+ * to override the rules.
+ */
+int smk_curacc(char *obj_label, u32 mode)
+{
+ int rc;
+
+ rc = smk_access(current->security, obj_label, mode);
+ if (rc == 0)
+ return 0;
+
+ /*
+ * Return if a specific label has been designated as the
+ * only one that gets privilege and current does not
+ * have that label.
+ */
+ if (smack_onlycap != NULL && smack_onlycap != current->security)
+ return rc;
+
+ if (capable(CAP_MAC_OVERRIDE))
+ return 0;
+
+ return rc;
+}
+
+static DEFINE_MUTEX(smack_known_lock);
+
+/**
+ * smk_import_entry - import a label, return the list entry
+ * @string: a text string that might be a Smack label
+ * @len: the maximum size, or zero if it is NULL terminated.
+ *
+ * Returns a pointer to the entry in the label list that
+ * matches the passed string, adding it if necessary.
+ */
+struct smack_known *smk_import_entry(const char *string, int len)
+{
+ struct smack_known *skp;
+ char smack[SMK_LABELLEN];
+ int found;
+ int i;
+
+ if (len <= 0 || len > SMK_MAXLEN)
+ len = SMK_MAXLEN;
+
+ for (i = 0, found = 0; i < SMK_LABELLEN; i++) {
+ if (found)
+ smack[i] = '\0';
+ else if (i >= len || string[i] > '~' || string[i] <= ' ' ||
+ string[i] == '/') {
+ smack[i] = '\0';
+ found = 1;
+ } else
+ smack[i] = string[i];
+ }
+
+ if (smack[0] == '\0')
+ return NULL;
+
+ mutex_lock(&smack_known_lock);
+
+ for (skp = smack_known; skp != NULL; skp = skp->smk_next)
+ if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0)
+ break;
+
+ if (skp == NULL) {
+ skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL);
+ if (skp != NULL) {
+ skp->smk_next = smack_known;
+ strncpy(skp->smk_known, smack, SMK_MAXLEN);
+ skp->smk_secid = smack_next_secid++;
+ skp->smk_cipso = NULL;
+ spin_lock_init(&skp->smk_cipsolock);
+ /*
+ * Make sure that the entry is actually
+ * filled before putting it on the list.
+ */
+ smp_mb();
+ smack_known = skp;
+ }
+ }
+
+ mutex_unlock(&smack_known_lock);
+
+ return skp;
+}
+
+/**
+ * smk_import - import a smack label
+ * @string: a text string that might be a Smack label
+ * @len: the maximum size, or zero if it is NULL terminated.
+ *
+ * Returns a pointer to the label in the label list that
+ * matches the passed string, adding it if necessary.
+ */
+char *smk_import(const char *string, int len)
+{
+ struct smack_known *skp;
+
+ skp = smk_import_entry(string, len);
+ if (skp == NULL)
+ return NULL;
+ return skp->smk_known;
+}
+
+/**
+ * smack_from_secid - find the Smack label associated with a secid
+ * @secid: an integer that might be associated with a Smack label
+ *
+ * Returns a pointer to the appropraite Smack label if there is one,
+ * otherwise a pointer to the invalid Smack label.
+ */
+char *smack_from_secid(const u32 secid)
+{
+ struct smack_known *skp;
+
+ for (skp = smack_known; skp != NULL; skp = skp->smk_next)
+ if (skp->smk_secid == secid)
+ return skp->smk_known;
+
+ /*
+ * If we got this far someone asked for the translation
+ * of a secid that is not on the list.
+ */
+ return smack_known_invalid.smk_known;
+}
+
+/**
+ * smack_to_secid - find the secid associated with a Smack label
+ * @smack: the Smack label
+ *
+ * Returns the appropriate secid if there is one,
+ * otherwise 0
+ */
+u32 smack_to_secid(const char *smack)
+{
+ struct smack_known *skp;
+
+ for (skp = smack_known; skp != NULL; skp = skp->smk_next)
+ if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0)
+ return skp->smk_secid;
+ return 0;
+}
+
+/**
+ * smack_from_cipso - find the Smack label associated with a CIPSO option
+ * @level: Bell & LaPadula level from the network
+ * @cp: Bell & LaPadula categories from the network
+ * @result: where to put the Smack value
+ *
+ * This is a simple lookup in the label table.
+ *
+ * This is an odd duck as far as smack handling goes in that
+ * it sends back a copy of the smack label rather than a pointer
+ * to the master list. This is done because it is possible for
+ * a foreign host to send a smack label that is new to this
+ * machine and hence not on the list. That would not be an
+ * issue except that adding an entry to the master list can't
+ * be done at that point.
+ */
+void smack_from_cipso(u32 level, char *cp, char *result)
+{
+ struct smack_known *kp;
+ char *final = NULL;
+
+ for (kp = smack_known; final == NULL && kp != NULL; kp = kp->smk_next) {
+ if (kp->smk_cipso == NULL)
+ continue;
+
+ spin_lock_bh(&kp->smk_cipsolock);
+
+ if (kp->smk_cipso->smk_level == level &&
+ memcmp(kp->smk_cipso->smk_catset, cp, SMK_LABELLEN) == 0)
+ final = kp->smk_known;
+
+ spin_unlock_bh(&kp->smk_cipsolock);
+ }
+ if (final == NULL)
+ final = smack_known_huh.smk_known;
+ strncpy(result, final, SMK_MAXLEN);
+ return;
+}
+
+/**
+ * smack_to_cipso - find the CIPSO option to go with a Smack label
+ * @smack: a pointer to the smack label in question
+ * @cp: where to put the result
+ *
+ * Returns zero if a value is available, non-zero otherwise.
+ */
+int smack_to_cipso(const char *smack, struct smack_cipso *cp)
+{
+ struct smack_known *kp;
+
+ for (kp = smack_known; kp != NULL; kp = kp->smk_next)
+ if (kp->smk_known == smack ||
+ strcmp(kp->smk_known, smack) == 0)
+ break;
+
+ if (kp == NULL || kp->smk_cipso == NULL)
+ return -ENOENT;
+
+ memcpy(cp, kp->smk_cipso, sizeof(struct smack_cipso));
+ return 0;
+}
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
new file mode 100644
index 0000000..6e2dc0b
--- /dev/null
+++ b/security/smack/smack_lsm.c
@@ -0,0 +1,2754 @@
+/*
+ * Simplified MAC Kernel (smack) security module
+ *
+ * This file contains the smack hook function implementations.
+ *
+ * Author:
+ * Casey Schaufler <casey@schaufler-ca.com>
+ *
+ * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/xattr.h>
+#include <linux/pagemap.h>
+#include <linux/mount.h>
+#include <linux/stat.h>
+#include <linux/ext2_fs.h>
+#include <linux/kd.h>
+#include <asm/ioctls.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/mutex.h>
+#include <linux/pipe_fs_i.h>
+#include <net/netlabel.h>
+#include <net/cipso_ipv4.h>
+#include <linux/audit.h>
+
+#include "smack.h"
+
+/*
+ * I hope these are the hokeyist lines of code in the module. Casey.
+ */
+#define DEVPTS_SUPER_MAGIC 0x1cd1
+#define SOCKFS_MAGIC 0x534F434B
+#define TMPFS_MAGIC 0x01021994
+
+/**
+ * smk_fetch - Fetch the smack label from a file.
+ * @ip: a pointer to the inode
+ * @dp: a pointer to the dentry
+ *
+ * Returns a pointer to the master list entry for the Smack label
+ * or NULL if there was no label to fetch.
+ */
+static char *smk_fetch(struct inode *ip, struct dentry *dp)
+{
+ int rc;
+ char in[SMK_LABELLEN];
+
+ if (ip->i_op->getxattr == NULL)
+ return NULL;
+
+ rc = ip->i_op->getxattr(dp, XATTR_NAME_SMACK, in, SMK_LABELLEN);
+ if (rc < 0)
+ return NULL;
+
+ return smk_import(in, rc);
+}
+
+/**
+ * new_inode_smack - allocate an inode security blob
+ * @smack: a pointer to the Smack label to use in the blob
+ *
+ * Returns the new blob or NULL if there's no memory available
+ */
+struct inode_smack *new_inode_smack(char *smack)
+{
+ struct inode_smack *isp;
+
+ isp = kzalloc(sizeof(struct inode_smack), GFP_KERNEL);
+ if (isp == NULL)
+ return NULL;
+
+ isp->smk_inode = smack;
+ isp->smk_flags = 0;
+ mutex_init(&isp->smk_lock);
+
+ return isp;
+}
+
+/*
+ * LSM hooks.
+ * We he, that is fun!
+ */
+
+/**
+ * smack_ptrace_may_access - Smack approval on PTRACE_ATTACH
+ * @ctp: child task pointer
+ *
+ * Returns 0 if access is OK, an error code otherwise
+ *
+ * Do the capability checks, and require read and write.
+ */
+static int smack_ptrace_may_access(struct task_struct *ctp, unsigned int mode)
+{
+ int rc;
+
+ rc = cap_ptrace_may_access(ctp, mode);
+ if (rc != 0)
+ return rc;
+
+ rc = smk_access(current->security, ctp->security, MAY_READWRITE);
+ if (rc != 0 && capable(CAP_MAC_OVERRIDE))
+ return 0;
+ return rc;
+}
+
+/**
+ * smack_ptrace_traceme - Smack approval on PTRACE_TRACEME
+ * @ptp: parent task pointer
+ *
+ * Returns 0 if access is OK, an error code otherwise
+ *
+ * Do the capability checks, and require read and write.
+ */
+static int smack_ptrace_traceme(struct task_struct *ptp)
+{
+ int rc;
+
+ rc = cap_ptrace_traceme(ptp);
+ if (rc != 0)
+ return rc;
+
+ rc = smk_access(ptp->security, current->security, MAY_READWRITE);
+ if (rc != 0 && has_capability(ptp, CAP_MAC_OVERRIDE))
+ return 0;
+ return rc;
+}
+
+/**
+ * smack_syslog - Smack approval on syslog
+ * @type: message type
+ *
+ * Require that the task has the floor label
+ *
+ * Returns 0 on success, error code otherwise.
+ */
+static int smack_syslog(int type)
+{
+ int rc;
+ char *sp = current->security;
+
+ rc = cap_syslog(type);
+ if (rc != 0)
+ return rc;
+
+ if (capable(CAP_MAC_OVERRIDE))
+ return 0;
+
+ if (sp != smack_known_floor.smk_known)
+ rc = -EACCES;
+
+ return rc;
+}
+
+
+/*
+ * Superblock Hooks.
+ */
+
+/**
+ * smack_sb_alloc_security - allocate a superblock blob
+ * @sb: the superblock getting the blob
+ *
+ * Returns 0 on success or -ENOMEM on error.
+ */
+static int smack_sb_alloc_security(struct super_block *sb)
+{
+ struct superblock_smack *sbsp;
+
+ sbsp = kzalloc(sizeof(struct superblock_smack), GFP_KERNEL);
+
+ if (sbsp == NULL)
+ return -ENOMEM;
+
+ sbsp->smk_root = smack_known_floor.smk_known;
+ sbsp->smk_default = smack_known_floor.smk_known;
+ sbsp->smk_floor = smack_known_floor.smk_known;
+ sbsp->smk_hat = smack_known_hat.smk_known;
+ sbsp->smk_initialized = 0;
+ spin_lock_init(&sbsp->smk_sblock);
+
+ sb->s_security = sbsp;
+
+ return 0;
+}
+
+/**
+ * smack_sb_free_security - free a superblock blob
+ * @sb: the superblock getting the blob
+ *
+ */
+static void smack_sb_free_security(struct super_block *sb)
+{
+ kfree(sb->s_security);
+ sb->s_security = NULL;
+}
+
+/**
+ * smack_sb_copy_data - copy mount options data for processing
+ * @type: file system type
+ * @orig: where to start
+ * @smackopts
+ *
+ * Returns 0 on success or -ENOMEM on error.
+ *
+ * Copy the Smack specific mount options out of the mount
+ * options list.
+ */
+static int smack_sb_copy_data(char *orig, char *smackopts)
+{
+ char *cp, *commap, *otheropts, *dp;
+
+ otheropts = (char *)get_zeroed_page(GFP_KERNEL);
+ if (otheropts == NULL)
+ return -ENOMEM;
+
+ for (cp = orig, commap = orig; commap != NULL; cp = commap + 1) {
+ if (strstr(cp, SMK_FSDEFAULT) == cp)
+ dp = smackopts;
+ else if (strstr(cp, SMK_FSFLOOR) == cp)
+ dp = smackopts;
+ else if (strstr(cp, SMK_FSHAT) == cp)
+ dp = smackopts;
+ else if (strstr(cp, SMK_FSROOT) == cp)
+ dp = smackopts;
+ else
+ dp = otheropts;
+
+ commap = strchr(cp, ',');
+ if (commap != NULL)
+ *commap = '\0';
+
+ if (*dp != '\0')
+ strcat(dp, ",");
+ strcat(dp, cp);
+ }
+
+ strcpy(orig, otheropts);
+ free_page((unsigned long)otheropts);
+
+ return 0;
+}
+
+/**
+ * smack_sb_kern_mount - Smack specific mount processing
+ * @sb: the file system superblock
+ * @data: the smack mount options
+ *
+ * Returns 0 on success, an error code on failure
+ */
+static int smack_sb_kern_mount(struct super_block *sb, void *data)
+{
+ struct dentry *root = sb->s_root;
+ struct inode *inode = root->d_inode;
+ struct superblock_smack *sp = sb->s_security;
+ struct inode_smack *isp;
+ char *op;
+ char *commap;
+ char *nsp;
+
+ spin_lock(&sp->smk_sblock);
+ if (sp->smk_initialized != 0) {
+ spin_unlock(&sp->smk_sblock);
+ return 0;
+ }
+ sp->smk_initialized = 1;
+ spin_unlock(&sp->smk_sblock);
+
+ for (op = data; op != NULL; op = commap) {
+ commap = strchr(op, ',');
+ if (commap != NULL)
+ *commap++ = '\0';
+
+ if (strncmp(op, SMK_FSHAT, strlen(SMK_FSHAT)) == 0) {
+ op += strlen(SMK_FSHAT);
+ nsp = smk_import(op, 0);
+ if (nsp != NULL)
+ sp->smk_hat = nsp;
+ } else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) {
+ op += strlen(SMK_FSFLOOR);
+ nsp = smk_import(op, 0);
+ if (nsp != NULL)
+ sp->smk_floor = nsp;
+ } else if (strncmp(op, SMK_FSDEFAULT,
+ strlen(SMK_FSDEFAULT)) == 0) {
+ op += strlen(SMK_FSDEFAULT);
+ nsp = smk_import(op, 0);
+ if (nsp != NULL)
+ sp->smk_default = nsp;
+ } else if (strncmp(op, SMK_FSROOT, strlen(SMK_FSROOT)) == 0) {
+ op += strlen(SMK_FSROOT);
+ nsp = smk_import(op, 0);
+ if (nsp != NULL)
+ sp->smk_root = nsp;
+ }
+ }
+
+ /*
+ * Initialize the root inode.
+ */
+ isp = inode->i_security;
+ if (isp == NULL)
+ inode->i_security = new_inode_smack(sp->smk_root);
+ else
+ isp->smk_inode = sp->smk_root;
+
+ return 0;
+}
+
+/**
+ * smack_sb_statfs - Smack check on statfs
+ * @dentry: identifies the file system in question
+ *
+ * Returns 0 if current can read the floor of the filesystem,
+ * and error code otherwise
+ */
+static int smack_sb_statfs(struct dentry *dentry)
+{
+ struct superblock_smack *sbp = dentry->d_sb->s_security;
+
+ return smk_curacc(sbp->smk_floor, MAY_READ);
+}
+
+/**
+ * smack_sb_mount - Smack check for mounting
+ * @dev_name: unused
+ * @nd: mount point
+ * @type: unused
+ * @flags: unused
+ * @data: unused
+ *
+ * Returns 0 if current can write the floor of the filesystem
+ * being mounted on, an error code otherwise.
+ */
+static int smack_sb_mount(char *dev_name, struct path *path,
+ char *type, unsigned long flags, void *data)
+{
+ struct superblock_smack *sbp = path->mnt->mnt_sb->s_security;
+
+ return smk_curacc(sbp->smk_floor, MAY_WRITE);
+}
+
+/**
+ * smack_sb_umount - Smack check for unmounting
+ * @mnt: file system to unmount
+ * @flags: unused
+ *
+ * Returns 0 if current can write the floor of the filesystem
+ * being unmounted, an error code otherwise.
+ */
+static int smack_sb_umount(struct vfsmount *mnt, int flags)
+{
+ struct superblock_smack *sbp;
+
+ sbp = mnt->mnt_sb->s_security;
+
+ return smk_curacc(sbp->smk_floor, MAY_WRITE);
+}
+
+/*
+ * Inode hooks
+ */
+
+/**
+ * smack_inode_alloc_security - allocate an inode blob
+ * @inode - the inode in need of a blob
+ *
+ * Returns 0 if it gets a blob, -ENOMEM otherwise
+ */
+static int smack_inode_alloc_security(struct inode *inode)
+{
+ inode->i_security = new_inode_smack(current->security);
+ if (inode->i_security == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+/**
+ * smack_inode_free_security - free an inode blob
+ * @inode - the inode with a blob
+ *
+ * Clears the blob pointer in inode
+ */
+static void smack_inode_free_security(struct inode *inode)
+{
+ kfree(inode->i_security);
+ inode->i_security = NULL;
+}
+
+/**
+ * smack_inode_init_security - copy out the smack from an inode
+ * @inode: the inode
+ * @dir: unused
+ * @name: where to put the attribute name
+ * @value: where to put the attribute value
+ * @len: where to put the length of the attribute
+ *
+ * Returns 0 if it all works out, -ENOMEM if there's no memory
+ */
+static int smack_inode_init_security(struct inode *inode, struct inode *dir,
+ char **name, void **value, size_t *len)
+{
+ char *isp = smk_of_inode(inode);
+
+ if (name) {
+ *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL);
+ if (*name == NULL)
+ return -ENOMEM;
+ }
+
+ if (value) {
+ *value = kstrdup(isp, GFP_KERNEL);
+ if (*value == NULL)
+ return -ENOMEM;
+ }
+
+ if (len)
+ *len = strlen(isp) + 1;
+
+ return 0;
+}
+
+/**
+ * smack_inode_link - Smack check on link
+ * @old_dentry: the existing object
+ * @dir: unused
+ * @new_dentry: the new object
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int smack_inode_link(struct dentry *old_dentry, struct inode *dir,
+ struct dentry *new_dentry)
+{
+ int rc;
+ char *isp;
+
+ isp = smk_of_inode(old_dentry->d_inode);
+ rc = smk_curacc(isp, MAY_WRITE);
+
+ if (rc == 0 && new_dentry->d_inode != NULL) {
+ isp = smk_of_inode(new_dentry->d_inode);
+ rc = smk_curacc(isp, MAY_WRITE);
+ }
+
+ return rc;
+}
+
+/**
+ * smack_inode_unlink - Smack check on inode deletion
+ * @dir: containing directory object
+ * @dentry: file to unlink
+ *
+ * Returns 0 if current can write the containing directory
+ * and the object, error code otherwise
+ */
+static int smack_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+ struct inode *ip = dentry->d_inode;
+ int rc;
+
+ /*
+ * You need write access to the thing you're unlinking
+ */
+ rc = smk_curacc(smk_of_inode(ip), MAY_WRITE);
+ if (rc == 0)
+ /*
+ * You also need write access to the containing directory
+ */
+ rc = smk_curacc(smk_of_inode(dir), MAY_WRITE);
+
+ return rc;
+}
+
+/**
+ * smack_inode_rmdir - Smack check on directory deletion
+ * @dir: containing directory object
+ * @dentry: directory to unlink
+ *
+ * Returns 0 if current can write the containing directory
+ * and the directory, error code otherwise
+ */
+static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ int rc;
+
+ /*
+ * You need write access to the thing you're removing
+ */
+ rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);
+ if (rc == 0)
+ /*
+ * You also need write access to the containing directory
+ */
+ rc = smk_curacc(smk_of_inode(dir), MAY_WRITE);
+
+ return rc;
+}
+
+/**
+ * smack_inode_rename - Smack check on rename
+ * @old_inode: the old directory
+ * @old_dentry: unused
+ * @new_inode: the new directory
+ * @new_dentry: unused
+ *
+ * Read and write access is required on both the old and
+ * new directories.
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int smack_inode_rename(struct inode *old_inode,
+ struct dentry *old_dentry,
+ struct inode *new_inode,
+ struct dentry *new_dentry)
+{
+ int rc;
+ char *isp;
+
+ isp = smk_of_inode(old_dentry->d_inode);
+ rc = smk_curacc(isp, MAY_READWRITE);
+
+ if (rc == 0 && new_dentry->d_inode != NULL) {
+ isp = smk_of_inode(new_dentry->d_inode);
+ rc = smk_curacc(isp, MAY_READWRITE);
+ }
+
+ return rc;
+}
+
+/**
+ * smack_inode_permission - Smack version of permission()
+ * @inode: the inode in question
+ * @mask: the access requested
+ * @nd: unused
+ *
+ * This is the important Smack hook.
+ *
+ * Returns 0 if access is permitted, -EACCES otherwise
+ */
+static int smack_inode_permission(struct inode *inode, int mask)
+{
+ /*
+ * No permission to check. Existence test. Yup, it's there.
+ */
+ if (mask == 0)
+ return 0;
+
+ return smk_curacc(smk_of_inode(inode), mask);
+}
+
+/**
+ * smack_inode_setattr - Smack check for setting attributes
+ * @dentry: the object
+ * @iattr: for the force flag
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ /*
+ * Need to allow for clearing the setuid bit.
+ */
+ if (iattr->ia_valid & ATTR_FORCE)
+ return 0;
+
+ return smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);
+}
+
+/**
+ * smack_inode_getattr - Smack check for getting attributes
+ * @mnt: unused
+ * @dentry: the object
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int smack_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
+{
+ return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ);
+}
+
+/**
+ * smack_inode_setxattr - Smack check for setting xattrs
+ * @dentry: the object
+ * @name: name of the attribute
+ * @value: unused
+ * @size: unused
+ * @flags: unused
+ *
+ * This protects the Smack attribute explicitly.
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int smack_inode_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ int rc = 0;
+
+ if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
+ strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
+ strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
+ if (!capable(CAP_MAC_ADMIN))
+ rc = -EPERM;
+ } else
+ rc = cap_inode_setxattr(dentry, name, value, size, flags);
+
+ if (rc == 0)
+ rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);
+
+ return rc;
+}
+
+/**
+ * smack_inode_post_setxattr - Apply the Smack update approved above
+ * @dentry: object
+ * @name: attribute name
+ * @value: attribute value
+ * @size: attribute size
+ * @flags: unused
+ *
+ * Set the pointer in the inode blob to the entry found
+ * in the master label list.
+ */
+static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ struct inode_smack *isp;
+ char *nsp;
+
+ /*
+ * Not SMACK
+ */
+ if (strcmp(name, XATTR_NAME_SMACK))
+ return;
+
+ if (size >= SMK_LABELLEN)
+ return;
+
+ isp = dentry->d_inode->i_security;
+
+ /*
+ * No locking is done here. This is a pointer
+ * assignment.
+ */
+ nsp = smk_import(value, size);
+ if (nsp != NULL)
+ isp->smk_inode = nsp;
+ else
+ isp->smk_inode = smack_known_invalid.smk_known;
+
+ return;
+}
+
+/*
+ * smack_inode_getxattr - Smack check on getxattr
+ * @dentry: the object
+ * @name: unused
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int smack_inode_getxattr(struct dentry *dentry, const char *name)
+{
+ return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ);
+}
+
+/*
+ * smack_inode_removexattr - Smack check on removexattr
+ * @dentry: the object
+ * @name: name of the attribute
+ *
+ * Removing the Smack attribute requires CAP_MAC_ADMIN
+ *
+ * Returns 0 if access is permitted, an error code otherwise
+ */
+static int smack_inode_removexattr(struct dentry *dentry, const char *name)
+{
+ int rc = 0;
+
+ if (strcmp(name, XATTR_NAME_SMACK) == 0 ||
+ strcmp(name, XATTR_NAME_SMACKIPIN) == 0 ||
+ strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) {
+ if (!capable(CAP_MAC_ADMIN))
+ rc = -EPERM;
+ } else
+ rc = cap_inode_removexattr(dentry, name);
+
+ if (rc == 0)
+ rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);
+
+ return rc;
+}
+
+/**
+ * smack_inode_getsecurity - get smack xattrs
+ * @inode: the object
+ * @name: attribute name
+ * @buffer: where to put the result
+ * @size: size of the buffer
+ * @err: unused
+ *
+ * Returns the size of the attribute or an error code
+ */
+static int smack_inode_getsecurity(const struct inode *inode,
+ const char *name, void **buffer,
+ bool alloc)
+{
+ struct socket_smack *ssp;
+ struct socket *sock;
+ struct super_block *sbp;
+ struct inode *ip = (struct inode *)inode;
+ char *isp;
+ int ilen;
+ int rc = 0;
+
+ if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
+ isp = smk_of_inode(inode);
+ ilen = strlen(isp) + 1;
+ *buffer = isp;
+ return ilen;
+ }
+
+ /*
+ * The rest of the Smack xattrs are only on sockets.
+ */
+ sbp = ip->i_sb;
+ if (sbp->s_magic != SOCKFS_MAGIC)
+ return -EOPNOTSUPP;
+
+ sock = SOCKET_I(ip);
+ if (sock == NULL || sock->sk == NULL)
+ return -EOPNOTSUPP;
+
+ ssp = sock->sk->sk_security;
+
+ if (strcmp(name, XATTR_SMACK_IPIN) == 0)
+ isp = ssp->smk_in;
+ else if (strcmp(name, XATTR_SMACK_IPOUT) == 0)
+ isp = ssp->smk_out;
+ else
+ return -EOPNOTSUPP;
+
+ ilen = strlen(isp) + 1;
+ if (rc == 0) {
+ *buffer = isp;
+ rc = ilen;
+ }
+
+ return rc;
+}
+
+
+/**
+ * smack_inode_listsecurity - list the Smack attributes
+ * @inode: the object
+ * @buffer: where they go
+ * @buffer_size: size of buffer
+ *
+ * Returns 0 on success, -EINVAL otherwise
+ */
+static int smack_inode_listsecurity(struct inode *inode, char *buffer,
+ size_t buffer_size)
+{
+ int len = strlen(XATTR_NAME_SMACK);
+
+ if (buffer != NULL && len <= buffer_size) {
+ memcpy(buffer, XATTR_NAME_SMACK, len);
+ return len;
+ }
+ return -EINVAL;
+}
+
+/**
+ * smack_inode_getsecid - Extract inode's security id
+ * @inode: inode to extract the info from
+ * @secid: where result will be saved
+ */
+static void smack_inode_getsecid(const struct inode *inode, u32 *secid)
+{
+ struct inode_smack *isp = inode->i_security;
+
+ *secid = smack_to_secid(isp->smk_inode);
+}
+
+/*
+ * File Hooks
+ */
+
+/**
+ * smack_file_permission - Smack check on file operations
+ * @file: unused
+ * @mask: unused
+ *
+ * Returns 0
+ *
+ * Should access checks be done on each read or write?
+ * UNICOS and SELinux say yes.
+ * Trusted Solaris, Trusted Irix, and just about everyone else says no.
+ *
+ * I'll say no for now. Smack does not do the frequent
+ * label changing that SELinux does.
+ */
+static int smack_file_permission(struct file *file, int mask)
+{
+ return 0;
+}
+
+/**
+ * smack_file_alloc_security - assign a file security blob
+ * @file: the object
+ *
+ * The security blob for a file is a pointer to the master
+ * label list, so no allocation is done.
+ *
+ * Returns 0
+ */
+static int smack_file_alloc_security(struct file *file)
+{
+ file->f_security = current->security;
+ return 0;
+}
+
+/**
+ * smack_file_free_security - clear a file security blob
+ * @file: the object
+ *
+ * The security blob for a file is a pointer to the master
+ * label list, so no memory is freed.
+ */
+static void smack_file_free_security(struct file *file)
+{
+ file->f_security = NULL;
+}
+
+/**
+ * smack_file_ioctl - Smack check on ioctls
+ * @file: the object
+ * @cmd: what to do
+ * @arg: unused
+ *
+ * Relies heavily on the correct use of the ioctl command conventions.
+ *
+ * Returns 0 if allowed, error code otherwise
+ */
+static int smack_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc = 0;
+
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ rc = smk_curacc(file->f_security, MAY_WRITE);
+
+ if (rc == 0 && (_IOC_DIR(cmd) & _IOC_READ))
+ rc = smk_curacc(file->f_security, MAY_READ);
+
+ return rc;
+}
+
+/**
+ * smack_file_lock - Smack check on file locking
+ * @file: the object
+ * @cmd unused
+ *
+ * Returns 0 if current has write access, error code otherwise
+ */
+static int smack_file_lock(struct file *file, unsigned int cmd)
+{
+ return smk_curacc(file->f_security, MAY_WRITE);
+}
+
+/**
+ * smack_file_fcntl - Smack check on fcntl
+ * @file: the object
+ * @cmd: what action to check
+ * @arg: unused
+ *
+ * Returns 0 if current has access, error code otherwise
+ */
+static int smack_file_fcntl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int rc;
+
+ switch (cmd) {
+ case F_DUPFD:
+ case F_GETFD:
+ case F_GETFL:
+ case F_GETLK:
+ case F_GETOWN:
+ case F_GETSIG:
+ rc = smk_curacc(file->f_security, MAY_READ);
+ break;
+ case F_SETFD:
+ case F_SETFL:
+ case F_SETLK:
+ case F_SETLKW:
+ case F_SETOWN:
+ case F_SETSIG:
+ rc = smk_curacc(file->f_security, MAY_WRITE);
+ break;
+ default:
+ rc = smk_curacc(file->f_security, MAY_READWRITE);
+ }
+
+ return rc;
+}
+
+/**
+ * smack_file_set_fowner - set the file security blob value
+ * @file: object in question
+ *
+ * Returns 0
+ * Further research may be required on this one.
+ */
+static int smack_file_set_fowner(struct file *file)
+{
+ file->f_security = current->security;
+ return 0;
+}
+
+/**
+ * smack_file_send_sigiotask - Smack on sigio
+ * @tsk: The target task
+ * @fown: the object the signal come from
+ * @signum: unused
+ *
+ * Allow a privileged task to get signals even if it shouldn't
+ *
+ * Returns 0 if a subject with the object's smack could
+ * write to the task, an error code otherwise.
+ */
+static int smack_file_send_sigiotask(struct task_struct *tsk,
+ struct fown_struct *fown, int signum)
+{
+ struct file *file;
+ int rc;
+
+ /*
+ * struct fown_struct is never outside the context of a struct file
+ */
+ file = container_of(fown, struct file, f_owner);
+ rc = smk_access(file->f_security, tsk->security, MAY_WRITE);
+ if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE))
+ return 0;
+ return rc;
+}
+
+/**
+ * smack_file_receive - Smack file receive check
+ * @file: the object
+ *
+ * Returns 0 if current has access, error code otherwise
+ */
+static int smack_file_receive(struct file *file)
+{
+ int may = 0;
+
+ /*
+ * This code relies on bitmasks.
+ */
+ if (file->f_mode & FMODE_READ)
+ may = MAY_READ;
+ if (file->f_mode & FMODE_WRITE)
+ may |= MAY_WRITE;
+
+ return smk_curacc(file->f_security, may);
+}
+
+/*
+ * Task hooks
+ */
+
+/**
+ * smack_task_alloc_security - "allocate" a task blob
+ * @tsk: the task in need of a blob
+ *
+ * Smack isn't using copies of blobs. Everyone
+ * points to an immutable list. No alloc required.
+ * No data copy required.
+ *
+ * Always returns 0
+ */
+static int smack_task_alloc_security(struct task_struct *tsk)
+{
+ tsk->security = current->security;
+
+ return 0;
+}
+
+/**
+ * smack_task_free_security - "free" a task blob
+ * @task: the task with the blob
+ *
+ * Smack isn't using copies of blobs. Everyone
+ * points to an immutable list. The blobs never go away.
+ * There is no leak here.
+ */
+static void smack_task_free_security(struct task_struct *task)
+{
+ task->security = NULL;
+}
+
+/**
+ * smack_task_setpgid - Smack check on setting pgid
+ * @p: the task object
+ * @pgid: unused
+ *
+ * Return 0 if write access is permitted
+ */
+static int smack_task_setpgid(struct task_struct *p, pid_t pgid)
+{
+ return smk_curacc(p->security, MAY_WRITE);
+}
+
+/**
+ * smack_task_getpgid - Smack access check for getpgid
+ * @p: the object task
+ *
+ * Returns 0 if current can read the object task, error code otherwise
+ */
+static int smack_task_getpgid(struct task_struct *p)
+{
+ return smk_curacc(p->security, MAY_READ);
+}
+
+/**
+ * smack_task_getsid - Smack access check for getsid
+ * @p: the object task
+ *
+ * Returns 0 if current can read the object task, error code otherwise
+ */
+static int smack_task_getsid(struct task_struct *p)
+{
+ return smk_curacc(p->security, MAY_READ);
+}
+
+/**
+ * smack_task_getsecid - get the secid of the task
+ * @p: the object task
+ * @secid: where to put the result
+ *
+ * Sets the secid to contain a u32 version of the smack label.
+ */
+static void smack_task_getsecid(struct task_struct *p, u32 *secid)
+{
+ *secid = smack_to_secid(p->security);
+}
+
+/**
+ * smack_task_setnice - Smack check on setting nice
+ * @p: the task object
+ * @nice: unused
+ *
+ * Return 0 if write access is permitted
+ */
+static int smack_task_setnice(struct task_struct *p, int nice)
+{
+ int rc;
+
+ rc = cap_task_setnice(p, nice);
+ if (rc == 0)
+ rc = smk_curacc(p->security, MAY_WRITE);
+ return rc;
+}
+
+/**
+ * smack_task_setioprio - Smack check on setting ioprio
+ * @p: the task object
+ * @ioprio: unused
+ *
+ * Return 0 if write access is permitted
+ */
+static int smack_task_setioprio(struct task_struct *p, int ioprio)
+{
+ int rc;
+
+ rc = cap_task_setioprio(p, ioprio);
+ if (rc == 0)
+ rc = smk_curacc(p->security, MAY_WRITE);
+ return rc;
+}
+
+/**
+ * smack_task_getioprio - Smack check on reading ioprio
+ * @p: the task object
+ *
+ * Return 0 if read access is permitted
+ */
+static int smack_task_getioprio(struct task_struct *p)
+{
+ return smk_curacc(p->security, MAY_READ);
+}
+
+/**
+ * smack_task_setscheduler - Smack check on setting scheduler
+ * @p: the task object
+ * @policy: unused
+ * @lp: unused
+ *
+ * Return 0 if read access is permitted
+ */
+static int smack_task_setscheduler(struct task_struct *p, int policy,
+ struct sched_param *lp)
+{
+ int rc;
+
+ rc = cap_task_setscheduler(p, policy, lp);
+ if (rc == 0)
+ rc = smk_curacc(p->security, MAY_WRITE);
+ return rc;
+}
+
+/**
+ * smack_task_getscheduler - Smack check on reading scheduler
+ * @p: the task object
+ *
+ * Return 0 if read access is permitted
+ */
+static int smack_task_getscheduler(struct task_struct *p)
+{
+ return smk_curacc(p->security, MAY_READ);
+}
+
+/**
+ * smack_task_movememory - Smack check on moving memory
+ * @p: the task object
+ *
+ * Return 0 if write access is permitted
+ */
+static int smack_task_movememory(struct task_struct *p)
+{
+ return smk_curacc(p->security, MAY_WRITE);
+}
+
+/**
+ * smack_task_kill - Smack check on signal delivery
+ * @p: the task object
+ * @info: unused
+ * @sig: unused
+ * @secid: identifies the smack to use in lieu of current's
+ *
+ * Return 0 if write access is permitted
+ *
+ * The secid behavior is an artifact of an SELinux hack
+ * in the USB code. Someday it may go away.
+ */
+static int smack_task_kill(struct task_struct *p, struct siginfo *info,
+ int sig, u32 secid)
+{
+ /*
+ * Sending a signal requires that the sender
+ * can write the receiver.
+ */
+ if (secid == 0)
+ return smk_curacc(p->security, MAY_WRITE);
+ /*
+ * If the secid isn't 0 we're dealing with some USB IO
+ * specific behavior. This is not clean. For one thing
+ * we can't take privilege into account.
+ */
+ return smk_access(smack_from_secid(secid), p->security, MAY_WRITE);
+}
+
+/**
+ * smack_task_wait - Smack access check for waiting
+ * @p: task to wait for
+ *
+ * Returns 0 if current can wait for p, error code otherwise
+ */
+static int smack_task_wait(struct task_struct *p)
+{
+ int rc;
+
+ rc = smk_access(current->security, p->security, MAY_WRITE);
+ if (rc == 0)
+ return 0;
+
+ /*
+ * Allow the operation to succeed if either task
+ * has privilege to perform operations that might
+ * account for the smack labels having gotten to
+ * be different in the first place.
+ *
+ * This breaks the strict subject/object access
+ * control ideal, taking the object's privilege
+ * state into account in the decision as well as
+ * the smack value.
+ */
+ if (capable(CAP_MAC_OVERRIDE) || has_capability(p, CAP_MAC_OVERRIDE))
+ return 0;
+
+ return rc;
+}
+
+/**
+ * smack_task_to_inode - copy task smack into the inode blob
+ * @p: task to copy from
+ * inode: inode to copy to
+ *
+ * Sets the smack pointer in the inode security blob
+ */
+static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
+{
+ struct inode_smack *isp = inode->i_security;
+ isp->smk_inode = p->security;
+}
+
+/*
+ * Socket hooks.
+ */
+
+/**
+ * smack_sk_alloc_security - Allocate a socket blob
+ * @sk: the socket
+ * @family: unused
+ * @priority: memory allocation priority
+ *
+ * Assign Smack pointers to current
+ *
+ * Returns 0 on success, -ENOMEM is there's no memory
+ */
+static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
+{
+ char *csp = current->security;
+ struct socket_smack *ssp;
+
+ ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
+ if (ssp == NULL)
+ return -ENOMEM;
+
+ ssp->smk_in = csp;
+ ssp->smk_out = csp;
+ ssp->smk_packet[0] = '\0';
+
+ sk->sk_security = ssp;
+
+ return 0;
+}
+
+/**
+ * smack_sk_free_security - Free a socket blob
+ * @sk: the socket
+ *
+ * Clears the blob pointer
+ */
+static void smack_sk_free_security(struct sock *sk)
+{
+ kfree(sk->sk_security);
+}
+
+/**
+ * smack_set_catset - convert a capset to netlabel mls categories
+ * @catset: the Smack categories
+ * @sap: where to put the netlabel categories
+ *
+ * Allocates and fills attr.mls.cat
+ */
+static void smack_set_catset(char *catset, struct netlbl_lsm_secattr *sap)
+{
+ unsigned char *cp;
+ unsigned char m;
+ int cat;
+ int rc;
+ int byte;
+
+ if (!catset)
+ return;
+
+ sap->flags |= NETLBL_SECATTR_MLS_CAT;
+ sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC);
+ sap->attr.mls.cat->startbit = 0;
+
+ for (cat = 1, cp = catset, byte = 0; byte < SMK_LABELLEN; cp++, byte++)
+ for (m = 0x80; m != 0; m >>= 1, cat++) {
+ if ((m & *cp) == 0)
+ continue;
+ rc = netlbl_secattr_catmap_setbit(sap->attr.mls.cat,
+ cat, GFP_ATOMIC);
+ }
+}
+
+/**
+ * smack_to_secattr - fill a secattr from a smack value
+ * @smack: the smack value
+ * @nlsp: where the result goes
+ *
+ * Casey says that CIPSO is good enough for now.
+ * It can be used to effect.
+ * It can also be abused to effect when necessary.
+ * Appologies to the TSIG group in general and GW in particular.
+ */
+static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp)
+{
+ struct smack_cipso cipso;
+ int rc;
+
+ switch (smack_net_nltype) {
+ case NETLBL_NLTYPE_CIPSOV4:
+ nlsp->domain = smack;
+ nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL;
+
+ rc = smack_to_cipso(smack, &cipso);
+ if (rc == 0) {
+ nlsp->attr.mls.lvl = cipso.smk_level;
+ smack_set_catset(cipso.smk_catset, nlsp);
+ } else {
+ nlsp->attr.mls.lvl = smack_cipso_direct;
+ smack_set_catset(smack, nlsp);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * smack_netlabel - Set the secattr on a socket
+ * @sk: the socket
+ *
+ * Convert the outbound smack value (smk_out) to a
+ * secattr and attach it to the socket.
+ *
+ * Returns 0 on success or an error code
+ */
+static int smack_netlabel(struct sock *sk)
+{
+ struct socket_smack *ssp;
+ struct netlbl_lsm_secattr secattr;
+ int rc;
+
+ ssp = sk->sk_security;
+ netlbl_secattr_init(&secattr);
+ smack_to_secattr(ssp->smk_out, &secattr);
+ rc = netlbl_sock_setattr(sk, &secattr);
+ netlbl_secattr_destroy(&secattr);
+
+ return rc;
+}
+
+/**
+ * smack_inode_setsecurity - set smack xattrs
+ * @inode: the object
+ * @name: attribute name
+ * @value: attribute value
+ * @size: size of the attribute
+ * @flags: unused
+ *
+ * Sets the named attribute in the appropriate blob
+ *
+ * Returns 0 on success, or an error code
+ */
+static int smack_inode_setsecurity(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ char *sp;
+ struct inode_smack *nsp = inode->i_security;
+ struct socket_smack *ssp;
+ struct socket *sock;
+ int rc = 0;
+
+ if (value == NULL || size > SMK_LABELLEN)
+ return -EACCES;
+
+ sp = smk_import(value, size);
+ if (sp == NULL)
+ return -EINVAL;
+
+ if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
+ nsp->smk_inode = sp;
+ return 0;
+ }
+ /*
+ * The rest of the Smack xattrs are only on sockets.
+ */
+ if (inode->i_sb->s_magic != SOCKFS_MAGIC)
+ return -EOPNOTSUPP;
+
+ sock = SOCKET_I(inode);
+ if (sock == NULL || sock->sk == NULL)
+ return -EOPNOTSUPP;
+
+ ssp = sock->sk->sk_security;
+
+ if (strcmp(name, XATTR_SMACK_IPIN) == 0)
+ ssp->smk_in = sp;
+ else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) {
+ ssp->smk_out = sp;
+ rc = smack_netlabel(sock->sk);
+ if (rc != 0)
+ printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",
+ __func__, -rc);
+ } else
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+/**
+ * smack_socket_post_create - finish socket setup
+ * @sock: the socket
+ * @family: protocol family
+ * @type: unused
+ * @protocol: unused
+ * @kern: unused
+ *
+ * Sets the netlabel information on the socket
+ *
+ * Returns 0 on success, and error code otherwise
+ */
+static int smack_socket_post_create(struct socket *sock, int family,
+ int type, int protocol, int kern)
+{
+ if (family != PF_INET || sock->sk == NULL)
+ return 0;
+ /*
+ * Set the outbound netlbl.
+ */
+ return smack_netlabel(sock->sk);
+}
+
+/**
+ * smack_flags_to_may - convert S_ to MAY_ values
+ * @flags: the S_ value
+ *
+ * Returns the equivalent MAY_ value
+ */
+static int smack_flags_to_may(int flags)
+{
+ int may = 0;
+
+ if (flags & S_IRUGO)
+ may |= MAY_READ;
+ if (flags & S_IWUGO)
+ may |= MAY_WRITE;
+ if (flags & S_IXUGO)
+ may |= MAY_EXEC;
+
+ return may;
+}
+
+/**
+ * smack_msg_msg_alloc_security - Set the security blob for msg_msg
+ * @msg: the object
+ *
+ * Returns 0
+ */
+static int smack_msg_msg_alloc_security(struct msg_msg *msg)
+{
+ msg->security = current->security;
+ return 0;
+}
+
+/**
+ * smack_msg_msg_free_security - Clear the security blob for msg_msg
+ * @msg: the object
+ *
+ * Clears the blob pointer
+ */
+static void smack_msg_msg_free_security(struct msg_msg *msg)
+{
+ msg->security = NULL;
+}
+
+/**
+ * smack_of_shm - the smack pointer for the shm
+ * @shp: the object
+ *
+ * Returns a pointer to the smack value
+ */
+static char *smack_of_shm(struct shmid_kernel *shp)
+{
+ return (char *)shp->shm_perm.security;
+}
+
+/**
+ * smack_shm_alloc_security - Set the security blob for shm
+ * @shp: the object
+ *
+ * Returns 0
+ */
+static int smack_shm_alloc_security(struct shmid_kernel *shp)
+{
+ struct kern_ipc_perm *isp = &shp->shm_perm;
+
+ isp->security = current->security;
+ return 0;
+}
+
+/**
+ * smack_shm_free_security - Clear the security blob for shm
+ * @shp: the object
+ *
+ * Clears the blob pointer
+ */
+static void smack_shm_free_security(struct shmid_kernel *shp)
+{
+ struct kern_ipc_perm *isp = &shp->shm_perm;
+
+ isp->security = NULL;
+}
+
+/**
+ * smack_shm_associate - Smack access check for shm
+ * @shp: the object
+ * @shmflg: access requested
+ *
+ * Returns 0 if current has the requested access, error code otherwise
+ */
+static int smack_shm_associate(struct shmid_kernel *shp, int shmflg)
+{
+ char *ssp = smack_of_shm(shp);
+ int may;
+
+ may = smack_flags_to_may(shmflg);
+ return smk_curacc(ssp, may);
+}
+
+/**
+ * smack_shm_shmctl - Smack access check for shm
+ * @shp: the object
+ * @cmd: what it wants to do
+ *
+ * Returns 0 if current has the requested access, error code otherwise
+ */
+static int smack_shm_shmctl(struct shmid_kernel *shp, int cmd)
+{
+ char *ssp;
+ int may;
+
+ switch (cmd) {
+ case IPC_STAT:
+ case SHM_STAT:
+ may = MAY_READ;
+ break;
+ case IPC_SET:
+ case SHM_LOCK:
+ case SHM_UNLOCK:
+ case IPC_RMID:
+ may = MAY_READWRITE;
+ break;
+ case IPC_INFO:
+ case SHM_INFO:
+ /*
+ * System level information.
+ */
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ ssp = smack_of_shm(shp);
+ return smk_curacc(ssp, may);
+}
+
+/**
+ * smack_shm_shmat - Smack access for shmat
+ * @shp: the object
+ * @shmaddr: unused
+ * @shmflg: access requested
+ *
+ * Returns 0 if current has the requested access, error code otherwise
+ */
+static int smack_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr,
+ int shmflg)
+{
+ char *ssp = smack_of_shm(shp);
+ int may;
+
+ may = smack_flags_to_may(shmflg);
+ return smk_curacc(ssp, may);
+}
+
+/**
+ * smack_of_sem - the smack pointer for the sem
+ * @sma: the object
+ *
+ * Returns a pointer to the smack value
+ */
+static char *smack_of_sem(struct sem_array *sma)
+{
+ return (char *)sma->sem_perm.security;
+}
+
+/**
+ * smack_sem_alloc_security - Set the security blob for sem
+ * @sma: the object
+ *
+ * Returns 0
+ */
+static int smack_sem_alloc_security(struct sem_array *sma)
+{
+ struct kern_ipc_perm *isp = &sma->sem_perm;
+
+ isp->security = current->security;
+ return 0;
+}
+
+/**
+ * smack_sem_free_security - Clear the security blob for sem
+ * @sma: the object
+ *
+ * Clears the blob pointer
+ */
+static void smack_sem_free_security(struct sem_array *sma)
+{
+ struct kern_ipc_perm *isp = &sma->sem_perm;
+
+ isp->security = NULL;
+}
+
+/**
+ * smack_sem_associate - Smack access check for sem
+ * @sma: the object
+ * @semflg: access requested
+ *
+ * Returns 0 if current has the requested access, error code otherwise
+ */
+static int smack_sem_associate(struct sem_array *sma, int semflg)
+{
+ char *ssp = smack_of_sem(sma);
+ int may;
+
+ may = smack_flags_to_may(semflg);
+ return smk_curacc(ssp, may);
+}
+
+/**
+ * smack_sem_shmctl - Smack access check for sem
+ * @sma: the object
+ * @cmd: what it wants to do
+ *
+ * Returns 0 if current has the requested access, error code otherwise
+ */
+static int smack_sem_semctl(struct sem_array *sma, int cmd)
+{
+ char *ssp;
+ int may;
+
+ switch (cmd) {
+ case GETPID:
+ case GETNCNT:
+ case GETZCNT:
+ case GETVAL:
+ case GETALL:
+ case IPC_STAT:
+ case SEM_STAT:
+ may = MAY_READ;
+ break;
+ case SETVAL:
+ case SETALL:
+ case IPC_RMID:
+ case IPC_SET:
+ may = MAY_READWRITE;
+ break;
+ case IPC_INFO:
+ case SEM_INFO:
+ /*
+ * System level information
+ */
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ ssp = smack_of_sem(sma);
+ return smk_curacc(ssp, may);
+}
+
+/**
+ * smack_sem_semop - Smack checks of semaphore operations
+ * @sma: the object
+ * @sops: unused
+ * @nsops: unused
+ * @alter: unused
+ *
+ * Treated as read and write in all cases.
+ *
+ * Returns 0 if access is allowed, error code otherwise
+ */
+static int smack_sem_semop(struct sem_array *sma, struct sembuf *sops,
+ unsigned nsops, int alter)
+{
+ char *ssp = smack_of_sem(sma);
+
+ return smk_curacc(ssp, MAY_READWRITE);
+}
+
+/**
+ * smack_msg_alloc_security - Set the security blob for msg
+ * @msq: the object
+ *
+ * Returns 0
+ */
+static int smack_msg_queue_alloc_security(struct msg_queue *msq)
+{
+ struct kern_ipc_perm *kisp = &msq->q_perm;
+
+ kisp->security = current->security;
+ return 0;
+}
+
+/**
+ * smack_msg_free_security - Clear the security blob for msg
+ * @msq: the object
+ *
+ * Clears the blob pointer
+ */
+static void smack_msg_queue_free_security(struct msg_queue *msq)
+{
+ struct kern_ipc_perm *kisp = &msq->q_perm;
+
+ kisp->security = NULL;
+}
+
+/**
+ * smack_of_msq - the smack pointer for the msq
+ * @msq: the object
+ *
+ * Returns a pointer to the smack value
+ */
+static char *smack_of_msq(struct msg_queue *msq)
+{
+ return (char *)msq->q_perm.security;
+}
+
+/**
+ * smack_msg_queue_associate - Smack access check for msg_queue
+ * @msq: the object
+ * @msqflg: access requested
+ *
+ * Returns 0 if current has the requested access, error code otherwise
+ */
+static int smack_msg_queue_associate(struct msg_queue *msq, int msqflg)
+{
+ char *msp = smack_of_msq(msq);
+ int may;
+
+ may = smack_flags_to_may(msqflg);
+ return smk_curacc(msp, may);
+}
+
+/**
+ * smack_msg_queue_msgctl - Smack access check for msg_queue
+ * @msq: the object
+ * @cmd: what it wants to do
+ *
+ * Returns 0 if current has the requested access, error code otherwise
+ */
+static int smack_msg_queue_msgctl(struct msg_queue *msq, int cmd)
+{
+ char *msp;
+ int may;
+
+ switch (cmd) {
+ case IPC_STAT:
+ case MSG_STAT:
+ may = MAY_READ;
+ break;
+ case IPC_SET:
+ case IPC_RMID:
+ may = MAY_READWRITE;
+ break;
+ case IPC_INFO:
+ case MSG_INFO:
+ /*
+ * System level information
+ */
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ msp = smack_of_msq(msq);
+ return smk_curacc(msp, may);
+}
+
+/**
+ * smack_msg_queue_msgsnd - Smack access check for msg_queue
+ * @msq: the object
+ * @msg: unused
+ * @msqflg: access requested
+ *
+ * Returns 0 if current has the requested access, error code otherwise
+ */
+static int smack_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
+ int msqflg)
+{
+ char *msp = smack_of_msq(msq);
+ int rc;
+
+ rc = smack_flags_to_may(msqflg);
+ return smk_curacc(msp, rc);
+}
+
+/**
+ * smack_msg_queue_msgsnd - Smack access check for msg_queue
+ * @msq: the object
+ * @msg: unused
+ * @target: unused
+ * @type: unused
+ * @mode: unused
+ *
+ * Returns 0 if current has read and write access, error code otherwise
+ */
+static int smack_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
+ struct task_struct *target, long type, int mode)
+{
+ char *msp = smack_of_msq(msq);
+
+ return smk_curacc(msp, MAY_READWRITE);
+}
+
+/**
+ * smack_ipc_permission - Smack access for ipc_permission()
+ * @ipp: the object permissions
+ * @flag: access requested
+ *
+ * Returns 0 if current has read and write access, error code otherwise
+ */
+static int smack_ipc_permission(struct kern_ipc_perm *ipp, short flag)
+{
+ char *isp = ipp->security;
+ int may;
+
+ may = smack_flags_to_may(flag);
+ return smk_curacc(isp, may);
+}
+
+/**
+ * smack_ipc_getsecid - Extract smack security id
+ * @ipcp: the object permissions
+ * @secid: where result will be saved
+ */
+static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid)
+{
+ char *smack = ipp->security;
+
+ *secid = smack_to_secid(smack);
+}
+
+/**
+ * smack_d_instantiate - Make sure the blob is correct on an inode
+ * @opt_dentry: unused
+ * @inode: the object
+ *
+ * Set the inode's security blob if it hasn't been done already.
+ */
+static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
+{
+ struct super_block *sbp;
+ struct superblock_smack *sbsp;
+ struct inode_smack *isp;
+ char *csp = current->security;
+ char *fetched;
+ char *final;
+ struct dentry *dp;
+
+ if (inode == NULL)
+ return;
+
+ isp = inode->i_security;
+
+ mutex_lock(&isp->smk_lock);
+ /*
+ * If the inode is already instantiated
+ * take the quick way out
+ */
+ if (isp->smk_flags & SMK_INODE_INSTANT)
+ goto unlockandout;
+
+ sbp = inode->i_sb;
+ sbsp = sbp->s_security;
+ /*
+ * We're going to use the superblock default label
+ * if there's no label on the file.
+ */
+ final = sbsp->smk_default;
+
+ /*
+ * If this is the root inode the superblock
+ * may be in the process of initialization.
+ * If that is the case use the root value out
+ * of the superblock.
+ */
+ if (opt_dentry->d_parent == opt_dentry) {
+ isp->smk_inode = sbsp->smk_root;
+ isp->smk_flags |= SMK_INODE_INSTANT;
+ goto unlockandout;
+ }
+
+ /*
+ * This is pretty hackish.
+ * Casey says that we shouldn't have to do
+ * file system specific code, but it does help
+ * with keeping it simple.
+ */
+ switch (sbp->s_magic) {
+ case SMACK_MAGIC:
+ /*
+ * Casey says that it's a little embarassing
+ * that the smack file system doesn't do
+ * extended attributes.
+ */
+ final = smack_known_star.smk_known;
+ break;
+ case PIPEFS_MAGIC:
+ /*
+ * Casey says pipes are easy (?)
+ */
+ final = smack_known_star.smk_known;
+ break;
+ case DEVPTS_SUPER_MAGIC:
+ /*
+ * devpts seems content with the label of the task.
+ * Programs that change smack have to treat the
+ * pty with respect.
+ */
+ final = csp;
+ break;
+ case SOCKFS_MAGIC:
+ /*
+ * Casey says sockets get the smack of the task.
+ */
+ final = csp;
+ break;
+ case PROC_SUPER_MAGIC:
+ /*
+ * Casey says procfs appears not to care.
+ * The superblock default suffices.
+ */
+ break;
+ case TMPFS_MAGIC:
+ /*
+ * Device labels should come from the filesystem,
+ * but watch out, because they're volitile,
+ * getting recreated on every reboot.
+ */
+ final = smack_known_star.smk_known;
+ /*
+ * No break.
+ *
+ * If a smack value has been set we want to use it,
+ * but since tmpfs isn't giving us the opportunity
+ * to set mount options simulate setting the
+ * superblock default.
+ */
+ default:
+ /*
+ * This isn't an understood special case.
+ * Get the value from the xattr.
+ *
+ * No xattr support means, alas, no SMACK label.
+ * Use the aforeapplied default.
+ * It would be curious if the label of the task
+ * does not match that assigned.
+ */
+ if (inode->i_op->getxattr == NULL)
+ break;
+ /*
+ * Get the dentry for xattr.
+ */
+ if (opt_dentry == NULL) {
+ dp = d_find_alias(inode);
+ if (dp == NULL)
+ break;
+ } else {
+ dp = dget(opt_dentry);
+ if (dp == NULL)
+ break;
+ }
+
+ fetched = smk_fetch(inode, dp);
+ if (fetched != NULL)
+ final = fetched;
+
+ dput(dp);
+ break;
+ }
+
+ if (final == NULL)
+ isp->smk_inode = csp;
+ else
+ isp->smk_inode = final;
+
+ isp->smk_flags |= SMK_INODE_INSTANT;
+
+unlockandout:
+ mutex_unlock(&isp->smk_lock);
+ return;
+}
+
+/**
+ * smack_getprocattr - Smack process attribute access
+ * @p: the object task
+ * @name: the name of the attribute in /proc/.../attr
+ * @value: where to put the result
+ *
+ * Places a copy of the task Smack into value
+ *
+ * Returns the length of the smack label or an error code
+ */
+static int smack_getprocattr(struct task_struct *p, char *name, char **value)
+{
+ char *cp;
+ int slen;
+
+ if (strcmp(name, "current") != 0)
+ return -EINVAL;
+
+ cp = kstrdup(p->security, GFP_KERNEL);
+ if (cp == NULL)
+ return -ENOMEM;
+
+ slen = strlen(cp);
+ *value = cp;
+ return slen;
+}
+
+/**
+ * smack_setprocattr - Smack process attribute setting
+ * @p: the object task
+ * @name: the name of the attribute in /proc/.../attr
+ * @value: the value to set
+ * @size: the size of the value
+ *
+ * Sets the Smack value of the task. Only setting self
+ * is permitted and only with privilege
+ *
+ * Returns the length of the smack label or an error code
+ */
+static int smack_setprocattr(struct task_struct *p, char *name,
+ void *value, size_t size)
+{
+ char *newsmack;
+
+ /*
+ * Changing another process' Smack value is too dangerous
+ * and supports no sane use case.
+ */
+ if (p != current)
+ return -EPERM;
+
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (value == NULL || size == 0 || size >= SMK_LABELLEN)
+ return -EINVAL;
+
+ if (strcmp(name, "current") != 0)
+ return -EINVAL;
+
+ newsmack = smk_import(value, size);
+ if (newsmack == NULL)
+ return -EINVAL;
+
+ p->security = newsmack;
+ return size;
+}
+
+/**
+ * smack_unix_stream_connect - Smack access on UDS
+ * @sock: one socket
+ * @other: the other socket
+ * @newsk: unused
+ *
+ * Return 0 if a subject with the smack of sock could access
+ * an object with the smack of other, otherwise an error code
+ */
+static int smack_unix_stream_connect(struct socket *sock,
+ struct socket *other, struct sock *newsk)
+{
+ struct inode *sp = SOCK_INODE(sock);
+ struct inode *op = SOCK_INODE(other);
+
+ return smk_access(smk_of_inode(sp), smk_of_inode(op), MAY_READWRITE);
+}
+
+/**
+ * smack_unix_may_send - Smack access on UDS
+ * @sock: one socket
+ * @other: the other socket
+ *
+ * Return 0 if a subject with the smack of sock could access
+ * an object with the smack of other, otherwise an error code
+ */
+static int smack_unix_may_send(struct socket *sock, struct socket *other)
+{
+ struct inode *sp = SOCK_INODE(sock);
+ struct inode *op = SOCK_INODE(other);
+
+ return smk_access(smk_of_inode(sp), smk_of_inode(op), MAY_WRITE);
+}
+
+/**
+ * smack_from_secattr - Convert a netlabel attr.mls.lvl/attr.mls.cat
+ * pair to smack
+ * @sap: netlabel secattr
+ * @sip: where to put the result
+ *
+ * Copies a smack label into sip
+ */
+static void smack_from_secattr(struct netlbl_lsm_secattr *sap, char *sip)
+{
+ char smack[SMK_LABELLEN];
+ int pcat;
+
+ if ((sap->flags & NETLBL_SECATTR_MLS_LVL) == 0) {
+ /*
+ * If there are flags but no level netlabel isn't
+ * behaving the way we expect it to.
+ *
+ * Without guidance regarding the smack value
+ * for the packet fall back on the network
+ * ambient value.
+ */
+ strncpy(sip, smack_net_ambient, SMK_MAXLEN);
+ return;
+ }
+ /*
+ * Get the categories, if any
+ */
+ memset(smack, '\0', SMK_LABELLEN);
+ if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0)
+ for (pcat = -1;;) {
+ pcat = netlbl_secattr_catmap_walk(sap->attr.mls.cat,
+ pcat + 1);
+ if (pcat < 0)
+ break;
+ smack_catset_bit(pcat, smack);
+ }
+ /*
+ * If it is CIPSO using smack direct mapping
+ * we are already done. WeeHee.
+ */
+ if (sap->attr.mls.lvl == smack_cipso_direct) {
+ memcpy(sip, smack, SMK_MAXLEN);
+ return;
+ }
+ /*
+ * Look it up in the supplied table if it is not a direct mapping.
+ */
+ smack_from_cipso(sap->attr.mls.lvl, smack, sip);
+ return;
+}
+
+/**
+ * smack_socket_sock_rcv_skb - Smack packet delivery access check
+ * @sk: socket
+ * @skb: packet
+ *
+ * Returns 0 if the packet should be delivered, an error code otherwise
+ */
+static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ struct netlbl_lsm_secattr secattr;
+ struct socket_smack *ssp = sk->sk_security;
+ char smack[SMK_LABELLEN];
+ int rc;
+
+ if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
+ return 0;
+
+ /*
+ * Translate what netlabel gave us.
+ */
+ memset(smack, '\0', SMK_LABELLEN);
+ netlbl_secattr_init(&secattr);
+ rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr);
+ if (rc == 0)
+ smack_from_secattr(&secattr, smack);
+ else
+ strncpy(smack, smack_net_ambient, SMK_MAXLEN);
+ netlbl_secattr_destroy(&secattr);
+ /*
+ * Receiving a packet requires that the other end
+ * be able to write here. Read access is not required.
+ * This is the simplist possible security model
+ * for networking.
+ */
+ rc = smk_access(smack, ssp->smk_in, MAY_WRITE);
+ if (rc != 0)
+ netlbl_skbuff_err(skb, rc, 0);
+ return rc;
+}
+
+/**
+ * smack_socket_getpeersec_stream - pull in packet label
+ * @sock: the socket
+ * @optval: user's destination
+ * @optlen: size thereof
+ * @len: max thereoe
+ *
+ * returns zero on success, an error code otherwise
+ */
+static int smack_socket_getpeersec_stream(struct socket *sock,
+ char __user *optval,
+ int __user *optlen, unsigned len)
+{
+ struct socket_smack *ssp;
+ int slen;
+ int rc = 0;
+
+ ssp = sock->sk->sk_security;
+ slen = strlen(ssp->smk_packet) + 1;
+
+ if (slen > len)
+ rc = -ERANGE;
+ else if (copy_to_user(optval, ssp->smk_packet, slen) != 0)
+ rc = -EFAULT;
+
+ if (put_user(slen, optlen) != 0)
+ rc = -EFAULT;
+
+ return rc;
+}
+
+
+/**
+ * smack_socket_getpeersec_dgram - pull in packet label
+ * @sock: the socket
+ * @skb: packet data
+ * @secid: pointer to where to put the secid of the packet
+ *
+ * Sets the netlabel socket state on sk from parent
+ */
+static int smack_socket_getpeersec_dgram(struct socket *sock,
+ struct sk_buff *skb, u32 *secid)
+
+{
+ struct netlbl_lsm_secattr secattr;
+ struct sock *sk;
+ char smack[SMK_LABELLEN];
+ int family = PF_INET;
+ u32 s;
+ int rc;
+
+ /*
+ * Only works for families with packets.
+ */
+ if (sock != NULL) {
+ sk = sock->sk;
+ if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
+ return 0;
+ family = sk->sk_family;
+ }
+ /*
+ * Translate what netlabel gave us.
+ */
+ memset(smack, '\0', SMK_LABELLEN);
+ netlbl_secattr_init(&secattr);
+ rc = netlbl_skbuff_getattr(skb, family, &secattr);
+ if (rc == 0)
+ smack_from_secattr(&secattr, smack);
+ netlbl_secattr_destroy(&secattr);
+
+ /*
+ * Give up if we couldn't get anything
+ */
+ if (rc != 0)
+ return rc;
+
+ s = smack_to_secid(smack);
+ if (s == 0)
+ return -EINVAL;
+
+ *secid = s;
+ return 0;
+}
+
+/**
+ * smack_sock_graft - graft access state between two sockets
+ * @sk: fresh sock
+ * @parent: donor socket
+ *
+ * Sets the netlabel socket state on sk from parent
+ */
+static void smack_sock_graft(struct sock *sk, struct socket *parent)
+{
+ struct socket_smack *ssp;
+ int rc;
+
+ if (sk == NULL)
+ return;
+
+ if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
+ return;
+
+ ssp = sk->sk_security;
+ ssp->smk_in = current->security;
+ ssp->smk_out = current->security;
+ ssp->smk_packet[0] = '\0';
+
+ rc = smack_netlabel(sk);
+ if (rc != 0)
+ printk(KERN_WARNING "Smack: \"%s\" netlbl error %d.\n",
+ __func__, -rc);
+}
+
+/**
+ * smack_inet_conn_request - Smack access check on connect
+ * @sk: socket involved
+ * @skb: packet
+ * @req: unused
+ *
+ * Returns 0 if a task with the packet label could write to
+ * the socket, otherwise an error code
+ */
+static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb,
+ struct request_sock *req)
+{
+ struct netlbl_lsm_secattr skb_secattr;
+ struct socket_smack *ssp = sk->sk_security;
+ char smack[SMK_LABELLEN];
+ int rc;
+
+ if (skb == NULL)
+ return -EACCES;
+
+ memset(smack, '\0', SMK_LABELLEN);
+ netlbl_secattr_init(&skb_secattr);
+ rc = netlbl_skbuff_getattr(skb, sk->sk_family, &skb_secattr);
+ if (rc == 0)
+ smack_from_secattr(&skb_secattr, smack);
+ else
+ strncpy(smack, smack_known_huh.smk_known, SMK_MAXLEN);
+ netlbl_secattr_destroy(&skb_secattr);
+ /*
+ * Receiving a packet requires that the other end
+ * be able to write here. Read access is not required.
+ *
+ * If the request is successful save the peer's label
+ * so that SO_PEERCRED can report it.
+ */
+ rc = smk_access(smack, ssp->smk_in, MAY_WRITE);
+ if (rc == 0)
+ strncpy(ssp->smk_packet, smack, SMK_MAXLEN);
+
+ return rc;
+}
+
+/*
+ * Key management security hooks
+ *
+ * Casey has not tested key support very heavily.
+ * The permission check is most likely too restrictive.
+ * If you care about keys please have a look.
+ */
+#ifdef CONFIG_KEYS
+
+/**
+ * smack_key_alloc - Set the key security blob
+ * @key: object
+ * @tsk: the task associated with the key
+ * @flags: unused
+ *
+ * No allocation required
+ *
+ * Returns 0
+ */
+static int smack_key_alloc(struct key *key, struct task_struct *tsk,
+ unsigned long flags)
+{
+ key->security = tsk->security;
+ return 0;
+}
+
+/**
+ * smack_key_free - Clear the key security blob
+ * @key: the object
+ *
+ * Clear the blob pointer
+ */
+static void smack_key_free(struct key *key)
+{
+ key->security = NULL;
+}
+
+/*
+ * smack_key_permission - Smack access on a key
+ * @key_ref: gets to the object
+ * @context: task involved
+ * @perm: unused
+ *
+ * Return 0 if the task has read and write to the object,
+ * an error code otherwise
+ */
+static int smack_key_permission(key_ref_t key_ref,
+ struct task_struct *context, key_perm_t perm)
+{
+ struct key *keyp;
+
+ keyp = key_ref_to_ptr(key_ref);
+ if (keyp == NULL)
+ return -EINVAL;
+ /*
+ * If the key hasn't been initialized give it access so that
+ * it may do so.
+ */
+ if (keyp->security == NULL)
+ return 0;
+ /*
+ * This should not occur
+ */
+ if (context->security == NULL)
+ return -EACCES;
+
+ return smk_access(context->security, keyp->security, MAY_READWRITE);
+}
+#endif /* CONFIG_KEYS */
+
+/*
+ * Smack Audit hooks
+ *
+ * Audit requires a unique representation of each Smack specific
+ * rule. This unique representation is used to distinguish the
+ * object to be audited from remaining kernel objects and also
+ * works as a glue between the audit hooks.
+ *
+ * Since repository entries are added but never deleted, we'll use
+ * the smack_known label address related to the given audit rule as
+ * the needed unique representation. This also better fits the smack
+ * model where nearly everything is a label.
+ */
+#ifdef CONFIG_AUDIT
+
+/**
+ * smack_audit_rule_init - Initialize a smack audit rule
+ * @field: audit rule fields given from user-space (audit.h)
+ * @op: required testing operator (=, !=, >, <, ...)
+ * @rulestr: smack label to be audited
+ * @vrule: pointer to save our own audit rule representation
+ *
+ * Prepare to audit cases where (@field @op @rulestr) is true.
+ * The label to be audited is created if necessay.
+ */
+static int smack_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
+{
+ char **rule = (char **)vrule;
+ *rule = NULL;
+
+ if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER)
+ return -EINVAL;
+
+ if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL)
+ return -EINVAL;
+
+ *rule = smk_import(rulestr, 0);
+
+ return 0;
+}
+
+/**
+ * smack_audit_rule_known - Distinguish Smack audit rules
+ * @krule: rule of interest, in Audit kernel representation format
+ *
+ * This is used to filter Smack rules from remaining Audit ones.
+ * If it's proved that this rule belongs to us, the
+ * audit_rule_match hook will be called to do the final judgement.
+ */
+static int smack_audit_rule_known(struct audit_krule *krule)
+{
+ struct audit_field *f;
+ int i;
+
+ for (i = 0; i < krule->field_count; i++) {
+ f = &krule->fields[i];
+
+ if (f->type == AUDIT_SUBJ_USER || f->type == AUDIT_OBJ_USER)
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * smack_audit_rule_match - Audit given object ?
+ * @secid: security id for identifying the object to test
+ * @field: audit rule flags given from user-space
+ * @op: required testing operator
+ * @vrule: smack internal rule presentation
+ * @actx: audit context associated with the check
+ *
+ * The core Audit hook. It's used to take the decision of
+ * whether to audit or not to audit a given object.
+ */
+static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule,
+ struct audit_context *actx)
+{
+ char *smack;
+ char *rule = vrule;
+
+ if (!rule) {
+ audit_log(actx, GFP_KERNEL, AUDIT_SELINUX_ERR,
+ "Smack: missing rule\n");
+ return -ENOENT;
+ }
+
+ if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER)
+ return 0;
+
+ smack = smack_from_secid(secid);
+
+ /*
+ * No need to do string comparisons. If a match occurs,
+ * both pointers will point to the same smack_known
+ * label.
+ */
+ if (op == AUDIT_EQUAL)
+ return (rule == smack);
+ if (op == AUDIT_NOT_EQUAL)
+ return (rule != smack);
+
+ return 0;
+}
+
+/**
+ * smack_audit_rule_free - free smack rule representation
+ * @vrule: rule to be freed.
+ *
+ * No memory was allocated.
+ */
+static void smack_audit_rule_free(void *vrule)
+{
+ /* No-op */
+}
+
+#endif /* CONFIG_AUDIT */
+
+/*
+ * smack_secid_to_secctx - return the smack label for a secid
+ * @secid: incoming integer
+ * @secdata: destination
+ * @seclen: how long it is
+ *
+ * Exists for networking code.
+ */
+static int smack_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
+{
+ char *sp = smack_from_secid(secid);
+
+ *secdata = sp;
+ *seclen = strlen(sp);
+ return 0;
+}
+
+/*
+ * smack_secctx_to_secid - return the secid for a smack label
+ * @secdata: smack label
+ * @seclen: how long result is
+ * @secid: outgoing integer
+ *
+ * Exists for audit and networking code.
+ */
+static int smack_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
+{
+ *secid = smack_to_secid(secdata);
+ return 0;
+}
+
+/*
+ * smack_release_secctx - don't do anything.
+ * @key_ref: unused
+ * @context: unused
+ * @perm: unused
+ *
+ * Exists to make sure nothing gets done, and properly
+ */
+static void smack_release_secctx(char *secdata, u32 seclen)
+{
+}
+
+struct security_operations smack_ops = {
+ .name = "smack",
+
+ .ptrace_may_access = smack_ptrace_may_access,
+ .ptrace_traceme = smack_ptrace_traceme,
+ .capget = cap_capget,
+ .capset_check = cap_capset_check,
+ .capset_set = cap_capset_set,
+ .capable = cap_capable,
+ .syslog = smack_syslog,
+ .settime = cap_settime,
+ .vm_enough_memory = cap_vm_enough_memory,
+
+ .bprm_apply_creds = cap_bprm_apply_creds,
+ .bprm_set_security = cap_bprm_set_security,
+ .bprm_secureexec = cap_bprm_secureexec,
+
+ .sb_alloc_security = smack_sb_alloc_security,
+ .sb_free_security = smack_sb_free_security,
+ .sb_copy_data = smack_sb_copy_data,
+ .sb_kern_mount = smack_sb_kern_mount,
+ .sb_statfs = smack_sb_statfs,
+ .sb_mount = smack_sb_mount,
+ .sb_umount = smack_sb_umount,
+
+ .inode_alloc_security = smack_inode_alloc_security,
+ .inode_free_security = smack_inode_free_security,
+ .inode_init_security = smack_inode_init_security,
+ .inode_link = smack_inode_link,
+ .inode_unlink = smack_inode_unlink,
+ .inode_rmdir = smack_inode_rmdir,
+ .inode_rename = smack_inode_rename,
+ .inode_permission = smack_inode_permission,
+ .inode_setattr = smack_inode_setattr,
+ .inode_getattr = smack_inode_getattr,
+ .inode_setxattr = smack_inode_setxattr,
+ .inode_post_setxattr = smack_inode_post_setxattr,
+ .inode_getxattr = smack_inode_getxattr,
+ .inode_removexattr = smack_inode_removexattr,
+ .inode_need_killpriv = cap_inode_need_killpriv,
+ .inode_killpriv = cap_inode_killpriv,
+ .inode_getsecurity = smack_inode_getsecurity,
+ .inode_setsecurity = smack_inode_setsecurity,
+ .inode_listsecurity = smack_inode_listsecurity,
+ .inode_getsecid = smack_inode_getsecid,
+
+ .file_permission = smack_file_permission,
+ .file_alloc_security = smack_file_alloc_security,
+ .file_free_security = smack_file_free_security,
+ .file_ioctl = smack_file_ioctl,
+ .file_lock = smack_file_lock,
+ .file_fcntl = smack_file_fcntl,
+ .file_set_fowner = smack_file_set_fowner,
+ .file_send_sigiotask = smack_file_send_sigiotask,
+ .file_receive = smack_file_receive,
+
+ .task_alloc_security = smack_task_alloc_security,
+ .task_free_security = smack_task_free_security,
+ .task_post_setuid = cap_task_post_setuid,
+ .task_setpgid = smack_task_setpgid,
+ .task_getpgid = smack_task_getpgid,
+ .task_getsid = smack_task_getsid,
+ .task_getsecid = smack_task_getsecid,
+ .task_setnice = smack_task_setnice,
+ .task_setioprio = smack_task_setioprio,
+ .task_getioprio = smack_task_getioprio,
+ .task_setscheduler = smack_task_setscheduler,
+ .task_getscheduler = smack_task_getscheduler,
+ .task_movememory = smack_task_movememory,
+ .task_kill = smack_task_kill,
+ .task_wait = smack_task_wait,
+ .task_reparent_to_init = cap_task_reparent_to_init,
+ .task_to_inode = smack_task_to_inode,
+ .task_prctl = cap_task_prctl,
+
+ .ipc_permission = smack_ipc_permission,
+ .ipc_getsecid = smack_ipc_getsecid,
+
+ .msg_msg_alloc_security = smack_msg_msg_alloc_security,
+ .msg_msg_free_security = smack_msg_msg_free_security,
+
+ .msg_queue_alloc_security = smack_msg_queue_alloc_security,
+ .msg_queue_free_security = smack_msg_queue_free_security,
+ .msg_queue_associate = smack_msg_queue_associate,
+ .msg_queue_msgctl = smack_msg_queue_msgctl,
+ .msg_queue_msgsnd = smack_msg_queue_msgsnd,
+ .msg_queue_msgrcv = smack_msg_queue_msgrcv,
+
+ .shm_alloc_security = smack_shm_alloc_security,
+ .shm_free_security = smack_shm_free_security,
+ .shm_associate = smack_shm_associate,
+ .shm_shmctl = smack_shm_shmctl,
+ .shm_shmat = smack_shm_shmat,
+
+ .sem_alloc_security = smack_sem_alloc_security,
+ .sem_free_security = smack_sem_free_security,
+ .sem_associate = smack_sem_associate,
+ .sem_semctl = smack_sem_semctl,
+ .sem_semop = smack_sem_semop,
+
+ .netlink_send = cap_netlink_send,
+ .netlink_recv = cap_netlink_recv,
+
+ .d_instantiate = smack_d_instantiate,
+
+ .getprocattr = smack_getprocattr,
+ .setprocattr = smack_setprocattr,
+
+ .unix_stream_connect = smack_unix_stream_connect,
+ .unix_may_send = smack_unix_may_send,
+
+ .socket_post_create = smack_socket_post_create,
+ .socket_sock_rcv_skb = smack_socket_sock_rcv_skb,
+ .socket_getpeersec_stream = smack_socket_getpeersec_stream,
+ .socket_getpeersec_dgram = smack_socket_getpeersec_dgram,
+ .sk_alloc_security = smack_sk_alloc_security,
+ .sk_free_security = smack_sk_free_security,
+ .sock_graft = smack_sock_graft,
+ .inet_conn_request = smack_inet_conn_request,
+
+ /* key management security hooks */
+#ifdef CONFIG_KEYS
+ .key_alloc = smack_key_alloc,
+ .key_free = smack_key_free,
+ .key_permission = smack_key_permission,
+#endif /* CONFIG_KEYS */
+
+ /* Audit hooks */
+#ifdef CONFIG_AUDIT
+ .audit_rule_init = smack_audit_rule_init,
+ .audit_rule_known = smack_audit_rule_known,
+ .audit_rule_match = smack_audit_rule_match,
+ .audit_rule_free = smack_audit_rule_free,
+#endif /* CONFIG_AUDIT */
+
+ .secid_to_secctx = smack_secid_to_secctx,
+ .secctx_to_secid = smack_secctx_to_secid,
+ .release_secctx = smack_release_secctx,
+};
+
+/**
+ * smack_init - initialize the smack system
+ *
+ * Returns 0
+ */
+static __init int smack_init(void)
+{
+ if (!security_module_enable(&smack_ops))
+ return 0;
+
+ printk(KERN_INFO "Smack: Initializing.\n");
+
+ /*
+ * Set the security state for the initial task.
+ */
+ current->security = &smack_known_floor.smk_known;
+
+ /*
+ * Initialize locks
+ */
+ spin_lock_init(&smack_known_unset.smk_cipsolock);
+ spin_lock_init(&smack_known_huh.smk_cipsolock);
+ spin_lock_init(&smack_known_hat.smk_cipsolock);
+ spin_lock_init(&smack_known_star.smk_cipsolock);
+ spin_lock_init(&smack_known_floor.smk_cipsolock);
+ spin_lock_init(&smack_known_invalid.smk_cipsolock);
+
+ /*
+ * Register with LSM
+ */
+ if (register_security(&smack_ops))
+ panic("smack: Unable to register with kernel.\n");
+
+ return 0;
+}
+
+/*
+ * Smack requires early initialization in order to label
+ * all processes and objects when they are created.
+ */
+security_initcall(smack_init);
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
new file mode 100644
index 0000000..c21d8c8
--- /dev/null
+++ b/security/smack/smackfs.c
@@ -0,0 +1,1095 @@
+/*
+ * Copyright (C) 2007 Casey Schaufler <casey@schaufler-ca.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2.
+ *
+ * Authors:
+ * Casey Schaufler <casey@schaufler-ca.com>
+ * Ahmed S. Darwish <darwish.07@gmail.com>
+ *
+ * Special thanks to the authors of selinuxfs.
+ *
+ * Karl MacMillan <kmacmillan@tresys.com>
+ * James Morris <jmorris@redhat.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/security.h>
+#include <linux/mutex.h>
+#include <net/netlabel.h>
+#include <net/cipso_ipv4.h>
+#include <linux/seq_file.h>
+#include <linux/ctype.h>
+#include <linux/audit.h>
+#include "smack.h"
+
+/*
+ * smackfs pseudo filesystem.
+ */
+
+enum smk_inos {
+ SMK_ROOT_INO = 2,
+ SMK_LOAD = 3, /* load policy */
+ SMK_CIPSO = 4, /* load label -> CIPSO mapping */
+ SMK_DOI = 5, /* CIPSO DOI */
+ SMK_DIRECT = 6, /* CIPSO level indicating direct label */
+ SMK_AMBIENT = 7, /* internet ambient label */
+ SMK_NLTYPE = 8, /* label scheme to use by default */
+ SMK_ONLYCAP = 9, /* the only "capable" label */
+};
+
+/*
+ * List locks
+ */
+static DEFINE_MUTEX(smack_list_lock);
+static DEFINE_MUTEX(smack_cipso_lock);
+static DEFINE_MUTEX(smack_ambient_lock);
+
+/*
+ * This is the "ambient" label for network traffic.
+ * If it isn't somehow marked, use this.
+ * It can be reset via smackfs/ambient
+ */
+char *smack_net_ambient = smack_known_floor.smk_known;
+
+/*
+ * This is the default packet marking scheme for network traffic.
+ * It can be reset via smackfs/nltype
+ */
+int smack_net_nltype = NETLBL_NLTYPE_CIPSOV4;
+
+/*
+ * This is the level in a CIPSO header that indicates a
+ * smack label is contained directly in the category set.
+ * It can be reset via smackfs/direct
+ */
+int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
+
+/*
+ * Unless a process is running with this label even
+ * having CAP_MAC_OVERRIDE isn't enough to grant
+ * privilege to violate MAC policy. If no label is
+ * designated (the NULL case) capabilities apply to
+ * everyone. It is expected that the hat (^) label
+ * will be used if any label is used.
+ */
+char *smack_onlycap;
+
+static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
+struct smk_list_entry *smack_list;
+
+#define SEQ_READ_FINISHED 1
+
+/*
+ * Values for parsing cipso rules
+ * SMK_DIGITLEN: Length of a digit field in a rule.
+ * SMK_CIPSOMIN: Minimum possible cipso rule length.
+ * SMK_CIPSOMAX: Maximum possible cipso rule length.
+ */
+#define SMK_DIGITLEN 4
+#define SMK_CIPSOMIN (SMK_LABELLEN + 2 * SMK_DIGITLEN)
+#define SMK_CIPSOMAX (SMK_CIPSOMIN + SMACK_CIPSO_MAXCATNUM * SMK_DIGITLEN)
+
+/*
+ * Values for parsing MAC rules
+ * SMK_ACCESS: Maximum possible combination of access permissions
+ * SMK_ACCESSLEN: Maximum length for a rule access field
+ * SMK_LOADLEN: Smack rule length
+ */
+#define SMK_ACCESS "rwxa"
+#define SMK_ACCESSLEN (sizeof(SMK_ACCESS) - 1)
+#define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN)
+
+
+/*
+ * Seq_file read operations for /smack/load
+ */
+
+static void *load_seq_start(struct seq_file *s, loff_t *pos)
+{
+ if (*pos == SEQ_READ_FINISHED)
+ return NULL;
+
+ return smack_list;
+}
+
+static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct smk_list_entry *skp = ((struct smk_list_entry *) v)->smk_next;
+
+ if (skp == NULL)
+ *pos = SEQ_READ_FINISHED;
+
+ return skp;
+}
+
+static int load_seq_show(struct seq_file *s, void *v)
+{
+ struct smk_list_entry *slp = (struct smk_list_entry *) v;
+ struct smack_rule *srp = &slp->smk_rule;
+
+ seq_printf(s, "%s %s", (char *)srp->smk_subject,
+ (char *)srp->smk_object);
+
+ seq_putc(s, ' ');
+
+ if (srp->smk_access & MAY_READ)
+ seq_putc(s, 'r');
+ if (srp->smk_access & MAY_WRITE)
+ seq_putc(s, 'w');
+ if (srp->smk_access & MAY_EXEC)
+ seq_putc(s, 'x');
+ if (srp->smk_access & MAY_APPEND)
+ seq_putc(s, 'a');
+ if (srp->smk_access == 0)
+ seq_putc(s, '-');
+
+ seq_putc(s, '\n');
+
+ return 0;
+}
+
+static void load_seq_stop(struct seq_file *s, void *v)
+{
+ /* No-op */
+}
+
+static struct seq_operations load_seq_ops = {
+ .start = load_seq_start,
+ .next = load_seq_next,
+ .show = load_seq_show,
+ .stop = load_seq_stop,
+};
+
+/**
+ * smk_open_load - open() for /smack/load
+ * @inode: inode structure representing file
+ * @file: "load" file pointer
+ *
+ * For reading, use load_seq_* seq_file reading operations.
+ */
+static int smk_open_load(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &load_seq_ops);
+}
+
+/**
+ * smk_set_access - add a rule to the rule list
+ * @srp: the new rule to add
+ *
+ * Looks through the current subject/object/access list for
+ * the subject/object pair and replaces the access that was
+ * there. If the pair isn't found add it with the specified
+ * access.
+ */
+static void smk_set_access(struct smack_rule *srp)
+{
+ struct smk_list_entry *sp;
+ struct smk_list_entry *newp;
+
+ mutex_lock(&smack_list_lock);
+
+ for (sp = smack_list; sp != NULL; sp = sp->smk_next)
+ if (sp->smk_rule.smk_subject == srp->smk_subject &&
+ sp->smk_rule.smk_object == srp->smk_object) {
+ sp->smk_rule.smk_access = srp->smk_access;
+ break;
+ }
+
+ if (sp == NULL) {
+ newp = kzalloc(sizeof(struct smk_list_entry), GFP_KERNEL);
+ newp->smk_rule = *srp;
+ newp->smk_next = smack_list;
+ smack_list = newp;
+ }
+
+ mutex_unlock(&smack_list_lock);
+
+ return;
+}
+
+/**
+ * smk_write_load - write() for /smack/load
+ * @filp: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ *
+ * Get one smack access rule from above.
+ * The format is exactly:
+ * char subject[SMK_LABELLEN]
+ * char object[SMK_LABELLEN]
+ * char access[SMK_ACCESSLEN]
+ *
+ * writes must be SMK_LABELLEN+SMK_LABELLEN+SMK_ACCESSLEN bytes.
+ */
+static ssize_t smk_write_load(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct smack_rule rule;
+ char *data;
+ int rc = -EINVAL;
+
+ /*
+ * Must have privilege.
+ * No partial writes.
+ * Enough data must be present.
+ */
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+ if (*ppos != 0)
+ return -EINVAL;
+ if (count != SMK_LOADLEN)
+ return -EINVAL;
+
+ data = kzalloc(count, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, count) != 0) {
+ rc = -EFAULT;
+ goto out;
+ }
+
+ rule.smk_subject = smk_import(data, 0);
+ if (rule.smk_subject == NULL)
+ goto out;
+
+ rule.smk_object = smk_import(data + SMK_LABELLEN, 0);
+ if (rule.smk_object == NULL)
+ goto out;
+
+ rule.smk_access = 0;
+
+ switch (data[SMK_LABELLEN + SMK_LABELLEN]) {
+ case '-':
+ break;
+ case 'r':
+ case 'R':
+ rule.smk_access |= MAY_READ;
+ break;
+ default:
+ goto out;
+ }
+
+ switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) {
+ case '-':
+ break;
+ case 'w':
+ case 'W':
+ rule.smk_access |= MAY_WRITE;
+ break;
+ default:
+ goto out;
+ }
+
+ switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) {
+ case '-':
+ break;
+ case 'x':
+ case 'X':
+ rule.smk_access |= MAY_EXEC;
+ break;
+ default:
+ goto out;
+ }
+
+ switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) {
+ case '-':
+ break;
+ case 'a':
+ case 'A':
+ rule.smk_access |= MAY_READ;
+ break;
+ default:
+ goto out;
+ }
+
+ smk_set_access(&rule);
+ rc = count;
+
+out:
+ kfree(data);
+ return rc;
+}
+
+static const struct file_operations smk_load_ops = {
+ .open = smk_open_load,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = smk_write_load,
+ .release = seq_release,
+};
+
+/**
+ * smk_cipso_doi - initialize the CIPSO domain
+ */
+static void smk_cipso_doi(void)
+{
+ int rc;
+ struct cipso_v4_doi *doip;
+ struct netlbl_audit audit_info;
+
+ audit_info.loginuid = audit_get_loginuid(current);
+ audit_info.sessionid = audit_get_sessionid(current);
+ audit_info.secid = smack_to_secid(current->security);
+
+ rc = netlbl_cfg_map_del(NULL, &audit_info);
+ if (rc != 0)
+ printk(KERN_WARNING "%s:%d remove rc = %d\n",
+ __func__, __LINE__, rc);
+
+ doip = kmalloc(sizeof(struct cipso_v4_doi), GFP_KERNEL);
+ if (doip == NULL)
+ panic("smack: Failed to initialize cipso DOI.\n");
+ doip->map.std = NULL;
+ doip->doi = smk_cipso_doi_value;
+ doip->type = CIPSO_V4_MAP_PASS;
+ doip->tags[0] = CIPSO_V4_TAG_RBITMAP;
+ for (rc = 1; rc < CIPSO_V4_TAG_MAXCNT; rc++)
+ doip->tags[rc] = CIPSO_V4_TAG_INVALID;
+
+ rc = netlbl_cfg_cipsov4_add_map(doip, NULL, &audit_info);
+ if (rc != 0) {
+ printk(KERN_WARNING "%s:%d add rc = %d\n",
+ __func__, __LINE__, rc);
+ kfree(doip);
+ }
+}
+
+/**
+ * smk_unlbl_ambient - initialize the unlabeled domain
+ */
+static void smk_unlbl_ambient(char *oldambient)
+{
+ int rc;
+ struct netlbl_audit audit_info;
+
+ audit_info.loginuid = audit_get_loginuid(current);
+ audit_info.sessionid = audit_get_sessionid(current);
+ audit_info.secid = smack_to_secid(current->security);
+
+ if (oldambient != NULL) {
+ rc = netlbl_cfg_map_del(oldambient, &audit_info);
+ if (rc != 0)
+ printk(KERN_WARNING "%s:%d remove rc = %d\n",
+ __func__, __LINE__, rc);
+ }
+
+ rc = netlbl_cfg_unlbl_add_map(smack_net_ambient, &audit_info);
+ if (rc != 0)
+ printk(KERN_WARNING "%s:%d add rc = %d\n",
+ __func__, __LINE__, rc);
+}
+
+/*
+ * Seq_file read operations for /smack/cipso
+ */
+
+static void *cipso_seq_start(struct seq_file *s, loff_t *pos)
+{
+ if (*pos == SEQ_READ_FINISHED)
+ return NULL;
+
+ return smack_known;
+}
+
+static void *cipso_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct smack_known *skp = ((struct smack_known *) v)->smk_next;
+
+ /*
+ * Omit labels with no associated cipso value
+ */
+ while (skp != NULL && !skp->smk_cipso)
+ skp = skp->smk_next;
+
+ if (skp == NULL)
+ *pos = SEQ_READ_FINISHED;
+
+ return skp;
+}
+
+/*
+ * Print cipso labels in format:
+ * label level[/cat[,cat]]
+ */
+static int cipso_seq_show(struct seq_file *s, void *v)
+{
+ struct smack_known *skp = (struct smack_known *) v;
+ struct smack_cipso *scp = skp->smk_cipso;
+ char *cbp;
+ char sep = '/';
+ int cat = 1;
+ int i;
+ unsigned char m;
+
+ if (scp == NULL)
+ return 0;
+
+ seq_printf(s, "%s %3d", (char *)&skp->smk_known, scp->smk_level);
+
+ cbp = scp->smk_catset;
+ for (i = 0; i < SMK_LABELLEN; i++)
+ for (m = 0x80; m != 0; m >>= 1) {
+ if (m & cbp[i]) {
+ seq_printf(s, "%c%d", sep, cat);
+ sep = ',';
+ }
+ cat++;
+ }
+
+ seq_putc(s, '\n');
+
+ return 0;
+}
+
+static void cipso_seq_stop(struct seq_file *s, void *v)
+{
+ /* No-op */
+}
+
+static struct seq_operations cipso_seq_ops = {
+ .start = cipso_seq_start,
+ .stop = cipso_seq_stop,
+ .next = cipso_seq_next,
+ .show = cipso_seq_show,
+};
+
+/**
+ * smk_open_cipso - open() for /smack/cipso
+ * @inode: inode structure representing file
+ * @file: "cipso" file pointer
+ *
+ * Connect our cipso_seq_* operations with /smack/cipso
+ * file_operations
+ */
+static int smk_open_cipso(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &cipso_seq_ops);
+}
+
+/**
+ * smk_write_cipso - write() for /smack/cipso
+ * @filp: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Accepts only one cipso rule per write call.
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_cipso(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct smack_known *skp;
+ struct smack_cipso *scp = NULL;
+ char mapcatset[SMK_LABELLEN];
+ int maplevel;
+ int cat;
+ int catlen;
+ ssize_t rc = -EINVAL;
+ char *data = NULL;
+ char *rule;
+ int ret;
+ int i;
+
+ /*
+ * Must have privilege.
+ * No partial writes.
+ * Enough data must be present.
+ */
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+ if (*ppos != 0)
+ return -EINVAL;
+ if (count < SMK_CIPSOMIN || count > SMK_CIPSOMAX)
+ return -EINVAL;
+
+ data = kzalloc(count + 1, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, count) != 0) {
+ rc = -EFAULT;
+ goto unlockedout;
+ }
+
+ data[count] = '\0';
+ rule = data;
+ /*
+ * Only allow one writer at a time. Writes should be
+ * quite rare and small in any case.
+ */
+ mutex_lock(&smack_cipso_lock);
+
+ skp = smk_import_entry(rule, 0);
+ if (skp == NULL)
+ goto out;
+
+ rule += SMK_LABELLEN;;
+ ret = sscanf(rule, "%d", &maplevel);
+ if (ret != 1 || maplevel > SMACK_CIPSO_MAXLEVEL)
+ goto out;
+
+ rule += SMK_DIGITLEN;
+ ret = sscanf(rule, "%d", &catlen);
+ if (ret != 1 || catlen > SMACK_CIPSO_MAXCATNUM)
+ goto out;
+
+ if (count != (SMK_CIPSOMIN + catlen * SMK_DIGITLEN))
+ goto out;
+
+ memset(mapcatset, 0, sizeof(mapcatset));
+
+ for (i = 0; i < catlen; i++) {
+ rule += SMK_DIGITLEN;
+ ret = sscanf(rule, "%d", &cat);
+ if (ret != 1 || cat > SMACK_CIPSO_MAXCATVAL)
+ goto out;
+
+ smack_catset_bit(cat, mapcatset);
+ }
+
+ if (skp->smk_cipso == NULL) {
+ scp = kzalloc(sizeof(struct smack_cipso), GFP_KERNEL);
+ if (scp == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ }
+
+ spin_lock_bh(&skp->smk_cipsolock);
+
+ if (scp == NULL)
+ scp = skp->smk_cipso;
+ else
+ skp->smk_cipso = scp;
+
+ scp->smk_level = maplevel;
+ memcpy(scp->smk_catset, mapcatset, sizeof(mapcatset));
+
+ spin_unlock_bh(&skp->smk_cipsolock);
+
+ rc = count;
+out:
+ mutex_unlock(&smack_cipso_lock);
+unlockedout:
+ kfree(data);
+ return rc;
+}
+
+static const struct file_operations smk_cipso_ops = {
+ .open = smk_open_cipso,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = smk_write_cipso,
+ .release = seq_release,
+};
+
+/**
+ * smk_read_doi - read() for /smack/doi
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @count: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t smk_read_doi(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char temp[80];
+ ssize_t rc;
+
+ if (*ppos != 0)
+ return 0;
+
+ sprintf(temp, "%d", smk_cipso_doi_value);
+ rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+
+ return rc;
+}
+
+/**
+ * smk_write_doi - write() for /smack/doi
+ * @filp: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_doi(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char temp[80];
+ int i;
+
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (count >= sizeof(temp) || count == 0)
+ return -EINVAL;
+
+ if (copy_from_user(temp, buf, count) != 0)
+ return -EFAULT;
+
+ temp[count] = '\0';
+
+ if (sscanf(temp, "%d", &i) != 1)
+ return -EINVAL;
+
+ smk_cipso_doi_value = i;
+
+ smk_cipso_doi();
+
+ return count;
+}
+
+static const struct file_operations smk_doi_ops = {
+ .read = smk_read_doi,
+ .write = smk_write_doi,
+};
+
+/**
+ * smk_read_direct - read() for /smack/direct
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @count: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t smk_read_direct(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char temp[80];
+ ssize_t rc;
+
+ if (*ppos != 0)
+ return 0;
+
+ sprintf(temp, "%d", smack_cipso_direct);
+ rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+
+ return rc;
+}
+
+/**
+ * smk_write_direct - write() for /smack/direct
+ * @filp: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_direct(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char temp[80];
+ int i;
+
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (count >= sizeof(temp) || count == 0)
+ return -EINVAL;
+
+ if (copy_from_user(temp, buf, count) != 0)
+ return -EFAULT;
+
+ temp[count] = '\0';
+
+ if (sscanf(temp, "%d", &i) != 1)
+ return -EINVAL;
+
+ smack_cipso_direct = i;
+
+ return count;
+}
+
+static const struct file_operations smk_direct_ops = {
+ .read = smk_read_direct,
+ .write = smk_write_direct,
+};
+
+/**
+ * smk_read_ambient - read() for /smack/ambient
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @cn: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t smk_read_ambient(struct file *filp, char __user *buf,
+ size_t cn, loff_t *ppos)
+{
+ ssize_t rc;
+ int asize;
+
+ if (*ppos != 0)
+ return 0;
+ /*
+ * Being careful to avoid a problem in the case where
+ * smack_net_ambient gets changed in midstream.
+ */
+ mutex_lock(&smack_ambient_lock);
+
+ asize = strlen(smack_net_ambient) + 1;
+
+ if (cn >= asize)
+ rc = simple_read_from_buffer(buf, cn, ppos,
+ smack_net_ambient, asize);
+ else
+ rc = -EINVAL;
+
+ mutex_unlock(&smack_ambient_lock);
+
+ return rc;
+}
+
+/**
+ * smk_write_ambient - write() for /smack/ambient
+ * @filp: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_ambient(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char in[SMK_LABELLEN];
+ char *oldambient;
+ char *smack;
+
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (count >= SMK_LABELLEN)
+ return -EINVAL;
+
+ if (copy_from_user(in, buf, count) != 0)
+ return -EFAULT;
+
+ smack = smk_import(in, count);
+ if (smack == NULL)
+ return -EINVAL;
+
+ mutex_lock(&smack_ambient_lock);
+
+ oldambient = smack_net_ambient;
+ smack_net_ambient = smack;
+ smk_unlbl_ambient(oldambient);
+
+ mutex_unlock(&smack_ambient_lock);
+
+ return count;
+}
+
+static const struct file_operations smk_ambient_ops = {
+ .read = smk_read_ambient,
+ .write = smk_write_ambient,
+};
+
+/**
+ * smk_read_onlycap - read() for /smack/onlycap
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @cn: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t smk_read_onlycap(struct file *filp, char __user *buf,
+ size_t cn, loff_t *ppos)
+{
+ char *smack = "";
+ ssize_t rc = -EINVAL;
+ int asize;
+
+ if (*ppos != 0)
+ return 0;
+
+ if (smack_onlycap != NULL)
+ smack = smack_onlycap;
+
+ asize = strlen(smack) + 1;
+
+ if (cn >= asize)
+ rc = simple_read_from_buffer(buf, cn, ppos, smack, asize);
+
+ return rc;
+}
+
+/**
+ * smk_write_onlycap - write() for /smack/onlycap
+ * @filp: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char in[SMK_LABELLEN];
+ char *sp = current->security;
+
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ /*
+ * This can be done using smk_access() but is done
+ * explicitly for clarity. The smk_access() implementation
+ * would use smk_access(smack_onlycap, MAY_WRITE)
+ */
+ if (smack_onlycap != NULL && smack_onlycap != sp)
+ return -EPERM;
+
+ if (count >= SMK_LABELLEN)
+ return -EINVAL;
+
+ if (copy_from_user(in, buf, count) != 0)
+ return -EFAULT;
+
+ /*
+ * Should the null string be passed in unset the onlycap value.
+ * This seems like something to be careful with as usually
+ * smk_import only expects to return NULL for errors. It
+ * is usually the case that a nullstring or "\n" would be
+ * bad to pass to smk_import but in fact this is useful here.
+ */
+ smack_onlycap = smk_import(in, count);
+
+ return count;
+}
+
+static const struct file_operations smk_onlycap_ops = {
+ .read = smk_read_onlycap,
+ .write = smk_write_onlycap,
+};
+
+struct option_names {
+ int o_number;
+ char *o_name;
+ char *o_alias;
+};
+
+static struct option_names netlbl_choices[] = {
+ { NETLBL_NLTYPE_RIPSO,
+ NETLBL_NLTYPE_RIPSO_NAME, "ripso" },
+ { NETLBL_NLTYPE_CIPSOV4,
+ NETLBL_NLTYPE_CIPSOV4_NAME, "cipsov4" },
+ { NETLBL_NLTYPE_CIPSOV4,
+ NETLBL_NLTYPE_CIPSOV4_NAME, "cipso" },
+ { NETLBL_NLTYPE_CIPSOV6,
+ NETLBL_NLTYPE_CIPSOV6_NAME, "cipsov6" },
+ { NETLBL_NLTYPE_UNLABELED,
+ NETLBL_NLTYPE_UNLABELED_NAME, "unlabeled" },
+};
+
+/**
+ * smk_read_nltype - read() for /smack/nltype
+ * @filp: file pointer, not actually used
+ * @buf: where to put the result
+ * @count: maximum to send along
+ * @ppos: where to start
+ *
+ * Returns number of bytes read or error code, as appropriate
+ */
+static ssize_t smk_read_nltype(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char bound[40];
+ ssize_t rc;
+ int i;
+
+ if (count < SMK_LABELLEN)
+ return -EINVAL;
+
+ if (*ppos != 0)
+ return 0;
+
+ sprintf(bound, "unknown");
+
+ for (i = 0; i < ARRAY_SIZE(netlbl_choices); i++)
+ if (smack_net_nltype == netlbl_choices[i].o_number) {
+ sprintf(bound, "%s", netlbl_choices[i].o_name);
+ break;
+ }
+
+ rc = simple_read_from_buffer(buf, count, ppos, bound, strlen(bound));
+
+ return rc;
+}
+
+/**
+ * smk_write_nltype - write() for /smack/nltype
+ * @filp: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_nltype(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char bound[40];
+ char *cp;
+ int i;
+
+ if (!capable(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ if (count >= 40)
+ return -EINVAL;
+
+ if (copy_from_user(bound, buf, count) != 0)
+ return -EFAULT;
+
+ bound[count] = '\0';
+ cp = strchr(bound, ' ');
+ if (cp != NULL)
+ *cp = '\0';
+ cp = strchr(bound, '\n');
+ if (cp != NULL)
+ *cp = '\0';
+
+ for (i = 0; i < ARRAY_SIZE(netlbl_choices); i++)
+ if (strcmp(bound, netlbl_choices[i].o_name) == 0 ||
+ strcmp(bound, netlbl_choices[i].o_alias) == 0) {
+ smack_net_nltype = netlbl_choices[i].o_number;
+ return count;
+ }
+ /*
+ * Not a valid choice.
+ */
+ return -EINVAL;
+}
+
+static const struct file_operations smk_nltype_ops = {
+ .read = smk_read_nltype,
+ .write = smk_write_nltype,
+};
+
+/**
+ * smk_fill_super - fill the /smackfs superblock
+ * @sb: the empty superblock
+ * @data: unused
+ * @silent: unused
+ *
+ * Fill in the well known entries for /smack
+ *
+ * Returns 0 on success, an error code on failure
+ */
+static int smk_fill_super(struct super_block *sb, void *data, int silent)
+{
+ int rc;
+ struct inode *root_inode;
+
+ static struct tree_descr smack_files[] = {
+ [SMK_LOAD] =
+ {"load", &smk_load_ops, S_IRUGO|S_IWUSR},
+ [SMK_CIPSO] =
+ {"cipso", &smk_cipso_ops, S_IRUGO|S_IWUSR},
+ [SMK_DOI] =
+ {"doi", &smk_doi_ops, S_IRUGO|S_IWUSR},
+ [SMK_DIRECT] =
+ {"direct", &smk_direct_ops, S_IRUGO|S_IWUSR},
+ [SMK_AMBIENT] =
+ {"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
+ [SMK_NLTYPE] =
+ {"nltype", &smk_nltype_ops, S_IRUGO|S_IWUSR},
+ [SMK_ONLYCAP] =
+ {"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
+ /* last one */ {""}
+ };
+
+ rc = simple_fill_super(sb, SMACK_MAGIC, smack_files);
+ if (rc != 0) {
+ printk(KERN_ERR "%s failed %d while creating inodes\n",
+ __func__, rc);
+ return rc;
+ }
+
+ root_inode = sb->s_root->d_inode;
+ root_inode->i_security = new_inode_smack(smack_known_floor.smk_known);
+
+ return 0;
+}
+
+/**
+ * smk_get_sb - get the smackfs superblock
+ * @fs_type: passed along without comment
+ * @flags: passed along without comment
+ * @dev_name: passed along without comment
+ * @data: passed along without comment
+ * @mnt: passed along without comment
+ *
+ * Just passes everything along.
+ *
+ * Returns what the lower level code does.
+ */
+static int smk_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data,
+ struct vfsmount *mnt)
+{
+ return get_sb_single(fs_type, flags, data, smk_fill_super, mnt);
+}
+
+static struct file_system_type smk_fs_type = {
+ .name = "smackfs",
+ .get_sb = smk_get_sb,
+ .kill_sb = kill_litter_super,
+};
+
+static struct vfsmount *smackfs_mount;
+
+/**
+ * init_smk_fs - get the smackfs superblock
+ *
+ * register the smackfs
+ *
+ * Do not register smackfs if Smack wasn't enabled
+ * on boot. We can not put this method normally under the
+ * smack_init() code path since the security subsystem get
+ * initialized before the vfs caches.
+ *
+ * Returns true if we were not chosen on boot or if
+ * we were chosen and filesystem registration succeeded.
+ */
+static int __init init_smk_fs(void)
+{
+ int err;
+
+ if (!security_module_enable(&smack_ops))
+ return 0;
+
+ err = register_filesystem(&smk_fs_type);
+ if (!err) {
+ smackfs_mount = kern_mount(&smk_fs_type);
+ if (IS_ERR(smackfs_mount)) {
+ printk(KERN_ERR "smackfs: could not mount!\n");
+ err = PTR_ERR(smackfs_mount);
+ smackfs_mount = NULL;
+ }
+ }
+
+ smk_cipso_doi();
+ smk_unlbl_ambient(NULL);
+
+ return err;
+}
+
+__initcall(init_smk_fs);
OpenPOWER on IntegriCloud