SOCK: Add support for parsing and evaluating Access Control Lists
This commit is contained in:
parent
54eda7a050
commit
10e561767f
2 changed files with 179 additions and 19 deletions
190
sim_sock.c
190
sim_sock.c
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue