#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 the 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)