All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
To: linux-security-module@vger.kernel.org,
	Casey Schaufler <casey@schaufler-ca.com>,
	Paul Moore <paul@paul-moore.com>,
	John Johansen <john.johansen@canonical.com>,
	Kees Cook <kees@kernel.org>
Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Subject: [PATCH 05/10] CaitSith: Add LSM interface management file.
Date: Thu,  3 Nov 2022 02:10:20 +0900	[thread overview]
Message-ID: <20221102171025.126961-5-penguin-kernel@I-love.SAKURA.ne.jp> (raw)
In-Reply-To: <20221102171025.126961-1-penguin-kernel@I-love.SAKURA.ne.jp>

This file is used for registering CaitSith module into the
security_hook_heads list. Further patches will not be interesting for
reviewers, for further patches are providing similar functions provided
by TOMOYO (but too different to share the code).

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
---
 security/caitsith/lsm.c | 1358 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 1358 insertions(+)
 create mode 100644 security/caitsith/lsm.c

diff --git a/security/caitsith/lsm.c b/security/caitsith/lsm.c
new file mode 100644
index 000000000000..1a487b4021c6
--- /dev/null
+++ b/security/caitsith/lsm.c
@@ -0,0 +1,1358 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * lsm.c
+ *
+ * Copyright (C) 2010-2013  Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
+ *
+ * Version: 0.2.10   2021/06/06
+ */
+
+#include "caitsith.h"
+#include <linux/lsm_hooks.h>
+
+/* Prototype definition. */
+static int __cs_alloc_task_security(const struct task_struct *task);
+static void __cs_free_task_security(const struct task_struct *task);
+
+/* Dummy security context for avoiding NULL pointer dereference. */
+static struct cs_security cs_oom_security = {
+	.cs_domain_info = &cs_kernel_domain
+};
+
+/* Dummy security context for avoiding NULL pointer dereference. */
+static struct cs_security cs_default_security = {
+	.cs_domain_info = &cs_kernel_domain
+};
+
+/* List of "struct cs_security". */
+struct list_head cs_task_security_list[CS_MAX_TASK_SECURITY_HASH];
+/* Lock for protecting cs_task_security_list[]. */
+static DEFINE_SPINLOCK(cs_task_security_list_lock);
+
+/* Original hooks. */
+static union security_list_options original_cred_prepare;
+static union security_list_options original_task_alloc;
+static union security_list_options original_task_free;
+
+#if !defined(CONFIG_SECURITY_CAITSITH_DEBUG)
+#define cs_debug_trace(pos) do { } while (0)
+#else
+#define cs_debug_trace(pos)						\
+	do {								\
+		static bool done;					\
+		if (!done) {						\
+			pr_info("CAITSITH: Debug trace: " pos " of 2\n"); \
+			done = true;					\
+		}							\
+	} while (0)
+#endif
+
+/**
+ * cs_clear_execve - Release memory used by do_execve().
+ *
+ * @ret:      0 if do_execve() succeeded, negative value otherwise.
+ * @security: Pointer to "struct cs_security".
+ *
+ * Returns nothing.
+ */
+static void cs_clear_execve(int ret, struct cs_security *security)
+{
+	struct cs_request_info *r = security->r;
+
+	if (security == &cs_default_security || security == &cs_oom_security ||
+	    !r)
+		return;
+	security->r = NULL;
+	cs_finish_execve(ret, r);
+}
+
+/**
+ * cs_task_alloc_security - Allocate memory for new tasks.
+ *
+ * @p: Pointer to "struct task_struct".
+ * @clone_flags: Flags passed to clone().
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_task_alloc_security(struct task_struct *p,
+				   unsigned long clone_flags)
+{
+	int rc = __cs_alloc_task_security(p);
+
+	if (rc)
+		return rc;
+	if (original_task_alloc.task_alloc) {
+		rc = original_task_alloc.task_alloc(p, clone_flags);
+		if (rc)
+			__cs_free_task_security(p);
+	}
+	return rc;
+}
+
+/**
+ * cs_task_free_security - Release memory for "struct task_struct".
+ *
+ * @p: Pointer to "struct task_struct".
+ *
+ * Returns nothing.
+ */
+static void cs_task_free_security(struct task_struct *p)
+{
+	struct cs_security *ptr = cs_find_task_security(p);
+	struct cs_request_info *r = ptr->r;
+
+	if (original_task_free.task_free)
+		original_task_free.task_free(p);
+	/*
+	 * Since an LSM hook for reverting domain transition is missing,
+	 * cs_finish_execve() is not called if exited immediately after
+	 * execve() failed.
+	 */
+	if (r) {
+		cs_debug_trace("2");
+		kfree(r);
+		ptr->r = NULL;
+	}
+	__cs_free_task_security(p);
+}
+
+/**
+ * __cs_free_task_security - Release memory associated with "struct task_struct".
+ *
+ * @task: Pointer to "struct task_struct".
+ *
+ * Returns nothing.
+ */
+static void __cs_free_task_security(const struct task_struct *task)
+{
+	unsigned long flags;
+	struct cs_security *ptr = cs_find_task_security(task);
+
+	if (ptr == &cs_default_security || ptr == &cs_oom_security)
+		return;
+	spin_lock_irqsave(&cs_task_security_list_lock, flags);
+	list_del_rcu(&ptr->list);
+	spin_unlock_irqrestore(&cs_task_security_list_lock, flags);
+	kfree_rcu(ptr, rcu);
+}
+
+/**
+ * cs_cred_prepare - Allocate memory for new credentials.
+ *
+ * @new: Pointer to "struct cred".
+ * @old: Pointer to "struct cred".
+ * @gfp: Memory allocation flags.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_cred_prepare(struct cred *new, const struct cred *old,
+			   gfp_t gfp)
+{
+	/*
+	 * For checking whether reverting domain transition is needed or not.
+	 *
+	 * See cs_find_task_security() for reason.
+	 */
+	if (gfp == GFP_KERNEL)
+		cs_find_task_security(current);
+	if (original_cred_prepare.cred_prepare)
+		return original_cred_prepare.cred_prepare(new, old, gfp);
+	return 0;
+}
+
+/**
+ * cs_bprm_committing_creds - A hook which is called when do_execve() succeeded.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ *
+ * Returns nothing.
+ */
+static void cs_bprm_committing_creds(struct linux_binprm *bprm)
+{
+	cs_clear_execve(0, cs_current_security());
+}
+
+#ifndef CONFIG_SECURITY_CAITSITH_OMIT_USERSPACE_LOADER
+
+/**
+ * cs_policy_loader_exists - Check whether /sbin/caitsith-init exists.
+ *
+ * Returns true if /sbin/caitsith-init exists, false otherwise.
+ */
+static _Bool cs_policy_loader_exists(void)
+{
+	struct path path;
+
+	if (kern_path(CONFIG_SECURITY_CAITSITH_POLICY_LOADER, LOOKUP_FOLLOW, &path)
+	    == 0) {
+		path_put(&path);
+		return 1;
+	}
+	pr_info("Not activating CaitSith as %s does not exist.\n",
+		CONFIG_SECURITY_CAITSITH_POLICY_LOADER);
+	return 0;
+}
+
+/**
+ * cs_load_policy - Run external policy loader to load policy.
+ *
+ * @filename: The program about to start.
+ *
+ * Returns nothing.
+ *
+ * This function checks whether @filename is /sbin/init, and if so
+ * invoke /sbin/caitsith-init and wait for the termination of
+ * /sbin/caitsith-init and then continues invocation of /sbin/init.
+ * /sbin/caitsith-init reads policy files in /etc/caitsith/ directory and
+ * writes to /sys/kernel/security/caitsith/ interfaces.
+ */
+static void cs_load_policy(const char *filename)
+{
+	static _Bool done;
+
+	if (done)
+		return;
+	if (strcmp(filename, CONFIG_SECURITY_CAITSITH_ACTIVATION_TRIGGER))
+		return;
+	if (!cs_policy_loader_exists())
+		return;
+	done = 1;
+	{
+		char *argv[2];
+		char *envp[3];
+
+		pr_info("Calling %s to load policy. Please wait.\n",
+			CONFIG_SECURITY_CAITSITH_POLICY_LOADER);
+		argv[0] = (char *) CONFIG_SECURITY_CAITSITH_POLICY_LOADER;
+		argv[1] = NULL;
+		envp[0] = "HOME=/";
+		envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+		envp[2] = NULL;
+		call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
+	}
+	cs_check_profile();
+}
+
+#endif
+
+/**
+ * cs_bprm_check_security - Check permission for execve().
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_bprm_check_security(struct linux_binprm *bprm)
+{
+	struct cs_security *security = cs_current_security();
+
+	if (security == &cs_default_security || security == &cs_oom_security)
+		return -ENOMEM;
+	if (security->r)
+		return 0;
+#ifndef CONFIG_SECURITY_CAITSITH_OMIT_USERSPACE_LOADER
+	if (!cs_policy_loaded)
+		cs_load_policy(bprm->filename);
+#endif
+	return cs_start_execve(bprm, &security->r);
+}
+
+/**
+ * cs_file_open - Check permission for open().
+ *
+ * @f:    Pointer to "struct file".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_file_open(struct file *f)
+{
+	return cs_open_permission(&f->f_path, f->f_flags);
+}
+
+#ifdef CONFIG_SECURITY_PATH
+
+/**
+ * cs_path_chown - Check permission for chown()/chgrp().
+ *
+ * @path:  Pointer to "struct path".
+ * @user:  User ID.
+ * @group: Group ID.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_path_chown(const struct path *path, kuid_t user, kgid_t group)
+{
+	return cs_chown_permission(path, user, group);
+}
+
+/**
+ * cs_path_chmod - Check permission for chmod().
+ *
+ * @path: Pointer to "struct path".
+ * @mode: Mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_path_chmod(const struct path *path, umode_t mode)
+{
+	return cs_chmod_permission(path, mode);
+}
+
+/**
+ * cs_path_chroot - Check permission for chroot().
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_path_chroot(const struct path *path)
+{
+	return cs_chroot_permission(path);
+}
+
+/**
+ * cs_path_truncate - Check permission for truncate().
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_path_truncate(const struct path *path)
+{
+	return cs_truncate_permission(path);
+}
+
+#else
+
+/**
+ * cs_inode_setattr - Check permission for chown()/chgrp()/chmod()/truncate().
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @attr:   Pointer to "struct iattr".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_inode_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	int rc = 0;
+	struct path path = { .mnt = NULL, .dentry = dentry };
+
+	if (attr->ia_valid & ATTR_UID)
+		rc = cs_chown_permission(&path, attr->ia_uid, INVALID_GID);
+	if (!rc && (attr->ia_valid & ATTR_GID))
+		rc = cs_chown_permission(&path, INVALID_UID, attr->ia_gid);
+	if (!rc && (attr->ia_valid & ATTR_MODE))
+		rc = cs_chmod_permission(&path, attr->ia_mode);
+	if (!rc && (attr->ia_valid & ATTR_SIZE))
+		rc = cs_truncate_permission(&path);
+	return rc;
+}
+
+#endif
+
+#ifdef CONFIG_SECURITY_CAITSITH_GETATTR
+
+/**
+ * cs_inode_getattr - Check permission for stat().
+ *
+ * @path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_inode_getattr(const struct path *path)
+{
+	return cs_getattr_permission(path);
+}
+
+#endif
+
+#ifdef CONFIG_SECURITY_PATH
+
+/**
+ * cs_path_mknod - Check permission for mknod().
+ *
+ * @dir:    Pointer to "struct path".
+ * @dentry: Pointer to "struct dentry".
+ * @mode:   Create mode.
+ * @dev:    Device major/minor number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_path_mknod(const struct path *dir, struct dentry *dentry,
+			 umode_t mode, unsigned int dev)
+{
+	struct path path = { .mnt = dir->mnt, .dentry = dentry };
+
+	return cs_mknod_permission(&path, mode, dev);
+}
+
+/**
+ * cs_path_mkdir - Check permission for mkdir().
+ *
+ * @dir:    Pointer to "struct path".
+ * @dentry: Pointer to "struct dentry".
+ * @mode:   Create mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_path_mkdir(const struct path *dir, struct dentry *dentry,
+			 umode_t mode)
+{
+	struct path path = { .mnt = dir->mnt, .dentry = dentry };
+
+	return cs_mkdir_permission(&path, mode);
+}
+
+/**
+ * cs_path_rmdir - Check permission for rmdir().
+ *
+ * @dir:    Pointer to "struct path".
+ * @dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_path_rmdir(const struct path *dir, struct dentry *dentry)
+{
+	struct path path = { .mnt = dir->mnt, .dentry = dentry };
+
+	return cs_rmdir_permission(&path);
+}
+
+/**
+ * cs_path_unlink - Check permission for unlink().
+ *
+ * @dir:    Pointer to "struct path".
+ * @dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_path_unlink(const struct path *dir, struct dentry *dentry)
+{
+	struct path path = { .mnt = dir->mnt, .dentry = dentry };
+
+	return cs_unlink_permission(&path);
+}
+
+/**
+ * cs_path_symlink - Check permission for symlink().
+ *
+ * @dir:      Pointer to "struct path".
+ * @dentry:   Pointer to "struct dentry".
+ * @old_name: Content of symbolic link.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_path_symlink(const struct path *dir, struct dentry *dentry,
+			   const char *old_name)
+{
+	struct path path = { .mnt = dir->mnt, .dentry = dentry };
+
+	return cs_symlink_permission(&path, old_name);
+}
+
+/**
+ * cs_path_rename - Check permission for rename().
+ *
+ * @old_dir:    Pointer to "struct path".
+ * @old_dentry: Pointer to "struct dentry".
+ * @new_dir:    Pointer to "struct path".
+ * @new_dentry: Pointer to "struct dentry".
+ * @flags:      Rename flags.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_path_rename(const struct path *old_dir,
+			  struct dentry *old_dentry,
+			  const struct path *new_dir,
+			  struct dentry *new_dentry,
+			  const unsigned int flags)
+{
+	struct path old = { .mnt = old_dir->mnt, .dentry = old_dentry };
+	struct path new = { .mnt = new_dir->mnt, .dentry = new_dentry };
+
+	if (flags & RENAME_EXCHANGE) {
+		const int err = cs_rename_permission(&new, &old);
+
+		if (err)
+			return err;
+	}
+	return cs_rename_permission(&old, &new);
+}
+
+/**
+ * cs_path_link - Check permission for link().
+ *
+ * @old_dentry: Pointer to "struct dentry".
+ * @new_dir:    Pointer to "struct path".
+ * @new_dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_path_link(struct dentry *old_dentry, const struct path *new_dir,
+			struct dentry *new_dentry)
+{
+	struct path old = { .mnt = new_dir->mnt, .dentry = old_dentry };
+	struct path new = { .mnt = new_dir->mnt, .dentry = new_dentry };
+
+	return cs_link_permission(&old, &new);
+}
+
+#else
+
+/**
+ * cs_inode_mknod - Check permission for mknod().
+ *
+ * @dir:    Pointer to "struct inode".
+ * @dentry: Pointer to "struct dentry".
+ * @mode:   Create mode.
+ * @dev:    Device major/minor number.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_inode_mknod(struct inode *dir, struct dentry *dentry,
+			  umode_t mode, dev_t dev)
+{
+	struct path path = { .mnt = NULL, .dentry = dentry };
+
+	return cs_mknod_permission(&path, mode, dev);
+}
+
+/**
+ * cs_inode_mkdir - Check permission for mkdir().
+ *
+ * @dir:    Pointer to "struct inode".
+ * @dentry: Pointer to "struct dentry".
+ * @mode:   Create mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_inode_mkdir(struct inode *dir, struct dentry *dentry,
+			  umode_t mode)
+{
+	struct path path = { .mnt = NULL, .dentry = dentry };
+
+	return cs_mkdir_permission(&path, mode);
+}
+
+/**
+ * cs_inode_rmdir - Check permission for rmdir().
+ *
+ * @dir:    Pointer to "struct inode".
+ * @dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_inode_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	struct path path = { .mnt = NULL, .dentry = dentry };
+
+	return cs_rmdir_permission(&path);
+}
+
+/**
+ * cs_inode_unlink - Check permission for unlink().
+ *
+ * @dir:    Pointer to "struct inode".
+ * @dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_inode_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct path path = { .mnt = NULL, .dentry = dentry };
+
+	return cs_unlink_permission(&path);
+}
+
+/**
+ * cs_inode_symlink - Check permission for symlink().
+ *
+ * @dir:      Pointer to "struct inode".
+ * @dentry:   Pointer to "struct dentry".
+ * @old_name: Content of symbolic link.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_inode_symlink(struct inode *dir, struct dentry *dentry,
+			    const char *old_name)
+{
+	struct path path = { .mnt = NULL, .dentry = dentry };
+
+	return cs_symlink_permission(&path, old_name);
+}
+
+/**
+ * cs_inode_rename - Check permission for rename().
+ *
+ * @old_dir:    Pointer to "struct inode".
+ * @old_dentry: Pointer to "struct dentry".
+ * @new_dir:    Pointer to "struct inode".
+ * @new_dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
+			   struct inode *new_dir, struct dentry *new_dentry)
+{
+	struct path old = { .mnt = NULL, .dentry = old_dentry };
+	struct path new = { .mnt = NULL, .dentry = new_dentry };
+
+	return cs_rename_permission(&old, &new);
+}
+
+/**
+ * cs_inode_link - Check permission for link().
+ *
+ * @old_dentry: Pointer to "struct dentry".
+ * @dir:        Pointer to "struct inode".
+ * @new_dentry: Pointer to "struct dentry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_inode_link(struct dentry *old_dentry, struct inode *dir,
+			 struct dentry *new_dentry)
+{
+	struct path old = { .mnt = NULL, .dentry = old_dentry };
+	struct path new = { .mnt = NULL, .dentry = new_dentry };
+
+	return cs_link_permission(&old, &new);
+}
+
+/**
+ * cs_inode_create - Check permission for creat().
+ *
+ * @dir:    Pointer to "struct inode".
+ * @dentry: Pointer to "struct dentry".
+ * @mode:   Create mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_inode_create(struct inode *dir, struct dentry *dentry,
+			   umode_t mode)
+{
+	struct path path = { .mnt = NULL, .dentry = dentry };
+
+	return cs_mknod_permission(&path, mode, 0);
+}
+
+#endif
+
+#ifdef CONFIG_SECURITY_CAITSITH_NETWORK
+
+#include <net/sock.h>
+
+/* Structure for remembering an accept()ed socket's status. */
+struct cs_socket_tag {
+	struct list_head list;
+	struct inode *inode;
+	int status;
+	struct rcu_head rcu;
+};
+
+/*
+ * List for managing accept()ed sockets.
+ * Since we don't need to keep an accept()ed socket into this list after
+ * once the permission was granted, the number of entries in this list is
+ * likely small. Therefore, we don't use hash tables.
+ */
+static LIST_HEAD(cs_accepted_socket_list);
+/* Lock for protecting cs_accepted_socket_list . */
+static DEFINE_SPINLOCK(cs_accepted_socket_list_lock);
+
+/**
+ * cs_update_socket_tag - Update tag associated with accept()ed sockets.
+ *
+ * @inode:  Pointer to "struct inode".
+ * @status: New status.
+ *
+ * Returns nothing.
+ *
+ * If @status == 0, memory for that socket will be released after RCU grace
+ * period.
+ */
+static void cs_update_socket_tag(struct inode *inode, int status)
+{
+	struct cs_socket_tag *ptr;
+	/*
+	 * Protect whole section because multiple threads may call this
+	 * function with same "sock" via cs_validate_socket().
+	 */
+	spin_lock(&cs_accepted_socket_list_lock);
+	rcu_read_lock();
+	list_for_each_entry_rcu(ptr, &cs_accepted_socket_list, list) {
+		if (ptr->inode != inode)
+			continue;
+		ptr->status = status;
+		if (status)
+			break;
+		list_del_rcu(&ptr->list);
+		kfree_rcu(ptr, rcu);
+		break;
+	}
+	rcu_read_unlock();
+	spin_unlock(&cs_accepted_socket_list_lock);
+}
+
+/**
+ * cs_validate_socket - Check post accept() permission if needed.
+ *
+ * @sock: Pointer to "struct socket".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_validate_socket(struct socket *sock)
+{
+	struct inode *inode = SOCK_INODE(sock);
+	struct cs_socket_tag *ptr;
+	int ret = 0;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(ptr, &cs_accepted_socket_list, list) {
+		if (ptr->inode != inode)
+			continue;
+		ret = ptr->status;
+		break;
+	}
+	rcu_read_unlock();
+	if (ret <= 0)
+		/*
+		 * This socket is not an accept()ed socket or this socket is
+		 * an accept()ed socket and post accept() permission is done.
+		 */
+		return ret;
+	/*
+	 * Check post accept() permission now.
+	 *
+	 * Strictly speaking, we need to pass both listen()ing socket and
+	 * accept()ed socket to __cs_socket_post_accept_permission().
+	 * But since socket's family and type are same for both sockets,
+	 * passing the accept()ed socket in place for the listen()ing socket
+	 * will work.
+	 */
+	ret = cs_socket_post_accept_permission(sock, sock);
+	/*
+	 * If permission was granted, we forget that this is an accept()ed
+	 * socket. Otherwise, we remember that this socket needs to return
+	 * error for subsequent socketcalls.
+	 */
+	cs_update_socket_tag(inode, ret);
+	return ret;
+}
+
+/**
+ * cs_socket_accept - Check permission for accept().
+ *
+ * @sock:    Pointer to "struct socket".
+ * @newsock: Pointer to "struct socket".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * This hook is used for setting up environment for doing post accept()
+ * permission check. If dereferencing sock->ops->something() were ordered by
+ * rcu_dereference(), we could replace sock->ops with "a copy of original
+ * sock->ops with modified sock->ops->accept()" using rcu_assign_pointer()
+ * in order to do post accept() permission check before returning to userspace.
+ * If we make the copy in security_socket_post_create(), it would be possible
+ * to safely replace sock->ops here, but we don't do so because we don't want
+ * to allocate memory for sockets which do not call sock->ops->accept().
+ * Therefore, we do post accept() permission check upon next socket syscalls
+ * rather than between sock->ops->accept() and returning to userspace.
+ * This means that if a socket was close()d before calling some socket
+ * syscalls, post accept() permission check will not be done.
+ */
+static int cs_socket_accept(struct socket *sock, struct socket *newsock)
+{
+	struct cs_socket_tag *ptr;
+	const int rc = cs_validate_socket(sock);
+
+	if (rc < 0)
+		return rc;
+	ptr = kzalloc(sizeof(*ptr), GFP_KERNEL);
+	if (!ptr)
+		return -ENOMEM;
+	/*
+	 * Subsequent LSM hooks will receive "newsock". Therefore, I mark
+	 * "newsock" as "an accept()ed socket but post accept() permission
+	 * check is not done yet" by allocating memory using inode of the
+	 * "newsock" as a search key.
+	 */
+	ptr->inode = SOCK_INODE(newsock);
+	ptr->status = 1; /* Check post accept() permission later. */
+	spin_lock(&cs_accepted_socket_list_lock);
+	list_add_tail_rcu(&ptr->list, &cs_accepted_socket_list);
+	spin_unlock(&cs_accepted_socket_list_lock);
+	return 0;
+}
+
+/**
+ * cs_socket_listen - Check permission for listen().
+ *
+ * @sock:    Pointer to "struct socket".
+ * @backlog: Backlog parameter.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_socket_listen(struct socket *sock, int backlog)
+{
+	const int rc = cs_validate_socket(sock);
+
+	if (rc < 0)
+		return rc;
+	return cs_socket_listen_permission(sock);
+}
+
+/**
+ * cs_socket_connect - Check permission for connect().
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_socket_connect(struct socket *sock, struct sockaddr *addr,
+			     int addr_len)
+{
+	const int rc = cs_validate_socket(sock);
+
+	if (rc < 0)
+		return rc;
+	return cs_socket_connect_permission(sock, addr, addr_len);
+}
+
+/**
+ * cs_socket_bind - Check permission for bind().
+ *
+ * @sock:     Pointer to "struct socket".
+ * @addr:     Pointer to "struct sockaddr".
+ * @addr_len: Size of @addr.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_socket_bind(struct socket *sock, struct sockaddr *addr,
+			  int addr_len)
+{
+	const int rc = cs_validate_socket(sock);
+
+	if (rc < 0)
+		return rc;
+	return cs_socket_bind_permission(sock, addr, addr_len);
+}
+
+/**
+ * cs_socket_sendmsg - Check permission for sendmsg().
+ *
+ * @sock: Pointer to "struct socket".
+ * @msg:  Pointer to "struct msghdr".
+ * @size: Size of message.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_socket_sendmsg(struct socket *sock, struct msghdr *msg,
+			     int size)
+{
+	const int rc = cs_validate_socket(sock);
+
+	if (rc < 0)
+		return rc;
+	return cs_socket_sendmsg_permission(sock, msg, size);
+}
+
+/**
+ * cs_socket_recvmsg - Check permission for recvmsg().
+ *
+ * @sock:  Pointer to "struct socket".
+ * @msg:   Pointer to "struct msghdr".
+ * @size:  Size of message.
+ * @flags: Flags.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_socket_recvmsg(struct socket *sock, struct msghdr *msg,
+			     int size, int flags)
+{
+	return cs_validate_socket(sock);
+}
+
+/**
+ * cs_socket_getsockname - Check permission for getsockname().
+ *
+ * @sock: Pointer to "struct socket".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_socket_getsockname(struct socket *sock)
+{
+	return cs_validate_socket(sock);
+}
+
+/**
+ * cs_socket_getpeername - Check permission for getpeername().
+ *
+ * @sock: Pointer to "struct socket".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_socket_getpeername(struct socket *sock)
+{
+	return cs_validate_socket(sock);
+}
+
+/**
+ * cs_socket_getsockopt - Check permission for getsockopt().
+ *
+ * @sock:    Pointer to "struct socket".
+ * @level:   Level.
+ * @optname: Option's name,
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_socket_getsockopt(struct socket *sock, int level, int optname)
+{
+	return cs_validate_socket(sock);
+}
+
+/**
+ * cs_socket_setsockopt - Check permission for setsockopt().
+ *
+ * @sock:    Pointer to "struct socket".
+ * @level:   Level.
+ * @optname: Option's name,
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_socket_setsockopt(struct socket *sock, int level, int optname)
+{
+	return cs_validate_socket(sock);
+}
+
+/**
+ * cs_socket_shutdown - Check permission for shutdown().
+ *
+ * @sock: Pointer to "struct socket".
+ * @how:  Shutdown mode.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_socket_shutdown(struct socket *sock, int how)
+{
+	return cs_validate_socket(sock);
+}
+
+#define SOCKFS_MAGIC 0x534F434B
+
+/**
+ * cs_inode_free_security - Release memory associated with an inode.
+ *
+ * @inode: Pointer to "struct inode".
+ *
+ * Returns nothing.
+ *
+ * We use this hook for releasing memory associated with an accept()ed socket.
+ */
+static void cs_inode_free_security(struct inode *inode)
+{
+	if (inode->i_sb && inode->i_sb->s_magic == SOCKFS_MAGIC)
+		cs_update_socket_tag(inode, 0);
+}
+
+#endif
+
+/**
+ * cs_sb_pivotroot - Check permission for pivot_root().
+ *
+ * @old_path: Pointer to "struct path".
+ * @new_path: Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_sb_pivotroot(const struct path *old_path,
+			   const struct path *new_path)
+{
+	return cs_pivot_root_permission(old_path, new_path);
+}
+
+/**
+ * cs_sb_mount - Check permission for mount().
+ *
+ * @dev_name:  Name of device file.
+ * @path:      Pointer to "struct path".
+ * @type:      Name of filesystem type. Maybe NULL.
+ * @flags:     Mount options.
+ * @data_page: Optional data. Maybe NULL.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_sb_mount(const char *dev_name, const struct path *path,
+		       const char *type, unsigned long flags, void *data_page)
+{
+	return cs_mount_permission(dev_name, path, type, flags, data_page);
+}
+
+/**
+ * cs_move_mount - Check permission for move_mount().
+ *
+ * @from_path: Pointer to "struct path".
+ * @to_path:   Pointer to "struct path".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_move_mount(const struct path *from_path,
+			 const struct path *to_path)
+{
+	return cs_move_mount_permission(from_path, to_path);
+}
+
+/**
+ * cs_sb_umount - Check permission for umount().
+ *
+ * @mnt:   Pointer to "struct vfsmount".
+ * @flags: Unmount flags.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_sb_umount(struct vfsmount *mnt, int flags)
+{
+	struct path path = { .mnt = mnt, .dentry = mnt->mnt_root };
+
+	return cs_umount_permission(&path, flags);
+}
+
+/**
+ * cs_file_fcntl - Check permission for fcntl().
+ *
+ * @file: Pointer to "struct file".
+ * @cmd:  Command number.
+ * @arg:  Value for @cmd.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_file_fcntl(struct file *file, unsigned int cmd,
+			 unsigned long arg)
+{
+	return cs_fcntl_permission(file, cmd, arg);
+}
+
+/**
+ * cs_file_ioctl - Check permission for ioctl().
+ *
+ * @filp: Pointer to "struct file".
+ * @cmd:  Command number.
+ * @arg:  Value for @cmd.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int cs_file_ioctl(struct file *filp, unsigned int cmd,
+			  unsigned long arg)
+{
+	return cs_ioctl_permission(filp, cmd, arg);
+}
+
+#define MY_HOOK_INIT LSM_HOOK_INIT
+
+static struct security_hook_list caitsith_hooks[] = {
+	/* Security context allocator. */
+	MY_HOOK_INIT(task_free, cs_task_free_security),
+	MY_HOOK_INIT(cred_prepare, cs_cred_prepare),
+	MY_HOOK_INIT(task_alloc, cs_task_alloc_security),
+	/* Security context updater for successful execve(). */
+	MY_HOOK_INIT(bprm_check_security, cs_bprm_check_security),
+	MY_HOOK_INIT(bprm_committing_creds, cs_bprm_committing_creds),
+	/* Various permission checker. */
+	MY_HOOK_INIT(file_open, cs_file_open),
+	MY_HOOK_INIT(file_fcntl, cs_file_fcntl),
+	MY_HOOK_INIT(file_ioctl, cs_file_ioctl),
+	MY_HOOK_INIT(sb_pivotroot, cs_sb_pivotroot),
+	MY_HOOK_INIT(sb_mount, cs_sb_mount),
+	MY_HOOK_INIT(move_mount, cs_move_mount),
+	MY_HOOK_INIT(sb_umount, cs_sb_umount),
+#ifdef CONFIG_SECURITY_PATH
+	MY_HOOK_INIT(path_mknod, cs_path_mknod),
+	MY_HOOK_INIT(path_mkdir, cs_path_mkdir),
+	MY_HOOK_INIT(path_rmdir, cs_path_rmdir),
+	MY_HOOK_INIT(path_unlink, cs_path_unlink),
+	MY_HOOK_INIT(path_symlink, cs_path_symlink),
+	MY_HOOK_INIT(path_rename, cs_path_rename),
+	MY_HOOK_INIT(path_link, cs_path_link),
+	MY_HOOK_INIT(path_truncate, cs_path_truncate),
+	MY_HOOK_INIT(path_chmod, cs_path_chmod),
+	MY_HOOK_INIT(path_chown, cs_path_chown),
+	MY_HOOK_INIT(path_chroot, cs_path_chroot),
+#else
+	MY_HOOK_INIT(inode_mknod, cs_inode_mknod),
+	MY_HOOK_INIT(inode_mkdir, cs_inode_mkdir),
+	MY_HOOK_INIT(inode_rmdir, cs_inode_rmdir),
+	MY_HOOK_INIT(inode_unlink, cs_inode_unlink),
+	MY_HOOK_INIT(inode_symlink, cs_inode_symlink),
+	MY_HOOK_INIT(inode_rename, cs_inode_rename),
+	MY_HOOK_INIT(inode_link, cs_inode_link),
+	MY_HOOK_INIT(inode_create, cs_inode_create),
+	MY_HOOK_INIT(inode_setattr, cs_inode_setattr),
+#endif
+#ifdef CONFIG_SECURITY_CAITSITH_GETATTR
+	MY_HOOK_INIT(inode_getattr, cs_inode_getattr),
+#endif
+#ifdef CONFIG_SECURITY_CAITSITH_NETWORK
+	MY_HOOK_INIT(socket_bind, cs_socket_bind),
+	MY_HOOK_INIT(socket_connect, cs_socket_connect),
+	MY_HOOK_INIT(socket_listen, cs_socket_listen),
+	MY_HOOK_INIT(socket_sendmsg, cs_socket_sendmsg),
+	MY_HOOK_INIT(socket_recvmsg, cs_socket_recvmsg),
+	MY_HOOK_INIT(socket_getsockname, cs_socket_getsockname),
+	MY_HOOK_INIT(socket_getpeername, cs_socket_getpeername),
+	MY_HOOK_INIT(socket_getsockopt, cs_socket_getsockopt),
+	MY_HOOK_INIT(socket_setsockopt, cs_socket_setsockopt),
+	MY_HOOK_INIT(socket_shutdown, cs_socket_shutdown),
+	MY_HOOK_INIT(socket_accept, cs_socket_accept),
+	MY_HOOK_INIT(inode_free_security, cs_inode_free_security),
+#endif
+};
+
+static inline void add_hook(struct security_hook_list *hook)
+{
+	hlist_add_tail_rcu(&hook->list, hook->head);
+}
+
+static void __init swap_hook(struct security_hook_list *hook,
+			     union security_list_options *original)
+{
+	struct hlist_head *list = hook->head;
+
+	if (hlist_empty(list)) {
+		add_hook(hook);
+	} else {
+		struct security_hook_list *shp =
+			hlist_entry(list->first, typeof(*shp), list);
+
+		while (shp->list.next)
+			shp = hlist_entry(shp->list.next, typeof(*shp), list);
+		*original = shp->hook;
+		/* Make sure that original callback is saved. */
+		smp_wmb();
+		shp->hook = hook->hook;
+	}
+}
+
+#if defined(CONFIG_STRICT_KERNEL_RWX) && !defined(CONFIG_SECURITY_WRITABLE_HOOKS)
+#include <linux/uaccess.h> /* copy_to_kernel_nofault() */
+#define NEED_TO_CHECK_HOOKS_ARE_WRITABLE
+
+#if defined(CONFIG_X86)
+#define MAX_RO_PAGES 1024
+static struct page *ro_pages[MAX_RO_PAGES] __initdata;
+static unsigned int ro_pages_len __initdata;
+
+static bool __init lsm_test_page_ro(void *addr)
+{
+	unsigned int i;
+	int unused;
+	struct page *page;
+
+	page = (struct page *) lookup_address((unsigned long) addr, &unused);
+	if (!page)
+		return false;
+	if (test_bit(_PAGE_BIT_RW, &(page->flags)))
+		return true;
+	for (i = 0; i < ro_pages_len; i++)
+		if (page == ro_pages[i])
+			return true;
+	if (ro_pages_len == MAX_RO_PAGES)
+		return false;
+	ro_pages[ro_pages_len++] = page;
+	return true;
+}
+
+static bool __init check_ro_pages(struct security_hook_heads *hooks)
+{
+	int i;
+	struct hlist_head *list = &hooks->capable;
+
+	if (!copy_to_kernel_nofault(list, list, sizeof(void *)))
+		return true;
+	for (i = 0; i < ARRAY_SIZE(caitsith_hooks); i++) {
+		struct hlist_head *head = caitsith_hooks[i].head;
+		struct security_hook_list *shp;
+
+		if (!lsm_test_page_ro(&head->first))
+			return false;
+		hlist_for_each_entry(shp, head, list)
+			if (!lsm_test_page_ro(&shp->list.next) ||
+			    !lsm_test_page_ro(&shp->list.pprev))
+				return false;
+	}
+	return true;
+}
+#else
+static bool __init check_ro_pages(struct security_hook_heads *hooks)
+{
+	struct hlist_head *list = &hooks->capable;
+
+	return !copy_to_kernel_nofault(list, list, sizeof(void *));
+}
+#endif
+#endif
+
+/**
+ * cs_init - Initialize this module.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int __init cs_init(void)
+{
+	int idx;
+#if defined(NEED_TO_CHECK_HOOKS_ARE_WRITABLE)
+	if (!check_ro_pages(&security_hook_heads)) {
+		pr_info("Can't update security_hook_heads due to write protected. Retry with rodata=0 kernel command line option added.\n");
+		return -EINVAL;
+	}
+#endif
+	for (idx = 0; idx < CS_MAX_TASK_SECURITY_HASH; idx++)
+		INIT_LIST_HEAD(&cs_task_security_list[idx]);
+	cs_init_module();
+#if defined(NEED_TO_CHECK_HOOKS_ARE_WRITABLE) && defined(CONFIG_X86)
+	for (idx = 0; idx < ro_pages_len; idx++)
+		set_bit(_PAGE_BIT_RW, &(ro_pages[idx]->flags));
+#endif
+	swap_hook(&caitsith_hooks[0], &original_task_free);
+	swap_hook(&caitsith_hooks[1], &original_cred_prepare);
+	swap_hook(&caitsith_hooks[2], &original_task_alloc);
+	for (idx = 3; idx < ARRAY_SIZE(caitsith_hooks); idx++)
+		add_hook(&caitsith_hooks[idx]);
+#if defined(NEED_TO_CHECK_HOOKS_ARE_WRITABLE) && defined(CONFIG_X86)
+	for (idx = 0; idx < ro_pages_len; idx++)
+		clear_bit(_PAGE_BIT_RW, &(ro_pages[idx]->flags));
+#endif
+	return 0;
+}
+
+module_init(cs_init);
+MODULE_LICENSE("GPL");
+
+/**
+ * cs_used_by_cred - Check whether the given domain is in use or not.
+ *
+ * @domain: Pointer to "struct cs_domain_info".
+ *
+ * Returns true if @domain is in use, false otherwise.
+ *
+ * Caller holds rcu_read_lock().
+ */
+bool cs_used_by_cred(const struct cs_domain_info *domain)
+{
+	return false;
+}
+
+/**
+ * cs_add_task_security - Add "struct cs_security" to list.
+ *
+ * @ptr:  Pointer to "struct cs_security".
+ * @list: Pointer to "struct list_head".
+ *
+ * Returns nothing.
+ */
+static void cs_add_task_security(struct cs_security *ptr,
+				 struct list_head *list)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cs_task_security_list_lock, flags);
+	list_add_rcu(&ptr->list, list);
+	spin_unlock_irqrestore(&cs_task_security_list_lock, flags);
+}
+
+/**
+ * __cs_alloc_task_security - Allocate memory for new tasks.
+ *
+ * @task: Pointer to "struct task_struct".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int __cs_alloc_task_security(const struct task_struct *task)
+{
+	struct cs_security *old_security = cs_current_security();
+	struct cs_security *new_security = kzalloc(sizeof(*new_security),
+						   GFP_KERNEL);
+	struct list_head *list = &cs_task_security_list
+		[hash_ptr((void *) task, CS_TASK_SECURITY_HASH_BITS)];
+
+	if (!new_security)
+		return -ENOMEM;
+	new_security->task = task;
+	new_security->cs_domain_info = old_security->cs_domain_info;
+	new_security->cs_flags = old_security->cs_flags;
+	cs_add_task_security(new_security, list);
+	return 0;
+}
+
+/**
+ * cs_find_task_security - Find "struct cs_security" for given task.
+ *
+ * @task: Pointer to "struct task_struct".
+ *
+ * Returns pointer to "struct cs_security" on success, &cs_oom_security on
+ * out of memory, &cs_default_security otherwise.
+ *
+ * If @task is current thread and "struct cs_security" for current thread was
+ * not found, I try to allocate it. But if allocation failed, current thread
+ * will be killed by SIGKILL. Note that if current->pid == 1, sending SIGKILL
+ * won't work.
+ */
+struct cs_security *cs_find_task_security(const struct task_struct *task)
+{
+	struct cs_security *ptr;
+	struct list_head *list = &cs_task_security_list
+		[hash_ptr((void *) task, CS_TASK_SECURITY_HASH_BITS)];
+	/* Make sure INIT_LIST_HEAD() in cs_mm_init() takes effect. */
+	while (!list->next)
+		smp_rmb();
+	rcu_read_lock();
+	list_for_each_entry_rcu(ptr, list, list) {
+		if (ptr->task != task)
+			continue;
+		rcu_read_unlock();
+		/*
+		 * Current thread needs to transit from old domain to new
+		 * domain before do_execve() succeeds in order to check
+		 * permission for interpreters and environment variables using
+		 * new domain's ACL rules. The domain transition has to be
+		 * visible from other CPU in order to allow interactive
+		 * enforcing mode. Also, the domain transition has to be
+		 * reverted if do_execve() failed. However, an LSM hook for
+		 * reverting domain transition is missing.
+		 *
+		 * security_prepare_creds() is called from prepare_creds() from
+		 * prepare_bprm_creds() from do_execve() before setting
+		 * current->in_execve flag, and current->in_execve flag is
+		 * cleared by the time next do_execve() request starts.
+		 * This means that we can emulate the missing LSM hook for
+		 * reverting domain transition, by calling this function from
+		 * security_prepare_creds().
+		 *
+		 * If current->in_execve is not set but ptr->cs_flags has
+		 * CS_TASK_IS_IN_EXECVE set, it indicates that do_execve()
+		 * has failed and reverting domain transition is needed.
+		 */
+		if (task == current &&
+		    (ptr->cs_flags & CS_TASK_IS_IN_EXECVE) &&
+		    !current->in_execve) {
+			cs_debug_trace("1");
+			cs_clear_execve(-1, ptr);
+		}
+		return ptr;
+	}
+	rcu_read_unlock();
+	if (task != current)
+		return &cs_default_security;
+	/* Use GFP_ATOMIC because caller may have called rcu_read_lock(). */
+	ptr = kzalloc(sizeof(*ptr), GFP_ATOMIC);
+	if (!ptr) {
+		pr_warn("Unable to allocate memory for pid=%u\n",
+			task->pid);
+		send_sig(SIGKILL, current, 0);
+		return &cs_oom_security;
+	}
+	*ptr = cs_default_security;
+	ptr->task = task;
+	cs_add_task_security(ptr, list);
+	return ptr;
+}
-- 
2.18.4


  parent reply	other threads:[~2022-11-02 17:12 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-02 17:10 [PATCH 01/10] security: Export security_hook_heads Tetsuo Handa
2022-11-02 17:10 ` [PATCH 02/10] mm: Export copy_to_kernel_nofault() Tetsuo Handa
2022-11-02 17:10 ` [PATCH 03/10] fs,kernel: Export d_absolute_path()/find_task_by_pid_ns()/find_task_by_vpid() Tetsuo Handa
2022-11-05 23:51   ` Serge E. Hallyn
2022-11-02 17:10 ` [PATCH 04/10] CaitSith: Add header file Tetsuo Handa
2022-11-02 17:57   ` Casey Schaufler
2022-11-05  2:43     ` Serge E. Hallyn
2022-11-05  4:05       ` Tetsuo Handa
2022-11-05 23:46         ` Serge E. Hallyn
2022-11-06  0:56           ` Tetsuo Handa
2022-11-07 18:59             ` Casey Schaufler
2022-11-08 10:18               ` Tetsuo Handa
2022-11-09  2:20                 ` Paul Moore
2022-11-09 10:13                   ` Tetsuo Handa
2022-11-09 14:48                     ` Paul Moore
2022-11-09 23:57                       ` Tetsuo Handa
2022-11-10  2:22                         ` Kees Cook
2022-11-10  4:10                           ` Tetsuo Handa
2022-11-10  4:45                             ` Paul Moore
2022-11-07 19:22         ` Paul Moore
2022-11-02 17:10 ` Tetsuo Handa [this message]
2022-11-02 19:05   ` [PATCH 05/10] CaitSith: Add LSM interface management file Kees Cook
2022-11-02 17:10 ` [PATCH 07/10] CaitSith: Add permission checking functions Tetsuo Handa
2022-11-02 17:10 ` [PATCH 08/10] CaitSith: Add pathname calculation functions Tetsuo Handa
2022-11-02 17:10 ` [PATCH 09/10] CaitSith: Add garbage collector functions Tetsuo Handa
2022-11-02 17:10 ` [PATCH 10/10] CaitSith: Add Kconfig and Makefile files Tetsuo Handa
     [not found] ` <20221102171025.126961-6-penguin-kernel@I-love.SAKURA.ne.jp>
2022-11-02 17:29   ` [PATCH 6a/10] CaitSith: Add policy management functions Tetsuo Handa
2022-11-02 17:29   ` [PATCH 6b/10] " Tetsuo Handa

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221102171025.126961-5-penguin-kernel@I-love.SAKURA.ne.jp \
    --to=penguin-kernel@i-love.sakura.ne.jp \
    --cc=casey@schaufler-ca.com \
    --cc=john.johansen@canonical.com \
    --cc=kees@kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=paul@paul-moore.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.