From 382c0a14be9cd438bad235a37487d348047ee117 Mon Sep 17 00:00:00 2001
From: Rogerio Guerra Borin <rogerio.borin@toradex.com>
Date: Mon, 6 Nov 2023 22:25:28 -0300
Subject: [PATCH 4/5] toradex: add implementation of bootargs protection

This only adds the code implementing the feature but does not integrate
it into U-Boot.

Upstream-Status: Inappropriate [TorizonCore specific]

Signed-off-by: Rogerio Guerra Borin <rogerio.borin@toradex.com>
---
 common/tdx-harden.c  | 231 +++++++++++++++++++++++++++++++++++++++++++
 include/tdx-harden.h |   9 +-
 2 files changed, 238 insertions(+), 2 deletions(-)

diff --git a/common/tdx-harden.c b/common/tdx-harden.c
index dbdb8c5e337..2a2a775ffec 100644
--- a/common/tdx-harden.c
+++ b/common/tdx-harden.c
@@ -3,12 +3,18 @@
  * Copyright 2023 Toradex
  */
 
+/* To see messages, also set CONFIG_LOG_MAX_LEVEL=8 and CONFIG_LOG_LEVEL=8. */
+/* #define DEBUG */
+/* #define LOG_DEBUG */
+
 #include <common.h>
 #include <compiler.h>
 #include <command.h>
 #include <console.h>
 #include <log.h>
 #include <fdt_support.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
 #include <asm/global_data.h>
 #include <tdx-harden.h>
 
@@ -42,6 +48,36 @@ enum dbg_hab_status_t dbg_hab_status = DBG_HAB_STATUS_AUTO;
 enum dbg_hdn_status_t dbg_hdn_status = DBG_HDN_STATUS_AUTO;
 #endif
 
+#ifdef CONFIG_TDX_BOOTARGS_PROTECTION
+/* Path of node in OS FDT containing all bootargs properties. */
+static const char bootargs_node_path[] = TDX_BOOTARGS_NODE_PATH;
+
+enum bootarg_param_type_t {
+	BPARAM_NONE,
+	BPARAM_INTEGER,
+	BPARAM_OSTREE_PATH,
+	BPARAM_GENERIC_UUID,
+};
+
+struct bootarg_spec_t {
+	const char *param;
+	enum bootarg_param_type_t type;
+	const char *conflict;
+};
+
+static const struct bootarg_spec_t bootarg_spec[] = {
+	{ "ostree=", BPARAM_OSTREE_PATH, NULL },
+	{ "root=PARTUUID=", BPARAM_GENERIC_UUID, "root=" }
+#if 0
+	/* Examples */
+	{ "loglevel=", BPARAM_INTEGER, NULL },
+	{ "nowb", BPARAM_NONE, NULL },
+#endif
+};
+
+#define BOOTARG_SPEC_LEN (sizeof(bootarg_spec) / sizeof(bootarg_spec[0]))
+#endif
+
 static int _tdx_hardening_enabled(void)
 {
 	const void *dis_prop;
@@ -210,6 +246,201 @@ void tdx_secure_boot_cmd(const char *cmd)
 }
 #endif
 
+#ifdef CONFIG_TDX_BOOTARGS_PROTECTION
+/**
+ * _tdx_valid_var_bootarg - Check single argument in bootargs
+ *
+ * TODO: Add support for quoted strings.
+ */
+static int _tdx_valid_var_bootarg(const char *value,
+				  enum bootarg_param_type_t type,
+				  const char **eptr)
+{
+	const char *valp = value;
+
+	debug("check value '%.10s...' against type=%d\n", value, (int) type);
+
+	switch (type) {
+	case BPARAM_NONE:
+		break;
+	case BPARAM_INTEGER: {
+		while (isdigit(*valp))
+			valp++;
+		if (valp == value)
+			return 0;
+		break;
+	}
+	case BPARAM_OSTREE_PATH: {
+		/* Accept only a limited set of characters. */
+		while (isalnum(*valp) || *valp == '/' || *valp == '.')
+			valp++;
+		if (valp == value)
+			return 0;
+		break;
+	}
+	case BPARAM_GENERIC_UUID: {
+		/* Accept hex digits and dashes. */
+		while (isxdigit(*valp) || *valp == '-')
+			valp++;
+		if (valp == value)
+			return 0;
+		break;
+	}
+	default:
+		printf("Unhandled bootarg param type %d\n", (int) type);
+		return 0;
+	}
+
+	/* Ensure argument is finished by space or NUL. */
+	if (*valp == '\0' || isspace(*valp)) {
+		if (eptr)
+			*eptr = valp;
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * _tdx_valid_var_bootargs - Check the variable part of bootargs
+ */
+static int _tdx_valid_var_bootargs(const char *bootargs, const char *reqargs)
+{
+	const char *args = bootargs, *value = NULL, *eptr;
+
+	while (*args) {
+		int bi;
+		for (bi = 0; bi < BOOTARG_SPEC_LEN; bi++) {
+			int plen = strlen(bootarg_spec[bi].param);
+			if (!strncmp(args, bootarg_spec[bi].param, plen)) {
+				debug("arg '%s'\n", bootarg_spec[bi].param);
+				value = &args[plen];
+				break;
+			}
+		}
+		if (bi >= BOOTARG_SPEC_LEN) {
+			eprintf("## Unexpected argument in variable bootargs: "
+				"%.16s...\n", args);
+			return 0;
+		}
+
+		if (!_tdx_valid_var_bootarg(value, bootarg_spec[bi].type, &eptr)) {
+			eprintf("## Argument validation failed for bootarg "
+				"%.16s...\n", args);
+			return 0;
+		}
+
+		/* Check if the parameter specified in the variable part conflicts
+                   with a parameter in the required (fixed) part; this prevents
+                   parameters to be overriden in the variable part when they are
+                   supposed to be present only in the fixed part of the bootargs. */
+		if (bootarg_spec[bi].conflict) {
+			const char *reqptr = strstr(reqargs, bootarg_spec[bi].conflict);
+			int conflict = 0;
+			if (reqptr && reqptr == reqargs) {
+				/* found at the beginning of the reqargs. */
+				conflict = 1;
+
+			} else if (reqptr && reqptr != reqargs) {
+				/* found not at the beginning: confirm. */
+				reqptr--;
+				if (isspace(*reqptr)) conflict = 1;
+			}
+			if (conflict) {
+				eprintf("## Conflicting argument in variable bootargs: "
+					"%.16s...\n", args);
+				return 0;
+			}
+		}
+
+		args = eptr;
+		args = skip_spaces(args);
+	}
+
+	return 1;
+}
+
+/**
+ * tdx_valid_bootargs - Check if bootargs string is valid
+ * Return: 1 if valid or 0 otherwise.
+ *
+ * Check bootargs string against information in FDT (the one passed to the OS);
+ * the FDT is expected to contain a copy of the initial part of the kernel
+ * command line (specifically the part that can be determined at build-time).
+ */
+int tdx_valid_bootargs(void *fdt, const char *bootargs)
+{
+	static const char req_prop[] = "required-bootargs";
+	const char *req_args = NULL;
+	const char *args = bootargs;
+	int req_len = 0;
+	int node_offset;
+
+	node_offset = fdt_path_offset(fdt, bootargs_node_path);
+	if (node_offset < 0) {
+		eprintf("## WARNING: Required node \"%s\" could not be found "
+			"in device-tree.\n", bootargs_node_path);
+		return 0;
+	}
+
+	req_args = fdt_getprop(fdt, node_offset, req_prop, &req_len);
+	if (!req_args) {
+		eprintf("## WARNING: Required property \"%s/%s\" could not be "
+			"found in device-tree.\n", bootargs_node_path, req_prop);
+		return 0;
+	}
+
+	debug("** bootargs(env)=\"%s\"\n", bootargs);
+	debug("** bootargs(fdt)=\"%.*s\" [L=%d]\n", req_len, req_args, req_len);
+
+	/* Strings should be NUL-terminated but let us be careful. */
+	req_len = strnlen(req_args, req_len);
+
+	/* First part of bootargs must match required property in FDT. */
+	if (req_len) {
+		args = skip_spaces(args);
+		debug("check 1st part:\n A: \"%.*s\"\n B: \"%.*s\"\n",
+		      req_len, args, req_len, req_args);
+		if (strncmp(args, req_args, req_len)) {
+			debug("req_args comparison failed\n");
+			goto fixpart_invalid;
+		}
+		args += req_len;
+	}
+
+	/* Second part (if any) is variable. */
+	if (*args) {
+		if (req_len && !isspace(*args)) {
+			debug("no space before variable args\n");
+			goto fixpart_invalid;
+		}
+		args = skip_spaces(args);
+	}
+
+	debug("variable part to validate: \"%s\"\n", args);
+	if (!_tdx_valid_var_bootargs(args, req_args))
+		goto varpart_invalid;
+
+	return 1;
+
+fixpart_invalid:
+	eprintf("## WARNING: Initial part of passed bootargs string (A) does "
+		"not match '%s' property (B) in device-tree.\n", req_prop);
+	eprintf("##  A: \"%s\"\n", skip_spaces(bootargs));
+	eprintf("##  B: \"%.*s\"\n", req_len, req_args);
+	return 0;
+
+varpart_invalid:
+	eprintf("## WARNING: Validation of the variable part of bootargs "
+		"failed; the full bootargs string (A) and its fixed part "
+		"(as defined in the '%s' property inside the device-tree) "
+		"follow:\n", req_prop);
+	eprintf("##  A: \"%s\"\n", skip_spaces(bootargs));
+	eprintf("##  B: \"%.*s\"\n", req_len, req_args);
+	return 0;
+}
+#endif
+
 static int hardening_info(void)
 {
 	int hdn_enabled = tdx_hardening_enabled();
diff --git a/include/tdx-harden.h b/include/tdx-harden.h
index 50e850ba2af..1cb61aed45e 100644
--- a/include/tdx-harden.h
+++ b/include/tdx-harden.h
@@ -26,14 +26,18 @@
  * };
  */
 
-/* Path of node in FDT containing all Secure Boot setup. */
+/* Path of node in control FDT containing all Secure Boot setup. */
 #define TDX_SECBOOT_NODE_PATH \
         "/chosen/toradex,secure-boot"
 
-/* Path of node in FDT containing command whitelist/blacklist. */
+/* Path of node in control FDT containing command whitelist/blacklist. */
 #define TDX_BOOTLDR_CMDS_NODE_PATH \
         "/chosen/toradex,secure-boot/bootloader-commands"
 
+/* Path of node in OS FDT containing all bootargs properties. */
+#define TDX_BOOTARGS_NODE_PATH \
+        "/chosen/toradex,secure-boot"
+
 struct cmd_tbl;
 
 int cmd_allowed_by_whitelist(struct cmd_tbl *cmd, int argc, char *const argv[]);
@@ -41,5 +45,6 @@ int tdx_secboot_dev_is_open(void);
 int tdx_hardening_enabled(void);
 int tdx_cli_access_enabled(void);
 void tdx_secure_boot_cmd(const char *cmd);
+int tdx_valid_bootargs(void *fdt, const char *bootargs);
 
 #endif	/* __TDX_HARDEN_H */
-- 
2.25.1

