#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/* Class AccessGroup
*
* IP network access list management
*
*/
#define _CLASS_AccessGroup_PRIVATE_
#include "access.h"
/* Modified the access list maintenance so exact matches (no mask bits)
* are kept at the head of the list and permission searches end on the
* first address match. This has the effect of giving exact matches
* a higher priority than wildcard matches. This is also the way the
* cisco access lists work.
*/
typedef struct _AccessCondition_ {
unsigned long address;
unsigned long mask;
enum PERM {
deny=0, permit=1, readonly=1,
writeonly=2, readwrite=3
} perm;
struct _AccessCondition_* next;
} condition_t;
typedef struct ACCESSLISTHEAD {
condition_t* head; /* first member of list */
condition_t* lastExact; /* last non-wildcard member of list */
condition_t* tail; /* last member of list */
} listhead_t;
#if defined (__STDC__)
int accessGroup_printOn (
AccessGroup self,
FILE* file
);
static int accessList_dispose (
listhead_t* alp
);
static int accessList_addCondition (
listhead_t* alp,
condition_t* condition
);
static int accessList_validateAddress (
listhead_t* alp,
unsigned long source,
int type
);
static int accessListPrintOn (
listhead_t* alp,
FILE* file
);
static int compileAccessCondition (
AccessGroup self,
int ac,
char** av,
condition_t* ptr
);
static int accessListVerifyAddress ();
#else
static int accessList_dispose ();
static int accessList_addCondition ();
static int accessList_validateAddress ();
static int compileAccessCondition ();
#endif
/*
*
* Access Group Functions
*/
/* Create new AccessGroup structure and return it to user
*/
AccessGroup
accessGroup_new ()
{
AccessGroup self;
extern char* calloc();
self = (AccessGroup)calloc (1, sizeof (*self));
return self;
}
int
accessGroup_dispose (self)
AccessGroup self;
{
int i;
if (self == 0)
return 0;
for (i = 1; i < self->max; i++) {
if (self->list[i].head)
accessList_dispose (&self->list[i]);
}
free (self);
return 0;
}
/* Add an access list specification
* Returns access list number (positive integer) on success.
* Returns 0 on failure. Consult external "char* accessError"
* for details.
*/
int
accessGroup_add (self, argc, argv)
AccessGroup self;
int argc;
char **argv;
{
condition_t condition;
int list;
self->accessError = 0;
if ((list = compileAccessCondition(self, argc, argv, &condition)) != 0)
accessGroup_addCondition (self, list, &condition);
return list;
}
int
accessGroup_addCondition (self, list, condition)
AccessGroup self;
condition_t* condition;
int list;
{
condition_t* cp, * lp;
listhead_t* hp;
char *tcp;
/* get enough space for the condition */
cp = (condition_t*)malloc(sizeof *condition);
/* copy argument into allocated storage */
*cp = *condition;
/* See if we need to make global list bigger
* If so, reallocate it and clear any extra.
*/
if (list > self->max) {
int newmax = list;
int newsize, oldsize;
listhead_t* newlist;
if (newmax - self->max < 10)
newmax = self->max + 10;
newsize = (newmax+1) * sizeof (self->list[0]);
oldsize = self->max
? ((self->max+1) * sizeof (self->list[0]))
: 0 ;
/* get new space and clear it */
newlist = (listhead_t*)malloc(newsize);
tcp = (char*) &newlist[0];
bzero (&tcp[oldsize], newsize-oldsize);
/* if there used to be space, copy it */
if (self->list) {
bcopy ((char*)self->list, (char*)newlist, oldsize);
free (self->list);
}
self->max = newmax;
self->list = newlist;
}
accessList_addCondition (&self->list[list], cp);
}
/* validate that the source system named by:
* 'source' is permitted to access the
* given filename according to access lists.
*
* Return 1 if access is permitted under old default rules
* (i.e., no access list governs); 0 if not.
*
* Return 2 if more particular access is permitted by an access list.
* This gets around the old public read/writable requirements.
*/
int
accessGroup_validateAddress (self, list, source, type)
AccessGroup self;
int list;
unsigned long source;
int type;
{
listhead_t* alp;
/* If the named list doesn't exist, use the default */
alp = &self->list[list];
if (list <= 0 || list > self->max || alp->head == 0) {
self->accessError = "Validate: bad list number";
return -1;
}
return accessListVerifyAddress (alp, source, type);
}
/* Debugging function */
accessGroup_printOn(self, file)
AccessGroup self;
FILE* file;
{
register int i;
for (i = 1; i <= self->max; i++) {
if (self->list[i].head != 0) {
fprintf (file, "access list %d:\n", i);
accessListPrintOn (&self->list[i], file);
}
}
}
/*
*
* AccessList Functions
*
* These are all static, as they are only called from AccessGroup functions.
*/
/* Dispose of (free all memory) for access list <num> in access group
* <self>
*/
static int
accessList_dispose (alp)
listhead_t* alp;
{
condition_t* cp, * lp;
for (cp = alp->head; cp; cp = lp) {
lp = cp->next;
free (cp);
}
alp->head = 0;
alp->tail = 0;
alp->lastExact = 0;
return 0;
}
/* Add an access condition to access list <list>.
* An access list is maintained in the order specified, except
* that exact matches are at the front of the list and wildcard
* matches are at the end.
*/
static int
accessList_addCondition (alp, cp)
listhead_t* alp;
condition_t* cp;
{
condition_t* lp;
/* if this particular list has no conditions, put this one
* at the head. Set 'lastExact' if this condition is not
* a wildcard.
*/
if ((lp = alp->tail) == 0) {
alp->tail = alp->head = cp;
cp->next = 0;
if (cp->mask == 0)
alp->lastExact = cp;
return 0;
}
/* Otherwise, if a mask is not specified, add this to the
* end of all those conditions without masks. This has
* the effect of checking all non-wildcard conditions first,
* and only if no match found checking the wildcard conditions.
*/
if (cp->mask == 0) {
condition_t* former;
/* The list is non-empty, and contains non-wildcard
* conditions. Put this one after the last exact
* condition.
*/
if ((former = alp->lastExact) != 0) {
cp->next = former->next;
former->next = cp;
/* If the last exact condition was also the
* end of the list, set the tail to this
* condition.
*/
if (former == lp)
alp->tail = cp;
}
/* Otherwise, the list contains only wildcard conditions.
* Put this one at the front.
*/
else {
alp->lastExact = cp;
cp->next = alp->head;
alp->head = cp;
}
}
/* Otherwise, just append it to the tail */
else {
lp->next = cp;
alp->tail = cp;
cp->next = 0;
}
return 0;
}
int
accessListVerifyAddress (alp, source, type)
listhead_t* alp;
unsigned long source;
int type;
{
condition_t* cp;
int permission = 0;
/* cycle through the conditions in this access list and
* check against source. Search ends on first match.
*/
for (cp = alp->head; cp != 0; cp = cp->next) {
if ((source & ~cp->mask) == cp->address) {
permission = cp->perm;
break;
}
}
/* Check permissions:
* If type is 'deny' refuse.
* If type is 'permit' return old default access permission
* If type is 'readonly' and asking for write permission, refuse.
* Otherwise accept.
*/
return (type & permission);
}
/* for debugging printouts */
static char* permNames[] ={ "deny", "readonly", "writeonly", "readwrite" };
#define NITEMS(X) (sizeof(X) / sizeof (X)[0])
#define PERMNAME(N) ((unsigned)(N) >= NITEMS(permNames) \
? "<unknown>" : permNames[N])
accessListPrintOn (alp, file)
listhead_t* alp;
FILE* file;
{
char* permission;
char addrbuf[32], maskbuf[32];
struct in_addr t;
condition_t* list;
list = alp->head;
while (list != 0) {
permission = PERMNAME(list->perm);
t.s_addr = list->address;
strcpy (addrbuf, inet_ntoa(t));
t.s_addr = list->mask;
strcpy (maskbuf, inet_ntoa(t));
fprintf (file, " >> %s %s %s\n",
permission, addrbuf, maskbuf);
list = list->next;
}
}
/* Compile a text description of an access list into
* binary format and store it in the argument location.
* As a side effect, set the external accessError
* to the textual description of any format error encountered.
*
* It returns the list for which the condition has been
* compiled.
*
* Arguments are word-parsed and packed into an argument
* vector like that used for "main".
*/
static int
compileAccessCondition (self, ac, av, ptr)
AccessGroup self;
int ac;
char** av;
condition_t* ptr;
{
int listnum;
/* an access command must have at least 3 arguments, but
* not more than four. Skip the command name itself.
*/
ac--, av++;
if (ac < 2 || ac > 4) {
self->accessError = "illegal argument count (< 2 || > 4)";
return 0;
}
/* The first argument must be numeric and must be a
* positive number.
*/
listnum = atoi (*av);
if (listnum <= 0) {
self->accessError = "list number not a positive integer";
return 0;
}
av++; ac--;
if (ac == 1 && **av == '-' || **av == '+') {
int addlist;
listhead_t* hp;
condition_t* cp;
if (**av == '-') {
self->accessError = "-<list> not implemented yet";
return 0;
}
if (!isdigit (av[0][1]) || (addlist = atoi (&av[0][1])) < 0) {
self->accessError =
"+<list> requires positive integer argument";
return 0;
}
hp = &self->list[addlist];
if (addlist > self->max || hp->head == 0) {
self->accessError = "+<list> argument not defined";
return 0;
}
/* Add all the conditions in the list to the current list */
for (cp = hp->head; cp->next ; cp = cp->next) {
accessGroup_addCondition (self, listnum, cp);
}
*ptr = *cp;
return listnum;
}
/* The second argument must either be "permit", "deny", "readonly"
* or "readwrite". "permit" gives old-style access.
*/
if (strcmp (*av, "permit") == 0)
ptr->perm = readonly;
else if(strcmp(*av, "deny") == 0)
ptr->perm = deny;
else if(strcmp(*av, "readonly") == 0)
ptr->perm = readonly;
else if(strcmp(*av, "writeonly") == 0)
ptr->perm = writeonly;
else if(strcmp(*av, "readwrite") == 0)
ptr->perm = readwrite;
else {
self->accessError =
"list keyword not {permit,deny,readonly,readwrite}";
return 0;
}
av++; ac--;
/* The third argument must either be dotted decimal, or hex
* integer with leading "0x".
*/
ptr->address = inet_addr(*av);
av++; ac--;
/* If there is a fourth argument, it must also be either
* dotted decimal, or hex integer with leading "0x".
* If absent, mask defaults to zero.
*/
if (ac > 0)
ptr->mask = inet_addr(*av);
else
ptr->mask = 0;
return listnum;
}
/* Debugging functions */
#ifdef TEST
#include <stdio.h>
extern char* cfgets();
extern int strsplit();
main (argc, argv)
int argc;
char** argv;
{
FILE* f;
char buf[1024];
char* vec[12];
int nf;
int num = 0;
int ret;
AccessGroup agp;
agp = accessGroup_new();
for (argc--,argv++; argc > 0; argc--,argv++) {
if ((f = fopen (*argv, "r")) == NULL) {
perror (*argv);
continue;
}
while (cfgets (buf, sizeof buf, f, &num)) {
if ((nf = strsplit (buf, vec, 12, " \t\n")) == 0)
continue;
if (accessGroup_add (agp, nf, vec) == 0) {
printf ("access list error '%s': line %d\n",
agp->accessError, num);
}
}
fclose (f);
accessGroup_printOn(agp, stdout);
}
printf ("Enter source internet address and list pairs:\n");
while (fgets(buf, sizeof buf, stdin)) {
nf = strsplit (buf, vec, 12, " \t\r\n");
if (nf != 2) {
printf ("bad input format: expected <addr,list>\n");
continue;
}
num = atoi (vec[1]);
ret = accessGroup_validateAddress (agp, num,
inet_addr(vec[0]), 3);
printf ("validate (%d, %s, 3) returns %d\n",
num, vec[0], ret);
if (ret == -1) {
printf ("validate error: '%s'\n",
agp->accessError);
}
}
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1