#include <pwd.h>
#include <sys/types.h>
#include <sys/signal.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "xlog.h"
+
#ifdef SYSV40
#include <netinet/in.h>
#include <rpc/rpcent.h>
static void logit(int severity, struct sockaddr_in *addr,
u_long procnum, u_long prognum, char *text);
static void toggle_verboselog(int sig);
+static int check_files(void);
+
int verboselog = 0;
int allow_severity = LOG_INFO;
int deny_severity = LOG_WARNING;
#define log_client(addr, proc, prog) \
logit(allow_severity, addr, proc, prog, "")
+#define ALLOW 1
+#define DENY 0
+
+typedef struct _haccess_t {
+ TAILQ_ENTRY(_haccess_t) list;
+ int access;
+ struct in_addr addr;
+} haccess_t;
+
+#define HASH_TABLE_SIZE 1021
+typedef struct _hash_head {
+ TAILQ_HEAD(host_list, _haccess_t) h_head;
+} hash_head;
+hash_head haccess_tbl[HASH_TABLE_SIZE];
+static haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long, u_long);
+static void haccess_add(struct sockaddr_in *addr, u_long, u_long, int);
+
+inline unsigned int strtoint(char *str)
+{
+ unsigned int n = 0;
+ int len = strlen(str);
+ int i;
+
+ for (i=0; i < len; i++)
+ n+=((int)str[i])*i;
+
+ return n;
+}
+inline int hashint(unsigned int num)
+{
+ return num % HASH_TABLE_SIZE;
+}
+#define HASH(_addr, _proc, _prog) \
+ hashint((strtoint((_addr))+(_proc)+(_prog)))
+
+void haccess_add(struct sockaddr_in *addr, u_long proc,
+ u_long prog, int access)
+{
+ hash_head *head;
+ haccess_t *hptr;
+ int hash;
+
+ hptr = (haccess_t *)malloc(sizeof(haccess_t));
+ if (hptr == NULL)
+ return;
+
+ hash = HASH(inet_ntoa(addr->sin_addr), proc, prog);
+ head = &(haccess_tbl[hash]);
+
+ hptr->access = access;
+ hptr->addr.s_addr = addr->sin_addr.s_addr;
+
+ if (TAILQ_EMPTY(&head->h_head))
+ TAILQ_INSERT_HEAD(&head->h_head, hptr, list);
+ else
+ TAILQ_INSERT_TAIL(&head->h_head, hptr, list);
+}
+haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long proc, u_long prog)
+{
+ hash_head *head;
+ haccess_t *hptr;
+ int hash;
+
+ hash = HASH(inet_ntoa(addr->sin_addr), proc, prog);
+ head = &(haccess_tbl[hash]);
+
+ TAILQ_FOREACH(hptr, &head->h_head, list) {
+ if (hptr->addr.s_addr == addr->sin_addr.s_addr)
+ return hptr;
+ }
+ return NULL;
+}
+
int
good_client(daemon, addr)
char *daemon;
char **sp;
char *tmpname;
- /* Check the IP address first. */
- if (hosts_ctl(daemon, "", inet_ntoa(addr->sin_addr), ""))
- return 1;
-
- /* Check the hostname. */
- hp = gethostbyaddr ((const char *) &(addr->sin_addr),
- sizeof (addr->sin_addr), AF_INET);
+ /* First check the address. */
+ if (hosts_ctl(daemon, "", inet_ntoa(addr->sin_addr), "") == DENY)
+ return DENY;
+
+ /* Now do the hostname lookup */
+ hp = gethostbyaddr ((const char *) &(addr->sin_addr),
+ sizeof (addr->sin_addr), AF_INET);
+ if (!hp) {
+ xlog(L_WARNING,
+ "Warning: Client IP address '%s' not found in host lookup",
+ inet_ntoa(addr->sin_addr));
+ return DENY; /* never heard of it. misconfigured DNS? */
+ }
- if (!hp)
- return 0;
+ /* Make sure the hostent is authorative. */
+ tmpname = strdup(hp->h_name);
+ if (!tmpname) {
+ xlog(L_WARNING, "Warning: No memory for Host access check");
+ return DENY;
+ }
+ hp = gethostbyname(tmpname);
+ if (!hp) {
+ xlog(L_WARNING,
+ "Warning: Client hostname '%s' not found in host lookup", tmpname);
+ free(tmpname);
+ return DENY; /* never heard of it. misconfigured DNS? */
+ }
+ free(tmpname);
- /* must make sure the hostent is authorative. */
- tmpname = alloca (strlen (hp->h_name) + 1);
- strcpy (tmpname, hp->h_name);
- hp = gethostbyname(tmpname);
- if (hp) {
- /* now make sure the "addr->sin_addr" is on the list */
+ /* Now make sure the address is on the list */
for (sp = hp->h_addr_list ; *sp ; sp++) {
- if (memcmp(*sp, &(addr->sin_addr), hp->h_length)==0)
- break;
+ if (memcmp(*sp, &(addr->sin_addr), hp->h_length) == 0)
+ break;
}
if (!*sp)
- /* it was a FAKE. */
- return 0;
- }
- else
- /* never heard of it. misconfigured DNS? */
- return 0;
-
- /* Check the official name first. */
- if (hosts_ctl(daemon, hp->h_name, "", ""))
- return 1;
-
- /* Check aliases. */
- for (sp = hp->h_aliases; *sp ; sp++) {
- if (hosts_ctl(daemon, *sp, "", ""))
- return 1;
- }
-
- /* No match */
- return 0;
+ return DENY; /* it was a FAKE. */
+
+ /* Check the official name and address. */
+ if (hosts_ctl(daemon, hp->h_name, inet_ntoa(addr->sin_addr), "") == DENY)
+ return DENY;
+
+ /* Now check aliases. */
+ for (sp = hp->h_aliases; *sp ; sp++) {
+ if (hosts_ctl(daemon, *sp, inet_ntoa(addr->sin_addr), "") == DENY)
+ return DENY;
+ }
+
+ return ALLOW;
}
/* check_startup - additional startup code */
(void) signal(SIGINT, toggle_verboselog);
}
+/* check_files - check to see if either access files have changed */
+
+static int check_files()
+{
+ static time_t allow_mtime, deny_mtime;
+ struct stat astat, dstat;
+ int changed = 0;
+
+ if (stat("/etc/hosts.allow", &astat) < 0)
+ astat.st_mtime = 0;
+ if (stat("/etc/hosts.deny", &dstat) < 0)
+ dstat.st_mtime = 0;
+
+ if(!astat.st_mtime || !dstat.st_mtime)
+ return changed;
+
+ if (astat.st_mtime != allow_mtime)
+ changed = 1;
+ else if (dstat.st_mtime != deny_mtime)
+ changed = 1;
+
+ allow_mtime = astat.st_mtime;
+ deny_mtime = dstat.st_mtime;
+
+ return changed;
+}
+
/* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
int
u_long proc;
u_long prog;
{
- if (!(from_local(addr) || good_client(daemon, addr))) {
- log_bad_host(addr, proc, prog);
- return (FALSE);
- }
- if (verboselog)
- log_client(addr, proc, prog);
+ haccess_t *acc = NULL;
+ int changed = check_files();
+
+ acc = haccess_lookup(addr, proc, prog);
+ if (acc && changed == 0)
+ return (acc->access);
+
+ if (!(from_local(addr) || good_client(daemon, addr))) {
+ log_bad_host(addr, proc, prog);
+ if (acc)
+ acc->access = FALSE;
+ else
+ haccess_add(addr, proc, prog, FALSE);
+ return (FALSE);
+ }
+ if (verboselog)
+ log_client(addr, proc, prog);
+
+ if (acc)
+ acc->access = TRUE;
+ else
+ haccess_add(addr, proc, prog, TRUE);
return (TRUE);
}