/*- * 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 #include #include #include #include #include #include #include #include #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; }