From 0ff5e8917bc3205d9887d5f714369a0072829edc Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 14 Nov 2013 13:43:19 +0100 Subject: [PATCH] add validation code Signed-off-by: John Crispin --- CMakeLists.txt | 11 + validate/cli.c | 28 ++ validate/libvalidate.h | 6 + validate/validate.c | 1015 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1060 insertions(+) create mode 100644 validate/cli.c create mode 100644 validate/libvalidate.h create mode 100644 validate/validate.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 54434d1..8cfbbc8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,3 +59,14 @@ TARGET_LINK_LIBRARIES(lsbloader ${LIBS}) INSTALL(TARGETS lsbloader RUNTIME DESTINATION sbin ) + +ADD_LIBRARY(validate SHARED validate/validate.c) +INSTALL(TARGETS validate + LIBRARY DESTINATION lib +) + +ADD_EXECUTABLE(validate_data validate/cli.c) +TARGET_LINK_LIBRARIES(validate_data ${LIBS} validate) +INSTALL(TARGETS validate_data + RUNTIME DESTINATION sbin +) diff --git a/validate/cli.c b/validate/cli.c new file mode 100644 index 0000000..dc5e96b --- /dev/null +++ b/validate/cli.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "libvalidate.h" + +int main(int argc, char **argv) +{ + bool rv; + + if (argc == 3) { + rv = dt_parse(argv[1], argv[2]); + + printf("%s - %s = %s\n", argv[1], argv[2], rv ? "true" : "false"); + + return rv ? 0 : 1; + } else if (argc > 3) { + + } + + return 0; +} diff --git a/validate/libvalidate.h b/validate/libvalidate.h new file mode 100644 index 0000000..d3b8e05 --- /dev/null +++ b/validate/libvalidate.h @@ -0,0 +1,6 @@ +#ifndef _VALIDATE_H__ +#define _VALIDATE_H__ + +bool dt_parse(const char *code, const char *value); + +#endif diff --git a/validate/validate.c b/validate/validate.c new file mode 100644 index 0000000..c3ae56e --- /dev/null +++ b/validate/validate.c @@ -0,0 +1,1015 @@ +/* + * Copyright (C) 2013 Jo-Philipp Wich + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation + * + * 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. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "libvalidate.h" + +enum dt_optype { + OP_UNKNOWN, + OP_BOOL, + OP_NUMBER, + OP_STRING, + OP_FUNCTION +}; + +struct dt_fun; + +struct dt_op { + enum dt_optype type; + const char *next; + int length; + int nextop; + union { + bool boolean; + double number; + const char *string; + struct dt_fun *function; + } value; +}; + +struct dt_state { + int pos; + int depth; + const char *value; + struct dt_op stack[32]; +}; + +struct dt_fun { + const char *name; + bool (*call)(struct dt_state *s, int nargs); +}; + +static bool +dt_test_number(double number, const char *value) +{ + char *e; + double n; + + n = strtod(value, &e); + + return (e > value && *e == 0 && n == number); +} + +static bool +dt_test_string(const char *s, const char *end, const char *value) +{ + bool esc = false; + + while (*value) + { + if (s > end) + return false; + + if (!esc && *s == '\\') + { + s++; + + if (s >= end) + break; + + esc = true; + continue; + } + + if (*s != *value) + return false; + + esc = false; + value++; + s++; + } + + return (*s == *value || (s > end && *value == 0)); +} + +static bool +dt_step(struct dt_state *s); + +static bool +dt_call(struct dt_state *s); + +static bool +dt_type_or(struct dt_state *s, int nargs) +{ + while (nargs--) + if (dt_step(s)) + return true; + + return false; +} + +static bool +dt_type_and(struct dt_state *s, int nargs) +{ + while (nargs--) + if (!dt_step(s)) + return false; + + return true; +} + +static bool +dt_type_not(struct dt_state *s, int nargs) +{ + if (!nargs) + return false; + + return !dt_step(s); +} + +static bool +dt_type_neg(struct dt_state *s, int nargs) +{ + bool rv; + const char *value = s->value; + + if (!nargs) + return false; + + if (*s->value == '!') + while (isspace(*++s->value)); + + rv = dt_step(s); + s->value = value; + + return rv; +} + +static bool +dt_type_list(struct dt_state *s, int nargs) +{ + bool rv = true; + int pos = s->pos; + char *p, *str = strdup(s->value); + const char *value = s->value; + + if (!str || !nargs) + return false; + + for (p = strtok(str, " \t"); p; p = strtok(NULL, " \t")) + { + s->value = p; + + if (!dt_step(s)) + { + rv = false; + break; + } + + s->pos = pos; + } + + s->value = value; + free(str); + + return rv; +} + +static bool +dt_type_min(struct dt_state *s, int nargs) +{ + int n; + char *e; + + if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER) + { + n = strtol(s->value, &e, 0); + + return (e > s->value && *e == 0 && + n >= s->stack[s->pos].value.number); + } + + return false; +} + +static bool +dt_type_max(struct dt_state *s, int nargs) +{ + int n; + char *e; + + if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER) + { + n = strtol(s->value, &e, 0); + + return (e > s->value && *e == 0 && + n <= s->stack[s->pos].value.number); + } + + return false; +} + +static bool +dt_type_range(struct dt_state *s, int nargs) +{ + int n; + char *e; + + if (nargs >= 2 && + s->stack[s->pos].type == OP_NUMBER && + s->stack[s->pos + 1].type == OP_NUMBER) + { + n = strtol(s->value, &e, 0); + + return (e > s->value && *e == 0 && + n >= s->stack[s->pos].value.number && + n <= s->stack[s->pos + 1].value.number); + } + + return false; +} + +static bool +dt_type_minlen(struct dt_state *s, int nargs) +{ + if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER) + return (strlen(s->value) >= s->stack[s->pos].value.number); + + return false; +} + +static bool +dt_type_maxlen(struct dt_state *s, int nargs) +{ + if (nargs >= 1 && s->stack[s->pos].type == OP_NUMBER) + return (strlen(s->value) <= s->stack[s->pos].value.number); + + return false; +} + +static bool +dt_type_rangelen(struct dt_state *s, int nargs) +{ + if (nargs >= 2 && + s->stack[s->pos].type == OP_NUMBER && + s->stack[s->pos + 1].type == OP_NUMBER) + return (strlen(s->value) >= s->stack[s->pos].value.number && + strlen(s->value) <= s->stack[s->pos + 1].value.number); + + return false; +} + +static bool +dt_type_int(struct dt_state *s, int nargs) +{ + char *e; + + strtol(s->value, &e, 0); + + return (e > s->value && *e == 0); +} + +static bool +dt_type_uint(struct dt_state *s, int nargs) +{ + int n; + char *e; + + n = strtol(s->value, &e, 0); + + return (e > s->value && *e == 0 && n >= 0); +} + +static bool +dt_type_float(struct dt_state *s, int nargs) +{ + char *e; + + strtod(s->value, &e); + + return (e > s->value && *e == 0); +} + +static bool +dt_type_ufloat(struct dt_state *s, int nargs) +{ + int n; + char *e; + + n = strtod(s->value, &e); + + return (e > s->value && *e == 0 && n >= 0.0); +} + +static bool +dt_type_bool(struct dt_state *s, int nargs) +{ + int i; + const char *values[] = { + "0", "off", "false", "no", + "1", "on", "true", "yes" + }; + + for (i = 0; i < sizeof(values) / sizeof(values[0]); i++) + if (!strcasecmp(values[i], s->value)) + return true; + + return false; +} + +static bool +dt_type_string(struct dt_state *s, int nargs) +{ + return true; +} + +static bool +dt_type_ip4addr(struct dt_state *s, int nargs) +{ + struct in6_addr a; + return inet_pton(AF_INET, s->value, &a); +} + +static bool +dt_type_ip6addr(struct dt_state *s, int nargs) +{ + struct in6_addr a; + return inet_pton(AF_INET6, s->value, &a); +} + +static bool +dt_type_ipaddr(struct dt_state *s, int nargs) +{ + return (dt_type_ip4addr(s, 0) || dt_type_ip6addr(s, 0)); +} + +static bool +dt_type_netmask4(struct dt_state *s, int nargs) +{ + int i; + struct in_addr a; + + if (!inet_pton(AF_INET, s->value, &a)) + return false; + + if (a.s_addr == 0) + return true; + + a.s_addr = ntohl(a.s_addr); + + for (i = 0; (i < 32) && !(a.s_addr & (1 << i)); i++); + + return ((uint32_t)(~((1 << i) - 1)) == a.s_addr); +} + +static bool +dt_type_netmask6(struct dt_state *s, int nargs) +{ + int i; + struct in6_addr a; + + if (!inet_pton(AF_INET6, s->value, &a)) + return false; + + for (i = 0; (i < 16) && (a.s6_addr[i] == 0xFF); i++); + + if (i == 16) + return true; + + if ((a.s6_addr[i] != 255) && (a.s6_addr[i] != 254) && + (a.s6_addr[i] != 252) && (a.s6_addr[i] != 248) && + (a.s6_addr[i] != 240) && (a.s6_addr[i] != 224) && + (a.s6_addr[i] != 192) && (a.s6_addr[i] != 128) && + (a.s6_addr[i] != 0)) + return false; + + for (; (i < 16) && (a.s6_addr[i] == 0); i++); + + return (i == 16); +} + +static bool +dt_type_cidr4(struct dt_state *s, int nargs) +{ + int n; + struct in_addr a; + char *p, buf[sizeof("255.255.255.255/32\0")]; + + if (strlen(s->value) >= sizeof(buf)) + return false; + + strcpy(buf, s->value); + p = strchr(buf, '/'); + + if (p) + { + *p++ = 0; + + n = strtoul(p, &p, 10); + + if ((*p != 0) || (n > 32)) + return false; + } + + return inet_pton(AF_INET, buf, &a); +} + +static bool +dt_type_cidr6(struct dt_state *s, int nargs) +{ + int n; + struct in6_addr a; + char *p, buf[sizeof("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255/128\0")]; + + if (strlen(s->value) >= sizeof(buf)) + return false; + + strcpy(buf, s->value); + p = strchr(buf, '/'); + + if (p) + { + *p++ = 0; + + n = strtoul(p, &p, 10); + + if ((*p != 0) || (n > 128)) + return false; + } + + return inet_pton(AF_INET6, buf, &a); +} + +static bool +dt_type_cidr(struct dt_state *s, int nargs) +{ + return (dt_type_cidr4(s, 0) || dt_type_cidr6(s, 0)); +} + +static bool +dt_type_ipmask4(struct dt_state *s, int nargs) +{ + bool rv; + struct in_addr a; + const char *value; + char *p, buf[sizeof("255.255.255.255/255.255.255.255\0")]; + + if (strlen(s->value) >= sizeof(buf)) + return false; + + strcpy(buf, s->value); + p = strchr(buf, '/'); + + if (p) + { + *p++ = 0; + + value = s->value; + s->value = p; + rv = dt_type_netmask4(s, 0); + s->value = value; + + if (!rv) + return false; + } + + return inet_pton(AF_INET, buf, &a); +} + +static bool +dt_type_ipmask6(struct dt_state *s, int nargs) +{ + bool rv; + struct in6_addr a; + const char *value; + char *p, buf[sizeof("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255/" + "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:255.255.255.255\0")]; + + if (strlen(s->value) >= sizeof(buf)) + return false; + + strcpy(buf, s->value); + p = strchr(buf, '/'); + + if (p) + { + *p++ = 0; + + value = s->value; + s->value = p; + rv = dt_type_netmask6(s, 0); + s->value = value; + + if (!rv) + return false; + } + + return inet_pton(AF_INET6, buf, &a); +} + +static bool +dt_type_ipmask(struct dt_state *s, int nargs) +{ + return (dt_type_ipmask4(s, 0) || dt_type_ipmask6(s, 0)); +} + +static bool +dt_type_port(struct dt_state *s, int nargs) +{ + int n; + char *e; + + n = strtoul(s->value, &e, 10); + + return (e > s->value && *e == 0 && n <= 65535); +} + +static bool +dt_type_portrange(struct dt_state *s, int nargs) +{ + int n, m; + char *e; + + n = strtoul(s->value, &e, 10); + + if (e == s->value || *e != '-') + return false; + + m = strtoul(e + 1, &e, 10); + + return (*e == 0 && n <= 65535 && m <= 65535 && n <= m); +} + +static bool +dt_type_macaddr(struct dt_state *s, int nargs) +{ + return !!ether_aton(s->value); +} + +static bool +dt_type_uciname(struct dt_state *s, int nargs) +{ + const char *p; + + for (p = s->value; + *p && ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || (*p == '_')); + p++); + + return (*p == 0); +} + +static bool +dt_type_wpakey(struct dt_state *s, int nargs) +{ + int len = strlen(s->value); + const char *p = s->value; + + if (len == 64) + { + while (isxdigit(*p)) + p++; + + return (*p == 0); + } + + return (len >= 8 && len <= 63); +} + +static bool +dt_type_wepkey(struct dt_state *s, int nargs) +{ + int len = strlen(s->value); + const char *p = s->value; + + if (!strncmp(p, "s:", 2)) + { + len -= 2; + p += 2; + } + + if (len == 10 || len == 26) + { + while (isxdigit(*p)) + p++; + + return (*p == 0); + } + + return (len == 5 || len == 13); +} + +static bool +dt_type_hostname(struct dt_state *s, int nargs) +{ + const char *p, *last; + + for (p = last = s->value; *p; p++) + { + if (*p == '.') + { + if ((p - last) == 0 || (p - last) > 63) + return false; + + last = p + 1; + continue; + } + else if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || (*p == '_') || (*p == '-')) + { + continue; + } + + return false; + } + + return ((p - last) > 0 && (p - last) <= 255); +} + +static bool +dt_type_host(struct dt_state *s, int nargs) +{ + return (dt_type_hostname(s, 0) || dt_type_ipaddr(s, 0)); +} + +static bool +dt_type_network(struct dt_state *s, int nargs) +{ + return (dt_type_uciname(s, 0) || dt_type_host(s, 0)); +} + +static bool +dt_type_phonedigit(struct dt_state *s, int nargs) +{ + const char *p; + + for (p = s->value; + *p && ((*p >= '0' && *p <= '9') || (*p == '*') || (*p == '#') || + (*p == '!') || (*p == '.')); + p++); + + return (*p == 0); +} + +static bool +dt_type_directory(struct dt_state *s, int nargs) +{ + struct stat st; + return (!stat(s->value, &st) && S_ISDIR(st.st_mode)); +} + + +static bool +dt_type_device(struct dt_state *s, int nargs) +{ + struct stat st; + return (!stat(s->value, &st) && + (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))); +} + +static bool +dt_type_file(struct dt_state *s, int nargs) +{ + struct stat st; + return (!stat(s->value, &st) && S_ISREG(st.st_mode)); +} + + +static struct dt_fun dt_types[] = { + { "or", dt_type_or }, + { "and", dt_type_and }, + { "not", dt_type_not }, + { "neg", dt_type_neg }, + { "list", dt_type_list }, + { "min", dt_type_min }, + { "max", dt_type_max }, + { "range", dt_type_range }, + { "minlength", dt_type_minlen }, + { "maxlength", dt_type_maxlen }, + { "rangelength", dt_type_rangelen }, + { "integer", dt_type_int }, + { "uinteger", dt_type_uint }, + { "float", dt_type_float }, + { "ufloat", dt_type_ufloat }, + { "bool", dt_type_bool }, + { "string", dt_type_string }, + { "ip4addr", dt_type_ip4addr }, + { "ip6addr", dt_type_ip6addr }, + { "ipaddr", dt_type_ipaddr }, + { "cidr4", dt_type_cidr4 }, + { "cidr6", dt_type_cidr6 }, + { "cidr", dt_type_cidr }, + { "netmask4", dt_type_netmask4 }, + { "netmask6", dt_type_netmask6 }, + { "ipmask4", dt_type_ipmask4 }, + { "ipmask6", dt_type_ipmask6 }, + { "ipmask", dt_type_ipmask }, + { "port", dt_type_port }, + { "portrange", dt_type_portrange }, + { "macaddr", dt_type_macaddr }, + { "uciname", dt_type_uciname }, + { "wpakey", dt_type_wpakey }, + { "wepkey", dt_type_wepkey }, + { "hostname", dt_type_hostname }, + { "host", dt_type_host }, + { "network", dt_type_network }, + { "phonedigit", dt_type_phonedigit }, + { "directory", dt_type_directory }, + { "device", dt_type_device }, + { "file", dt_type_file }, + + { } +}; + +static struct dt_fun * +dt_lookup_function(const char *s, const char *e) +{ + struct dt_fun *fun = dt_types; + + while (fun->name) + { + if (!strncmp(fun->name, s, e - s) && *(fun->name + (e - s)) == '\0') + return fun; + + fun++; + } + + return NULL; +} + +static bool +dt_parse_atom(struct dt_state *s, const char *label, const char *end) +{ + char q, *e; + const char *p; + bool esc; + double dval; + struct dt_fun *func; + struct dt_op *op = &s->stack[s->depth]; + + if ((s->depth + 1) >= (sizeof(s->stack) / sizeof(s->stack[0]))) + { + printf("Syntax error, expression too long\n"); + return false; + } + + while (isspace(*label)) + label++; + + /* test whether label is a float */ + dval = strtod(label, &e); + + if (e > label) + { + op->next = e; + op->type = OP_NUMBER; + op->value.number = dval; + op->nextop = ++s->depth; + + return true; + } + else if ((*label == '"') || (*label == '\'')) + { + for (p = label + 1, q = *label, esc = false; p <= end; p++) + { + if (esc) + { + esc = false; + continue; + } + else if (*p == '\\') + { + esc = true; + continue; + } + else if (*p == q) + { + op->next = p + 1; + op->type = OP_STRING; + op->length = (p - label) - 2; + op->value.string = label + 1; + op->nextop = ++s->depth; + + return true; + } + } + + printf("Syntax error, unterminated string\n"); + return false; + } + else if (*label) + { + for (p = label; + p <= end && ((*p >= 'A' && *p <= 'Z') || + (*p >= 'a' && *p <= 'z') || + (*p >= '0' && *p <= '9') || + (*p == '_')); + p++); + + func = dt_lookup_function(label, p); + + if (!func) + { + printf("Syntax error, unrecognized function\n"); + return false; + } + + op->next = p; + op->type = OP_FUNCTION; + op->value.function = func; + op->nextop = ++s->depth; + + return true; + } + + printf("Syntax error, unexpected EOF\n"); + return false; +} + +static bool +dt_parse_list(struct dt_state *s, const char *code, const char *end); + +static bool +dt_parse_expr(const char *code, const char *end, struct dt_state *s) +{ + struct dt_op *tok; + + if (!dt_parse_atom(s, code, end)) + return false; + + tok = &s->stack[s->depth - 1]; + + while (isspace(*tok->next)) + tok->next++; + + if (tok->type == OP_FUNCTION) + { + if (*tok->next == '(') + { + end--; + + while (isspace(*end) && end > tok->next + 1) + end--; + + return dt_parse_list(s, tok->next + 1, end); + } + else if (tok->next == end) + { + return dt_parse_list(s, tok->next, tok->next); + } + + printf("Syntax error, expected '(' or EOF after function label\n"); + return false; + } + else if (tok->next == end) + { + return true; + } + + printf("Syntax error, expected ',' after literal\n"); + return false; +} + +static bool +dt_parse_list(struct dt_state *s, const char *code, const char *end) +{ + char c; + bool esc; + int nest; + const char *p, *last; + struct dt_op *fptr; + + if (!code) + return false; + + fptr = &s->stack[s->depth - 1]; + + for (nest = 0, p = last = code, esc = false, c = *p; + p <= end; + p++, c = (p < end) ? *p : '\0') + { + if (esc) + { + esc = false; + continue; + } + + switch (c) + { + case '\\': + esc = true; + break; + + case '(': + nest++; + break; + + case ')': + nest--; + break; + + case ',': + case '\0': + if (nest <= 0) + { + if (p > last) + { + if (!dt_parse_expr(last, p, s)) + return false; + + fptr->length++; + } + + last = p + 1; + } + + break; + } + } + + fptr->nextop = s->depth; + return true; +} + +static bool +dt_step(struct dt_state *s) +{ + bool rv; + struct dt_op *op = &s->stack[s->pos]; + + switch (op->type) + { + case OP_BOOL: + rv = op->value.boolean; + break; + + case OP_NUMBER: + rv = dt_test_number(op->value.number, s->value); + break; + + case OP_STRING: + rv = dt_test_string(op->value.string, op->value.string + op->length, s->value); + break; + + case OP_FUNCTION: + rv = dt_call(s); + break; + + default: + rv = false; + break; + } + + s->pos = op->nextop; + return rv; +} + +static bool +dt_call(struct dt_state *s) +{ + bool rv; + struct dt_op *fptr = &s->stack[s->pos]; + struct dt_fun *func = fptr->value.function; + + s->pos++; + + rv = func->call(s, fptr->length); + + s->pos = fptr->nextop; + + return rv; +} + +bool +dt_parse(const char *code, const char *value) +{ + struct dt_state s = { + .depth = 1, + .stack = { + { + .type = OP_FUNCTION, + .value.function = &dt_types[0], + .next = code + } + } + }; + + if (!value || !*value) + return false; + + if (!dt_parse_list(&s, code, code + strlen(code))) + return false; + + s.value = value; + + return dt_call(&s); +} -- 2.11.0