#include <stdlib.h>
|
#include <stddef.h>
|
#include <string.h>
|
#include <netinet/in.h>
|
#include <arpa/inet.h>
|
#include <errno.h>
|
|
#include "node_internal.h"
|
#include "context_internal.h"
|
#include "debug.h"
|
|
struct sepol_node {
|
|
/* Network address and mask */
|
char *addr;
|
size_t addr_sz;
|
|
char *mask;
|
size_t mask_sz;
|
|
/* Protocol */
|
int proto;
|
|
/* Context */
|
sepol_context_t *con;
|
};
|
|
struct sepol_node_key {
|
|
/* Network address and mask */
|
char *addr;
|
size_t addr_sz;
|
|
char *mask;
|
size_t mask_sz;
|
|
/* Protocol */
|
int proto;
|
};
|
|
/* Converts a string represtation (addr_str)
|
* to a numeric representation (addr_bytes) */
|
|
static int node_parse_addr(sepol_handle_t * handle,
|
const char *addr_str, int proto, char *addr_bytes)
|
{
|
|
switch (proto) {
|
|
case SEPOL_PROTO_IP4:
|
{
|
struct in_addr in_addr;
|
|
if (inet_pton(AF_INET, addr_str, &in_addr) <= 0) {
|
ERR(handle, "could not parse IPv4 address "
|
"%s: %s", addr_str, strerror(errno));
|
return STATUS_ERR;
|
}
|
|
memcpy(addr_bytes, &in_addr.s_addr, 4);
|
break;
|
}
|
case SEPOL_PROTO_IP6:
|
{
|
struct in6_addr in_addr;
|
|
if (inet_pton(AF_INET6, addr_str, &in_addr) <= 0) {
|
ERR(handle, "could not parse IPv6 address "
|
"%s: %s", addr_str, strerror(errno));
|
return STATUS_ERR;
|
}
|
|
memcpy(addr_bytes, in_addr.s6_addr, 16);
|
break;
|
}
|
default:
|
ERR(handle, "unsupported protocol %u, could not "
|
"parse address", proto);
|
return STATUS_ERR;
|
}
|
|
return STATUS_SUCCESS;
|
}
|
|
/* Allocates a sufficiently large buffer (addr, addr_sz)
|
* according to the protocol */
|
|
static int node_alloc_addr(sepol_handle_t * handle,
|
int proto, char **addr, size_t * addr_sz)
|
{
|
|
char *tmp_addr = NULL;
|
size_t tmp_addr_sz;
|
|
switch (proto) {
|
|
case SEPOL_PROTO_IP4:
|
tmp_addr_sz = 4;
|
tmp_addr = malloc(4);
|
if (!tmp_addr)
|
goto omem;
|
break;
|
|
case SEPOL_PROTO_IP6:
|
tmp_addr_sz = 16;
|
tmp_addr = malloc(16);
|
if (!tmp_addr)
|
goto omem;
|
break;
|
|
default:
|
ERR(handle, "unsupported protocol %u", proto);
|
goto err;
|
}
|
|
*addr = tmp_addr;
|
*addr_sz = tmp_addr_sz;
|
return STATUS_SUCCESS;
|
|
omem:
|
ERR(handle, "out of memory");
|
|
err:
|
free(tmp_addr);
|
ERR(handle, "could not allocate address of protocol %s",
|
sepol_node_get_proto_str(proto));
|
return STATUS_ERR;
|
}
|
|
/* Converts a numeric representation (addr_bytes)
|
* to a string representation (addr_str), according to
|
* the protocol */
|
|
static int node_expand_addr(sepol_handle_t * handle,
|
char *addr_bytes, int proto, char *addr_str)
|
{
|
|
switch (proto) {
|
|
case SEPOL_PROTO_IP4:
|
{
|
struct in_addr addr;
|
memset(&addr, 0, sizeof(struct in_addr));
|
memcpy(&addr.s_addr, addr_bytes, 4);
|
|
if (inet_ntop(AF_INET, &addr, addr_str,
|
INET_ADDRSTRLEN) == NULL) {
|
|
ERR(handle,
|
"could not expand IPv4 address to string: %s",
|
strerror(errno));
|
return STATUS_ERR;
|
}
|
break;
|
}
|
|
case SEPOL_PROTO_IP6:
|
{
|
struct in6_addr addr;
|
memset(&addr, 0, sizeof(struct in6_addr));
|
memcpy(&addr.s6_addr[0], addr_bytes, 16);
|
if (inet_ntop(AF_INET6, &addr, addr_str,
|
INET6_ADDRSTRLEN) == NULL) {
|
|
ERR(handle,
|
"could not expand IPv6 address to string: %s",
|
strerror(errno));
|
return STATUS_ERR;
|
}
|
break;
|
}
|
|
default:
|
ERR(handle, "unsupported protocol %u, could not"
|
" expand address to string", proto);
|
return STATUS_ERR;
|
}
|
|
return STATUS_SUCCESS;
|
}
|
|
/* Allocates a sufficiently large address string (addr)
|
* according to the protocol */
|
|
static int node_alloc_addr_string(sepol_handle_t * handle,
|
int proto, char **addr)
|
{
|
|
char *tmp_addr = NULL;
|
|
switch (proto) {
|
|
case SEPOL_PROTO_IP4:
|
tmp_addr = malloc(INET_ADDRSTRLEN);
|
if (!tmp_addr)
|
goto omem;
|
break;
|
|
case SEPOL_PROTO_IP6:
|
tmp_addr = malloc(INET6_ADDRSTRLEN);
|
if (!tmp_addr)
|
goto omem;
|
break;
|
|
default:
|
ERR(handle, "unsupported protocol %u", proto);
|
goto err;
|
}
|
|
*addr = tmp_addr;
|
return STATUS_SUCCESS;
|
|
omem:
|
ERR(handle, "out of memory");
|
|
err:
|
free(tmp_addr);
|
ERR(handle, "could not allocate string buffer for "
|
"address of protocol %s", sepol_node_get_proto_str(proto));
|
return STATUS_ERR;
|
}
|
|
/* Key */
|
int sepol_node_key_create(sepol_handle_t * handle,
|
const char *addr,
|
const char *mask,
|
int proto, sepol_node_key_t ** key_ptr)
|
{
|
|
sepol_node_key_t *tmp_key =
|
(sepol_node_key_t *) calloc(1, sizeof(sepol_node_key_t));
|
if (!tmp_key)
|
goto omem;
|
|
if (node_alloc_addr(handle, proto, &tmp_key->addr, &tmp_key->addr_sz) <
|
0)
|
goto err;
|
if (node_parse_addr(handle, addr, proto, tmp_key->addr) < 0)
|
goto err;
|
|
if (node_alloc_addr(handle, proto, &tmp_key->mask, &tmp_key->mask_sz) <
|
0)
|
goto err;
|
if (node_parse_addr(handle, mask, proto, tmp_key->mask) < 0)
|
goto err;
|
|
tmp_key->proto = proto;
|
|
*key_ptr = tmp_key;
|
return STATUS_SUCCESS;
|
|
omem:
|
ERR(handle, "out of memory");
|
|
err:
|
sepol_node_key_free(tmp_key);
|
ERR(handle, "could not create node key for (%s, %s, %s)",
|
addr, mask, sepol_node_get_proto_str(proto));
|
return STATUS_ERR;
|
}
|
|
hidden_def(sepol_node_key_create)
|
|
void sepol_node_key_unpack(const sepol_node_key_t * key,
|
const char **addr, const char **mask, int *proto)
|
{
|
|
*addr = key->addr;
|
*mask = key->mask;
|
*proto = key->proto;
|
}
|
|
hidden_def(sepol_node_key_unpack)
|
|
int sepol_node_key_extract(sepol_handle_t * handle,
|
const sepol_node_t * node,
|
sepol_node_key_t ** key_ptr)
|
{
|
|
sepol_node_key_t *tmp_key =
|
(sepol_node_key_t *) calloc(1, sizeof(sepol_node_key_t));
|
if (!tmp_key)
|
goto omem;
|
|
tmp_key->addr = malloc(node->addr_sz);
|
tmp_key->mask = malloc(node->mask_sz);
|
|
if (!tmp_key->addr || !tmp_key->mask)
|
goto omem;
|
|
memcpy(tmp_key->addr, node->addr, node->addr_sz);
|
memcpy(tmp_key->mask, node->mask, node->mask_sz);
|
tmp_key->addr_sz = node->addr_sz;
|
tmp_key->mask_sz = node->mask_sz;
|
tmp_key->proto = node->proto;
|
|
*key_ptr = tmp_key;
|
return STATUS_SUCCESS;
|
|
omem:
|
sepol_node_key_free(tmp_key);
|
ERR(handle, "out of memory, could not extract node key");
|
return STATUS_ERR;
|
}
|
|
void sepol_node_key_free(sepol_node_key_t * key)
|
{
|
|
if (!key)
|
return;
|
|
free(key->addr);
|
free(key->mask);
|
free(key);
|
}
|
|
hidden_def(sepol_node_key_free)
|
|
int sepol_node_compare(const sepol_node_t * node, const sepol_node_key_t * key)
|
{
|
|
int rc1, rc2;
|
|
if ((node->addr_sz < key->addr_sz) || (node->mask_sz < key->mask_sz))
|
return -1;
|
|
else if ((node->addr_sz > key->addr_sz) ||
|
(node->mask_sz > key->mask_sz))
|
return 1;
|
|
rc1 = memcmp(node->addr, key->addr, node->addr_sz);
|
rc2 = memcmp(node->mask, key->mask, node->mask_sz);
|
|
return (rc2 != 0) ? rc2 : rc1;
|
}
|
|
int sepol_node_compare2(const sepol_node_t * node, const sepol_node_t * node2)
|
{
|
|
int rc1, rc2;
|
|
if ((node->addr_sz < node2->addr_sz) ||
|
(node->mask_sz < node2->mask_sz))
|
return -1;
|
|
else if ((node->addr_sz > node2->addr_sz) ||
|
(node->mask_sz > node2->mask_sz))
|
return 1;
|
|
rc1 = memcmp(node->addr, node2->addr, node->addr_sz);
|
rc2 = memcmp(node->mask, node2->mask, node->mask_sz);
|
|
return (rc2 != 0) ? rc2 : rc1;
|
}
|
|
/* Addr */
|
int sepol_node_get_addr(sepol_handle_t * handle,
|
const sepol_node_t * node, char **addr)
|
{
|
|
char *tmp_addr = NULL;
|
|
if (node_alloc_addr_string(handle, node->proto, &tmp_addr) < 0)
|
goto err;
|
|
if (node_expand_addr(handle, node->addr, node->proto, tmp_addr) < 0)
|
goto err;
|
|
*addr = tmp_addr;
|
return STATUS_SUCCESS;
|
|
err:
|
free(tmp_addr);
|
ERR(handle, "could not get node address");
|
return STATUS_ERR;
|
}
|
|
hidden_def(sepol_node_get_addr)
|
|
int sepol_node_get_addr_bytes(sepol_handle_t * handle,
|
const sepol_node_t * node,
|
char **buffer, size_t * bsize)
|
{
|
|
char *tmp_buf = malloc(node->addr_sz);
|
if (!tmp_buf) {
|
ERR(handle, "out of memory, could not get address bytes");
|
return STATUS_ERR;
|
}
|
|
memcpy(tmp_buf, node->addr, node->addr_sz);
|
*buffer = tmp_buf;
|
*bsize = node->addr_sz;
|
return STATUS_SUCCESS;
|
}
|
|
hidden_def(sepol_node_get_addr_bytes)
|
|
int sepol_node_set_addr(sepol_handle_t * handle,
|
sepol_node_t * node, int proto, const char *addr)
|
{
|
|
char *tmp_addr = NULL;
|
size_t tmp_addr_sz;
|
|
if (node_alloc_addr(handle, proto, &tmp_addr, &tmp_addr_sz) < 0)
|
goto err;
|
|
if (node_parse_addr(handle, addr, proto, tmp_addr) < 0)
|
goto err;
|
|
free(node->addr);
|
node->addr = tmp_addr;
|
node->addr_sz = tmp_addr_sz;
|
return STATUS_SUCCESS;
|
|
err:
|
free(tmp_addr);
|
ERR(handle, "could not set node address to %s", addr);
|
return STATUS_ERR;
|
}
|
|
hidden_def(sepol_node_set_addr)
|
|
int sepol_node_set_addr_bytes(sepol_handle_t * handle,
|
sepol_node_t * node,
|
const char *addr, size_t addr_sz)
|
{
|
|
char *tmp_addr = malloc(addr_sz);
|
if (!tmp_addr) {
|
ERR(handle, "out of memory, could not " "set node address");
|
return STATUS_ERR;
|
}
|
|
memcpy(tmp_addr, addr, addr_sz);
|
free(node->addr);
|
node->addr = tmp_addr;
|
node->addr_sz = addr_sz;
|
return STATUS_SUCCESS;
|
}
|
|
hidden_def(sepol_node_set_addr_bytes)
|
|
/* Mask */
|
int sepol_node_get_mask(sepol_handle_t * handle,
|
const sepol_node_t * node, char **mask)
|
{
|
|
char *tmp_mask = NULL;
|
|
if (node_alloc_addr_string(handle, node->proto, &tmp_mask) < 0)
|
goto err;
|
|
if (node_expand_addr(handle, node->mask, node->proto, tmp_mask) < 0)
|
goto err;
|
|
*mask = tmp_mask;
|
return STATUS_SUCCESS;
|
|
err:
|
free(tmp_mask);
|
ERR(handle, "could not get node netmask");
|
return STATUS_ERR;
|
}
|
|
hidden_def(sepol_node_get_mask)
|
|
int sepol_node_get_mask_bytes(sepol_handle_t * handle,
|
const sepol_node_t * node,
|
char **buffer, size_t * bsize)
|
{
|
|
char *tmp_buf = malloc(node->mask_sz);
|
if (!tmp_buf) {
|
ERR(handle, "out of memory, could not get netmask bytes");
|
return STATUS_ERR;
|
}
|
|
memcpy(tmp_buf, node->mask, node->mask_sz);
|
*buffer = tmp_buf;
|
*bsize = node->mask_sz;
|
return STATUS_SUCCESS;
|
}
|
|
hidden_def(sepol_node_get_mask_bytes)
|
|
int sepol_node_set_mask(sepol_handle_t * handle,
|
sepol_node_t * node, int proto, const char *mask)
|
{
|
|
char *tmp_mask = NULL;
|
size_t tmp_mask_sz;
|
|
if (node_alloc_addr(handle, proto, &tmp_mask, &tmp_mask_sz) < 0)
|
goto err;
|
|
if (node_parse_addr(handle, mask, proto, tmp_mask) < 0)
|
goto err;
|
|
free(node->mask);
|
node->mask = tmp_mask;
|
node->mask_sz = tmp_mask_sz;
|
return STATUS_SUCCESS;
|
|
err:
|
free(tmp_mask);
|
ERR(handle, "could not set node netmask to %s", mask);
|
return STATUS_ERR;
|
}
|
|
hidden_def(sepol_node_set_mask)
|
|
int sepol_node_set_mask_bytes(sepol_handle_t * handle,
|
sepol_node_t * node,
|
const char *mask, size_t mask_sz)
|
{
|
|
char *tmp_mask = malloc(mask_sz);
|
if (!tmp_mask) {
|
ERR(handle, "out of memory, could not " "set node netmask");
|
return STATUS_ERR;
|
}
|
memcpy(tmp_mask, mask, mask_sz);
|
free(node->mask);
|
node->mask = tmp_mask;
|
node->mask_sz = mask_sz;
|
return STATUS_SUCCESS;
|
}
|
|
hidden_def(sepol_node_set_mask_bytes)
|
|
/* Protocol */
|
int sepol_node_get_proto(const sepol_node_t * node)
|
{
|
|
return node->proto;
|
}
|
|
hidden_def(sepol_node_get_proto)
|
|
void sepol_node_set_proto(sepol_node_t * node, int proto)
|
{
|
|
node->proto = proto;
|
}
|
|
hidden_def(sepol_node_set_proto)
|
|
const char *sepol_node_get_proto_str(int proto)
|
{
|
|
switch (proto) {
|
case SEPOL_PROTO_IP4:
|
return "ipv4";
|
case SEPOL_PROTO_IP6:
|
return "ipv6";
|
default:
|
return "???";
|
}
|
}
|
|
hidden_def(sepol_node_get_proto_str)
|
|
/* Create */
|
int sepol_node_create(sepol_handle_t * handle, sepol_node_t ** node)
|
{
|
|
sepol_node_t *tmp_node = (sepol_node_t *) malloc(sizeof(sepol_node_t));
|
|
if (!tmp_node) {
|
ERR(handle, "out of memory, could not create " "node record");
|
return STATUS_ERR;
|
}
|
|
tmp_node->addr = NULL;
|
tmp_node->addr_sz = 0;
|
tmp_node->mask = NULL;
|
tmp_node->mask_sz = 0;
|
tmp_node->proto = SEPOL_PROTO_IP4;
|
tmp_node->con = NULL;
|
*node = tmp_node;
|
|
return STATUS_SUCCESS;
|
}
|
|
hidden_def(sepol_node_create)
|
|
/* Deep copy clone */
|
int sepol_node_clone(sepol_handle_t * handle,
|
const sepol_node_t * node, sepol_node_t ** node_ptr)
|
{
|
|
sepol_node_t *new_node = NULL;
|
if (sepol_node_create(handle, &new_node) < 0)
|
goto err;
|
|
/* Copy address, mask, protocol */
|
new_node->addr = malloc(node->addr_sz);
|
new_node->mask = malloc(node->mask_sz);
|
if (!new_node->addr || !new_node->mask)
|
goto omem;
|
|
memcpy(new_node->addr, node->addr, node->addr_sz);
|
memcpy(new_node->mask, node->mask, node->mask_sz);
|
new_node->addr_sz = node->addr_sz;
|
new_node->mask_sz = node->mask_sz;
|
new_node->proto = node->proto;
|
|
/* Copy context */
|
if (node->con &&
|
(sepol_context_clone(handle, node->con, &new_node->con) < 0))
|
goto err;
|
|
*node_ptr = new_node;
|
return STATUS_SUCCESS;
|
|
omem:
|
ERR(handle, "out of memory");
|
|
err:
|
ERR(handle, "could not clone node record");
|
sepol_node_free(new_node);
|
return STATUS_ERR;
|
}
|
|
/* Destroy */
|
void sepol_node_free(sepol_node_t * node)
|
{
|
|
if (!node)
|
return;
|
|
sepol_context_free(node->con);
|
free(node->addr);
|
free(node->mask);
|
free(node);
|
}
|
|
hidden_def(sepol_node_free)
|
|
/* Context */
|
sepol_context_t *sepol_node_get_con(const sepol_node_t * node)
|
{
|
|
return node->con;
|
}
|
|
hidden_def(sepol_node_get_con)
|
|
int sepol_node_set_con(sepol_handle_t * handle,
|
sepol_node_t * node, sepol_context_t * con)
|
{
|
|
sepol_context_t *newcon;
|
|
if (sepol_context_clone(handle, con, &newcon) < 0) {
|
ERR(handle, "out of memory, could not set node context");
|
return STATUS_ERR;
|
}
|
|
sepol_context_free(node->con);
|
node->con = newcon;
|
return STATUS_SUCCESS;
|
}
|
|
hidden_def(sepol_node_set_con)
|