2008-01-09 01:02:58

by Kentaro Takeda

[permalink] [raw]
Subject: [TOMOYO #6 retry 18/21] LSM adapter functions.

To avoid namespace_sem deadlock, this patch uses
"current->last_vfsmount" associated by wrapper functions.

Signed-off-by: Kentaro Takeda <[email protected]>
Signed-off-by: Tetsuo Handa <[email protected]>
---
security/tomoyo/tomoyo.c | 825 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 825 insertions(+)

--- /dev/null
+++ linux-2.6-mm/security/tomoyo/tomoyo.c
@@ -0,0 +1,825 @@
+/*
+ * security/tomoyo/tomoyo.c
+ *
+ * LSM hooks for TOMOYO Linux.
+ */
+
+#include "tomoyo.h"
+#include "realpath.h"
+
+#define MAX_SOCK_ADDR 128 /* net/socket.c */
+
+LIST_HEAD(domain_list);
+
+static struct kmem_cache *tmy_cachep;
+
+static int tmy_task_alloc_security(struct task_struct *p)
+{
+ struct tmy_security *ptr = kmem_cache_alloc(tmy_cachep, GFP_KERNEL);
+
+ if (!ptr)
+ return -ENOMEM;
+ memcpy(ptr, TMY_SECURITY, sizeof(*ptr));
+ p->security = ptr;
+ return 0;
+}
+
+static void tmy_task_free_security(struct task_struct *p)
+{
+ kmem_cache_free(tmy_cachep, p->security);
+}
+
+static int tmy_bprm_alloc_security(struct linux_binprm *bprm)
+{
+ TMY_SECURITY->prev_domain = TMY_SECURITY->domain;
+ return 0;
+}
+
+static int tmy_bprm_check_security(struct linux_binprm *bprm)
+{
+ struct domain_info *next_domain = NULL;
+ int retval = 0;
+
+ tmy_load_policy(bprm->filename);
+
+ /*
+ * TMY_CHECK_READ_FOR_OPEN_EXEC bit indicates whether this function is
+ * called by do_execve() or not.
+ * If called by do_execve(), I do domain transition.
+ */
+ if (!(TMY_SECURITY->flags
+ & TMY_CHECK_READ_FOR_OPEN_EXEC)) {
+ retval = tmy_find_next_domain(bprm, &next_domain);
+ if (retval == 0) {
+ struct domain_info *domain = TMY_SECURITY->domain;
+ TMY_SECURITY->domain = next_domain;
+ retval = tmy_check_environ(bprm);
+ if (!retval)
+ TMY_SECURITY->flags |=
+ TMY_CHECK_READ_FOR_OPEN_EXEC;
+ else
+ TMY_SECURITY->domain = domain;
+ }
+ }
+
+ return retval;
+}
+
+static void tmy_bprm_post_apply_creds(struct linux_binprm *bprm)
+{
+ TMY_SECURITY->prev_domain = TMY_SECURITY->domain;
+}
+
+static void tmy_bprm_free_security(struct linux_binprm *bprm)
+{
+ TMY_SECURITY->domain = TMY_SECURITY->prev_domain;
+ TMY_SECURITY->flags &= ~TMY_CHECK_READ_FOR_OPEN_EXEC;
+}
+
+static int tmy_sysctl(struct ctl_table *table, int op)
+{
+ int error;
+ char *name;
+
+ if ((op & 6) == 0)
+ return 0;
+
+ name = sysctlpath_from_table(table);
+ if (!name)
+ return -ENOMEM;
+
+ error = tmy_file_perm(name, op & 6, "sysctl");
+ tmy_free(name);
+
+ return error;
+}
+
+static int tmy_inode_permission(struct inode *inode,
+ int mask,
+ struct nameidata *nd)
+{
+ int flag = 0;
+
+ if (S_ISDIR(inode->i_mode)) /* ignore because inode is directory */
+ return 0;
+ /*
+ if (!nd) {
+ printk("tmy_inode_permission: NULL nameidata\n");
+ dump_stack();
+ return 0;
+ } else if (!nd->mnt) {
+ printk("tmy_inode_permission: NULL vfsmount\n");
+ dump_stack();
+ return 0;
+ }
+ */
+ if (!nd || !nd->path.dentry || !nd->path.mnt)
+ return 0;
+ /*
+ * If called by other than do_execve(), I check for read permission of
+ * interpreter.
+ * Unlike DAC, I don't check for read permission of pathname passed to
+ * do_execve().
+ * TOMOYO Linux checks for program's execute permission and
+ * interpreter's read permission.
+ */
+ if ((mask == MAY_EXEC) &&
+ (TMY_SECURITY->flags & TMY_CHECK_READ_FOR_OPEN_EXEC))
+ mask = MAY_READ;
+ if ((mask == MAY_EXEC) || (mask == 0))
+ return 0;
+
+ if (mask == (MAY_READ | MAY_EXEC))
+ flag |= O_RDONLY + 1;
+ else {
+ if (mask & MAY_READ)
+ flag |= O_RDONLY + 1;
+ if (mask & MAY_WRITE)
+ flag |= O_WRONLY + 1;
+ if ((mask & MAY_APPEND))
+ flag |= O_APPEND;
+ }
+
+ return tmy_open_perm(nd->path.dentry, nd->path.mnt, flag);
+}
+
+static int tmy_do_single_write_perm(int operation, struct dentry *dentry)
+{
+ struct vfsmount *mnt = current->last_vfsmount;
+ if (!dentry || !mnt)
+ return 0;
+ if (!sbin_init_started)
+ return 0;
+ return tmy_single_write_perm(operation, dentry, mnt);
+}
+
+static int tmy_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ int err = 0;
+ unsigned int ia_valid = iattr->ia_valid;
+ if (ia_valid & ATTR_MODE)
+ err = tmy_capable(TMY_SYS_CHMOD);
+ if (!err && (ia_valid & (ATTR_UID | ATTR_GID)))
+ err = tmy_capable(TMY_SYS_CHOWN);
+ if (err)
+ return err;
+ if (ia_valid & ATTR_SIZE)
+ return tmy_do_single_write_perm(TMY_TYPE_TRUNCATE_ACL, dentry);
+ return 0;
+}
+
+static int tmy_inode_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ return tmy_do_single_write_perm(TMY_TYPE_CREATE_ACL, dentry);
+}
+
+static int tmy_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+ const int err = tmy_capable(TMY_SYS_UNLINK);
+ if (err)
+ return err;
+ return tmy_do_single_write_perm(TMY_TYPE_UNLINK_ACL, dentry);
+}
+
+static int tmy_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ return tmy_do_single_write_perm(TMY_TYPE_MKDIR_ACL, dentry);
+}
+
+static int tmy_inode_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ return tmy_do_single_write_perm(TMY_TYPE_RMDIR_ACL, dentry);
+}
+
+static int tmy_inode_symlink(struct inode *dir,
+ struct dentry *dentry,
+ const char *old_name)
+{
+ const int err = tmy_capable(TMY_SYS_SYMLINK);
+ if (err)
+ return err;
+ return tmy_do_single_write_perm(TMY_TYPE_SYMLINK_ACL, dentry);
+}
+
+static int tmy_inode_mknod(struct inode *inode,
+ struct dentry *dentry,
+ int mode,
+ dev_t dev)
+{
+ int err;
+ if (S_ISCHR(mode)) {
+ err = tmy_capable(TMY_CREATE_CHAR_DEV);
+ if (err)
+ return err;
+ return tmy_do_single_write_perm(TMY_TYPE_MKCHAR_ACL, dentry);
+ }
+ if (S_ISBLK(mode)) {
+ err = tmy_capable(TMY_CREATE_BLOCK_DEV);
+ if (err)
+ return err;
+ return tmy_do_single_write_perm(TMY_TYPE_MKBLOCK_ACL, dentry);
+ }
+ if (S_ISFIFO(mode)) {
+ err = tmy_capable(TMY_CREATE_FIFO);
+ if (err)
+ return err;
+ return tmy_do_single_write_perm(TMY_TYPE_MKFIFO_ACL, dentry);
+ }
+ if (S_ISSOCK(mode)) {
+ err = tmy_capable(TMY_CREATE_UNIX_SOCKET);
+ if (err)
+ return err;
+ return tmy_do_single_write_perm(TMY_TYPE_MKSOCK_ACL, dentry);
+ }
+
+ return 0;
+}
+
+static int tmy_do_double_write_perm(int operation,
+ struct dentry *old_dentry,
+ struct dentry *new_dentry)
+{
+ struct vfsmount *mnt = current->last_vfsmount;
+ if (!old_dentry || !new_dentry || !mnt)
+ return 0;
+ if (!sbin_init_started)
+ return 0;
+ return tmy_double_write_perm(operation, old_dentry, mnt,
+ new_dentry, mnt);
+}
+
+static int tmy_inode_link(struct dentry *old_dentry,
+ struct inode *inode,
+ struct dentry *new_dentry)
+{
+ const int err = tmy_capable(TMY_SYS_LINK);
+ if (err)
+ return err;
+ return tmy_do_double_write_perm(TMY_TYPE_LINK_ACL,
+ old_dentry, new_dentry);
+}
+
+static int tmy_inode_rename(struct inode *old_inode,
+ struct dentry *old_dentry,
+ struct inode *new_inode,
+ struct dentry *new_dentry)
+{
+ const int err = tmy_capable(TMY_SYS_RENAME);
+ if (err)
+ return err;
+ return tmy_do_double_write_perm(TMY_TYPE_RENAME_ACL,
+ old_dentry,
+ new_dentry);
+}
+
+static int tmy_file_fcntl(struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ if (!(arg & O_APPEND))
+ return tmy_rewrite_perm(file);
+ return 0;
+}
+
+static int tmy_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ switch (cmd) {
+ case FIOCLEX:
+ case FIONCLEX:
+ case FIONBIO:
+ case FIOASYNC:
+ case FIOQSIZE:
+ return 0;
+ case FIBMAP:
+ case FIGETBSZ:
+ case FIONREAD:
+ if (S_ISREG(file->f_path.dentry->d_inode->i_mode))
+ return 0;
+ }
+ return tmy_capable(TMY_SYS_IOCTL);
+}
+
+static int tmy_socket_listen(struct socket *sock, int backlog)
+{
+ char addr[MAX_SOCK_ADDR];
+ int addr_len;
+ int error;
+
+ /* I don't check if called by kernel process. */
+ if (segment_eq(get_fs(), KERNEL_DS))
+ return 0;
+
+ if (sock->type != SOCK_STREAM)
+ return 0;
+ if (sock->sk->sk_family != PF_INET && sock->sk->sk_family != PF_INET6)
+ return 0;
+
+ error = tmy_capable(TMY_INET_STREAM_SOCKET_LISTEN);
+ if (error)
+ return error;
+
+ if (sock->ops->getname(sock, (struct sockaddr *) addr, &addr_len, 0))
+ return -EPERM;
+
+ switch (((struct sockaddr *) addr)->sa_family) {
+ struct sockaddr_in6 *in6;
+ struct sockaddr_in *in;
+
+ case AF_INET6:
+ in6 = (struct sockaddr_in6 *) addr;
+ error = tmy_network_listen_acl(1, in6->sin6_addr.s6_addr,
+ in6->sin6_port);
+ break;
+ case AF_INET:
+ in = (struct sockaddr_in *) addr;
+ error = tmy_network_listen_acl(0, (u8 *) &in->sin_addr,
+ in->sin_port);
+ break;
+ }
+
+ return error;
+}
+
+static int tmy_socket_create(int family, int type, int protocol, int kern)
+{
+ int error = 0;
+ if (kern)
+ return 0;
+ if (family == PF_INET || family == PF_INET6) {
+ switch (type) {
+ case SOCK_STREAM:
+ return tmy_capable(TMY_INET_STREAM_SOCKET_CREATE);
+ break;
+ case SOCK_DGRAM:
+ return tmy_capable(TMY_USE_INET_DGRAM_SOCKET);
+ break;
+ case SOCK_RAW:
+ return tmy_capable(TMY_USE_INET_RAW_SOCKET);
+ break;
+ }
+ } else if (family == PF_PACKET) {
+ return tmy_capable(TMY_USE_PACKET_SOCKET);
+ } else if (family == PF_ROUTE) {
+ return tmy_capable(TMY_USE_ROUTE_SOCKET);
+ }
+ return error;
+}
+
+static int tmy_socket_connect(struct socket *sock,
+ struct sockaddr *addr,
+ int addr_len0)
+{
+ unsigned int addr_len = (unsigned int) addr_len0;
+ int error = 0;
+ const unsigned int type = sock->type;
+
+ /* I don't check if called by kernel process. */
+ if (segment_eq(get_fs(), KERNEL_DS))
+ return 0;
+
+ if (type == SOCK_STREAM) {
+ error = tmy_capable(TMY_INET_STREAM_SOCKET_CONNECT);
+ if (error)
+ return error;
+ }
+
+ if (type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_RAW)
+ return 0;
+
+ switch (addr->sa_family) {
+ struct sockaddr_in6 *in6;
+ struct sockaddr_in *in;
+
+ case AF_INET6:
+ if (addr_len < SIN6_LEN_RFC2133)
+ break;
+
+ in6 = (struct sockaddr_in6 *) addr;
+ if (type != SOCK_RAW)
+ error = tmy_network_connect_acl(1, type,
+ in6->sin6_addr.s6_addr,
+ in6->sin6_port);
+ else {
+ const u16 port = htons(sock->sk->sk_protocol);
+
+ error = tmy_network_connect_acl(1, SOCK_RAW,
+ in6->sin6_addr.s6_addr,
+ port);
+ }
+ break;
+
+ case AF_INET:
+ if (addr_len < sizeof(struct sockaddr_in))
+ break;
+
+ in = (struct sockaddr_in *) addr;
+ if (type != SOCK_RAW)
+ error = tmy_network_connect_acl(0, type,
+ (u8 *) &in->sin_addr,
+ in->sin_port);
+ else {
+ const u16 port = htons(sock->sk->sk_protocol);
+
+ error = tmy_network_connect_acl(0, SOCK_RAW,
+ (u8 *) &in->sin_addr,
+ port);
+ }
+ break;
+ }
+
+ return error;
+}
+
+static int tmy_socket_bind(struct socket *sock,
+ struct sockaddr *addr,
+ int addr_len0)
+{
+ unsigned int addr_len = (unsigned int) addr_len0;
+ int error = 0;
+ const unsigned int type = sock->type;
+
+ /* I don't check if called by kernel process. */
+ if (segment_eq(get_fs(), KERNEL_DS))
+ return 0;
+
+ if (type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_RAW)
+ return error;
+
+ switch (addr->sa_family) {
+ struct sockaddr_in6 *in6;
+ struct sockaddr_in *in;
+
+ case AF_INET6:
+ if (addr_len < SIN6_LEN_RFC2133)
+ break;
+
+ in6 = ((struct sockaddr_in6 *) addr);
+ if (type != SOCK_RAW)
+ error = tmy_network_bind_acl(1, type,
+ in6->sin6_addr.s6_addr,
+ in6->sin6_port);
+ else {
+ const u16 port = htons(sock->sk->sk_protocol);
+
+ error = tmy_network_bind_acl(1, SOCK_RAW,
+ in6->sin6_addr.s6_addr,
+ port);
+ }
+ break;
+
+ case AF_INET:
+ if (addr_len < sizeof(struct sockaddr_in))
+ break;
+
+ in = (struct sockaddr_in *) addr;
+ if (type != SOCK_RAW)
+ error = tmy_network_bind_acl(0, type,
+ (u8 *) &in->sin_addr,
+ in->sin_port);
+ else {
+ const u16 port = htons(sock->sk->sk_protocol);
+
+ error = tmy_network_bind_acl(0, SOCK_RAW,
+ (u8 *) &in->sin_addr,
+ port);
+ }
+ break;
+ }
+
+ return error;
+}
+
+static int tmy_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
+{
+ int error = 0;
+ const int type = sock->type;
+ struct sockaddr *addr = (struct sockaddr *) msg->msg_name;
+ const unsigned int addr_len = msg->msg_namelen;
+
+ /* I don't check if called by kernel process. */
+ if (segment_eq(get_fs(), KERNEL_DS))
+ return 0;
+
+ if (!addr || (type != SOCK_DGRAM && type != SOCK_RAW))
+ return error;
+
+ switch (addr->sa_family) {
+ struct sockaddr_in6 *in6;
+ struct sockaddr_in *in;
+ u16 port;
+
+ case AF_INET6:
+ if (addr_len < SIN6_LEN_RFC2133)
+ break;
+
+ in6 = (struct sockaddr_in6 *) addr;
+ port = htons(sock->sk->sk_protocol);
+ error = tmy_network_sendmsg_acl(1, type, in6->sin6_addr.s6_addr,
+ type == SOCK_DGRAM ?
+ in6->sin6_port : port);
+ break;
+
+ case AF_INET:
+ if (addr_len < sizeof(struct sockaddr_in))
+ break;
+
+ in = (struct sockaddr_in *) addr;
+ port = htons(sock->sk->sk_protocol);
+ error = tmy_network_sendmsg_acl(0, type, (u8 *) &in->sin_addr,
+ type == SOCK_DGRAM ?
+ in->sin_port : port);
+ break;
+ }
+
+ return error;
+}
+
+static int tmy_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+ int error = 0;
+ int addr_len;
+ char addr[MAX_SOCK_ADDR];
+ struct sockaddr *sockaddr = (struct sockaddr *) addr;
+
+ /* I don't check if called by kernel process. */
+ if (segment_eq(get_fs(), KERNEL_DS))
+ return 0;
+
+ if ((newsock->sk->sk_family != PF_INET) &&
+ (newsock->sk->sk_family != PF_INET6))
+ return error;
+
+ if (newsock->ops->getname(newsock, sockaddr, &addr_len, 2) == 0) {
+ switch (sockaddr->sa_family) {
+ struct sockaddr_in6 *in6;
+ struct sockaddr_in *in;
+
+ case AF_INET6:
+ in6 = (struct sockaddr_in6 *) addr;
+ error = tmy_network_accept_acl(1,
+ in6->sin6_addr.s6_addr,
+ in6->sin6_port);
+ break;
+
+ case AF_INET:
+ in = (struct sockaddr_in *) addr;
+ error = tmy_network_accept_acl(0, (u8 *) &in->sin_addr,
+ in->sin_port);
+ break;
+ }
+ } else
+ error = -EPERM;
+
+ if (error)
+ return -ECONNABORTED;
+ return error;
+}
+
+static int tmy_socket_post_recv_datagram(struct sock *sk,
+ struct sk_buff *skb,
+ unsigned int flags)
+{
+ int error = 0;
+ const unsigned int type = sk->sk_type;
+
+ /* skb_recv_datagram() didn't dequeue. */
+ if (!skb)
+ return 0;
+
+ /* skb_recv_datagram() can be called from interrupt context. */
+ if (in_interrupt())
+ return 0;
+ /* I don't check if called by kernel process. */
+ if (segment_eq(get_fs(), KERNEL_DS))
+ return 0;
+
+ if (type != SOCK_DGRAM && type != SOCK_RAW)
+ return 0;
+
+ switch (sk->sk_family) {
+ struct sockaddr_in6 sin6;
+ struct sockaddr_in sin;
+ u16 port;
+
+ case AF_INET6:
+
+ if (type == SOCK_DGRAM) {
+ /* UDP IPv6 */
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = udp_hdr(skb)->source;
+
+ if (skb->protocol == htons(ETH_P_IP))
+ ipv6_addr_set(&sin6.sin6_addr, 0, 0,
+ htonl(0xffff),
+ ip_hdr(skb)->saddr);
+ else
+ ipv6_addr_copy(&sin6.sin6_addr,
+ &ipv6_hdr(skb)->saddr);
+
+ port = sin6.sin6_port;
+ } else {
+ /* RAW IPv6 */
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = 0;
+ ipv6_addr_copy(&sin6.sin6_addr, &ipv6_hdr(skb)->saddr);
+
+ port = htons(sk->sk_protocol);
+ }
+
+ error = tmy_network_recvmsg_acl(1, type,
+ sin6.sin6_addr.s6_addr, port);
+
+ break;
+
+ case AF_INET:
+
+ if (type == SOCK_DGRAM) {
+ /* UDP IPv4 */
+ sin.sin_family = AF_INET;
+ sin.sin_port = udp_hdr(skb)->source;
+ sin.sin_addr.s_addr = ip_hdr(skb)->saddr;
+
+ port = sin.sin_port;
+ } else {
+ /* RAW IPv4 */
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = ip_hdr(skb)->saddr;
+ sin.sin_port = 0;
+
+ port = htons(sk->sk_protocol);
+ }
+
+ error = tmy_network_recvmsg_acl(0, type,
+ (u8 *) &sin.sin_addr, port);
+
+ break;
+
+ }
+
+ if (error)
+ error = -EAGAIN;
+ return error;
+}
+
+static int tmy_sb_mount(char *dev_name,
+ struct nameidata *nd,
+ char *type,
+ unsigned long flags,
+ void *data)
+{
+ char *buf;
+ char *dir_name;
+ int error;
+
+ error = tmy_capable(TMY_SYS_MOUNT);
+ if (error)
+ return error;
+
+ buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ dir_name = d_path(&nd->path, buf, PAGE_SIZE);
+
+ if (IS_ERR(dir_name))
+ error = PTR_ERR(dir_name);
+ else
+ error = tmy_mount_perm(dev_name, dir_name, type, flags);
+
+ if (!error && (flags & MS_REMOUNT) == 0)
+ error = tmy_conceal_mount(nd);
+
+ kfree(buf);
+ return error;
+}
+
+static int tmy_sb_umount(struct vfsmount *mnt, int flags)
+{
+ const int err = tmy_capable(TMY_SYS_UMOUNT);
+ if (err)
+ return err;
+ return tmy_umount_perm(mnt);
+}
+
+static int tmy_settime(struct timespec *ts, struct timezone *tz)
+{
+ return tmy_capable(TMY_SYS_SETTIME);
+}
+
+static int tmy_sb_pivotroot(struct nameidata *old_nd, struct nameidata *new_nd)
+{
+ const int err = tmy_capable(TMY_SYS_PIVOT_ROOT);
+ if (err)
+ return err;
+ return tmy_pivot_root_perm(old_nd, new_nd);
+}
+
+static int tmy_task_capable(struct task_struct *tsk, int cap)
+{
+ int err = 0;
+ if (cap == CAP_SYS_CHROOT)
+ err = tmy_capable(TMY_SYS_CHROOT);
+ else if (cap == CAP_SYS_TTY_CONFIG)
+ err = tmy_capable(TMY_SYS_VHANGUP);
+ else if (cap == CAP_SYS_BOOT)
+ err = tmy_capable(TMY_SYS_REBOOT);
+ else if (cap == CAP_SYS_TIME)
+ err = tmy_capable(TMY_SYS_SETTIME);
+ if (err)
+ return err;
+ if (cap_raised(tsk->cap_effective, cap))
+ return 0;
+ return -EPERM;
+}
+
+#ifdef __HAVE_LSM_TASK_KILL_UNLOCKED
+static int tmy_task_kill_unlocked(int pid, int sig)
+{
+ const int err = tmy_capable(TMY_SYS_KILL);
+ if (err)
+ return err;
+ return tmy_signal_acl(sig, pid);
+}
+
+static int tmy_task_tkill_unlocked(int pid, int sig)
+{
+ const int err = tmy_capable(TMY_SYS_KILL);
+ if (err)
+ return err;
+ return tmy_signal_acl(sig, pid);
+}
+
+static int tmy_task_tgkill_unlocked(int tgid, int pid, int sig)
+{
+ const int err = tmy_capable(TMY_SYS_KILL);
+ if (err)
+ return err;
+ return tmy_signal_acl(sig, pid);
+}
+#endif
+
+static struct security_operations tomoyo_security_ops = {
+ .task_alloc_security = tmy_task_alloc_security,
+ .task_free_security = tmy_task_free_security,
+ .bprm_alloc_security = tmy_bprm_alloc_security,
+ .bprm_check_security = tmy_bprm_check_security,
+ .bprm_post_apply_creds = tmy_bprm_post_apply_creds,
+ .bprm_free_security = tmy_bprm_free_security,
+ .sysctl = tmy_sysctl,
+ .inode_permission = tmy_inode_permission,
+ .inode_setattr = tmy_inode_setattr,
+ .inode_create = tmy_inode_create,
+ .inode_unlink = tmy_inode_unlink,
+ .inode_mkdir = tmy_inode_mkdir,
+ .inode_rmdir = tmy_inode_rmdir,
+ .inode_symlink = tmy_inode_symlink,
+ .inode_mknod = tmy_inode_mknod,
+ .inode_link = tmy_inode_link,
+ .inode_rename = tmy_inode_rename,
+ .file_fcntl = tmy_file_fcntl,
+ .file_ioctl = tmy_file_ioctl,
+ .socket_listen = tmy_socket_listen,
+ .socket_create = tmy_socket_create,
+ .socket_connect = tmy_socket_connect,
+ .socket_bind = tmy_socket_bind,
+ .socket_sendmsg = tmy_socket_sendmsg,
+ .sb_mount = tmy_sb_mount,
+ .sb_umount = tmy_sb_umount,
+ .sb_pivotroot = tmy_sb_pivotroot,
+ .settime = tmy_settime,
+ .capable = tmy_task_capable,
+ .socket_post_accept = tmy_socket_post_accept,
+ .socket_post_recv_datagram = tmy_socket_post_recv_datagram,
+#ifdef __HAVE_LSM_TASK_KILL_UNLOCKED
+ .task_kill_unlocked = tmy_task_kill_unlocked,
+ .task_tkill_unlocked = tmy_task_tkill_unlocked,
+ .task_tgkill_unlocked = tmy_task_tgkill_unlocked,
+#endif
+};
+
+static int __init tmy_init(void)
+{
+
+ /* register ourselves with the security framework */
+ if (register_security(&tomoyo_security_ops))
+ panic("Failure registering TOMOYO Linux");
+
+ printk(KERN_INFO "TOMOYO Linux initialized\n");
+
+ INIT_LIST_HEAD(&KERNEL_DOMAIN.list);
+ INIT_LIST_HEAD(&KERNEL_DOMAIN.acl_info_list);
+ KERNEL_DOMAIN.domainname = tmy_save_name(TMY_ROOT_NAME);
+ list_add_tail_mb(&KERNEL_DOMAIN.list, &domain_list);
+ tmy_cachep = kmem_cache_create("tomoyo_security",
+ sizeof(struct tmy_security),
+ 0, SLAB_PANIC, NULL);
+ init_task.security = kmem_cache_alloc(tmy_cachep, GFP_KERNEL);
+ ((struct tmy_security *) init_task.security)->domain = &KERNEL_DOMAIN;
+ ((struct tmy_security *) init_task.security)->prev_domain = NULL;
+ ((struct tmy_security *) init_task.security)->flags = 0;
+
+ return 0;
+}
+
+security_initcall(tmy_init);

--