/* * rpc.dracd: Dynamic Relay Authorization Control daemon */ #include #include #include #include #include #ifdef TI_RPC #include #endif #include #include #include #include #include #include #include #include #include #include #ifdef FLOCK_LOCK #include #endif #ifdef SYSINFO #include #endif #ifdef GETHOST #include #include #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"); } } } } } /**/