398 lines
6.5 KiB
C
398 lines
6.5 KiB
C
#include "ratfs.h"
|
|
#include <ip.h>
|
|
|
|
enum {
|
|
ACCEPT = 0, /* verbs in control file */
|
|
REFUSED,
|
|
DENIED,
|
|
DIALUP,
|
|
BLOCKED,
|
|
DELAY,
|
|
NONE,
|
|
|
|
Subchar = '#', /* character substituted for '/' in file names */
|
|
};
|
|
|
|
static Keyword actions[] = {
|
|
"allow", ACCEPT,
|
|
"accept", ACCEPT,
|
|
"block", BLOCKED,
|
|
"deny", DENIED,
|
|
"dial", DIALUP,
|
|
"relay", DELAY,
|
|
"delay", DELAY,
|
|
0, NONE,
|
|
};
|
|
|
|
static void acctinsert(Node*, char*);
|
|
static char* getline(Biobuf*);
|
|
static void ipinsert(Node*, char*);
|
|
static void ipsort(void);
|
|
|
|
/*
|
|
* Input the configuration file
|
|
* Currently we only process the "ournets"
|
|
* specification.
|
|
*/
|
|
void
|
|
getconf(void)
|
|
{
|
|
Biobuf *bp;
|
|
char *cp;
|
|
Node *np, *dir, **l;
|
|
|
|
if(debugfd >= 0)
|
|
fprint(debugfd, "loading %s\n", conffile);
|
|
|
|
bp = Bopen(conffile, OREAD);
|
|
if(bp == 0)
|
|
return;
|
|
|
|
dir = finddir(Trusted);
|
|
if(dir == 0)
|
|
return;
|
|
|
|
/*
|
|
* if this isn't the first time, purge permanent entries
|
|
*/
|
|
trustedqid = Qtrustedfile;
|
|
if(lastconftime){
|
|
l = &dir->children;
|
|
for(np = dir->children; np; np = *l){
|
|
if(np->d.type == Trustedperm){
|
|
*l = np->sibs;
|
|
free(np);
|
|
} else {
|
|
np->d.qid.path = trustedqid++;
|
|
l = &np->sibs;
|
|
}
|
|
}
|
|
dir->count = 0;
|
|
}
|
|
|
|
for(;;){
|
|
cp = getline(bp);
|
|
if(cp == 0)
|
|
break;
|
|
if (strcmp(cp, "ournets") == 0){
|
|
for(cp += strlen(cp)+1; cp && *cp; cp += strlen(cp)+1){
|
|
np = newnode(dir, cp, Trustedperm, 0111, trustedqid++);
|
|
cidrparse(&np->ip, cp);
|
|
subslash(cp);
|
|
np->d.name = atom(cp);
|
|
}
|
|
}
|
|
}
|
|
Bterm(bp);
|
|
lastconftime = time(0);
|
|
}
|
|
|
|
/*
|
|
* Reload the control file, if necessary
|
|
*/
|
|
void
|
|
reload(void)
|
|
{
|
|
int type, action;
|
|
Biobuf *bp;
|
|
char *cp;
|
|
Node *np, *dir;
|
|
|
|
if(debugfd >= 0)
|
|
fprint(debugfd,"loading %s\n", ctlfile);
|
|
|
|
bp = Bopen(ctlfile, OREAD);
|
|
if(bp == 0)
|
|
return;
|
|
|
|
if(lastctltime){
|
|
for(dir = root->children; dir; dir = dir->sibs){
|
|
if (dir->d.type != Addrdir)
|
|
continue;
|
|
for(np = dir->children; np; np = np->sibs)
|
|
np->count = 0;
|
|
}
|
|
}
|
|
|
|
for(;;){
|
|
cp = getline(bp);
|
|
if(cp == 0)
|
|
break;
|
|
type = *cp;
|
|
if(type == '*'){
|
|
cp++;
|
|
if(*cp == 0) /* space before keyword */
|
|
cp++;
|
|
}
|
|
action = findkey(cp, actions);
|
|
if (action == NONE)
|
|
continue;
|
|
if (action == ACCEPT)
|
|
dir = dirwalk("allow", root);
|
|
else
|
|
if (action == DELAY)
|
|
dir = dirwalk("delay", root);
|
|
else
|
|
dir = dirwalk(cp, root);
|
|
if(dir == 0)
|
|
continue;
|
|
|
|
for(cp += strlen(cp)+1; cp && *cp; cp += strlen(cp)+1){
|
|
if(type == '*')
|
|
acctinsert(dir, cp);
|
|
else
|
|
ipinsert(dir, cp);
|
|
}
|
|
}
|
|
Bterm(bp);
|
|
ipsort();
|
|
dummy.d.mtime = dummy.d.atime = lastctltime = time(0);
|
|
}
|
|
|
|
/*
|
|
* get a canonicalized line: a string of null-terminated lower-case
|
|
* tokens with a two null bytes at the end.
|
|
*/
|
|
static char*
|
|
getline(Biobuf *bp)
|
|
{
|
|
char c, *cp, *p, *q;
|
|
int n;
|
|
|
|
static char *buf;
|
|
static int bufsize;
|
|
|
|
for(;;){
|
|
cp = Brdline(bp, '\n');
|
|
if(cp == 0)
|
|
return 0;
|
|
n = Blinelen(bp);
|
|
cp[n-1] = 0;
|
|
if(buf == 0 || bufsize < n+1){
|
|
bufsize += 512;
|
|
if(bufsize < n+1)
|
|
bufsize = n+1;
|
|
buf = realloc(buf, bufsize);
|
|
if(buf == 0)
|
|
break;
|
|
}
|
|
q = buf;
|
|
for (p = cp; *p; p++){
|
|
c = *p;
|
|
if(c == '\\' && p[1]) /* we don't allow \<newline> */
|
|
c = *++p;
|
|
else
|
|
if(c == '#')
|
|
break;
|
|
else
|
|
if(c == ' ' || c == '\t' || c == ',')
|
|
if(q == buf || q[-1] == 0)
|
|
continue;
|
|
else
|
|
c = 0;
|
|
*q++ = tolower(c);
|
|
}
|
|
if(q != buf){
|
|
if(q[-1])
|
|
*q++ = 0;
|
|
*q = 0;
|
|
break;
|
|
}
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
* Match a keyword
|
|
*/
|
|
int
|
|
findkey(char *val, Keyword *p)
|
|
{
|
|
|
|
for(; p->name; p++)
|
|
if(strcmp(val, p->name) == 0)
|
|
break;
|
|
return p->code;
|
|
}
|
|
|
|
/*
|
|
* parse a cidr specification in either IP/mask or IP#mask format
|
|
*/
|
|
void
|
|
cidrparse(Cidraddr *cidr, char *cp)
|
|
{
|
|
|
|
char *p, *slash;
|
|
int c;
|
|
ulong a, m;
|
|
uchar addr[IPv4addrlen];
|
|
uchar mask[IPv4addrlen];
|
|
char buf[64];
|
|
|
|
/*
|
|
* find '/' or '#' character in the cidr specification
|
|
*/
|
|
slash = 0;
|
|
for(p = buf; p < buf+sizeof(buf)-1 && *cp; p++) {
|
|
c = *cp++;
|
|
switch(c) {
|
|
case Subchar:
|
|
c = '/';
|
|
slash = p;
|
|
break;
|
|
case '/':
|
|
slash = p;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
*p = c;
|
|
}
|
|
*p = 0;
|
|
|
|
v4parsecidr(addr, mask, buf);
|
|
a = nhgetl(addr);
|
|
m = nhgetl(mask);
|
|
/*
|
|
* if a mask isn't specified, we build a minimal mask
|
|
* instead of using the default mask for that net. in this
|
|
* case we never allow a class A mask (0xff000000).
|
|
*/
|
|
if(slash == 0){
|
|
m = 0xff000000;
|
|
p = buf;
|
|
for(p = strchr(p, '.'); p && p[1]; p = strchr(p+1, '.'))
|
|
m = (m>>8)|0xff000000;
|
|
|
|
/* force at least a class B */
|
|
m |= 0xffff0000;
|
|
}
|
|
cidr->ipaddr = a;
|
|
cidr->mask = m;
|
|
}
|
|
|
|
/*
|
|
* Substitute Subchar ('#') for '/'
|
|
*/
|
|
char*
|
|
subslash(char *os)
|
|
{
|
|
char *s;
|
|
|
|
for(s=os; *s; s++)
|
|
if(*s == '/')
|
|
*s = Subchar;
|
|
return os;
|
|
}
|
|
|
|
/*
|
|
* Insert an account pseudo-file in a directory
|
|
*/
|
|
static void
|
|
acctinsert(Node *np, char *cp)
|
|
{
|
|
int i;
|
|
char *tmp;
|
|
Address *ap;
|
|
|
|
static char *dangerous[] = { "*", "!", "*!", "!*", "*!*", 0 };
|
|
|
|
if(cp == 0 || *cp == 0)
|
|
return;
|
|
|
|
/* rule out dangerous patterns */
|
|
for (i = 0; dangerous[i]; i++)
|
|
if(strcmp(cp, dangerous[i])== 0)
|
|
return;
|
|
|
|
np = dirwalk("account", np);
|
|
if(np == 0)
|
|
return;
|
|
|
|
i = np->count++;
|
|
if(i >= np->allocated){
|
|
np->allocated = np->count;
|
|
np->addrs = realloc(np->addrs, np->allocated*sizeof(Address));
|
|
if(np->addrs == 0)
|
|
fatal("out of memory");
|
|
}
|
|
|
|
ap = &np->addrs[i]; /* new entry on end */
|
|
tmp = strdup(cp);
|
|
if(tmp == nil)
|
|
fatal("out of memory");
|
|
subslash(tmp);
|
|
ap->name = atom(tmp);
|
|
free(tmp);
|
|
}
|
|
|
|
/*
|
|
* Insert an IP address pseudo-file in a directory
|
|
*/
|
|
static void
|
|
ipinsert(Node *np, char *cp)
|
|
{
|
|
char *tmp;
|
|
int i;
|
|
Address *ap;
|
|
if(cp == 0 || *cp == 0)
|
|
return;
|
|
|
|
np = dirwalk("ip", np);
|
|
if(np == 0)
|
|
return;
|
|
|
|
i = np->count++;
|
|
if(i >= np->allocated){
|
|
np->allocated = np->count;
|
|
np->addrs = realloc(np->addrs, np->allocated*sizeof(Address));
|
|
if(np->addrs == 0)
|
|
fatal("out of memory");
|
|
}
|
|
|
|
ap = &np->addrs[i]; /* new entry on end */
|
|
tmp = strdup(cp);
|
|
if(tmp == nil)
|
|
fatal("out of memory");
|
|
subslash(tmp);
|
|
ap->name = atom(tmp);
|
|
free(tmp);
|
|
cidrparse(&ap->ip, cp);
|
|
}
|
|
|
|
int
|
|
ipcomp(void *a, void *b)
|
|
{
|
|
ulong aip, bip;
|
|
|
|
aip = ((Address*)a)->ip.ipaddr;
|
|
bip = ((Address*)b)->ip.ipaddr;
|
|
if(aip > bip)
|
|
return 1;
|
|
if(aip < bip)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Sort a directory of IP addresses
|
|
*/
|
|
static void
|
|
ipsort(void)
|
|
{
|
|
int base;
|
|
Node *dir, *np;
|
|
|
|
base = Qaddrfile;
|
|
for(dir = root->children; dir; dir = dir->sibs){
|
|
if (dir->d.type != Addrdir)
|
|
continue;
|
|
for(np = dir->children; np; np = np->sibs){
|
|
if(np->d.type == IPaddr && np->count && np->addrs)
|
|
qsort(np->addrs, np->count, sizeof(Address), ipcomp);
|
|
np->baseqid = base;
|
|
base += np->count;
|
|
}
|
|
}
|
|
}
|