/*-
* Copyright (c) 2004 Free (Olivier Beyssac)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>
#include "netlist.h"
#include "utils.h"
#define ACL_MAX_LINE 80
#define WHITELIST_MAX_LINE 80
#define trim_buffer(buf) \
do { \
size_t i; \
for (i = strlen(buf) - 1; \
i > 0 && (buf[i] == '\n' || buf[i] == '\r' \
|| buf[i] == '\t' || buf[i] == ' '); i--) \
buf[i] = '\0'; \
} while(0)
static unsigned int bitmasks[] = { 0x00000000, 0x80000000,
0xc0000000, 0xe0000000,
0xf0000000, 0xf8000000,
0xfc000000, 0xfe000000,
0xff000000, 0xff800000,
0xffc00000, 0xffe00000,
0xfff00000, 0xfff80000,
0xfffc0000, 0xfffe0000,
0xffff0000, 0xffff8000,
0xffffc000, 0xffffe000,
0xfffff000, 0xfffff800,
0xfffffc00, 0xfffffe00,
0xffffff00, 0xffffff80,
0xffffffc0, 0xffffffe0,
0xfffffff0, 0xfffffff8,
0xfffffffc, 0xfffffffe,
0xffffffff };
struct netlist
{
size_t size; /* netlist size */
in_addr_t *net; /* Networks list */
in_addr_t *mask; /* Masks list */
int *mode; /* Permissions list */
};
/* Return a newly allocated acl */
extern netlist netlist_init(void)
{
netlist tmp;
tmp = calloc(sizeof(struct netlist), 1);
return tmp;
}
/* Free a netlist */
extern void netlist_free(const netlist nl)
{
nl->size = 0;
free(nl->net);
free(nl->mask);
free(nl->mode);
}
/* Search for an IP in the netlist. Return the mode if found, -1 otherwise.
If not NULL, parameter index is modified to get position in the list */
static int netlist_search(const netlist nl, const in_addr_t ip, int *index)
{
int i, j;
if (nl->size == 0)
return -1;
for (i = 0, j = nl->size - 1;
i < j && nl->net[i] != ip && nl->net[j] != ip;
i++, j--);
if (nl->net[i] == ip) {
if (index != NULL)
*index = i;
return nl->mode[i];
}
if (nl->net[j] == ip) {
if (index != NULL)
*index = j;
return nl->mode[j];
}
return -1;
}
/* Add or update IP/mask in the netlist */
static int netlist_add(const netlist nl, const in_addr_t ip,
const in_addr_t mask, const int mode)
{
int index;
if (netlist_search(nl, ip, &index) != -1) {
nl->mode[index] = mode;
return 1;
}
nl->size++;
nl->net = realloc(nl->net, sizeof(in_addr_t) * nl->size);
nl->mask = realloc(nl->mask, sizeof(int) * nl->size);
nl->mode = realloc(nl->mode, sizeof(int) * nl->size);
nl->net[nl->size - 1] = ip;
nl->mask[nl->size - 1] = mask;
nl->mode[nl->size - 1] = mode;
return 1;
}
/* Convert the string-mode to an int-mode (ignoring whitespaces and tabs) */
static int acl_mode_str2int(char *str_mode)
{
int mode;
char *p, *q;
char svc;
size_t i, len, svl;
/* Trim the beginning */
for (p = str_mode; *p && (*p == ' ' || *p == '\t'); p++);
/* Trim the end */
q = p;
len = strlen(q);
for (i = len - 1; i > 0 && (q[i] == ' ' || q[i] == '\t'); i--);
/* Remember how to restore the buffer to its initial state */
savebufpos(svc, svl, q, i+1);
if (strcmp(p, "submit") == 0)
mode = ACL_M_SUBMIT;
else if (strcmp(p, "query") == 0)
mode = ACL_M_QUERY;
else if (strcmp(p, "insert") == 0)
mode = ACL_M_INSERT;
else if (strcmp(p, "decrement") == 0)
mode = ACL_M_DECR;
else if (strcmp(p, "all") == 0)
mode = ACL_M_ALL;
else {
restorebuf(svc, svl, q);
syslog(LOG_ERR, "invalid mode in ACL file: %s", str_mode);
return -1;
}
restorebuf(svc, svl, q);
return mode;
}
/* Parse buf and fill IP/Mask. Return 1 if successful, 0 otherwise */
static int netlist_fill_ip_mask(char *buf, in_addr_t *ip, in_addr_t *mask)
{
char *b, *p;
char svc;
size_t svl;
struct in_addr inp;
int tmpmask;
*ip = 0;
*mask = 0;
/* Ignore leading whitespaces and tabs */
for (b = buf; *b != '\0' && (*b == ' ' || *b == '\t'); b++);
if ((p = strchr(b, '/')) == NULL) {
*mask = bitmasks[32];
if (inet_aton(b, &inp) == 0) {
syslog(LOG_ERR, "invalid IP address %s", buf);
return 0;
}
*ip = ntohl(inp.s_addr);
return 1;
}
/* Remember how to unbreak the buffer */
savebufpos(svc, svl, b, strlen(buf) - strlen(p));
p++;
if (!inet_aton(b, &inp)) {
syslog(LOG_ERR, "invalid IP address %s", buf);
return 0;
}
*ip = ntohl(inp.s_addr);
if ((tmpmask = xstrtol(p)) == -1) {
if (!inet_aton(p, &inp)) {
syslog(LOG_ERR, "invalid netmask %s", b);
return 0;
} else {
*mask = ntohl(inp.s_addr);
}
} else
*mask = bitmasks[tmpmask];
*ip &= *mask;
restorebuf(svc, svl, b);
return 1;
}
/* Load ACL from file. Return 1 on success, 0 if file was not found, -1
for all other errors */
extern int netlist_acl_getfromfile(const netlist nl, const char *filename)
{
FILE *f;
char buf[ACL_MAX_LINE];
char *str_mode, *cur_mode;
int mode, tmp_mode;
in_addr_t ip, mask;
if ((f = fopen(filename, "r")) == NULL) {
if (errno == ENOENT)
return 0;
syslog(LOG_ERR, "fopen %s: %s", filename, strerror(errno));
return -1;
}
while (fgets(buf, sizeof(buf), f)) {
size_t len;
len = strlen(buf);
if ((len >= 1 && (buf[0] == '#' || buf[0] == '\n'))
|| (len >= 2 && buf[0] == '\r' && buf[1] == '\n'))
continue;
trim_buffer(buf);
if ((str_mode = strchr(buf, ':')) == NULL)
return -1;
*str_mode = '\0';
if (!netlist_fill_ip_mask(buf, &ip, &mask)) {
fclose(f);
return -1;
}
str_mode++;
mode = 0;
/* Parse mode list */
while ((cur_mode = strchr(str_mode, ','))) {
*cur_mode = '\0';
cur_mode++;
if ((tmp_mode = acl_mode_str2int(str_mode)) == -1) {
fclose(f);
return -1;
} else
mode |= tmp_mode;
str_mode = cur_mode;
}
if ((tmp_mode = acl_mode_str2int(str_mode)) == -1) {
fclose(f);
return -1;
} else
mode |= tmp_mode;
netlist_add(nl, ip, mask, mode);
}
fclose(f);
return 1;
}
/* Load whitelist from file. Return 1 on success, 0 if file was not found, -1
for all other errors */
extern int netlist_whitelist_getfromfile(const netlist nl,
const char *filename)
{
FILE *f;
char buf[WHITELIST_MAX_LINE];
in_addr_t ip, mask;
if ((f = fopen(filename, "r")) == NULL) {
if (errno == ENOENT)
return 0;
syslog(LOG_ERR, "fopen %s: %s", filename, strerror(errno));
return -1;
}
while (fgets(buf, sizeof(buf), f)) {
size_t len;
len = strlen(buf);
if ((len >= 1 && (buf[0] == '#' || buf[0] == '\n'))
|| (len >= 2 && buf[0] == '\r' && buf[1] == '\n'))
continue;
trim_buffer(buf);
if (!netlist_fill_ip_mask(buf, &ip, &mask)) {
fclose(f);
return -1;
}
netlist_add(nl, ip, mask, 1);
}
fclose(f);
return 1;
}
/* Return allowed actions for IP */
extern int netlist_getmode(const netlist nl, const in_addr_t ip)
{
size_t i;
if (nl->size == 0)
return ACL_M_ALL;
for (i = 0; i < nl->size; i++)
if ((ip & nl->mask[i]) == nl->net[i])
return nl->mode[i];
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1