From f5fe5452f6bc8bcf7ee9945f70dab651c3910c5c Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Wed, 22 Sep 2021 15:54:50 +0200 Subject: [PATCH] conf: rework allow/deny parser Refactor the (cmd)allow/deny parser and make it more strict in what input it accepts. Check the scanned numbers and require whole input to be processed. Move the parser to cmdparse to make it available to the client. --- cmdparse.c | 79 +++++++++++++++++++++++++++++++++++++++++ cmdparse.h | 3 ++ conf.c | 102 ++++++----------------------------------------------- 3 files changed, 92 insertions(+), 92 deletions(-) diff --git a/cmdparse.c b/cmdparse.c index e2c5115..d44b426 100644 --- a/cmdparse.c +++ b/cmdparse.c @@ -181,6 +181,85 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src) /* ================================================== */ +int +CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits) +{ + char *p, *net, *slash; + uint32_t a, b, c; + int bits, len, n; + + p = CPS_SplitWord(line); + + if (strcmp(line, "all") == 0) { + *all = 1; + net = p; + p = CPS_SplitWord(p); + } else { + *all = 0; + net = line; + } + + /* Make sure there are no other arguments */ + if (*p) + return 0; + + /* No specified address or network means all IPv4 and IPv6 addresses */ + if (!*net) { + ip->family = IPADDR_UNSPEC; + *subnet_bits = 0; + return 1; + } + + slash = strchr(net, '/'); + if (slash) { + if (sscanf(slash + 1, "%d%n", &bits, &len) != 1 || slash[len + 1] || bits < 0) + return 0; + *slash = '\0'; + } else { + bits = -1; + } + + if (UTI_StringToIP(net, ip)) { + if (bits >= 0) + *subnet_bits = bits; + else + *subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32; + return 1; + } + + /* Check for a shortened IPv4 network notation using only 1, 2, or 3 decimal + numbers. This is different than the numbers-and-dots notation accepted + by inet_aton()! */ + + a = b = c = 0; + n = sscanf(net, "%"PRIu32"%n.%"PRIu32"%n.%"PRIu32"%n", &a, &len, &b, &len, &c, &len); + + if (n > 0 && !net[len]) { + if (a > 255 || b > 255 || c > 255) + return 0; + + ip->family = IPADDR_INET4; + ip->addr.in4 = (a << 24) | (b << 16) | (c << 8); + + if (bits >= 0) + *subnet_bits = bits; + else + *subnet_bits = n * 8; + + return 1; + } + + /* The last possibility is a hostname */ + if (bits < 0 && DNS_Name2IPAddress(net, ip, 1) == DNS_Success) { + *subnet_bits = ip->family == IPADDR_INET6 ? 128 : 32; + return 1; + } + + return 0; +} + +/* ================================================== */ + int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance) { diff --git a/cmdparse.h b/cmdparse.h index 199d972..fd1eb43 100644 --- a/cmdparse.h +++ b/cmdparse.h @@ -39,6 +39,9 @@ typedef struct { /* Parse a command to add an NTP server or peer */ extern int CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src); +/* Parse a command to allow/deny access */ +extern int CPS_ParseAllowDeny(char *line, int *all, IPAddr *ip, int *subnet_bits); + /* Parse a command to enable local reference */ extern int CPS_ParseLocal(char *line, int *stratum, int *orphan, double *distance); diff --git a/conf.c b/conf.c index 93e93fc..06ea1e5 100644 --- a/conf.c +++ b/conf.c @@ -1217,100 +1217,18 @@ parse_ntstrustedcerts(char *line) static void parse_allow_deny(char *line, ARR_Instance restrictions, int allow) { - char *p; - unsigned long a, b, c, d, n; - int all = 0; - AllowDeny *new_node = NULL; - IPAddr ip_addr; + int all, subnet_bits; + AllowDeny *node; + IPAddr ip; - p = line; + if (!CPS_ParseAllowDeny(line, &all, &ip, &subnet_bits)) + command_parse_error(); - if (!strncmp(p, "all", 3)) { - all = 1; - p = CPS_SplitWord(line); - } - - if (!*p) { - /* Empty line applies to all addresses */ - new_node = (AllowDeny *)ARR_GetNewElement(restrictions); - new_node->allow = allow; - new_node->all = all; - new_node->ip.family = IPADDR_UNSPEC; - new_node->subnet_bits = 0; - } else { - char *slashpos; - slashpos = strchr(p, '/'); - if (slashpos) *slashpos = 0; - - check_number_of_args(p, 1); - n = 0; - if (UTI_StringToIP(p, &ip_addr) || - (n = sscanf(p, "%lu.%lu.%lu.%lu", &a, &b, &c, &d)) >= 1) { - new_node = (AllowDeny *)ARR_GetNewElement(restrictions); - new_node->allow = allow; - new_node->all = all; - - if (n == 0) { - new_node->ip = ip_addr; - if (ip_addr.family == IPADDR_INET6) - new_node->subnet_bits = 128; - else - new_node->subnet_bits = 32; - } else { - new_node->ip.family = IPADDR_INET4; - - a &= 0xff; - b &= 0xff; - c &= 0xff; - d &= 0xff; - - switch (n) { - case 1: - new_node->ip.addr.in4 = (a<<24); - new_node->subnet_bits = 8; - break; - case 2: - new_node->ip.addr.in4 = (a<<24) | (b<<16); - new_node->subnet_bits = 16; - break; - case 3: - new_node->ip.addr.in4 = (a<<24) | (b<<16) | (c<<8); - new_node->subnet_bits = 24; - break; - case 4: - new_node->ip.addr.in4 = (a<<24) | (b<<16) | (c<<8) | d; - new_node->subnet_bits = 32; - break; - default: - assert(0); - } - } - - if (slashpos) { - int specified_subnet_bits, n; - n = sscanf(slashpos+1, "%d", &specified_subnet_bits); - if (n == 1) { - new_node->subnet_bits = specified_subnet_bits; - } else { - command_parse_error(); - } - } - - } else { - if (!slashpos && DNS_Name2IPAddress(p, &ip_addr, 1) == DNS_Success) { - new_node = (AllowDeny *)ARR_GetNewElement(restrictions); - new_node->allow = allow; - new_node->all = all; - new_node->ip = ip_addr; - if (ip_addr.family == IPADDR_INET6) - new_node->subnet_bits = 128; - else - new_node->subnet_bits = 32; - } else { - command_parse_error(); - } - } - } + node = ARR_GetNewElement(restrictions); + node->allow = allow; + node->all = all; + node->ip = ip; + node->subnet_bits = subnet_bits; } /* ================================================== */