[U-Boot] [PATCH v3 17/18] env: Add support for access control to .flags
Joe Hershberger
joe.hershberger at ni.com
Thu Nov 1 17:39:54 CET 2012
Add support for read-only, write-once, and change-default.
Signed-off-by: Joe Hershberger <joe.hershberger at ni.com>
---
README | 13 ++++-
common/cmd_nvedit.c | 31 ++++++++--
common/env_common.c | 18 ++++++
common/env_flags.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++--
include/env_flags.h | 50 ++++++++++++++++-
include/environment.h | 3 +
tools/env/fw_env.c | 74 ++++++++++++++++++++++--
7 files changed, 324 insertions(+), 18 deletions(-)
diff --git a/README b/README
index 58ac4e7..ed3f57d 100644
--- a/README
+++ b/README
@@ -3022,7 +3022,8 @@ Configuration Settings:
The format of the list is:
type_attribute = [s|d|x|b|i|m]
- attributes = type_attribute
+ access_atribute = [a|r|o|c]
+ attributes = type_attribute[access_atribute]
entry = variable_name[:attributes]
list = entry[,list]
@@ -3034,6 +3035,12 @@ Configuration Settings:
i - IP address
m - MAC address
+ The access attributes are:
+ a - Any (default)
+ r - Read-only
+ o - Write-once
+ c - Change-default
+
- CONFIG_ENV_FLAGS_LIST_DEFAULT
Define this to a list (string) to define the ".flags"
envirnoment variable in the default or embedded environment.
@@ -3045,6 +3052,10 @@ Configuration Settings:
list, simply add an entry for the same variable name to the
".flags" variable.
+- CONFIG_ENV_ACCESS_IGNORE_FORCE
+ If defined, don't allow the -f switch to env set override variable
+ access flags.
+
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/cmd_nvedit.c b/common/cmd_nvedit.c
index 4d2e86d..4c77ced 100644
--- a/common/cmd_nvedit.c
+++ b/common/cmd_nvedit.c
@@ -442,8 +442,11 @@ int do_env_callback(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
static int print_static_flags(const char *var_name, const char *flags)
{
enum env_flags_vartype type = env_flags_parse_vartype(flags);
+ enum env_flags_varaccess access = env_flags_parse_varaccess(flags);
- printf("\t%-20s %-20s\n", var_name, env_flags_get_vartype_name(type));
+ printf("\t%-20s %-20s %-20s\n", var_name,
+ env_flags_get_vartype_name(type),
+ env_flags_get_varaccess_name(access));
return 0;
}
@@ -451,13 +454,17 @@ static int print_static_flags(const char *var_name, const char *flags)
static int print_active_flags(ENTRY *entry)
{
enum env_flags_vartype type;
+ enum env_flags_varaccess access;
if (entry->flags == 0)
return 0;
type = (enum env_flags_vartype)
(entry->flags & ENV_FLAGS_VARTYPE_BIN_MASK);
- printf("\t%-20s %-20s\n", entry->key, env_flags_get_vartype_name(type));
+ access = env_flags_parse_varaccess_from_binflags(entry->flags);
+ printf("\t%-20s %-20s %-20s\n", entry->key,
+ env_flags_get_vartype_name(type),
+ env_flags_get_varaccess_name(access));
return 0;
}
@@ -475,17 +482,29 @@ int do_env_flags(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
env_flags_print_vartypes();
puts("\n");
+ /* Print the available variable access types */
+ printf("Available variable access flags (position %d):\n",
+ ENV_FLAGS_VARACCESS_LOC);
+ puts("\tFlag\tVariable Access Name\n");
+ puts("\t----\t--------------------\n");
+ env_flags_print_varaccess();
+ puts("\n");
+
/* Print the static flags that may exist */
puts("Static flags:\n");
- printf("\t%-20s %-20s\n", "Variable Name", "Variable Type");
- printf("\t%-20s %-20s\n", "-------------", "-------------");
+ printf("\t%-20s %-20s %-20s\n", "Variable Name", "Variable Type",
+ "Variable Access");
+ printf("\t%-20s %-20s %-20s\n", "-------------", "-------------",
+ "---------------");
env_attr_walk(ENV_FLAGS_LIST_STATIC, print_static_flags);
puts("\n");
/* walk through each variable and print the flags if non-default */
puts("Active flags:\n");
- printf("\t%-20s %-20s\n", "Variable Name", "Variable Type");
- printf("\t%-20s %-20s\n", "-------------", "-------------");
+ printf("\t%-20s %-20s %-20s\n", "Variable Name", "Variable Type",
+ "Variable Access");
+ printf("\t%-20s %-20s %-20s\n", "-------------", "-------------",
+ "---------------");
hwalk_r(&env_htab, print_active_flags);
return 0;
}
diff --git a/common/env_common.c b/common/env_common.c
index b711337..3021efe 100644
--- a/common/env_common.c
+++ b/common/env_common.c
@@ -95,6 +95,24 @@ int getenv_yesno(const char *var)
1 : 0;
}
+/*
+ * Look up the variable from the default environment
+ */
+char *getenv_default(const char *name)
+{
+ char *ret_val;
+ unsigned long really_valid = gd->env_valid;
+ unsigned long real_gd_flags = gd->flags;
+
+ /* Pretend that the image is bad. */
+ gd->flags &= ~GD_FLG_ENV_READY;
+ gd->env_valid = 0;
+ ret_val = getenv(name);
+ gd->env_valid = really_valid;
+ gd->flags = real_gd_flags;
+ return ret_val;
+}
+
void set_default_env(const char *s)
{
int flags = 0;
diff --git a/common/env_flags.c b/common/env_flags.c
index 7afc423..9bf7f2d 100644
--- a/common/env_flags.c
+++ b/common/env_flags.c
@@ -43,6 +43,17 @@
#endif
static const char env_flags_vartype_rep[] = "sdxb" ENV_FLAGS_NET_VARTYPE_REPS;
+static const char env_flags_varaccess_rep[] = "aroc";
+static const int env_flags_varaccess_mask[] = {
+ 0,
+ ENV_FLAGS_VARACCESS_PREVENT_DELETE |
+ ENV_FLAGS_VARACCESS_PREVENT_CREATE |
+ ENV_FLAGS_VARACCESS_PREVENT_OVERWR,
+ ENV_FLAGS_VARACCESS_PREVENT_DELETE |
+ ENV_FLAGS_VARACCESS_PREVENT_OVERWR,
+ ENV_FLAGS_VARACCESS_PREVENT_DELETE |
+ ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR};
+
#ifdef CONFIG_CMD_ENV_FLAGS
static const char * const env_flags_vartype_names[] = {
"string",
@@ -54,6 +65,12 @@ static const char * const env_flags_vartype_names[] = {
"MAC address",
#endif
};
+static const char * const env_flags_varaccess_names[] = {
+ "any",
+ "read-only",
+ "write-once",
+ "change-default",
+};
/*
* Print the whole list of available type flags.
@@ -70,12 +87,34 @@ void env_flags_print_vartypes(void)
}
/*
+ * Print the whole list of available access flags.
+ */
+void env_flags_print_varaccess(void)
+{
+ enum env_flags_varaccess curaccess = (enum env_flags_varaccess)0;
+
+ while (curaccess != env_flags_varaccess_end) {
+ printf("\t%c -\t%s\n", env_flags_varaccess_rep[curaccess],
+ env_flags_varaccess_names[curaccess]);
+ curaccess++;
+ }
+}
+
+/*
* Return the name of the type.
*/
const char *env_flags_get_vartype_name(enum env_flags_vartype type)
{
return env_flags_vartype_names[type];
}
+
+/*
+ * Return the name of the access.
+ */
+const char *env_flags_get_varaccess_name(enum env_flags_varaccess access)
+{
+ return env_flags_varaccess_names[access];
+}
#endif /* CONFIG_CMD_ENV_FLAGS */
/*
@@ -95,6 +134,40 @@ enum env_flags_vartype env_flags_parse_vartype(const char *flags)
return env_flags_vartype_string;
}
+/*
+ * Parse the flags string from a .flags attribute list into the varaccess enum.
+ */
+enum env_flags_varaccess env_flags_parse_varaccess(const char *flags)
+{
+ char *access = strchr(env_flags_varaccess_rep,
+ flags[ENV_FLAGS_VARACCESS_LOC]);
+
+ if (access != NULL)
+ return (enum env_flags_varaccess)
+ (access - &env_flags_varaccess_rep[0]);
+
+ printf("## Warning: Unknown environment variable access method '%c'\n",
+ flags[ENV_FLAGS_VARACCESS_LOC]);
+ return env_flags_varaccess_any;
+}
+
+/*
+ * Parse the binary flags from a hash table entry into the varaccess enum.
+ */
+enum env_flags_varaccess env_flags_parse_varaccess_from_binflags(int binflags)
+{
+ int i;
+
+ for (i = 0; i < sizeof(env_flags_varaccess_mask); i++)
+ if (env_flags_varaccess_mask[i] ==
+ (binflags & ENV_FLAGS_VARACCESS_BIN_MASK))
+ return (enum env_flags_varaccess)i;
+
+ puts("Warning: Non-standard access flags.\n");
+
+ return env_flags_varaccess_any;
+}
+
static inline int is_hex_prefix(const char *value)
{
return value[0] == '0' && (value[1] == 'x' || value[1] == 'X');
@@ -237,6 +310,23 @@ enum env_flags_vartype env_flags_get_type(const char *name)
}
/*
+ * Look up the access of a variable directly from the .flags var.
+ */
+enum env_flags_varaccess env_flags_get_varaccess(const char *name)
+{
+ const char *flags_list = getenv(ENV_FLAGS_VAR);
+ char flags[ENV_FLAGS_ATTR_MAX_LEN + 1];
+
+ if (env_flags_lookup(flags_list, name, flags))
+ return env_flags_varaccess_any;
+
+ if (strlen(flags) <= ENV_FLAGS_VARACCESS_LOC)
+ return env_flags_varaccess_any;
+
+ return env_flags_parse_varaccess(flags);
+}
+
+/*
* Validate that the proposed new value for "name" is valid according to the
* defined flags for that variable, if any.
*/
@@ -257,6 +347,21 @@ int env_flags_validate_type(const char *name, const char *value)
}
/*
+ * Validate that the proposed access to variable "name" is valid according to
+ * the defined flags for that variable, if any.
+ */
+int env_flags_validate_varaccess(const char *name, int check_mask)
+{
+ enum env_flags_varaccess access;
+ int access_mask;
+
+ access = env_flags_get_varaccess(name);
+ access_mask = env_flags_varaccess_mask[access];
+
+ return (check_mask & access_mask) != 0;
+}
+
+/*
* Validate the parameters to "env set" directly
*/
int env_flags_validate_env_set_params(int argc, char * const argv[])
@@ -287,7 +392,12 @@ int env_flags_validate_env_set_params(int argc, char * const argv[])
*/
static int env_parse_flags_to_bin(const char *flags)
{
- return env_flags_parse_vartype(flags) & ENV_FLAGS_VARTYPE_BIN_MASK;
+ int binflags;
+
+ binflags = env_flags_parse_vartype(flags) & ENV_FLAGS_VARTYPE_BIN_MASK;
+ binflags |= env_flags_varaccess_mask[env_flags_parse_varaccess(flags)];
+
+ return binflags;
}
/*
@@ -372,13 +482,10 @@ int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op,
int flag)
{
const char *name;
-#if !defined(CONFIG_ENV_OVERWRITE) && defined(CONFIG_OVERWRITE_ETHADDR_ONCE) \
-&& defined(CONFIG_ETHADDR)
const char *oldval = NULL;
if (op != env_op_create)
oldval = item->data;
-#endif
name = item->key;
@@ -417,6 +524,44 @@ int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op,
}
}
+ /* check for access permission */
+#ifndef CONFIG_ENV_ACCESS_IGNORE_FORCE
+ if (flag & H_FORCE)
+ return 0;
+#endif
+ switch (op) {
+ case env_op_delete:
+ if (item->flags & ENV_FLAGS_VARACCESS_PREVENT_DELETE) {
+ printf("## Error: Can't delete \"%s\"\n", name);
+ return 1;
+ }
+ break;
+ case env_op_overwrite:
+ if (item->flags & ENV_FLAGS_VARACCESS_PREVENT_OVERWR) {
+ printf("## Error: Can't overwrite \"%s\"\n", name);
+ return 1;
+ } else if (item->flags &
+ ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR) {
+ const char *defval = getenv_default(name);
+
+ if (defval == NULL)
+ defval = "";
+ printf("oldval: %s defval: %s\n", oldval, defval);
+ if (strcmp(oldval, defval) != 0) {
+ printf("## Error: Can't overwrite \"%s\"\n",
+ name);
+ return 1;
+ }
+ }
+ break;
+ case env_op_create:
+ if (item->flags & ENV_FLAGS_VARACCESS_PREVENT_CREATE) {
+ printf("## Error: Can't create \"%s\"\n", name);
+ return 1;
+ }
+ break;
+ }
+
return 0;
}
diff --git a/include/env_flags.h b/include/env_flags.h
index 7e72523..0bdae07 100644
--- a/include/env_flags.h
+++ b/include/env_flags.h
@@ -36,9 +36,18 @@ enum env_flags_vartype {
env_flags_vartype_end
};
+enum env_flags_varaccess {
+ env_flags_varaccess_any,
+ env_flags_varaccess_readonly,
+ env_flags_varaccess_writeonce,
+ env_flags_varaccess_changedefault,
+ env_flags_varaccess_end
+};
+
#define ENV_FLAGS_VAR ".flags"
#define ENV_FLAGS_ATTR_MAX_LEN 2
#define ENV_FLAGS_VARTYPE_LOC 0
+#define ENV_FLAGS_VARACCESS_LOC 1
#ifndef CONFIG_ENV_FLAGS_LIST_STATIC
#define CONFIG_ENV_FLAGS_LIST_STATIC ""
@@ -53,15 +62,31 @@ enum env_flags_vartype {
*/
void env_flags_print_vartypes(void);
/*
+ * Print the whole list of available access flags.
+ */
+void env_flags_print_varaccess(void);
+/*
* Return the name of the type.
*/
const char *env_flags_get_vartype_name(enum env_flags_vartype type);
+/*
+ * Return the name of the access.
+ */
+const char *env_flags_get_varaccess_name(enum env_flags_varaccess access);
#endif
/*
* Parse the flags string from a .flags attribute list into the vartype enum.
*/
enum env_flags_vartype env_flags_parse_vartype(const char *flags);
+/*
+ * Parse the flags string from a .flags attribute list into the varaccess enum.
+ */
+enum env_flags_varaccess env_flags_parse_varaccess(const char *flags);
+/*
+ * Parse the binary flags from a hash table entry into the varaccess enum.
+ */
+enum env_flags_varaccess env_flags_parse_varaccess_from_binflags(int binflags);
#ifdef USE_HOSTCC
/*
@@ -69,11 +94,25 @@ enum env_flags_vartype env_flags_parse_vartype(const char *flags);
*/
enum env_flags_vartype env_flags_get_type(const char *name);
/*
+ * Look up the access of a variable directly from the .flags var.
+ */
+enum env_flags_varaccess env_flags_get_access(const char *name);
+/*
* Validate the newval for its type to conform with the requirements defined by
* its flags (directly looked at the .flags var).
*/
int env_flags_validate_type(const char *name, const char *newval);
/*
+ * Validate the newval for its access to conform with the requirements defined
+ * by its flags (directly looked at the .flags var).
+ */
+int env_flags_validate_access(const char *name, int check_mask);
+/*
+ * Validate that the proposed access to variable "name" is valid according to
+ * the defined flags for that variable, if any.
+ */
+int env_flags_validate_varaccess(const char *name, int check_mask);
+/*
* Validate the parameters passed to "env set" for type compliance
*/
int env_flags_validate_env_set_params(int argc, char * const argv[]);
@@ -94,13 +133,18 @@ void env_flags_init(ENTRY *var_entry);
int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op,
int flag);
+#endif /* USE_HOSTCC */
+
/*
* These are the binary flags used in the environment entry->flags variable to
* decribe properties of veriables in the table
*/
-#define ENV_FLAGS_VARTYPE_BIN_MASK 0x00000007
+#define ENV_FLAGS_VARTYPE_BIN_MASK 0x00000007
/* The actual variable type values use the enum value (within the mask) */
-
-#endif /* USE_HOSTCC */
+#define ENV_FLAGS_VARACCESS_PREVENT_DELETE 0x00000008
+#define ENV_FLAGS_VARACCESS_PREVENT_CREATE 0x00000010
+#define ENV_FLAGS_VARACCESS_PREVENT_OVERWR 0x00000020
+#define ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR 0x00000040
+#define ENV_FLAGS_VARACCESS_BIN_MASK 0x00000078
#endif /* __ENV_FLAGS_H__ */
diff --git a/include/environment.h b/include/environment.h
index 00e59ba..e64b43d 100644
--- a/include/environment.h
+++ b/include/environment.h
@@ -181,6 +181,9 @@ unsigned char env_get_char_memory(int index);
/* Function that updates CRC of the enironment */
void env_crc_update(void);
+/* Look up the variable from the default environment */
+char *getenv_default(const char *name);
+
/* [re]set to the default environment */
void set_default_env(const char *s);
diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c
index 5be36fc..a596a1b 100644
--- a/tools/env/fw_env.c
+++ b/tools/env/fw_env.c
@@ -182,6 +182,32 @@ char *fw_getenv (char *name)
}
/*
+ * Search the default environment for a variable.
+ * Return the value, if found, or NULL, if not found.
+ */
+char *fw_getdefenv(char *name)
+{
+ char *env, *nxt;
+
+ for (env = default_environment; *env; env = nxt + 1) {
+ char *val;
+
+ for (nxt = env; *nxt; ++nxt) {
+ if (nxt >= &default_environment[ENV_SIZE]) {
+ fprintf(stderr, "## Error: "
+ "default environment not terminated\n");
+ return NULL;
+ }
+ }
+ val = envmatch(name, env);
+ if (!val)
+ continue;
+ return val;
+ }
+ return NULL;
+}
+
+/*
* Print the current definition of one, or more, or all
* environment variables
*/
@@ -282,6 +308,7 @@ int fw_env_write(char *name, char *value)
int len;
char *env, *nxt;
char *oldval = NULL;
+ int deleting, creating, overwriting;
/*
* search if variable with this name already exists
@@ -299,10 +326,49 @@ int fw_env_write(char *name, char *value)
break;
}
- /*
- * Delete any existing definition
- */
- if (oldval) {
+ deleting = (oldval && !(value && strlen(value)));
+ creating = (!oldval && (value && strlen(value)));
+ overwriting = (oldval && (value && strlen(value)));
+
+ /* check for permission */
+ if (deleting) {
+ if (env_flags_validate_varaccess(name,
+ ENV_FLAGS_VARACCESS_PREVENT_DELETE)) {
+ printf("Can't delete \"%s\"\n", name);
+ errno = EROFS;
+ return -1;
+ }
+ } else if (overwriting) {
+ if (env_flags_validate_varaccess(name,
+ ENV_FLAGS_VARACCESS_PREVENT_OVERWR)) {
+ printf("Can't overwrite \"%s\"\n", name);
+ errno = EROFS;
+ return -1;
+ } else if (env_flags_validate_varaccess(name,
+ ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR)) {
+ const char *defval = fw_getdefenv(name);
+
+ if (defval == NULL)
+ defval = "";
+ if (strcmp(oldval, defval)
+ != 0) {
+ printf("Can't overwrite \"%s\"\n", name);
+ errno = EROFS;
+ return -1;
+ }
+ }
+ } else if (creating) {
+ if (env_flags_validate_varaccess(name,
+ ENV_FLAGS_VARACCESS_PREVENT_CREATE)) {
+ printf("Can't create \"%s\"\n", name);
+ errno = EROFS;
+ return -1;
+ }
+ } else
+ /* Nothing to do */
+ return 0;
+
+ if (deleting || overwriting) {
#ifndef CONFIG_ENV_OVERWRITE
/*
* Ethernet Address and serial# can be set only once
--
1.7.11.5
More information about the U-Boot
mailing list