/* access.c - functions for access control
Copyright (C) 2003 Russell Kroll <rkroll@exploits.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "common.h"
#include "access.h"
struct acl_t *acl_head = NULL;
struct access_t *access_head = NULL;
#ifndef HAVE_IPV6
static int mask_cmp(const struct sockaddr_in *ip_addr,
const struct sockaddr_in *net_addr, unsigned int prefix)
{
#else
static int mask_cmp (const struct sockaddr_storage *ip_addr,
const struct sockaddr_storage *net_addr, unsigned int prefix)
{
if (ip_addr->ss_family == AF_INET)
{
#endif
struct in_addr *ip = &((struct sockaddr_in *)ip_addr)->sin_addr;
struct in_addr *net = &((struct sockaddr_in *)net_addr)->sin_addr;
return ((ip->s_addr & prefix) == net->s_addr);
#ifdef HAVE_IPV6
}
if ((ip_addr->ss_family == AF_INET6) && (net_addr->ss_family == AF_INET6))
{
struct in6_addr ip6;
struct in6_addr *net = &((struct sockaddr_in6 *)net_addr)->sin6_addr;
unsigned char i = (prefix >> 3);
memcpy (&ip6, &((struct sockaddr_in6 *)ip_addr)->sin6_addr, sizeof (struct in6_addr));
if (prefix % 8)
ip6.s6_addr[i++] &= 0xff << (8 - (prefix % 8));
while (i < sizeof(ip6.s6_addr))
ip6.s6_addr[i++] = 0;
return (memcmp(ip6.s6_addr, net->s6_addr, sizeof(ip6.s6_addr)) == 0);
}
if ((ip_addr->ss_family == AF_INET6) && (net_addr->ss_family == AF_INET))
{
struct in6_addr *ip6 = &((struct sockaddr_in6 *)ip_addr)->sin6_addr;
struct in_addr *net = &((struct sockaddr_in *)net_addr)->sin_addr;
return (IN6_IS_ADDR_V4MAPPED(ip6) &&
((((const uint32_t *)ip6)[3] & prefix) == net->s_addr));
}
fatal_with_errno(EXIT_FAILURE, "mask_cmp: Unknown address family");
#endif
}
/* see if <addr> matches the acl <aclname> */
#ifndef HAVE_IPV6
int acl_check(const char *aclname, const struct sockaddr_in *addr)
#else
int acl_check(const char *aclname, const struct sockaddr_storage *addr)
#endif
{
struct acl_t *tmp;
for (tmp = acl_head; tmp != NULL; tmp = tmp->next)
{
if (strcmp(tmp->name, aclname))
continue;
if (mask_cmp(addr, &tmp->addr, tmp->mask))
return 1; /* match */
}
return 0; /* not found */
}
/* return ACCEPT/REJECT based on source address */
#ifndef HAVE_IPV6
int access_check(const struct sockaddr_in *addr)
#else
int access_check(const struct sockaddr_storage *addr)
#endif
{
struct access_t *tmp;
int ret;
for (tmp = access_head; tmp != NULL; tmp = tmp->next)
{
ret = acl_check(tmp->aclname, addr);
upsdebugx(3, "acl_check: %s: match %d", tmp->aclname, ret);
if (ret == 1) {
upsdebugx(1, "ACL [%s] matches, action=%d",
tmp->aclname, tmp->action);
return tmp->action;
}
}
/* fail safe */
return ACCESS_REJECT;
}
/* add to the master list of ACL names */
void acl_add(const char *aclname, char *ipblock)
{
struct acl_t *tmp, *last;
char *addr, *mask;
/* are we sane? */
if ((!aclname) || (!ipblock))
return;
/*
* 192.168.1.1/32 valid
* 192.168.1.1/255.255.255.255 valid
* 192.168.1.1 invalid
* ::FFFF:192.168.1.0/120 valid
* ::FFFF:192.168.1.1 invalid
* ::1/128 valid
* ::1 invalid
*/
if (((addr = strtok(ipblock, "/")) == NULL) || ((mask = strtok(NULL, "\0")) == NULL))
fatalx(EXIT_FAILURE, "Can't parse ACL %s %s", aclname, ipblock);
tmp = last = acl_head;
while (tmp != NULL)
{
last = tmp;
tmp = tmp->next;
}
/* memset (&saddr, 0, sizeof (struct sockaddr_storage)); */
tmp = xmalloc(sizeof(struct acl_t));
memset(tmp, 0, sizeof (struct acl_t));
tmp->name = xstrdup(aclname);
tmp->next = NULL;
#ifndef HAVE_IPV6
if (strstr(mask, ".") == NULL)
{
/* must be a /nn CIDR type block */
tmp->mask = strtol(mask, NULL, 10);
if (tmp->mask < 0 || tmp->mask > 32)
{
free (tmp);
fatal_with_errno(EXIT_FAILURE, "Invalid CIDR type block: Must be > 0 && < 32");
}
tmp->mask = htonl(0xffffffff << (32 - tmp->mask));
}
else
{
/* must be a n.n.n.n dotted quad block */
tmp->mask = inet_addr(mask);
}
tmp->addr.sin_addr.s_addr = inet_addr(addr) & tmp->mask;
#else
if (strstr(addr, ":") == NULL)
{
struct sockaddr_in s4; /* IPv4 address */
/* mask */
if (inet_pton(AF_INET, mask, &s4.sin_addr) < 1)
{
/* must be a /nn CIDR type block */
tmp->mask = strtol(mask, NULL, 10);
if (tmp->mask < 0 || tmp->mask > 32)
{
free (tmp);
fatal_with_errno(EXIT_FAILURE, "Invalid CIDR type block: Must be > 0 && < 32");
}
tmp->mask = htonl(0xffffffff << (32 - tmp->mask));
}
else
{
/* must be a n.n.n.n dotted quad block */
tmp->mask = s4.sin_addr.s_addr;
}
/* address */
memset(&s4, 0, sizeof (struct sockaddr_in));
s4.sin_family = AF_INET;
/* apply mask to address */
if (inet_pton(AF_INET, addr, &s4.sin_addr) < 1)
{
free(tmp);
fatal_with_errno(EXIT_FAILURE, "Invalid IPv4 address: \"%s\"", addr);
}
else
{
s4.sin_addr.s_addr &= tmp->mask;
}
memcpy(&(tmp->addr), &s4, sizeof(struct sockaddr_in));
tmp->addr.ss_family = AF_INET;
}
else
{
struct sockaddr_in6 s6; /* IPv6 address */
/* prefix */
tmp->mask = strtol(mask, NULL, 10);
/* address */
memset(&s6, 0, sizeof(struct sockaddr_in6));
s6.sin6_family = AF_INET6;
if (inet_pton(AF_INET6, addr, &s6.sin6_addr) < 1)
{
free(tmp);
fatal_with_errno(EXIT_FAILURE, "Invalid IPv6 address: \"%s\"", addr);
}
/* apply prefix to address */
if (tmp->mask < 0 || tmp->mask > 128)
{
free (tmp);
fatal_with_errno(EXIT_FAILURE, "Invalid IPv6 prefix");
}
else
{
unsigned char i = (tmp->mask >> 3);
if ((tmp->mask) % 8)
s6.sin6_addr.s6_addr[i++] &= 0xff << (8 - ((tmp->mask) % 8));
while (i < sizeof(s6.sin6_addr.s6_addr))
s6.sin6_addr.s6_addr[i++] = 0;
}
memcpy(&(tmp->addr), &s6, sizeof(struct sockaddr_in6));
tmp->addr.ss_family = AF_INET6;
}
#endif
if (last == NULL) /* first */
acl_head = tmp;
else
last->next = tmp;
}
void acl_free(void)
{
struct acl_t *ptr, *next;
ptr = acl_head;
while (ptr)
{
next = ptr->next;
free(ptr->name);
free(ptr);
ptr = next;
}
acl_head = NULL;
}
void access_free(void)
{
struct access_t *ptr, *next;
ptr = access_head;
while (ptr)
{
next = ptr->next;
free(ptr->aclname);
free(ptr);
ptr = next;
}
access_head = NULL;
}
static void access_append(int action, const char *aclname)
{
struct access_t *tmp, *last;
tmp = last = access_head;
while (tmp != NULL)
{
last = tmp;
tmp = tmp->next;
}
tmp = xmalloc(sizeof(struct access_t));
tmp->action = action;
tmp->aclname = xstrdup(aclname);
tmp->next = NULL;
if (last)
last->next = tmp;
else
access_head = tmp;
}
void access_add(int type, int numargs, const char **arg)
{
int i;
for (i = 0; i < numargs; i++)
access_append(type, arg[i]);
}
syntax highlighted by Code2HTML, v. 0.9.1