env: Add support for access control to .flags
authorJoe Hershberger <joe.hershberger@ni.com>
Wed, 12 Dec 2012 04:16:34 +0000 (22:16 -0600)
committerTom Rini <trini@ti.com>
Thu, 13 Dec 2012 18:46:56 +0000 (11:46 -0700)
Add support for read-only, write-once, and change-default.

Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>
README
common/cmd_nvedit.c
common/env_common.c
common/env_flags.c
include/env_flags.h
include/environment.h
tools/env/fw_env.c

diff --git a/README b/README
index 08c0fccb5e6419e0d9d0cda6f7456a5622a235f0..b5c1c0377b1b4dbb27835ebeea92768bfc03d28c 100644 (file)
--- a/README
+++ b/README
@@ -3128,7 +3128,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]
 
@@ -3140,6 +3141,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.
@@ -3151,6 +3158,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:
index 468b89cc9c93558d7ec58a0cb02214c7b2a79d8b..e8dfbf5d85e64a24c4f0a5289ab600e92cc8789f 100644 (file)
@@ -447,8 +447,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;
 }
@@ -456,13 +459,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;
 }
@@ -480,17 +487,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;
 }
index bb18070c54af55dd847a1b239ac98ffb2f76dabf..906b41fccada86dc41d58d23ad42f74e7d5476b2 100644 (file)
@@ -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;
index 09f93d59d63367580f436dd82c16167ed6f86e92..4caf12e697035f3c912cb309065f6376fc264920 100644 (file)
 #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.
@@ -69,6 +86,20 @@ 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.
  */
@@ -76,6 +107,14 @@ 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 */
 
 /*
@@ -100,6 +139,46 @@ 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;
+
+       if (strlen(flags) <= ENV_FLAGS_VARACCESS_LOC)
+               return env_flags_varaccess_any;
+
+       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;
+
+       printf("Warning: Non-standard access flags. (0x%x)\n",
+               binflags & ENV_FLAGS_VARACCESS_BIN_MASK);
+
+       return env_flags_varaccess_any;
+}
+
 static inline int is_hex_prefix(const char *value)
 {
        return value[0] == '0' && (value[1] == 'x' || value[1] == 'X');
@@ -241,6 +320,23 @@ enum env_flags_vartype env_flags_get_type(const char *name)
        return env_flags_parse_vartype(flags);
 }
 
+/*
+ * 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.
@@ -261,6 +357,21 @@ int env_flags_validate_type(const char *name, const char *value)
        return 0;
 }
 
+/*
+ * 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
  */
@@ -292,7 +403,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;
 }
 
 /*
@@ -377,13 +493,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;
 
@@ -422,6 +535,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;
 }
 
index 7e72523f02666e06db474b2c7d49f63b35ab49d1..0bdae078389e169d981ed0055278b69e4b86148d 100644 (file)
@@ -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 ""
@@ -52,27 +61,57 @@ enum env_flags_vartype {
  * Print the whole list of available type flags.
  */
 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
 /*
  * Look up the type of a variable directly from the .flags var.
  */
 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
  */
@@ -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__ */
index 00e59ba789c0965f420ed072ffdbeeda03d90a73..e64b43d2d94834760553dc9a0cd06f632022394b 100644 (file)
@@ -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);
 
index 5be36fc35956b69d36f131fe3d99558f68b6d058..a596a1b0dc84fbb4be9f864cf1b043c53d541d24 100644 (file)
@@ -181,6 +181,32 @@ char *fw_getenv (char *name)
        return NULL;
 }
 
+/*
+ * 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