[U-Boot] [PATCH 10/12] env: acl: Add environment variable access control list

Joe Hershberger joe.hershberger at ni.com
Fri Aug 17 22:49:44 CEST 2012


Currently just validates variable types as decimal, hexidecimal,
boolean, ip address, and mac address.  Call
env_acl_validate_setenv_params() from setenv() in both cmd_nvedit.c
and fw_env.c.

If the entry is not found in the env ACL, then look in the static one.
This allows the env to override the static definitions, but prevents
the need to have every definition in the environment distracting you.

Move min macro from fw_env.c to fw_env.h... it is needed by env_acl.c
when building for Linux.

Need to build in _ctype for isdigit for Linux.

Signed-off-by: Joe Hershberger <joe.hershberger at ni.com>
---
 README                |  31 ++++++
 common/Makefile       |   1 +
 common/cmd_nvedit.c   |   8 ++
 common/env_acl.c      | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++
 common/env_common.c   |   3 +
 common/env_embedded.c |   3 +
 include/env_acl.h     |  49 +++++++++
 tools/env/Makefile    |   2 +
 tools/env/fw_env.c    |  25 +++--
 tools/env/fw_env.h    |   6 ++
 10 files changed, 392 insertions(+), 6 deletions(-)
 create mode 100644 common/env_acl.c
 create mode 100644 include/env_acl.h

diff --git a/README b/README
index fb9d904..2e34bcd 100644
--- a/README
+++ b/README
@@ -2908,6 +2908,37 @@ Configuration Settings:
 	cases. This setting can be used to tune behaviour; see
 	lib/hashtable.c for details.
 
+- CONFIG_ENV_ACL
+	Enable validation of the values given to enviroment variables when
+	calling env set.  Variables can be restricted to only decimal,
+	hexadecimal, or boolean.  If CONFIG_CMD_NET is also defined,
+	the variables can also be restricted to IP address or MAC address.
+
+	The format of the list is:
+		type_attribute = [s|d|x|b|i|m]
+		attributes = type_attribute
+		entry = variable_name[:attributes]
+		list = entry[,list]
+
+	The type attributes are:
+		s - String (default)
+		d - Decimal
+		x - Hexadecimal
+		b - Boolean
+		i - IP address
+		m - MAC address
+
+	- CONFIG_ENV_ACL_DEFAULT
+		Define this to a list (string) to define the "acl" envirnoment
+		variable in the default or embedded environment.
+
+	- CONFIG_ENV_ACL_STATIC
+		Define this to a list (string) to define validation that
+		should be done if an entry is not found in the "acl"
+		environment variable.  To override a setting in the static
+		list, simply add an entry for the same variable name to the
+		"acl" variable.
+
 The following definitions that deal with the placement and management
 of environment data (variable area); in general, we support the
 following configurations:
diff --git a/common/Makefile b/common/Makefile
index 3d62775..64522e1 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -44,6 +44,7 @@ COBJS-y += cmd_version.o
 
 # environment
 COBJS-y += env_common.o
+COBJS-$(CONFIG_ENV_ACL) += env_acl.o
 COBJS-$(CONFIG_ENV_IS_IN_DATAFLASH) += env_dataflash.o
 COBJS-$(CONFIG_ENV_IS_IN_EEPROM) += env_eeprom.o
 XCOBJS-$(CONFIG_ENV_IS_EMBEDDED) += env_embedded.o
diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c
index 0d5b957..b761b2c 100644
--- a/common/cmd_nvedit.c
+++ b/common/cmd_nvedit.c
@@ -43,6 +43,9 @@
 #include <common.h>
 #include <command.h>
 #include <environment.h>
+#if defined(CONFIG_ENV_ACL)
+#include <env_acl.h>
+#endif
 #include <search.h>
 #include <errno.h>
 #include <malloc.h>
@@ -217,6 +220,11 @@ int _do_env_set(int flag, int argc, char * const argv[])
 		return 1;
 	}
 
+#ifdef CONFIG_ENV_ACL
+	if (env_acl_validate_env_set_params(argc, argv) < 0)
+		return 1;
+#endif
+
 	env_id++;
 	/*
 	 * search if variable with this name already exists
diff --git a/common/env_acl.c b/common/env_acl.c
new file mode 100644
index 0000000..7c86243
--- /dev/null
+++ b/common/env_acl.c
@@ -0,0 +1,270 @@
+/*
+ * (C) Copyright 2010
+ * Joe Hershberger, National Instruments, joe.hershberger at ni.com
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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/string.h>
+#include <linux/ctype.h>
+
+#ifdef USE_HOSTCC /* Eliminate "ANSI does not permit..." warnings */
+#include <stdint.h>
+#include <stdio.h>
+#include "fw_env.h"
+#define getenv fw_getenv
+#else
+#include <common.h>
+#endif
+
+#include <env_acl.h>
+
+#ifdef CONFIG_CMD_NET
+#define ENV_ACL_NET_TYPE_REPS "im"
+#else
+#define ENV_ACL_NET_TYPE_REPS ""
+#endif
+
+#ifndef CONFIG_ENV_ACL_STATIC
+#define CONFIG_ENV_ACL_STATIC ""
+#endif
+
+static const char env_acl_static[] = CONFIG_ENV_ACL_STATIC "\0";
+static const char env_acl_type_rep[] = "sdxb" ENV_ACL_NET_TYPE_REPS;
+
+static int _env_acl_lookup_r(const char *name, char *attributes, int static_acl)
+{
+	const char *acl;
+	const char *entry = NULL;
+
+	if (static_acl)
+		acl = &env_acl_static[0];
+	else
+		acl = getenv(ENV_ACL_LIST_VAR_NAME);
+
+	if (!attributes)
+		/* bad parameter */
+		return -1;
+	if (!acl)
+		/* list not found */
+		return 1;
+
+	entry = strstr(acl, name);
+	while (entry != NULL) {
+		if ((entry == acl || *(entry - 1) == ENV_ACL_LIST_DELIM ||
+		    *(entry - 1) == ' ') &&
+		    (*(entry + strlen(name)) == ENV_ACL_ATTR_SEP ||
+		     *(entry + strlen(name)) == ENV_ACL_LIST_DELIM ||
+		     *(entry + strlen(name)) == '\0' ||
+		     *(entry + strlen(name)) == ' '))
+			break;
+		entry++;
+		entry = strstr(entry, name);
+	}
+	if (entry != NULL) {
+		int len;
+
+		/* skip the name */
+		entry += strlen(name);
+		/* skip spaces */
+		while (*entry == ' ')
+			entry++;
+		if (*entry != ENV_ACL_ATTR_SEP)
+			len = 0;
+		else {
+			const char *delim;
+			const char delims[2] = {ENV_ACL_LIST_DELIM, ' '};
+
+			/* skip the attr sep */
+			entry += 1;
+			/* skip spaces */
+			while (*entry == ' ')
+				entry++;
+
+			delim = strpbrk(entry, delims);
+			if (delim == NULL)
+				len = strlen(entry);
+			else
+				len = delim - entry;
+			len = min(len, ENV_ACL_ATTR_MAX_LEN);
+			memcpy(attributes, entry, len);
+		}
+		attributes[len] = '\0';
+
+		/* success */
+		return 0;
+	}
+	/* not found in list */
+	return 2;
+}
+
+static int env_acl_lookup_r(const char *name, char *attributes)
+{
+	int ret_val;
+	/* try the env first */
+	ret_val = _env_acl_lookup_r(name, attributes, 0);
+	if (ret_val != 0) {
+		/* if not found in the env, look in the static list */
+		ret_val = _env_acl_lookup_r(name, attributes, 1);
+	}
+	return ret_val;
+}
+
+enum env_acl_var_type env_acl_get_type(const char *name)
+{
+	char *type;
+	char attr[ENV_ACL_ATTR_MAX_LEN + 1];
+
+	if (env_acl_lookup_r(name, attr))
+		return ENV_ACL_VAR_TYPE_STRING;
+
+	if (strlen(attr) <= ENV_ACL_TYPE_LOC)
+		return ENV_ACL_VAR_TYPE_STRING;
+
+	type = strchr(env_acl_type_rep, attr[ENV_ACL_TYPE_LOC]);
+
+	if (type != NULL)
+		return (enum env_acl_var_type)(type - &env_acl_type_rep[0]);
+
+	printf("## Warning: Unknown environment variable type '%c'\n",
+		attr[ENV_ACL_TYPE_LOC]);
+	return ENV_ACL_VAR_TYPE_STRING;
+}
+
+static inline int is_hex_prefix(const char *value)
+{
+	return value[0] == '0' && (value[1] == 'x' || value[1] == 'X');
+}
+
+static void skip_num(int hex, const char *value, const char **end,
+	int max_digits)
+{
+	int i;
+
+	if (hex && is_hex_prefix(value))
+		value += 2;
+
+	for (i = max_digits; i != 0; i--) {
+		if (hex && !isxdigit(*value))
+			break;
+		if (!hex && !isdigit(*value))
+			break;
+		value++;
+	}
+	if (end != NULL)
+		*end = value;
+}
+
+static int _env_acl_validate_type(const char *value, enum env_acl_var_type type)
+{
+	const char *end;
+	const char *cur;
+	int i;
+
+	switch (type) {
+	case ENV_ACL_VAR_TYPE_STRING:
+		break;
+	case ENV_ACL_VAR_TYPE_DECIMAL:
+		skip_num(0, value, &end, -1);
+		if (*end != '\0')
+			return -1;
+		break;
+	case ENV_ACL_VAR_TYPE_HEX:
+		skip_num(1, value, &end, -1);
+		if (*end != '\0')
+			return -1;
+		if (value + 2 == end && is_hex_prefix(value))
+			return -1;
+		break;
+	case ENV_ACL_VAR_TYPE_BOOL:
+		if (value[0] != '0' && value[0] != '1')
+			return -1;
+		if (value[1] != '\0')
+			return -1;
+		break;
+#ifdef CONFIG_CMD_NET
+	case ENV_ACL_VAR_TYPE_IPADDR:
+		cur = value;
+		for (i = 0; i < 4; i++) {
+			skip_num(0, cur, &end, 3);
+			if (cur == end)
+				return -1;
+			if (i != 3 && *end != '.')
+				return -1;
+			if (i == 3 && *end != '\0')
+				return -1;
+			cur = end + 1;
+		}
+		break;
+	case ENV_ACL_VAR_TYPE_MACADDR:
+		cur = value;
+		for (i = 0; i < 6; i++) {
+			skip_num(1, cur, &end, 2);
+			if (cur == end)
+				return -1;
+			if (cur + 2 == end && is_hex_prefix(cur))
+				return -1;
+			if (i != 5 && *end != ':')
+				return -1;
+			if (i == 5 && *end != '\0')
+				return -1;
+			cur = end + 1;
+		}
+		break;
+#endif
+	}
+
+	/* OK */
+	return 0;
+}
+
+int env_acl_validate_type(const char *name, const char *value)
+{
+	enum env_acl_var_type type = env_acl_get_type(name);
+
+	if (value == NULL)
+		return 0;
+	if (_env_acl_validate_type(value, type) < 0) {
+		printf("## Error: acl type check failure for "
+			"\"%s\" <= \"%s\" (type: %c)\n",
+			name, value, env_acl_type_rep[type]);
+		return -1;
+	}
+	return 0;
+}
+
+int env_acl_validate_env_set_params(int argc, char * const argv[])
+{
+	if ((argc >= 3) && argv[2] != NULL) {
+		enum env_acl_var_type type = env_acl_get_type(argv[1]);
+
+		/*
+		 * we don't currently check types that need more than
+		 * one argument
+		 */
+		if (type != ENV_ACL_VAR_TYPE_STRING && argc > 3) {
+			printf("## Error: too many parameters for setting "
+				"\"%s\"\n", argv[1]);
+			return -1;
+		}
+		return env_acl_validate_type(argv[1], argv[2]);
+	}
+	/* ok */
+	return 0;
+}
diff --git a/common/env_common.c b/common/env_common.c
index e58971d..c32c846 100644
--- a/common/env_common.c
+++ b/common/env_common.c
@@ -127,6 +127,9 @@ const uchar default_environment[] = {
 	"soc="		CONFIG_SYS_SOC			"\0"
 #endif
 #endif
+#ifdef	CONFIG_ENV_ACL_DEFAULT
+	"acl="		CONFIG_ENV_ACL_DEFAULT		"\0"
+#endif
 #ifdef	CONFIG_EXTRA_ENV_SETTINGS
 	CONFIG_EXTRA_ENV_SETTINGS
 #endif
diff --git a/common/env_embedded.c b/common/env_embedded.c
index 3872878..949fb95 100644
--- a/common/env_embedded.c
+++ b/common/env_embedded.c
@@ -190,6 +190,9 @@ env_t environment __PPCENV__ = {
 	"soc="		CONFIG_SYS_SOC			"\0"
 #endif
 #endif
+#ifdef	CONFIG_ENV_ACL_DEFAULT
+	"acl="		CONFIG_ENV_ACL_DEFAULT		"\0"
+#endif
 #ifdef	CONFIG_EXTRA_ENV_SETTINGS
 	CONFIG_EXTRA_ENV_SETTINGS
 #endif
diff --git a/include/env_acl.h b/include/env_acl.h
new file mode 100644
index 0000000..9b0a199
--- /dev/null
+++ b/include/env_acl.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 2010
+ * Joe Hershberger, National Instruments, joe.hershberger at ni.com
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 __ENV_ACL_H__
+#define __ENV_ACL_H__
+
+enum env_acl_var_type {
+	ENV_ACL_VAR_TYPE_STRING,
+	ENV_ACL_VAR_TYPE_DECIMAL,
+	ENV_ACL_VAR_TYPE_HEX,
+	ENV_ACL_VAR_TYPE_BOOL,
+#ifdef CONFIG_CMD_NET
+	ENV_ACL_VAR_TYPE_IPADDR,
+	ENV_ACL_VAR_TYPE_MACADDR,
+#endif
+};
+
+#define ENV_ACL_LIST_DELIM	','
+#define ENV_ACL_ATTR_SEP	':'
+#define ENV_ACL_LIST_VAR_NAME	"acl"
+#define ENV_ACL_ATTR_MAX_LEN	2
+#define ENV_ACL_TYPE_LOC	0
+
+enum env_acl_var_type env_acl_get_type(const char *name);
+int env_acl_validate_type(const char *name, const char *value);
+int env_acl_validate_env_set_params(int argc, char * const argv[]);
+
+
+#endif /* __ENV_ACL_H__ */
diff --git a/tools/env/Makefile b/tools/env/Makefile
index ab73c8c..82b49a0 100644
--- a/tools/env/Makefile
+++ b/tools/env/Makefile
@@ -24,12 +24,14 @@
 include $(TOPDIR)/config.mk
 
 HOSTSRCS := $(SRCTREE)/lib/crc32.c  fw_env.c  fw_env_main.c
+HOSTSRCS += $(SRCTREE)/lib/ctype.c $(SRCTREE)/common/env_acl.c
 HEADERS	:= fw_env.h $(OBJTREE)/include/config.h
 
 # Compile for a hosted environment on the target
 HOSTCPPFLAGS  = -idirafter $(SRCTREE)/include \
 		-idirafter $(OBJTREE)/include2 \
 		-idirafter $(OBJTREE)/include \
+		-idirafter $(SRCTREE)/tools/env \
 		-DUSE_HOSTCC \
 		-DTEXT_BASE=$(TEXT_BASE)
 
diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c
index a4cd179..f7807b5 100644
--- a/tools/env/fw_env.c
+++ b/tools/env/fw_env.c
@@ -45,16 +45,14 @@
 
 #include "fw_env.h"
 
+#if defined(CONFIG_ENV_ACL)
+#include <env_acl.h>
+#endif
+
 #define MAX_BYTES_PER_READ 0x20
 
 #define WHITESPACE(c) ((c == '\t') || (c == ' '))
 
-#define min(x, y) ({				\
-	typeof(x) _min1 = (x);			\
-	typeof(y) _min2 = (y);			\
-	(void) (&_min1 == &_min2);		\
-	_min1 < _min2 ? _min1 : _min2; })
-
 struct envdev_s {
 	char devname[16];		/* Device name */
 	ulong devoff;			/* Device offset */
@@ -203,6 +201,9 @@ static char default_environment[] = {
 #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
 	"pcidelay=" MK_STR (CONFIG_PCI_BOOTDELAY) "\0"
 #endif
+#ifdef	CONFIG_ENV_ACL_DEFAULT
+	"acl=" CONFIG_ENV_ACL_DEFAULT "\0"
+#endif
 #ifdef  CONFIG_EXTRA_ENV_SETTINGS
 	CONFIG_EXTRA_ENV_SETTINGS
 #endif
@@ -484,6 +485,11 @@ int fw_setenv(int argc, char *argv[])
 
 	name = argv[1];
 
+#if defined(CONFIG_ENV_ACL)
+	if (env_acl_validate_env_set_params(argc, argv) < 0)
+		return 1;
+#endif
+
 	len = 0;
 	for (i = 2; i < argc; ++i)
 		len += strlen(argv[i]) + 1;
@@ -611,6 +617,13 @@ int fw_parse_script(char *fname)
 			name, val ? val : " removed");
 #endif
 
+#if defined(CONFIG_ENV_ACL)
+		if (env_acl_validate_type(name, val) < 0) {
+			ret = -1;
+			break;
+		}
+#endif
+
 		/*
 		 * If there is an error setting a variable,
 		 * try to save the environment and returns an error
diff --git a/tools/env/fw_env.h b/tools/env/fw_env.h
index a1a6807..eca168a 100644
--- a/tools/env/fw_env.h
+++ b/tools/env/fw_env.h
@@ -77,3 +77,9 @@ extern int fw_env_write(char *name, char *value);
 extern int fw_env_close(void);
 
 extern unsigned	long  crc32	 (unsigned long, const unsigned char *, unsigned);
+
+#define min(x, y) ({				\
+	typeof(x) _min1 = (x);			\
+	typeof(y) _min2 = (y);			\
+	(void) (&_min1 == &_min2);		\
+	_min1 < _min2 ? _min1 : _min2; })
-- 
1.7.11.5



More information about the U-Boot mailing list