/*
* rpc.dracd: Dynamic Relay Authorization Control daemon
*/
#include <sys/time.h>
#include <time.h>
#include <sys/types.h>
#include <errno.h>
#include <syslog.h>
#ifdef TI_RPC
#include <netconfig.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <db.h>
#include <signal.h>
#include <fcntl.h>
#ifdef FLOCK_LOCK
#include <sys/file.h>
#endif
#ifdef SYSINFO
#include <sys/systeminfo.h>
#endif
#ifdef GETHOST
#include <unistd.h>
#include <limits.h>
#endif
#ifndef DB_VERSION_MAJOR
#define DB_VERSION_MAJOR 1
#endif
#include "drac.h"
struct net_def {
struct net_def *nd_next;
struct in_addr nd_mask;
struct in_addr nd_addr;
};
int initdb = 0; /* initialize database option */
int terminate = 0; /* terminate daemon flag */
long explimit = 30 * 60; /* expiry limit (seconds) */
char *dbfile = DBFILE; /* database file name */
char *alfile = ALFILE; /* allow file name */
struct net_def *net_tbl; /* trusted network table */
#if DB_VERSION_MAJOR < 2
#elif DB_VERSION_MAJOR == 2
DB_ENV *dbenv; /* db environment structure */
#else
#endif
DB *dbp; /* DB structure */
int dbfd; /* db file descriptor */
#ifdef DEBUG
FILE *debugf;
#endif
/* On SIGTERM, must close db */
void catcher(n) int n; {
terminate = 1;
}
/* Parse command-line options */
main(argc, argv) int argc; char **argv; {
int c;
extern char *optarg;
extern int optind;
while ((c = getopt(argc, argv, "ie:")) != EOF) {
switch (c) {
case 'i':
initdb = 1;
break;
case 'e':
explimit = atoi(optarg) * 60;
break;
case '?':
fprintf(stderr, "Usage: %s [-i] [-e expire] [dbfile]\n",
argv[0]);
exit(2);
}
}
if ( optind < argc ) dbfile = argv[optind];
dracmain(); /* the main function from rpcgen */
exit(1);
}
/* Called once after fork */
drac_run() {
int sel;
time_t nexte, now;
fd_set readfs;
struct timeval to;
#if DB_VERSION_MAJOR < 2
#ifndef REQ_HASH
BTREEINFO bti;
#endif
#elif DB_VERSION_MAJOR == 2
DB_ENV adbenv;
DB_INFO dbinfo;
#else
#endif
#ifdef DEBUG
debugf = fopen("/var/tmp/drac.debug", "a+");
#endif
iniclist();
/* Schedule the next expire */
nexte = time((time_t *)NULL) + explimit / 10;
/* Catch SIGTERM */
signal(SIGTERM, catcher);
/* Open the database and save the file descriptor */
#if DB_VERSION_MAJOR < 2
#ifdef REQ_HASH
errno = 0;
dbp = dbopen(dbfile,
(initdb) ? O_CREAT|O_TRUNC|O_RDWR : O_CREAT|O_RDWR,
0644, DB_HASH, (void *)NULL);
#else
memset(&bti, 0, sizeof(bti));
bti.psize = 512;
errno = 0;
dbp = dbopen(dbfile,
(initdb) ? O_CREAT|O_TRUNC|O_RDWR : O_CREAT|O_RDWR,
0644, DB_BTREE, &bti);
#endif
#elif DB_VERSION_MAJOR == 2
memset(&adbenv, 0, sizeof(adbenv));
dbenv = &adbenv;
memset(&dbinfo, 0, sizeof(dbinfo));
dbinfo.db_pagesize = 512;
#ifdef REQ_HASH
errno = db_open(dbfile, DB_HASH,
(initdb) ? DB_TRUNCATE|DB_CREATE : DB_CREATE,
0644, dbenv, &dbinfo, &dbp);
#else
errno = db_open(dbfile, DB_BTREE,
(initdb) ? DB_TRUNCATE|DB_CREATE : DB_CREATE,
0644, dbenv, &dbinfo, &dbp);
#endif
#elif DB_VERSION_MAJOR >= 4 && DB_VERSION_MINOR >= 1
errno = db_create(&dbp, NULL, 0);
if ( errno != 0 ) {
syslog(LOG_ERR, "drac_run db_create failed: %m");
exit(3);
}
dbp->set_pagesize(dbp, 512);
#ifdef REQ_HASH
errno = dbp->open(dbp, NULL, dbfile, NULL, DB_HASH,
(initdb) ? DB_TRUNCATE|DB_CREATE : DB_CREATE,
0644);
#else
errno = dbp->open(dbp, NULL, dbfile, NULL, DB_BTREE,
(initdb) ? DB_TRUNCATE|DB_CREATE : DB_CREATE,
0644);
#endif
#else
errno = db_create(&dbp, NULL, 0);
if ( errno != 0 ) {
syslog(LOG_ERR, "drac_run db_create failed: %m");
exit(3);
}
dbp->set_pagesize(dbp, 512);
#ifdef REQ_HASH
errno = dbp->open(dbp, dbfile, NULL, DB_HASH,
(initdb) ? DB_TRUNCATE|DB_CREATE : DB_CREATE,
0644);
#else
errno = dbp->open(dbp, dbfile, NULL, DB_BTREE,
(initdb) ? DB_TRUNCATE|DB_CREATE : DB_CREATE,
0644);
#endif
#endif
if ( errno != 0 ) {
syslog(LOG_ERR, "drac_run open failed: %m");
exit(3);
}
#if DB_VERSION_MAJOR < 2
errno = 0;
dbfd = dbp->fd(dbp);
#else
errno = dbp->fd(dbp, &dbfd);
#endif
if ( errno != 0 ) {
syslog(LOG_ERR, "drac_run fd failed: %m");
exit(3);
}
/* Repeat forever */
while (1) {
#ifdef DEBUG
fprintf(debugf, "Select bits: %x\n", svc_fdset.fds_bits[0]);
fflush(debugf);
#endif
/* Wait for action */
memcpy(&readfs, &svc_fdset, sizeof(fd_set));
to.tv_sec = explimit / 10;
to.tv_usec = 0L;
sel = select(FD_SETSIZE, &readfs, (fd_set *)NULL, (fd_set *)NULL, &to);
now = time((time_t *)NULL);
switch ( sel ) {
case 0: /* Timeout */
nexte = now + explimit / 10;
expire();
break;
case (-1): /* Error */
if ( errno != EINTR ) {
syslog(LOG_ERR, "drac_run select failed: %m");
}
else if ( terminate ) {
#if DB_VERSION_MAJOR < 2
(void)dbp->close(dbp);
#else
(void)dbp->close(dbp, 0);
#endif
exit(0);
}
break;
default: /* Ready */
if ( now >= nexte ) {
nexte = now + explimit / 10;
expire();
}
svc_getreqset(&readfs); /* Service the request */
}
}
}
/* Add an entry to the database */
addstat *
#ifdef DASH_C
dracproc_add_1_svc(argp, rqstp)
#else
dracproc_add_1(argp, rqstp)
#endif
drac_add_parm *argp;
struct svc_req *rqstp; {
static addstat result;
#ifdef TI_RPC
struct netbuf *nb;
struct netconfig *nc;
char *cad, *pt;
#endif
#ifdef SOCK_RPC
struct sockaddr_in *si;
#endif
struct in_addr client_ip, requ_ip;
DBT key, data;
char akey[32], alimit[32];
struct net_def *nd;
result = ADD_SUCCESS;
/* Get the IP address of the client */
#ifdef TI_RPC
if ( (nc = getnetconfigent(rqstp->rq_xprt->xp_netid)) == NULL
|| (nb = svc_getrpccaller(rqstp->rq_xprt)) == NULL
|| (cad = taddr2uaddr(nc, nb)) == NULL ) {
if (nc) freenetconfigent(nc);
result = ADD_SYSERR;
return (&result);
}
if ( (pt = strrchr(cad, '.')) != NULL ) *pt = '\0';
if ( (pt = strrchr(cad, '.')) != NULL ) *pt = '\0';
client_ip.s_addr = inet_addr(cad);
freenetconfigent(nc);
free(cad);
#endif
#ifdef SOCK_RPC
if ( (si = svc_getcaller(rqstp->rq_xprt)) == NULL ) {
result = ADD_SYSERR;
return (&result);
}
client_ip.s_addr = si->sin_addr.s_addr;
#endif
#ifdef DEBUG
fprintf(debugf, "Client Address: %s\n", inet_ntoa(client_ip));
fflush(debugf);
#endif
/* Check agains the table of trusted clients */
for ( nd = net_tbl; nd != NULL; nd = nd->nd_next ) {
if ( (client_ip.s_addr & nd->nd_mask.s_addr)
== nd->nd_addr.s_addr ) break;
}
if ( nd == NULL ) {
result = ADD_PERM;
return (&result);
}
/* Set up for the add */
requ_ip.s_addr = htonl(argp->ip_addr); /* to network byte order */
#ifdef DEBUG
fprintf(debugf, "Requested IP Address: %s\n",
inet_ntoa(requ_ip));
fflush(debugf);
#endif
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
strcpy(akey, inet_ntoa(requ_ip));
#ifdef CIDR_KEY
strcat(akey, "/32");
#endif
key.data = akey;
#ifdef TERM_KD
key.size = strlen(akey) + 1;
#else
key.size = strlen(akey);
#endif
sprintf(alimit, "%lu", time((time_t *)NULL) + explimit);
data.data = alimit;
#ifdef TERM_KD
data.size = strlen(alimit) + 1;
#else
data.size = strlen(alimit);
#endif
/* Do the add and sync, with locking */
if ( lockdb() == (-1) ) {
syslog(LOG_ERR, "dracproc_add_1 lockdb failed: %m");
}
#if DB_VERSION_MAJOR < 2
errno = 0;
dbp->put(dbp, &key, &data, 0);
#else
errno = dbp->put(dbp, NULL, &key, &data, 0);
#endif
if ( errno != 0 ) {
syslog(LOG_ERR, "dracproc_add_1 put failed: %m");
result = ADD_SYSERR;
}
#if DB_VERSION_MAJOR < 2
errno = 0;
dbp->sync(dbp, 0);
#else
errno = dbp->sync(dbp, 0);
#endif
if ( errno != 0 ) {
syslog(LOG_ERR, "dracproc_add_1 sync failed: %m");
}
(void)unlockdb();
/* Send result code back to client */
return (&result);
}
/* Expire old entries from the database */
expire() {
#if DB_VERSION_MAJOR < 2
int seqerr, flags;
#else
DBC *dbcp;
#endif
DBT key, data;
time_t old;
char alimit[32];
/* Oldest possible entry is now */
old = time((time_t *)NULL);
/* Lock the database */
if ( lockdb() == (-1) ) {
syslog(LOG_ERR, "expire lockdb failed: %m");
}
/* Obtain a cursor */
#if DB_VERSION_MAJOR < 2
flags = R_FIRST;
#else
# if DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >=6
errno = dbp->cursor(dbp, NULL, &dbcp, 0);
#else
errno = dbp->cursor(dbp, NULL, &dbcp);
#endif
if ( errno != 0 ) {
syslog(LOG_ERR, "expire cursor failed: %m");
return 0;
}
#endif
/* Scan the database, deleting old entries */
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
#if DB_VERSION_MAJOR < 2
while ((seqerr = dbp->seq(dbp, &key, &data, flags)) == 0) {
flags = R_NEXT;
#else
while ((errno = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0) {
#endif
memcpy(alimit, data.data, data.size);
#ifndef TERM_KD
alimit[data.size] = '\0';
#endif
if ( strtoul(alimit, (char **)NULL, 10) < old ) {
#if DB_VERSION_MAJOR < 2
errno = 0;
dbp->del(dbp, &key, R_CURSOR);
#else
errno = dbcp->c_del(dbcp, 0);
#endif
if ( errno != 0 ) {
syslog(LOG_ERR, "expire c_del failed: %m");
}
}
}
#if DB_VERSION_MAJOR < 2
if (seqerr != 1) {
#else
if (errno != DB_NOTFOUND) {
#endif
syslog(LOG_ERR, "expire c_get failed: %m");
}
/* Discard the cursor */
#if DB_VERSION_MAJOR < 2
errno = 0;
dbp->sync(dbp, 0);
#else
(void)dbcp->c_close(dbcp);
errno = dbp->sync(dbp, 0);
#endif
if ( errno != 0 ) {
syslog(LOG_ERR, "expire sync failed: %m");
}
/* Unlock the database */
(void) unlockdb();
}
/* Lock the database */
lockdb() {
#ifdef FCNTL_LOCK
struct flock lfd;
memset(&lfd, 0, sizeof(lfd));
lfd.l_type = F_WRLCK;
return fcntl(dbfd, F_SETLKW, &lfd);
#endif
#ifdef FLOCK_LOCK
return flock(dbfd, LOCK_EX);
#endif
}
/* Unlock the database */
unlockdb() {
#ifdef FCNTL_LOCK
struct flock lfd;
memset(&lfd, 0, sizeof(lfd));
lfd.l_type = F_UNLCK;
return fcntl(dbfd, F_SETLK, &lfd);
#endif
#ifdef FLOCK_LOCK
return flock(dbfd, LOCK_UN);
#endif
}
/* Initialize the trusted client table */
/* All in network byte order */
iniclist() {
FILE *alfp;
char buf[128], mask[32], addr[32], hname[128];
struct net_def *nd;
struct hostent *he;
unsigned long **p;
/* Check if the allow file exists */
if ( (alfp = fopen(alfile, "r")) != NULL ) {
/* Read lines from the file */
while ( fgets(buf, sizeof(buf), alfp) != NULL ) {
if ( buf[0] == '#' ) continue;
if ( sscanf(buf, "%[0-9.] %[0-9.]", mask, addr) == 2 ) {
/* Create table entry from the line */
nd = (struct net_def *)malloc(sizeof(struct net_def));
if ( nd != NULL ) {
nd->nd_next = net_tbl;
nd->nd_mask.s_addr = inet_addr(mask);
nd->nd_addr.s_addr = inet_addr(addr);
net_tbl = nd;
}
else {
syslog(LOG_ERR, "iniclist malloc failed");
}
}
}
}
else {
/* Default to loopback address */
nd = (struct net_def *)malloc(sizeof(struct net_def));
if ( nd != NULL ) {
nd->nd_next = net_tbl;
nd->nd_mask.s_addr = htonl(0xffffffff);
nd->nd_addr.s_addr = htonl(0x7f000001);
net_tbl = nd;
}
/* Add local IP addresses */
#ifdef SYSINFO
(void)sysinfo(SI_HOSTNAME, hname, sizeof(hname));
#endif
#ifdef GETHOST
(void)gethostname(hname, sizeof(hname));
#endif
if ( (he = gethostbyname(hname)) != NULL ) {
for (p = (unsigned long **)he->h_addr_list; *p != NULL; p++) {
nd = (struct net_def *)malloc(sizeof(struct net_def));
if ( nd != NULL ) {
nd->nd_next = net_tbl;
nd->nd_mask.s_addr = htonl(0xffffffff);
nd->nd_addr.s_addr = **p;
net_tbl = nd;
}
else {
syslog(LOG_ERR, "iniclist malloc failed");
}
}
}
}
}
/**/
syntax highlighted by Code2HTML, v. 0.9.1