Add support for read-only and write-once.

Signed-off-by: Joe Hershberger <[email protected]>
---
 README              | 13 +++++++++++-
 common/cmd_nvedit.c | 51 ++++++++++++++++++++++++++++++++++++++++++++
 common/env_acl.c    | 38 +++++++++++++++++++++++++++++++++
 include/env_acl.h   | 14 ++++++++++++
 tools/env/fw_env.c  | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 176 insertions(+), 1 deletion(-)

diff --git a/README b/README
index 2e34bcd..fa665f0 100644
--- a/README
+++ b/README
@@ -2094,6 +2094,11 @@ The following options need to be configured:
                serial# is unaffected by this, i. e. it remains
                read-only.]
 
+               The same can be accomplished in a more flexible way
+               for any variable by defining CONFIG_ENV_ACL and
+               specifying the type of access to allow to each
+               variable.
+
 - Protected RAM:
                CONFIG_PRAM
 
@@ -2916,7 +2921,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]
+               attributes = type_attribute[access_atribute]
                entry = variable_name[:attributes]
                list = entry[,list]
 
@@ -2928,6 +2934,11 @@ Configuration Settings:
                i - IP address
                m - MAC address
 
+       The access attributes are:
+               a - Any (default)
+               r - Read-only
+               o - Write-once (change default)
+
        - CONFIG_ENV_ACL_DEFAULT
                Define this to a list (string) to define the "acl" envirnoment
                variable in the default or embedded environment.
diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c
index b761b2c..13b6e08 100644
--- a/common/cmd_nvedit.c
+++ b/common/cmd_nvedit.c
@@ -200,6 +200,23 @@ static int do_env_grep(cmd_tbl_t *cmdtp, int flag,
 }
 #endif
 
+#ifdef CONFIG_ENV_ACL
+/*
+ * Look up the variable from the default environment
+ */
+static char *getdefenv(const char *name)
+{
+       char *ret_val;
+       unsigned long really_valid = gd->env_valid;
+
+       /* Pretend that the image is bad. */
+       gd->env_valid = 0;
+       ret_val = getenv(name);
+       gd->env_valid = really_valid;
+       return ret_val;
+}
+#endif
+
 /*
  * Set a new environment variable,
  * or replace or delete an existing one.
@@ -237,6 +254,40 @@ int _do_env_set(int flag, int argc, char * const argv[])
        creating = (!ep && ((argc >= 3) && argv[2] != NULL));
        overwriting = (ep && ((argc >= 3) && argv[2] != NULL));
 
+#ifdef CONFIG_ENV_ACL
+       /* check for permission */
+       if (deleting) {
+               if (env_acl_validate_access(name, ENV_ACL_PREVENT_DELETE)) {
+                       printf("## Error: Can't delete \"%s\"\n", name);
+                       return 1;
+               }
+       } else if (overwriting) {
+               if (env_acl_validate_access(name, ENV_ACL_PREVENT_OVERWR)) {
+                       printf("## Error: Can't overwrite \"%s\"\n", name);
+                       return 1;
+               } else if (env_acl_validate_access(name,
+                   ENV_ACL_PREVENT_NONDEF_OVERWR)) {
+                       const char *defval = getdefenv(name);
+
+                       if (defval == NULL)
+                               defval = "";
+                       if (strcmp(ep->data, defval)
+                           != 0) {
+                               printf("## Error: Can't overwrite \"%s\"\n",
+                                       name);
+                               return 1;
+                       }
+               }
+       } else if (creating) {
+               if (env_acl_validate_access(name, ENV_ACL_PREVENT_CREATE)) {
+                       printf("## Error: Can't create \"%s\"\n", name);
+                       return 1;
+               }
+       } else
+               /* Nothing to do */
+               return 0;
+#endif
+
        /* Check for console redirection */
        if (strcmp(name, "stdin") == 0)
                console = stdin;
diff --git a/common/env_acl.c b/common/env_acl.c
index 7c86243..1a75e09 100644
--- a/common/env_acl.c
+++ b/common/env_acl.c
@@ -47,6 +47,12 @@
 
 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 const char env_acl_access_rep[] = "aro";
+static const char env_acl_access_mask[] = {
+       0,
+       ENV_ACL_PREVENT_DELETE | ENV_ACL_PREVENT_CREATE |
+               ENV_ACL_PREVENT_OVERWR,
+       ENV_ACL_PREVENT_DELETE | ENV_ACL_PREVENT_NONDEF_OVERWR};
 
 static int _env_acl_lookup_r(const char *name, char *attributes, int 
static_acl)
 {
@@ -147,6 +153,27 @@ enum env_acl_var_type env_acl_get_type(const char *name)
        return ENV_ACL_VAR_TYPE_STRING;
 }
 
+enum env_acl_var_access env_acl_get_access(const char *name)
+{
+       char *access;
+       char attr[ENV_ACL_ATTR_MAX_LEN + 1];
+       if (env_acl_lookup_r(name, attr))
+               return ENV_ACL_VAR_ACCESS_ANY;
+
+       if (strlen(attr) <= ENV_ACL_ACCESS_LOC)
+               return ENV_ACL_VAR_ACCESS_ANY;
+
+       access = strchr(env_acl_access_rep, attr[ENV_ACL_ACCESS_LOC]);
+
+       if (access != NULL)
+               return (enum env_acl_var_access)
+                       (access - &env_acl_access_rep[0]);
+
+       printf("## Warning: Unknown environment variable access method '%c'\n",
+               attr[ENV_ACL_ACCESS_LOC]);
+       return ENV_ACL_VAR_ACCESS_ANY;
+}
+
 static inline int is_hex_prefix(const char *value)
 {
        return value[0] == '0' && (value[1] == 'x' || value[1] == 'X');
@@ -249,6 +276,17 @@ int env_acl_validate_type(const char *name, const char 
*value)
        return 0;
 }
 
+int env_acl_validate_access(const char *name, int check_mask)
+{
+       enum env_acl_var_access access;
+       int access_mask;
+
+       access = env_acl_get_access(name);
+       access_mask = env_acl_access_mask[access];
+
+       return (check_mask & access_mask) != 0;
+}
+
 int env_acl_validate_env_set_params(int argc, char * const argv[])
 {
        if ((argc >= 3) && argv[2] != NULL) {
diff --git a/include/env_acl.h b/include/env_acl.h
index 9b0a199..09618f9 100644
--- a/include/env_acl.h
+++ b/include/env_acl.h
@@ -35,14 +35,28 @@ enum env_acl_var_type {
 #endif
 };
 
+enum env_acl_var_access {
+       ENV_ACL_VAR_ACCESS_ANY,
+       ENV_ACL_VAR_ACCESS_READ,
+       ENV_ACL_VAR_ACCESS_SET_ONCE,
+};
+
+#define ENV_ACL_PREVENT_DELETE         0x01
+#define ENV_ACL_PREVENT_CREATE         0x02
+#define ENV_ACL_PREVENT_OVERWR         0x04
+#define ENV_ACL_PREVENT_NONDEF_OVERWR  0x08
+
 #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
+#define ENV_ACL_ACCESS_LOC     1
 
 enum env_acl_var_type env_acl_get_type(const char *name);
+enum env_acl_var_access env_acl_get_access(const char *name);
 int env_acl_validate_type(const char *name, const char *value);
+int env_acl_validate_access(const char *name, int check_mask);
 int env_acl_validate_env_set_params(int argc, char * const argv[]);
 
 
diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c
index f7807b5..60ff06c 100644
--- a/tools/env/fw_env.c
+++ b/tools/env/fw_env.c
@@ -268,6 +268,34 @@ char *fw_getenv (char *name)
        return NULL;
 }
 
+#ifdef CONFIG_ENV_ACL
+/*
+ * 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;
+}
+#endif
+
 /*
  * Print the current definition of one, or more, or all
  * environment variables
@@ -391,6 +419,39 @@ int fw_env_write(char *name, char *value)
        creating = (!oldval && (value && strlen(value)));
        overwriting = (oldval && (value && strlen(value)));
 
+#ifdef CONFIG_ENV_ACL
+       /* check for permission */
+       if (deleting) {
+               if (env_acl_validate_access(name, ENV_ACL_PREVENT_DELETE)) {
+                       printf("Can't delete \"%s\"\n", name);
+                       return 1;
+               }
+       } else if (overwriting) {
+               if (env_acl_validate_access(name, ENV_ACL_PREVENT_OVERWR)) {
+                       printf("Can't overwrite \"%s\"\n", name);
+                       return 1;
+               } else if (env_acl_validate_access(name,
+                   ENV_ACL_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);
+                               return 1;
+                       }
+               }
+       } else if (creating) {
+               if (env_acl_validate_access(name, ENV_ACL_PREVENT_CREATE)) {
+                       printf("Can't create \"%s\"\n", name);
+                       return 1;
+               }
+       } else
+               /* Nothing to do */
+               return 0;
+#endif
+
        if (deleting || overwriting) {
 #ifndef CONFIG_ENV_OVERWRITE
                /*
-- 
1.7.11.5

_______________________________________________
U-Boot mailing list
[email protected]
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to