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 /* OS independent routines
sim_parse_addr parse a hostname/ipaddress from port and apply defaults and sim_parse_addr parse a hostname/ipaddress from port and apply defaults
optionally validate an address match 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 /* sim_parse_addr host:port
Presumption is that the input, if it doesn't contain a ':' character is a port specifier. Presumption is that the cptr input, if it doesn't contain a ':' character
If the host field contains one or more colon characters (i.e. it is an IPv6 address), is a port specifier. If the host field contains one or more colon characters
the IPv6 address MUST be enclosed in square bracket characters (i.e. Domain Literal format) (i.e. it is an IPv6 address), the IPv6 address MUST be enclosed in square
bracket characters (i.e. Domain Literal format)
Inputs: Inputs:
cptr = pointer to input string cptr = pointer to input string
default_host host = optional pointer to host buffer
= optional pointer to default host if none specified
host_len = length of host buffer host_len = length of host buffer
default_port default_host = optional pointer to default host if none specified
= optional pointer to default port if none specified in cptr
port = optional pointer to port buffer
port_len = length of 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 validate_addr = optional name/addr which is checked to be equivalent
to the host result of parsing the other input. This to the host result of parsing the other input. This
address would usually be returned by sim_accept_conn. 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: Outputs:
host = pointer to buffer for IP address (may be NULL), 0 = none host = pointer to buffer for IP address (may be NULL), 0 = none
port = pointer to buffer for IP port (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) 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]; char gbuf[CBUFSIZE], default_pbuf[CBUFSIZE];
const char *hostp; const char *hostp;
@ -545,7 +558,8 @@ if ((host != NULL) && (host_len != 0))
if ((port != NULL) && (port_len != 0)) if ((port != NULL) && (port_len != 0))
memset (port, 0, port_len); memset (port, 0, port_len);
if ((cptr == NULL) || (*cptr == 0)) { 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; return -1;
if ((host == NULL) || (port == NULL)) if ((host == NULL) || (port == NULL))
return -1; /* no place */ return -1; /* no place */
@ -572,7 +586,7 @@ else { /* No colon in input */
portp = gbuf; /* Input is the port specifier */ portp = gbuf; /* Input is the port specifier */
hostp = (const char *)default_host; /* host is defaulted if provided */ hostp = (const char *)default_host; /* host is defaulted if provided */
} }
if (portp != NULL) { if ((portp != NULL) && (*portp != '\0')) {
portval = strtoul(portp, &endc, 10); portval = strtoul(portp, &endc, 10);
if ((*endc == '\0') && ((portval == 0) || (portval > 65535))) if ((*endc == '\0') && ((portval == 0) || (portval > 65535)))
return -1; /* numeric value too big */ return -1; /* numeric value too big */
@ -590,7 +604,7 @@ if (port) /* port wanted? */
else else
strcpy (port, portp); strcpy (port, portp);
} }
if (hostp != NULL) { if ((hostp != NULL) && (*hostp != '\0')) {
if (']' == hostp[strlen(hostp)-1]) { if (']' == hostp[strlen(hostp)-1]) {
if ('[' != hostp[0]) if ('[' != hostp[0])
return -1; /* invalid domain literal */ return -1; /* invalid domain literal */
@ -626,16 +640,15 @@ if (validate_addr) {
struct addrinfo *ai_host, *ai_validate, *ai, *aiv; struct addrinfo *ai_host, *ai_validate, *ai, *aiv;
int status; int status;
if (hostp == NULL) if ((hostp == NULL) ||
return -1; (0 != p_getaddrinfo(hostp, NULL, NULL, &ai_host)))
if (p_getaddrinfo(hostp, NULL, NULL, &ai_host))
return -1; return -1;
if (p_getaddrinfo(validate_addr, NULL, NULL, &ai_validate)) { if (p_getaddrinfo(validate_addr, NULL, NULL, &ai_validate)) {
p_freeaddrinfo (ai_host); p_freeaddrinfo (ai_host);
return -1; return -1;
} }
status = -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) { for (aiv = ai_validate; aiv != NULL; aiv = aiv->ai_next) {
if ((ai->ai_addrlen == aiv->ai_addrlen) && if ((ai->ai_addrlen == aiv->ai_addrlen) &&
(ai->ai_family == aiv->ai_family) && (ai->ai_family == aiv->ai_family) &&
@ -660,6 +673,149 @@ if (validate_addr) {
return 0; 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 /* sim_parse_addr_ex localport:host:port
Presumption is that the input, if it doesn't contain a ':' character is a port specifier. 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 #define sim_printf printf
#endif #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 (const char *cptr, char *host, size_t hostlen, const char *default_host,
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); 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_REUSEADDR 0x0001
#define SIM_SOCK_OPT_DATAGRAM 0x0002 #define SIM_SOCK_OPT_DATAGRAM 0x0002
#define SIM_SOCK_OPT_NODELAY 0x0004 #define SIM_SOCK_OPT_NODELAY 0x0004