uci: reject invalid section and option names
authorJo-Philipp Wich <jo@mein.io>
Wed, 8 Aug 2018 16:14:30 +0000 (18:14 +0200)
committerJo-Philipp Wich <jo@mein.io>
Wed, 8 Aug 2018 21:58:37 +0000 (23:58 +0200)
The invoked libuci functions do not reliably check their arguments, causing
malformed section and option names to end up in the delta file, letting the
uci cli and other components to segfault when processung such invalid
entries.

In order to prevent that, manually test received values before passing them
on to libuci.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
uci.c

diff --git a/uci.c b/uci.c
index a1b83117abaed2823193ab110bfd4d211cf02d38..bb7adb034d11880bc45375de43cf173d5a515a08 100644 (file)
--- a/uci.c
+++ b/uci.c
@@ -181,6 +181,60 @@ static const struct blobmsg_policy rpc_uci_rollback_policy[__RPC_B_MAX] = {
                                                .type = BLOBMSG_TYPE_STRING },
 };
 
+/*
+ * Validate a uci name
+ */
+static bool
+rpc_uci_verify_str(const char *name, bool extended, bool type)
+{
+       const char *c;
+       char *e;
+
+       if (!name || !*name)
+               return false;
+
+       if (extended && *name != '@')
+               extended = false;
+
+       for (c = name + extended; *c; c++)
+               if (!isalnum(*c) && *c != '_' && ((!type && !extended) || *c != '-'))
+                       break;
+
+       if (extended) {
+               if (*c != '[')
+                       return false;
+
+               strtol(++c, &e, 10);
+
+               return (e > c && *e == ']' && *(e+1) == 0);
+       }
+
+       return (*c == 0);
+}
+
+/*
+ * Check that string is a valid, shell compatible uci name
+ */
+static bool rpc_uci_verify_name(const char *name) {
+       return rpc_uci_verify_str(name, false, false);
+}
+
+/*
+ * Check that string is a valid section type name
+ */
+static bool rpc_uci_verify_type(const char *type) {
+       return rpc_uci_verify_str(type, false, true);
+}
+
+/*
+ * Check that the string is a valid section id, optionally in extended
+ * lookup notation
+ */
+static bool rpc_uci_verify_section(const char *section) {
+       return rpc_uci_verify_str(section, true, false);
+}
+
+
 /*
  * Turn uci error state into ubus return code
  */
@@ -644,6 +698,13 @@ rpc_uci_add(struct ubus_context *ctx, struct ubus_object *obj,
        if (!rpc_uci_write_access(tb[RPC_A_SESSION], tb[RPC_A_CONFIG]))
                return UBUS_STATUS_PERMISSION_DENIED;
 
+       if (!rpc_uci_verify_type(blobmsg_data(tb[RPC_A_TYPE])))
+               return UBUS_STATUS_INVALID_ARGUMENT;
+
+       if (tb[RPC_A_NAME] &&
+           !rpc_uci_verify_name(blobmsg_data(tb[RPC_A_NAME])))
+               return UBUS_STATUS_INVALID_ARGUMENT;
+
        ptr.package = blobmsg_data(tb[RPC_A_CONFIG]);
 
        if (uci_load(cursor, ptr.package, &p))
@@ -676,6 +737,9 @@ rpc_uci_add(struct ubus_context *ctx, struct ubus_object *obj,
                        ptr.o = NULL;
                        ptr.option = blobmsg_name(cur);
 
+                       if (!rpc_uci_verify_name(ptr.option))
+                               continue;
+
                        if (rpc_uci_lookup(&ptr) || !ptr.s)
                                continue;
 
@@ -726,6 +790,9 @@ rpc_uci_merge_set(struct blob_attr *opt, struct uci_ptr *ptr)
        ptr->option = blobmsg_name(opt);
        ptr->value = NULL;
 
+       if (!rpc_uci_verify_name(ptr->option))
+               return;
+
        if (rpc_uci_lookup(ptr) || !ptr->s)
                return;
 
@@ -774,6 +841,10 @@ rpc_uci_set(struct ubus_context *ctx, struct ubus_object *obj,
        if (!rpc_uci_write_access(tb[RPC_S_SESSION], tb[RPC_S_CONFIG]))
                return UBUS_STATUS_PERMISSION_DENIED;
 
+       if (tb[RPC_S_SECTION] &&
+           !rpc_uci_verify_section(blobmsg_data(tb[RPC_S_SECTION])))
+               return UBUS_STATUS_INVALID_ARGUMENT;
+
        ptr.package = blobmsg_data(tb[RPC_S_CONFIG]);
 
        if (uci_load(cursor, ptr.package, &p))
@@ -937,6 +1008,9 @@ rpc_uci_rename(struct ubus_context *ctx, struct ubus_object *obj,
        ptr.section = blobmsg_data(tb[RPC_R_SECTION]);
        ptr.value   = blobmsg_data(tb[RPC_R_NAME]);
 
+       if (!rpc_uci_verify_name(ptr.value))
+               return UBUS_STATUS_INVALID_ARGUMENT;
+
        if (tb[RPC_R_OPTION])
                ptr.option = blobmsg_data(tb[RPC_R_OPTION]);