SOCK: Add support for parsing and evaluating Access Control Lists

This commit is contained in:
Mark Pizzolato 2022-02-04 07:58:09 -08:00
parent 54eda7a050
commit 10e561767f
2 changed files with 179 additions and 19 deletions

View file

@ -501,27 +501,38 @@ int load_ws2(void) {
/* OS independent routines
sim_parse_addr parse a hostname/ipaddress from port and apply defaults and
optionally validate an address match
sim_parse_addr parse a hostname/ipaddress from port and apply defaults
and optionally validate an address match
sim_addr_acl_check parse a hostname/ipaddress (possibly in CIDR form) and
test against an acl
*/
/* sim_parse_addr host:port
Presumption is that the input, if it doesn't contain a ':' character is a port specifier.
If the host field contains one or more colon characters (i.e. it is an IPv6 address),
the IPv6 address MUST be enclosed in square bracket characters (i.e. Domain Literal format)
Presumption is that the cptr input, if it doesn't contain a ':' character
is a port specifier. If the host field contains one or more colon characters
(i.e. it is an IPv6 address), the IPv6 address MUST be enclosed in square
bracket characters (i.e. Domain Literal format)
Inputs:
cptr = pointer to input string
default_host
= optional pointer to default host if none specified
host = optional pointer to host buffer
host_len = length of host buffer
default_port
= optional pointer to default port if none specified
default_host = optional pointer to default host if none specified
in cptr
port = optional pointer to port buffer
port_len = length of port buffer
default_port = optional pointer to default port if none specified
in cptr
validate_addr = optional name/addr which is checked to be equivalent
to the host result of parsing the other input. This
address would usually be returned by sim_accept_conn.
The validate_addr can also be a CIDR address specifier
which will match against the provided host.
If the validate_addr is provided with cptr as NULL,
the validate_addr is parsed for reasonableness and
the result returned with 0 indicating a reasonable
value and -1 indicating a parsing error.
Outputs:
host = pointer to buffer for IP address (may be NULL), 0 = none
port = pointer to buffer for IP port (may be NULL), 0 = none
@ -532,7 +543,9 @@ int load_ws2(void) {
doesn't match the parsed host)
*/
int sim_parse_addr (const char *cptr, char *host, size_t host_len, const char *default_host, char *port, size_t port_len, const char *default_port, const char *validate_addr)
int sim_parse_addr (const char *cptr, char *host, size_t host_len, const char *default_host,
char *port, size_t port_len, const char *default_port,
const char *validate_addr)
{
char gbuf[CBUFSIZE], default_pbuf[CBUFSIZE];
const char *hostp;
@ -545,7 +558,8 @@ if ((host != NULL) && (host_len != 0))
if ((port != NULL) && (port_len != 0))
memset (port, 0, port_len);
if ((cptr == NULL) || (*cptr == 0)) {
if (((default_host == NULL) || (*default_host == 0)) || ((default_port == NULL) || (*default_port == 0)))
if (((default_host == NULL) || (*default_host == 0)) ||
((default_port == NULL) || (*default_port == 0)))
return -1;
if ((host == NULL) || (port == NULL))
return -1; /* no place */
@ -572,7 +586,7 @@ else { /* No colon in input */
portp = gbuf; /* Input is the port specifier */
hostp = (const char *)default_host; /* host is defaulted if provided */
}
if (portp != NULL) {
if ((portp != NULL) && (*portp != '\0')) {
portval = strtoul(portp, &endc, 10);
if ((*endc == '\0') && ((portval == 0) || (portval > 65535)))
return -1; /* numeric value too big */
@ -590,7 +604,7 @@ if (port) /* port wanted? */
else
strcpy (port, portp);
}
if (hostp != NULL) {
if ((hostp != NULL) && (*hostp != '\0')) {
if (']' == hostp[strlen(hostp)-1]) {
if ('[' != hostp[0])
return -1; /* invalid domain literal */
@ -626,16 +640,15 @@ if (validate_addr) {
struct addrinfo *ai_host, *ai_validate, *ai, *aiv;
int status;
if (hostp == NULL)
return -1;
if (p_getaddrinfo(hostp, NULL, NULL, &ai_host))
if ((hostp == NULL) ||
(0 != p_getaddrinfo(hostp, NULL, NULL, &ai_host)))
return -1;
if (p_getaddrinfo(validate_addr, NULL, NULL, &ai_validate)) {
p_freeaddrinfo (ai_host);
return -1;
}
status = -1;
for (ai = ai_host; ai != NULL; ai = ai->ai_next) {
for (ai = ai_host; (ai != NULL) && (status == -1); ai = ai->ai_next) {
for (aiv = ai_validate; aiv != NULL; aiv = aiv->ai_next) {
if ((ai->ai_addrlen == aiv->ai_addrlen) &&
(ai->ai_family == aiv->ai_family) &&
@ -660,6 +673,149 @@ if (validate_addr) {
return 0;
}
/* sim_addr_acl_check host:port,acl
parse a hostname/ipaddress (possibly in CIDR form) and
test against an acl
Inputs:
validate_addr = This address would usually be returned by
sim_accept_conn. The validate_addr can also be a
CIDR address specifier and in that mode, acl should
be NULL so that we're just validating the syntax
of what will likely become an entry in an acl list.
If the validate_addr is provided with cptr as NULL,
the validate_addr is parsed for reasonableness and
the result returned with 0 indicating a reasonable
value and -1 indicating a parsing error.
acl = pointer to acl string which is comprised of comma
separated entries each which may have a + or -
prefix that indicated a permit or deny status when
the entry matches. Each entry may specify a CIDR
form match criteria.
Outputs:
result = status (0 on complete success or -1 if
parsing can't happen due to bad syntax, a value is
out of range or the validate_addr matches a reject
entry in the acl or it is not mentioned at all in
the acl.
*/
int sim_addr_acl_check (const char *validate_addr, const char *acl)
{
int status = -1;
int done = 0;
struct addrinfo *ai_validate;
unsigned long bits = 0;
char *c, *c1, v_cpy[256];
if (validate_addr == NULL)
return status;
c = strchr (validate_addr, '/');
if (c != NULL) {
bits = strtoul (c + 1, &c1, 10);
if ((bits == 0) || (bits > 128) || (*c1 != '\0'))
return status;
if ((c - validate_addr) > sizeof (v_cpy) - 1)
return status;
memcpy (v_cpy, validate_addr, c - validate_addr); /* Copy everything before the / */
v_cpy[1 + c - validate_addr] = '\0'; /* NUL terminate the result */
validate_addr = v_cpy; /* Use the original string minus the prefix specifier */
}
if (p_getaddrinfo(validate_addr, NULL, NULL, &ai_validate))
return status;
if (acl == NULL) { /* Just checking validate_addr syntax? */
status = 0;
if ((ai_validate->ai_family == AF_INET) && (bits > 32))
status = -1;
p_freeaddrinfo (ai_validate);
return status;
}
status = -1;
while ((*acl != '\0') && !done) {
struct addrinfo *ai_rule, *ai, *aiv;
int permit;
unsigned long bits = 0;
char *c, *c1, rule[260];
permit = (*acl == '+');
c = strchr (acl, ',');
if (c != NULL) {
if ((c - acl) > sizeof (rule))
break; /* Too big - error */
memcpy (rule, acl + 1, c - (acl + 1));
rule[c - (acl + 1)] = '\0';
}
else {
if (strlen (acl) > sizeof (rule))
break; /* Too big - error */
strcpy (rule, acl + 1);
}
acl += strlen (rule) + 1 + (c != NULL);
c = strchr (rule, '/');
if (c != NULL) {
bits = strtoul (c + 1, &c1, 10);
if ((bits == 0) || (bits > 128) || (*c1 != '\0'))
break;
*c = '\0';
}
if (p_getaddrinfo(rule, NULL, NULL, &ai_rule))
break;
for (ai = ai_rule; (ai != NULL) && (done == 0); ai = ai->ai_next) {
for (aiv = ai_validate; aiv != NULL; aiv = aiv->ai_next) {
if ((ai->ai_addrlen == aiv->ai_addrlen) &&
(ai->ai_family == aiv->ai_family)) {
unsigned int bit, addr_bits;
unsigned char *da, *dav;
if (ai->ai_family == AF_INET) {
da = (unsigned char *)&((struct sockaddr_in *)ai->ai_addr)->sin_addr;
dav = (unsigned char *)&((struct sockaddr_in *)aiv->ai_addr)->sin_addr;
addr_bits = 32;
}
#if !defined(AF_INET6)
else {
done = 1;
break;
}
#else
else {
if (ai->ai_family == AF_INET6) {
da = (unsigned char *)&((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
dav = (unsigned char *)&((struct sockaddr_in6 *)aiv->ai_addr)->sin6_addr;
addr_bits = 128;
}
else {
done = 1;
break;
}
}
#endif
if (bits == 0) /* Bits not specified? */
bits = addr_bits; /* Use them all */
for (bit=0; (bit < bits) && (bit < addr_bits); bit++) {
unsigned int bitmask = 1 << (7 - (bit & 7));
if ((da[bit>>3] & bitmask) != (dav[bit>>3] & bitmask))
break;
}
if (bit == bits) { /* All desired bits matched? */
done = 1;
status = permit ? 0 : -1;
break;
}
}
}
}
p_freeaddrinfo (ai_rule);
}
p_freeaddrinfo (ai_validate);
return status;
}
/* sim_parse_addr_ex localport:host:port
Presumption is that the input, if it doesn't contain a ':' character is a port specifier.

View file

@ -111,8 +111,12 @@ extern "C" {
#define sim_printf printf
#endif
int sim_parse_addr (const char *cptr, char *host, size_t hostlen, const char *default_host, char *port, size_t port_len, const char *default_port, const char *validate_addr);
int sim_parse_addr_ex (const char *cptr, char *host, size_t hostlen, const char *default_host, char *port, size_t port_len, char *localport, size_t local_port_len, const char *default_port);
int sim_parse_addr (const char *cptr, char *host, size_t hostlen, const char *default_host,
char *port, size_t port_len, const char *default_port,
const char *validate_addr);
int sim_parse_addr_ex (const char *cptr, char *host, size_t hostlen, const char *default_host,
char *port, size_t port_len, char *localport, size_t local_port_len, const char *default_port);
int sim_addr_acl_check (const char *validate_addr, const char *acl);
#define SIM_SOCK_OPT_REUSEADDR 0x0001
#define SIM_SOCK_OPT_DATAGRAM 0x0002
#define SIM_SOCK_OPT_NODELAY 0x0004