/*
* LIB/HOSTAUTH.C
*
* (c)Copyright 1997-1998, Matthew Dillon, All Rights Reserved. Refer to
* the COPYRIGHT file in the base directory of this distribution
* for specific rights granted.
*
*/
#include "defs.h"
Prototype int LoadHostAccess(time_t t, int force, int rebuildtime);
Prototype char *Authenticate(int fd, struct sockaddr *res, char *ipst, char *label);
Prototype void DumpHostCache(const char *fname);
#define TESTTYPE_NONE 0
#define TESTTYPE_ANY 1
#define TESTTYPE_IP 2
#define TESTTYPE_FQDN 4
typedef struct HostAccess {
char entry[256];
int testtype;
char addrst[NI_MAXHOST];
char label[256];
} HostAccess;
time_t HATDSec = 0;
time_t HAMTime = 0;
time_t HACMTime = 0;
time_t DNMTime = 0;
int convHost(char *host, HostAccess *HA, FILE *fo);
void loadDiabloHosts(FILE *fo, int t, int *count);
void loadDnewsFeeds(FILE *fo, int t, int *count);
/*
* LoadAccess() - [re]load diablo.hosts file
*
* This load is to create a cache file of the diablo.hosts file
* that contains the IP addresses of all the hosts found. This
* makes authentication a lot quicker and easier.
*
* If a time of '0' is passed, we don't fork and don't use syslog
*
*/
int
LoadHostAccess(time_t t, int force, int rebuildtime)
{
int timeChanged = 0;
struct stat st = { 0 };
pid_t pid;
/*
* check for diablo.hosts file modified once a minute
*/
if (!force && (t / 10 <= HATDSec))
return(0);
timeChanged = 1;
HATDSec = t / 10;
/* Don't do this if diablo.hosts hasn't been modified or we are
* within the rebuild time limit
*/
if (!force && (t - HACMTime) < rebuildtime &&
stat(PatDbExpand(DHostsCachePat), &st) == 0) {
int ok = 1;
if (stat(PatLibExpand(DiabloHostsPat), &st) == 0) {
if (st.st_mtime != HAMTime)
ok = 0;
} else {
if (HAMTime != 0) /* File deleted */
ok = 0;
}
if (stat(PatLibExpand(DNewsfeedsPat), &st) == 0) {
if (st.st_mtime != DNMTime)
ok = 0;
} else {
if (DNMTime != 0) /* File deleted */
ok = 0;
}
if (ok)
return(0);
}
if (stat(PatLibExpand(DiabloHostsPat), &st) == 0)
HAMTime = st.st_mtime;
else
HAMTime = 0;
if (stat(PatLibExpand(DNewsfeedsPat), &st) == 0)
DNMTime = st.st_mtime;
else
DNMTime = 0;
HACMTime = t;
/*
* Do the DNS lookups in the background
* The main loop does a wait to cleanup the children
*/
if (t != 0)
pid = fork();
else
pid = 0;
if (pid == 0) {
FILE *fo;
int lockfd;
char buf[PATH_MAX];
int count = 0;
if ((lockfd = open(PatDbExpand(DHostsLockPat), O_RDWR|O_CREAT, 0644)) < 0) {
if (t != 0)
logit(LOG_INFO, "%s : Unable to create lockfile (%s)",
PatDbExpand(DHostsLockPat), strerror(errno));
else
printf("%s : Unable to create lockfile (%s)\n",
PatDbExpand(DHostsLockPat), strerror(errno));
exit(0);
}
if (xflock(lockfd, XLOCK_EX|XLOCK_NB) == -1) {
if (t != 0)
logit(LOG_INFO, "%s : Rebuild already in progress",
PatDbExpand(DHostsCachePat));
else
printf("%s : Rebuild already in progress\n",
PatDbExpand(DHostsCachePat));
close(lockfd);
exit(0);
}
sprintf(buf, "%d", (int)getpid());
write(lockfd, buf, strlen(buf));
snprintf(buf, sizeof(buf), "%s.new", PatDbExpand(DHostsCachePat));
fo = fopen(buf, "w");
if (fo == NULL) {
if (t != 0)
logit(LOG_CRIT, "%s: %s", buf, strerror(errno));
else
fprintf(stderr, "%s: %s\n", buf, strerror(errno));
xflock(lockfd, XLOCK_UN);
exit(1);
}
if (t != 0)
logit(LOG_INFO, "Rebuilding host cache");
else
printf("Rebuilding host cache\n");
loadDiabloHosts(fo, t, &count);
loadDnewsFeeds(fo, t, &count);
if (count == 0)
logit(LOG_INFO, "No incoming hosts found");
else if (DebugOpt)
printf("Found %d incoming hosts\n", count);
fclose(fo);
/*
* Now rename the newly created cache to its correct name
*/
snprintf(buf, sizeof(buf), "%s.new", PatDbExpand(DHostsCachePat));
if ((stat(buf, &st) == 0 && st.st_size > 0) ||
stat(PatDbExpand(DHostsCachePat), &st) != 0) {
rename(buf, PatDbExpand(DHostsCachePat));
} else {
unlink(buf);
}
xflock(lockfd, XLOCK_UN);
close(lockfd);
if (t != 0)
exit(0);
}
return(pid);
}
int
convHost(char *host, HostAccess *HA, FILE *fo)
{
int found = 0;
/*
* Handle the forcing of entry type
*/
HA->testtype = TESTTYPE_NONE;
if (strncmp(host, "IP:", 3) == 0) {
HA->testtype |= TESTTYPE_IP;
host += 3;
} else if (strncmp(host, "FQDN:", 5) == 0) {
HA->testtype |= TESTTYPE_FQDN;
host += 5;
}
/*
* If this entry is an IP, then only match against an IP
* otherwise only match against the hostname
*/
if (HA->testtype == TESTTYPE_NONE) {
if (IsIpAddr(host))
HA->testtype |= TESTTYPE_IP;
else
HA->testtype |= TESTTYPE_FQDN;
}
strcpy(HA->entry, host);
HA->addrst[0] = 0;
/*
* Don't lookup IP for wildcards/CIDR
*/
if (strchr(host, '*') != NULL || strchr(host, '?') != NULL ||
strchr(host, '/') != NULL) {
fwrite(HA, sizeof(*HA), 1, fo);
return(1);
}
#ifdef INET6
{
struct addrinfo *ai0;
if (getaddrinfo(host, NULL, NULL, &ai0) == 0) {
struct addrinfo *ai;
for (ai = ai0; ai; ai = ai->ai_next) {
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, HA->addrst,
sizeof(HA->addrst), NULL,
0, NI_NUMERICHOST) == 0)
HA->testtype |= TESTTYPE_IP;
fwrite(HA, sizeof(*HA), 1, fo);
found++;
}
freeaddrinfo(ai0);
}
}
#else
{
struct hostent *he;
int i;
if ((he = gethostbyname(host)) != NULL)
for (i = 0; he->h_addr_list && he->h_addr_list[i] != NULL; ++i) {
struct in_addr *haddr = (void *)he->h_addr_list[i];
strcpy(HA->addrst, inet_ntoa(*haddr));
HA->testtype |= TESTTYPE_IP;
fwrite(HA, sizeof(*HA), 1, fo);
found++;
}
}
#endif
return(found);
}
void
loadDiabloHosts(FILE *fo, int t, int *count)
{
FILE *fi;
char buf[PATH_MAX];
HostAccess HA;
char *s;
char *l;
char *p;
if (DebugOpt)
printf("Search for hosts in %s\n", PatLibExpand(DiabloHostsPat));
fi = fopen(PatLibExpand(DiabloHostsPat), "r");
if (fi == NULL)
return;
bzero(&HA, sizeof(HA));
while (fgets(buf, sizeof(buf), fi) != NULL) {
p = buf;
if (*p == '\n' || *p == '#')
continue;
HA.testtype = TESTTYPE_NONE;
if (strncmp(p, "IP:", 3) == 0) {
HA.testtype |= TESTTYPE_IP;
p += 3;
} else if (strncmp(p, "FQDN:", 5) == 0) {
HA.testtype |= TESTTYPE_FQDN;
p += 5;
}
s = strtok(p, ": \t\r\n"); /* hostname */
if (s == NULL || !*s)
continue;
l = strtok(NULL, ": \t\r\n"); /* label */
if (l == NULL || !*l)
continue;
if (strlen(s) > 255 || strlen(l) > 255)
continue;
strcpy(HA.label, l);
if (convHost(s, &HA, fo))
(*count)++;
bzero(&HA, sizeof(HA));
}
fclose(fi);
}
void
loadDnewsFeeds(FILE *fo, int t, int *count)
{
FILE *fi;
char buf[512];
HostAccess HA;
char label[255];
if (DebugOpt)
printf("Search for hosts in %s\n", PatLibExpand(DNewsfeedsPat));
fi = fopen(PatLibExpand(DNewsfeedsPat), "r");
if (fi == NULL) {
if (t != 0)
logit(LOG_CRIT, "%s: %s", PatLibExpand(DNewsfeedsPat),
strerror(errno));
else
fprintf(stderr, "%s: %s\n", PatLibExpand(DNewsfeedsPat),
strerror(errno));
exit(1);
}
bzero(&HA, sizeof(HA));
while (fgets(buf, sizeof(buf), fi) != NULL) {
char *cmd = strtok(buf, " \t\n");
char *p = (cmd != NULL) ? strtok(NULL, " \t\n") : NULL;
if (cmd == NULL || p == NULL || *cmd == '#')
continue;
if (strcmp(cmd, "end") == 0) {
label[0] = 0;
}
if (strcmp(cmd, "label") == 0) {
strcpy(label, p);
continue;
}
if (!label[0])
continue;
if (strcmp(cmd, "inhost") == 0 || strcmp(cmd, "host") == 0) {
strcpy(HA.label, label);
if (convHost(p, &HA, fo))
(*count)++;
bzero(&HA, sizeof(HA));
continue;
}
}
fclose(fi);
}
/*
* AUTHENTICATION() - authenticate a new connection
*
* We follow the following procedure:
*
* - if access entry is IP, only match with IP
* - if access entry is FQDN, only match with FQDN
* - if IP has no hostname, only match IP
* - if IP Fwd/Rev mismatch, only match IP
* - if IP has valid hname, match with IP or hname
*
* Returns the hostname or IP if hostname not found/invalid
*/
char *
Authenticate(int fd, struct sockaddr *res, char *ipst, char *label)
{
char *hname = NULL; /* authenticated hostname or NULL */
char *pname = NULL; /* IP address (as string) */
int isValid = 0;
/*
* check reverse lookup verses forward lookup, set either hname or uname
* or neither, but never both.
*/
#ifdef INET6
{
char *st;
char hostst[NI_MAXHOST];
int error;
st = NetAddrToSt(0, res, 1, 0, 0);
if (st != NULL) {
pname = zallocStr(&SysMemPool, st);
} else {
logit(LOG_ERR, "getnameinfo: Unable to get numeric address");
return(NULL);
}
st[0]= 0;
if ((error = getnameinfo(res, SA_LEN(res), hostst, sizeof(hostst),
NULL, 0, NI_NAMEREQD)) == 0) {
if (TestForwardLookup(hostst, (const char *)ipst, res) >= 0)
hname = zallocStr(&SysMemPool, hostst);
} else {
fprintf(stderr, "getnameinfo: %d", error);
}
}
#else
struct sockaddr_in *sin = (struct sockaddr_in *)res;
/*
* set pname
*/
pname = zallocStr(&SysMemPool, inet_ntoa(sin->sin_addr));
{
struct hostent *he;
he = gethostbyaddr(
(char *)&sin->sin_addr,
sizeof(sin->sin_addr),
AF_INET
);
if (he != NULL) {
hname = zallocStr(&SysMemPool, he->h_name);
if (TestForwardLookup(hname, (const char *)ipst, res) < 0) {
zfreeStr(&SysMemPool, &hname);
hname = NULL;
}
}
}
#endif /* INET6 */
/*
* Check IP against cache file of diablo.hosts.
*/
{
FILE *fi = fopen(PatDbExpand(DHostsCachePat), "r");
if (fi) {
HostAccess HA;
while (fread(&HA, sizeof(HA), 1, fi) == 1) {
if (HA.addrst[0] && (HA.testtype & TESTTYPE_IP) && strcmp(ipst, HA.addrst) == 0) {
isValid = 1;
break;
} else if ((strchr(HA.entry, '/') != NULL) && (HA.testtype & TESTTYPE_IP) && CidrMatch(HA.entry, pname)) {
isValid = 2;
break;
} else if (pname && (HA.testtype & TESTTYPE_IP) && WildCaseCmp(HA.entry, pname) == 0) {
isValid = 3;
break;
} else if (hname && (HA.testtype & TESTTYPE_FQDN) && strcasecmp(HA.entry, hname) == 0) {
isValid = 4;
break;
} else if (hname && (HA.testtype & TESTTYPE_FQDN) && WildCaseCmp(HA.entry, hname) == 0) {
isValid = 5;
break;
}
}
if (isValid) {
char *method = "UNKNOWN";
if (isValid == 1)
method = "Exact IP match";
if (isValid == 2)
method = "CIDR match";
if (isValid == 3)
method = "Wildcmp IP match";
if (isValid == 4)
method = "Exact Hostname match";
if (isValid == 5)
method = "Wildcmp hostname match";
logit(LOG_DEBUG, "Matched %s (%s) to %s (label:%s) using %s",
hname ? hname : "NOTFOUND", pname,
HA.entry, HA.label,
method);
if (HA.label[0])
strcpy(label, HA.label);
else
label[0] = 0;
}
fclose(fi);
} else {
logit(LOG_ERR, "%s file not found", PatDbExpand(DHostsCachePat));
}
}
/*
* If we have a valid connection, but we were
* unable to resolve the FQDN, we make the FQDN
* the IP address.
*/
if (isValid) {
if (hname == NULL) {
hname = pname;
pname = NULL;
}
} else {
if (hname)
zfreeStr(&SysMemPool, &hname);
}
zfreeStr(&SysMemPool, &pname);
return(hname);
}
void
DumpHostCache(const char *fname)
{
FILE *fi = fopen(fname, "r");
if (fi) {
HostAccess HA;
char tt[20];
while (fread(&HA, sizeof(HA), 1, fi) == 1) {
strcpy(tt, "");
if (HA.testtype & TESTTYPE_NONE)
strcat(tt, "NONE:");
if (HA.testtype & TESTTYPE_IP)
strcat(tt, "IP:");
if (HA.testtype & TESTTYPE_FQDN)
strcat(tt, "H:");
if (HA.testtype & TESTTYPE_ANY)
strcat(tt, "ANY:");
printf("%s label=%s type=%s ip=%s\n", HA.entry, HA.label, tt,
(HA.testtype & TESTTYPE_IP) ? HA.addrst : "NONE");
}
} else {
fprintf(stderr, "Unable to open %s\n", fname);
}
}
syntax highlighted by Code2HTML, v. 0.9.1