From: Miroslav Lichvar Date: Wed, 22 Sep 2021 13:54:50 +0000 (+0200) Subject: conf: rework allow/deny parser X-Git-Tag: 4.2-pre1~52 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f5fe5452f6bc8bcf7ee9945f70dab651c3910c5c;p=thirdparty%2Fchrony.git 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. --- diff --git a/cmdparse.c b/cmdparse.c index e2c51153..d44b426c 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 199d9722..fd1eb439 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 93e93fc6..06ea1e51 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; - - p = line; - - if (!strncmp(p, "all", 3)) { - all = 1; - p = CPS_SplitWord(line); - } + int all, subnet_bits; + AllowDeny *node; + IPAddr ip; - 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(); - } - } + if (!CPS_ParseAllowDeny(line, &all, &ip, &subnet_bits)) + 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; } /* ================================================== */