#include "config.h"
#ifdef ACCESS_RESTRICTED /* Comment out whole file */
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/socket.h>
#ifndef accel
#include <time.h>
#endif
#include <netinet/in.h>
#include "lint.h"
/* check a given internet address against patterns given in an ACCESS.ALLOW
* file.
* Written by Markus Wild for Loch Ness in 1991.
* Spread by gec with permission of Markus Wild.
* Source is in the public domain. No charges allowed.
*
* notice the time stamp when we last scanned the file, and rescan if it
* changed since that (this enables us to edit the file without having to
* reboot lpmud to read it)
* NOTICE: when changing the access file, the old tables ARE DISCARDED. This
* means, that users logged in will not count for the per-class
* maximum. This will normalize, as soon as users log out, as the
* "currently" counter will not go below 0.
*/
extern char *inet_ntoa(), *strtok();
/* simply remove this line if you wish access to your system to be logged */
#undef ACCESS_LOG
/* maximal string length to be output */
#define MAX_MESSAGE 255
struct access_class {
int class_num; /* the class number */
int currently; /* currently <= this number of logged in users */
int maximum; /* 0: disallowed, -1: no maximum */
char message[MAX_MESSAGE]; /* message to be printed in case a login can't be permitted */
};
struct access_address {
int addr[4]; /* [0..255]: number, -1: all */
int hstart, hend; /* start/end hour */
struct access_class *ac;
};
static struct access_address *addr_tab = 0;
static int addr_tab_index, addr_tab_size;
static struct access_class *class_tab = 0;
static int class_tab_index, class_tab_size;
static time_t last_read = 0;
static void log_access ();
/* check the file, and if it was changed, (re)read it into memory */
static void
check_read_file (name)
char *name;
{
struct stat stb;
FILE *in;
if (!(stat (name, &stb)))
{
if (stb.st_mtime > last_read)
{
/* throw away the old information */
if (addr_tab) FREE((char *)addr_tab), addr_tab = 0;
if (class_tab) FREE((char *)class_tab), class_tab = 0;
addr_tab_size = 10;
addr_tab_index = 0;
addr_tab = (struct access_address *)
DMALLOC(addr_tab_size * sizeof (struct access_address),
122, "check_read_file: addr_tab");
class_tab_size = 10;
class_tab_index = 0;
class_tab = (struct access_class *)
DMALLOC(class_tab_size * sizeof (struct access_class),
1, "check_read_file: class_tab");
if (in = fopen (name, "r"))
{
while (!feof (in))
{
char buffer [2*MAX_MESSAGE]; /* heuristic ;-)) */
char addr1[4], addr2[4], addr3[4], addr4[4];
struct access_address aa;
struct access_class ac;
char *cp;
int i;
if (! fgets (buffer, 2*MAX_MESSAGE, in)) break;
if (buffer[0] == '#') continue; /* a comment, skip */
/* if there is no ':' in there, this is probably an empty line */
if (! strchr (buffer, ':')) continue;
/* more or less no error-checking ;-)) */
strncpy (addr1, strtok (buffer, "."), 3);
strncpy (addr2, strtok (0, "."), 3);
strncpy (addr3, strtok (0, "."), 3);
strncpy (addr4, strtok (0, ":"), 3);
ac.class_num = atoi (strtok (0, ":"));
ac.currently = 0;
ac.maximum = atoi (((cp = strtok (0, ":")), (cp && *cp) ? cp : "0"));
aa.hstart = atoi (((cp = strtok (0, ":")), (cp && *cp) ? cp : "0"));
aa.hend = atoi (((cp = strtok (0, ":")), (cp && *cp) ? cp : "0"));
strncpy (ac.message,
(cp = strtok (0, "\n")) ? cp : "", MAX_MESSAGE-1);
/* check whether this class is already defined */
for (i = 0; i < class_tab_index; i++)
if (class_tab[i].class_num == ac.class_num)
{
/* in this case just set a pointer to the defined class */
aa.ac = &class_tab[i];
break;
}
/* if not, define it */
if (i == class_tab_index)
{
class_tab[class_tab_index] = ac;
if (++class_tab_index == class_tab_size)
{
class_tab_size <<= 1;
class_tab = (struct access_class *)
DREALLOC((char *)class_tab,
class_tab_size * sizeof (struct access_class),
2, "check_read_file: class_tab: realloc");
}
aa.ac = &class_tab[i];
}
/* now set up the address, * maps into -1, anything else is vanilla */
aa.addr[0] = strcmp(addr1, "*") ? atoi (addr1) : -1;
aa.addr[1] = strcmp(addr2, "*") ? atoi (addr2) : -1;
aa.addr[2] = strcmp(addr3, "*") ? atoi (addr3) : -1;
aa.addr[3] = strcmp(addr4, "*") ? atoi (addr4) : -1;
/* and add it to our address table */
addr_tab[addr_tab_index] = aa;
if (++addr_tab_index == addr_tab_size)
{
addr_tab_size <<= 1;
addr_tab = (struct access_address *)
DREALLOC((char *)addr_tab,
addr_tab_size * sizeof (struct access_address),
3, "check_read_file: addr_tab: realloc");
}
} /* over total input */
fclose (in);
last_read = stb.st_mtime;
return;
} /* if open succeeded */
}
}
else
fprintf(stderr,"Couldn't open %s for reading.\n",name);
}
/* the main function, validate an address (peer of given socket).
* return 0, if access is not permitted, else return a pointer to the
* corresponding class. Pass that pointer on logout to "release_host_access".
*/
struct access_class *
allow_host_access (sockfd, outfd)
int sockfd, outfd;
{
struct sockaddr_in apa;
int addr[4];
int len;
char *ipname, *fname;
struct access_address *ap;
int i;
#define STRING(str) str,strlen(str)
fname = (char *)DMALLOC(strlen(ACCESS_FILE) + 1, 4,
"allow_host_access: fname");
sprintf (fname,"%s",ACCESS_FILE);
if (fname[0] == '/')
strcpy (fname,fname+1);
check_read_file (fname);
FREE(fname);
len = sizeof (apa);
if (getpeername (sockfd, (struct sockaddr *)&apa, &len) == -1)
{
perror ("getpeername");
write (outfd, STRING ("Sorry, internal MudOS error.\n"));
return 0;
}
#if !defined(NeXT) && !defined(__SEQUENT__)
ipname = inet_ntoa(apa.sin_addr);
#else
ipname = inet_ntoa(ntohl(apa.sin_addr));
#endif
sscanf (ipname, "%d.%d.%d.%d", addr, addr + 1, addr + 2, addr + 3);
for (i = 0, ap = addr_tab; i < addr_tab_index; i++, ap++)
{
int pos;
/* check for address. match if either equal or wildcard */
for (pos = 0; pos < 4; pos++)
if (ap->addr[pos] != addr[pos] && ap->addr[pos] != -1) break;
if (pos == 4) /* a match */
{
/* if hstart and hend are not == 0, check whether ap is in the
* interval */
if (ap->hstart || ap->hend)
{
time_t now = time(0);
struct tm *tm = localtime(&now);
if (ap->hstart < ap->hend)
{
if (tm->tm_hour < ap->hstart || tm->tm_hour > ap->hend)
continue;
}
else
{
if (tm->tm_hour > ap->hend && tm->tm_hour < ap->hstart)
continue;
}
}
/* no maxmium? */
if (ap->ac->maximum == -1)
{ /*gc*/
log_access (ipname, 1);
return ap->ac;
}
/* else there is a maximum, in the worst case 0 */
if (ap->ac->currently >= ap->ac->maximum)
{
write (outfd, ap->ac->message, strlen (ap->ac->message));
write (outfd, "\n", 1);
shutdown (outfd, 2); close (outfd);
log_access (ipname, 0);
return 0;
}
/* bump up the counter */
ap->ac->currently ++;
log_access (ipname, 1);
return ap->ac;
}
}
/* default is: don't allow access */
write (outfd, STRING("Sorry, you're not allowed to use this LPmud.\n"));
shutdown (outfd, 2); close (outfd);
log_access (ipname, 0);
return 0;
}
/* decrement the currently counter once. This is called, when a user loggs out. */
void
release_host_access (class)
struct access_class *class;
{
if (class->maximum != -1 && class->currently > 0)
-- class->currently;
}
static void
log_access (addr, ok)
char *addr;
int ok;
{
#ifdef ACCESS_LOG
char *fname;
FILE *log;
fname = DMALLOC(strlen(ACCESS_LOG)+1, 5, "log_access: fname");
sprintf (fname,"%s",ACCESS_LOG);
if (fname[0] == '/')
strcpy (fname,fname+1);
log = fopen (fname, "a");
if (log)
{
fprintf (log, "%s: %s\n", addr, ok ? "granted" : "denied");
fclose (log);
}
#endif
}
#endif /* ACCESS_RESTRICTED */