public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: "Dr. Greg" <greg@enjellic.com>
To: linux-security-module@vger.kernel.org,
	linux-kernel@vger.kernel.org, corbet@lwn.net
Subject: [PATCH 08/13] Add namespace implementation.
Date: Mon, 10 Jul 2023 05:23:14 -0500	[thread overview]
Message-ID: <20230710102319.19716-9-greg@enjellic.com> (raw)
In-Reply-To: <20230710102319.19716-1-greg@enjellic.com>

TSEM implements a security modeling namespace that allows
security models to be implemented that are independent of other
security modeling namespaces.  This allows multiple security
models and modes of modeling (external vs. internal) to be
implemented.  A security modeling namespace is conceptually
similar to other resource namespaces implemented in the kernel

The TSEM control plane is used to cause a process to leave the
root security modeling namespace and institute a new subordinate
modeling namespace.  Additional processes that fork from this
process inherit the the characteristics of the security modeling
namespace.

Each modeling namespace has a unique numeric identifier that is
implemented as an incremented unsigned 64 bit value in order to
preclude overflow.  The id value of 0 is reserved for the root
security modeling namespace.

Each security modeling namespace is designated as either
internally or externally modeled.  An internally modeled namespace
has its security model implemented by a Trusted Modeling Agent
(TMA) implementation that is run in the context of the kernel.

Externally modeled namespaces have a description of the security
event exported to a trust orchestrator running in userspace.
That trust orchestrator has an associated Trusted Modeling Agent
running in a context that implements the root of trust for the
security namespace.

A process that exports a security event description is scheduled
away into an interruptible sleep state.  The trust orchestrator
that created the external modeling namespace is responsible for
using the TSEM control plane to wake the process up and set the
trust status of the process to be trusted or untrusted.  Only
processes that carries the CAP_ML capability can wake up a
process and set its trust status.

An init function is surfaced from this file that is called by the
TSEM initialization function.  This function is responsible for
creating a workqueue that will handle asynchronous release of
resources that were allocated for a modeling domain, including
the release of the pseudo-file that was created for exporting
domain events.

Signed-off-by: Greg Wettstein <greg@enjellic.com>
---
 security/tsem/namespace.c | 347 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 347 insertions(+)
 create mode 100644 security/tsem/namespace.c

diff --git a/security/tsem/namespace.c b/security/tsem/namespace.c
new file mode 100644
index 000000000000..ba2e8f550838
--- /dev/null
+++ b/security/tsem/namespace.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Copyright (C) 2023 Enjellic Systems Development, LLC
+ * Author: Dr. Greg Wettstein <greg@enjellic.com>
+ *
+ * This file implements TSEM namespaces.
+ */
+
+#include "tsem.h"
+
+static u64 context_id;
+
+struct context_key {
+	struct list_head list;
+	u64 context_id;
+	u8 key[HASH_MAX_DIGESTSIZE];
+};
+
+DEFINE_MUTEX(context_id_mutex);
+LIST_HEAD(context_id_list);
+
+static void remove_task_key(u64 context_id)
+{
+	struct context_key *entry, *tmp_entry;
+
+	list_for_each_entry_safe(entry, tmp_entry, &context_id_list, list) {
+		if (context_id == entry->context_id) {
+			list_del(&entry->list);
+			kfree(entry);
+			break;
+		}
+	}
+}
+
+static int generate_task_key(const char *keystr, u64 context_id,
+			     struct tsem_task *t_ttask,
+			     struct tsem_task *p_ttask)
+{
+	int retn;
+	bool found_key, valid_key = false;
+	unsigned int size = tsem_digestsize();
+	struct context_key *entry;
+
+	while (!valid_key) {
+		get_random_bytes(t_ttask->task_key, size);
+		retn = tsem_ns_event_key(t_ttask->task_key, keystr,
+					 p_ttask->task_key);
+		if (retn)
+			goto done;
+
+		if (list_empty(&context_id_list))
+			break;
+
+		found_key = false;
+		list_for_each_entry(entry, &context_id_list, list) {
+			if (memcmp(entry->key, p_ttask->task_key, size) == 0)
+				found_key = true;
+		}
+		if (!found_key)
+			valid_key = true;
+	}
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry) {
+		retn = -ENOMEM;
+		goto done;
+	}
+
+	entry->context_id = context_id;
+	memcpy(entry->key, p_ttask->task_key, size);
+	list_add_tail(&entry->list, &context_id_list);
+	retn = 0;
+
+ done:
+	return retn;
+}
+
+static struct tsem_external *allocate_external(u64 context_id,
+					       const char *keystr)
+{
+	int retn = -ENOMEM;
+	char bufr[20 + 1];
+	struct tsem_external *external;
+	struct tsem_task *t_ttask = tsem_task(current);
+	struct tsem_task *p_ttask = tsem_task(current->real_parent);
+
+	external = kzalloc(sizeof(*external), GFP_KERNEL);
+	if (!external)
+		goto done;
+
+	retn = generate_task_key(keystr, context_id, t_ttask, p_ttask);
+	if (retn)
+		goto done;
+
+	spin_lock_init(&external->export_lock);
+	INIT_LIST_HEAD(&external->export_list);
+
+	init_waitqueue_head(&external->wq);
+
+	scnprintf(bufr, sizeof(bufr), "%llu", context_id);
+	external->dentry = tsem_fs_create_external(bufr);
+	if (IS_ERR(external->dentry)) {
+		retn = PTR_ERR(external->dentry);
+		external->dentry = NULL;
+	} else
+		retn = 0;
+
+ done:
+	if (retn) {
+		memset(t_ttask->task_key, '\0', tsem_digestsize());
+		memset(p_ttask->task_key, '\0', tsem_digestsize());
+		kfree(external);
+		remove_task_key(context_id);
+		external = ERR_PTR(retn);
+	} else
+		p_ttask->tma_for_ns = context_id;
+
+	return external;
+}
+
+static void wq_put(struct work_struct *work)
+{
+	struct tsem_context *ctx;
+
+	ctx = container_of(work, struct tsem_context, work);
+
+	if (ctx->external) {
+		mutex_lock(&context_id_mutex);
+		remove_task_key(ctx->id);
+		mutex_unlock(&context_id_mutex);
+
+		securityfs_remove(ctx->external->dentry);
+		tsem_export_magazine_free(ctx->external);
+		kfree(ctx->external);
+	} else
+		tsem_model_free(ctx);
+
+	crypto_free_shash(ctx->tfm);
+	tsem_event_magazine_free(ctx);
+	kfree(ctx->digestname);
+	kfree(ctx);
+}
+
+static void ns_free(struct kref *kref)
+{
+	struct tsem_context *ctx;
+
+	ctx = container_of(kref, struct tsem_context, kref);
+
+	INIT_WORK(&ctx->work, wq_put);
+	if (!queue_work(system_wq, &ctx->work))
+		WARN_ON_ONCE(1);
+}
+
+/**
+ * tsem_ns_put() - Release a reference to a modeling context.
+ * @ctx: A pointer to the TMA context for which a reference is
+ *	 to be released.
+ *
+ * This function is called to release a reference to a TMA modeling
+ * domain.  The release of the last reference calls the ns_free()
+ * function that schedules the actual work to release the resources
+ * associated with the namespace to a workqueue.
+ */
+void tsem_ns_put(struct tsem_context *ctx)
+{
+	kref_put(&ctx->kref, ns_free);
+}
+
+/**
+ * tsem_ns_event_key() - Generate TMA authentication key.
+ * @task_key: A pointer to the buffer containing the task identification
+ *	      key that was randomly generated for the modeling domain.
+ * @keystr: A pointer to the buffer containing the TMA authentication key
+ *	    in ASCII hexadecimal form.
+ *
+ * This function generates the authentication key that will be used
+ * to validate a call by a TMA to set the trust status of the process.
+ *
+ * Return: This function returns 0 if the key was properly generated
+ *	   or a negative value if a hashing error occurred.
+ */
+int tsem_ns_event_key(u8 *task_key, const char *keystr, u8 *key)
+{
+	bool retn;
+	u8 tma_key[HASH_MAX_DIGESTSIZE];
+	SHASH_DESC_ON_STACK(shash, tfm);
+
+	retn = hex2bin(tma_key, keystr, tsem_digestsize());
+	if (retn)
+		return -EINVAL;
+
+	shash->tfm = tsem_digest();
+	retn = crypto_shash_init(shash);
+	if (retn)
+		return retn;
+
+	retn = crypto_shash_update(shash, task_key, tsem_digestsize());
+	if (retn)
+		return retn;
+
+	return crypto_shash_finup(shash, tma_key, tsem_digestsize(), key);
+}
+
+static struct crypto_shash *configure_digest(const char *digest,
+					     char **digestname,
+					     u8 *zero_digest)
+{
+	int retn;
+	struct crypto_shash *tfm;
+	SHASH_DESC_ON_STACK(shash, tfm);
+
+	*digestname = kstrdup(digest, GFP_KERNEL);
+	if (!*digestname)
+		return ERR_PTR(-ENOMEM);
+
+	tfm = crypto_alloc_shash(digest, 0, 0);
+	if (IS_ERR(tfm))
+		return tfm;
+
+	shash->tfm = tfm;
+	retn = crypto_shash_digest(shash, NULL, 0, zero_digest);
+	if (retn) {
+		crypto_free_shash(tfm);
+		tfm = NULL;
+	}
+
+	return tfm;
+}
+
+/**
+ * tsem_ns_create() - Create a TSEM modeling namespace.
+ * @type:   The type of namespace being created.
+ * @digest: A null terminated character buffer containing the name
+ *	    of the hash function that is to be used for the modeling
+ *	    domain.
+ * @ns:     The enumeration type that specifies whether the security
+ *	    event descriptions should reference the initial user
+ *	    namespace or the current user namespace.
+ * @key:    A pointer to a null-terminated buffer containing the key
+ *	    that will be used to authenticate the TMA's ability to set
+ *	    the trust status of a process.
+ *
+ * This function is used to create either an internally or externally
+ * modeled TSEM namespace.  The type of the namespace to be created
+ * is specified with the tsem_control_type enumeration value.  A
+ * request for an internally model namespace causes a new structure to be
+ * allocated that will hold the description of the security model.
+ * An externally modeled domain will have a control structure allocated
+ * that manages the export of security event descriptions to the
+ * trust orchestrator that is responsible for running the TMA
+ * implementation.
+ *
+ * Return: This function returns 0 if the namespace was created and
+ *	   a negative error value on error.
+ */
+int tsem_ns_create(const enum tsem_control_type type, const char *digest,
+		   const enum tsem_ns_reference ns, const char *key,
+		   unsigned int cache_size)
+{
+	u8 zero_digest[HASH_MAX_DIGESTSIZE];
+	char *use_digest;
+	int retn = -ENOMEM;
+	u64 new_id;
+	struct tsem_task *tsk = tsem_task(current);
+	struct tsem_context *new_ctx;
+	struct tsem_model *model = NULL;
+	struct crypto_shash *tfm;
+
+	tfm = configure_digest(digest, &use_digest, zero_digest);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	new_ctx = kzalloc(sizeof(*new_ctx), GFP_KERNEL);
+	if (!new_ctx)
+		return retn;
+
+	mutex_lock(&context_id_mutex);
+	new_id = context_id + 1;
+
+	retn = tsem_event_magazine_allocate(new_ctx, cache_size);
+	if (retn)
+		goto done;
+
+	if (type == TSEM_CONTROL_INTERNAL) {
+		model = tsem_model_allocate(cache_size);
+		if (!model)
+			goto done;
+		new_ctx->model = model;
+	}
+	if (type == TSEM_CONTROL_EXTERNAL) {
+		if (crypto_shash_digestsize(tfm)*2 != strlen(key)) {
+			retn = -EINVAL;
+			goto done;
+		}
+
+		new_ctx->external = allocate_external(new_id, key);
+		if (IS_ERR(new_ctx->external)) {
+			retn = PTR_ERR(new_ctx->external);
+			new_ctx->external = NULL;
+			goto done;
+		}
+
+		retn = tsem_export_magazine_allocate(new_ctx->external,
+						     cache_size);
+		if (retn)
+			goto done;
+	}
+
+	kref_init(&new_ctx->kref);
+
+	new_ctx->id = new_id;
+	new_ctx->tfm = tfm;
+	new_ctx->digestname = use_digest;
+	memcpy(new_ctx->zero_digest, zero_digest,
+	       crypto_shash_digestsize(tfm));
+
+	if (ns == TSEM_NS_CURRENT)
+		new_ctx->use_current_ns = true;
+	memcpy(new_ctx->actions, tsk->context->actions,
+	       sizeof(new_ctx->actions));
+	retn = 0;
+
+ done:
+	if (retn) {
+		remove_task_key(new_id);
+		crypto_free_shash(tfm);
+		tsem_event_magazine_free(new_ctx);
+		kfree(use_digest);
+		if (new_ctx->external)
+			tsem_export_magazine_free(new_ctx->external);
+		kfree(new_ctx->external);
+		kfree(new_ctx);
+		kfree(model);
+	} else {
+		context_id = new_id;
+		tsk->context = new_ctx;
+		if (type == TSEM_CONTROL_EXTERNAL)
+			retn = tsem_export_aggregate();
+		else
+			retn = tsem_model_add_aggregate();
+	}
+
+	mutex_unlock(&context_id_mutex);
+	return retn;
+}
-- 
2.39.1


  parent reply	other threads:[~2023-07-10 11:03 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-07-10 10:23 [PATCH 00/13] Implement Trusted Security Event Modeling Dr. Greg
2023-07-10 10:23 ` [PATCH 01/13] Update MAINTAINERS file Dr. Greg
2023-07-10 20:00   ` Randy Dunlap
2023-07-15 22:45     ` Dr. Greg
2023-07-10 10:23 ` [PATCH 02/13] Add TSEM specific documentation Dr. Greg
2023-07-11  4:37   ` Randy Dunlap
2023-07-17  0:36     ` Dr. Greg
2023-07-17  1:56       ` Randy Dunlap
2023-08-08 18:48   ` Serge Hallyn
2023-08-11 20:22     ` Dr. Greg
2024-01-04 15:54       ` Paul Moore
2024-01-05  3:54         ` Dr. Greg
2024-01-04  4:00   ` [PATCH 2/13] " Paul Moore
2024-01-05  2:55     ` Dr. Greg
2024-01-08 11:43     ` Dr. Greg
2024-02-05 16:09       ` Paul Moore
2024-02-19 11:16         ` Dr. Greg
2023-07-10 10:23 ` [PATCH 03/13] Implement CAP_TRUST capability Dr. Greg
2023-08-07 20:21   ` Casey Schaufler
2023-08-15 10:19     ` Dr. Greg
2023-08-15 17:15       ` Casey Schaufler
2023-07-10 10:23 ` [PATCH 04/13] Add TSEM master header file Dr. Greg
2023-08-07 20:39   ` Casey Schaufler
2023-08-10  2:57     ` Dr. Greg
2023-08-10 15:03       ` Casey Schaufler
2023-07-10 10:23 ` [PATCH 05/13] Add primary TSEM implementation file Dr. Greg
2023-08-07 21:00   ` Casey Schaufler
2023-08-11  7:21     ` Dr. Greg
2023-07-10 10:23 ` [PATCH 06/13] Add root domain trust implementation Dr. Greg
2023-07-10 10:23 ` [PATCH 07/13] Implement TSEM control plane Dr. Greg
2023-07-10 10:23 ` Dr. Greg [this message]
2023-07-10 10:23 ` [PATCH 09/13] Add security event description export facility Dr. Greg
2023-07-10 10:23 ` [PATCH 10/13] Add event description implementation Dr. Greg
2023-07-10 10:23 ` [PATCH 11/13] Implement security event mapping Dr. Greg
2023-07-10 10:23 ` [PATCH 12/13] Implement an internal Trusted Modeling Agent Dr. Greg
2023-07-10 10:23 ` [PATCH 13/13] Activate the configuration and build of the TSEM LSM Dr. Greg

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=20230710102319.19716-9-greg@enjellic.com \
    --to=greg@enjellic.com \
    --cc=corbet@lwn.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox