src/acc.c
/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following functions.
- sigalrm
- rfc931_lookup
- filterReadConfigs
- authReadConfig
- striplocaldomain
- authorise
- getAuth
- authWrap
- fillAuth
/* $id$
* $Copyright$
*/
#include "nglobal.h"
#include "network.h"
#include "filter.h"
#include "reg.h"
#include "acc.h"
#include "authinfo.h"
#ifdef HAVE_LIBWRAP
# include <tcpd.h>
#endif
#include <setjmp.h>
jmp_buf jmp;
#ifdef HAVE_LIBWRAP
int deny_severity;
int allow_severity;
#endif
static RETSIGTYPE
sigalrm (int sig)
/* [<][>][^][v][top][bottom][index][help] */
{
longjmp (jmp, 1);
}
/*
* rfc931_lookup - return remote user name, given socket structures
* loosly based around ideas in Wietse Venema's rfc931 routine in tcp wrappers
*/
static char *rfc931_lookup (struct sockaddr_in *our_sin, struct sockaddr_in *remote_sin)
/* [<][>][^][v][top][bottom][index][help] */
{
unsigned remote_port;
unsigned our_port;
struct sockaddr_in remote_query_sin;
struct sockaddr_in our_query_sin;
volatile int sock;
static char user[128];
char buf[512];
FILE *fh = NULL; /* stop gcc complaining */
RETSIGTYPE (*al)(int) = NULL;
long al_s;
our_query_sin = *our_sin;
our_query_sin.sin_port = 0;
remote_query_sin = *remote_sin;
remote_query_sin.sin_port = htons (113);
al = signal (SIGALRM, sigalrm);
al_s = alarm (con->rfc931Timeout);
if (setjmp (jmp) == 1)
goto err;
if ((sock = socket (AF_INET, SOCK_STREAM, 0)) == -1
|| bind (sock, (struct sockaddr *) &our_query_sin, sizeof (our_query_sin)) == -1)
{
logw (("couldn't socket()/bind() for rfc931 request"));
goto err;
}
if (connect (sock, (struct sockaddr *) &remote_query_sin, sizeof (remote_query_sin)) == -1)
goto err;
fh = fdopen (sock, "r+");
setbuf(fh, NULL); /* sunos4.1.x doesn't work correctly with buffered bidirectional stdio pipes */
fprintf (fh, "%u,%u\r\n", ntohs (remote_sin->sin_port), ntohs (our_sin->sin_port));
fflush (fh);
if (fgets (buf, sizeof buf, fh)
&& !ferror (fh)
&& sscanf (buf, "%u , %u : USERID :%*[^:]:%127s", &remote_port, &our_port, user) == 3
&& ntohs (remote_sin->sin_port) == remote_port
&& ntohs (our_sin->sin_port) == our_port)
{
char *p = strchr (user, '\r');
if (p)
*p = '\0';
alarm (0);
signal (SIGALRM, SIG_DFL);
nonblock (sock);
close (sock);
if (al)
{
signal(SIGALRM, al);
if (al_s > 0)
alarm(al_s);
} else
signal (SIGALRM, SIG_DFL);
return user;
}
err:
alarm (0);
if (al)
{
signal(SIGALRM, al);
if (al_s > 0)
alarm(al_s);
} else
signal (SIGALRM, SIG_DFL);
nonblock (sock);
close (sock);
return NULL;
}
/* linked list of authentication entries */
struct authent *authent = NULL;
/* linked index into filter-file-chains */
static struct filter_list_index
{
struct filter_list_index *next;
char *name; /* name of file */
struct filter *filter; /* poitner to head of list of filters */
} *filter_list_index;
static struct filter_chain *filterReadConfigs (char *files)
/* [<][>][^][v][top][bottom][index][help] */
{
struct filter_chain *head=NULL, *fc=NULL;
char *fi;
for (fi = strtok(files, ","); fi; fi = strtok(NULL, ","))
{
char buf[MAX_LINE];
struct filter *f=NULL;
struct filter_list_index *fli = NULL;
FILE *fp;
int n;
fp = fopen(fi, "r");
if (!fp)
{
loge (("couldn't open filter rule file %s...skipped", fi));
continue;
}
if (!fc)
{
fc=head=Scalloc(1, sizeof *fc);
} else
{
fc->next = Scalloc(1, sizeof *fc);
fc = fc->next;
}
for (fli = filter_list_index; fli; fli=fli->next)
{
if (strEq(fli->name, fi))
{
fc->filter = fli->filter;
goto cont;
}
}
for (n=0, *buf='\0'; *buf || fgets(buf, sizeof buf, fp); n++)
{
char scope[127], weight_buf[32], *weight=weight_buf, options[MAX_LINE], pat[MAX_LINE];
struct strStack *st;
char *opt;
int err_code;
strStripEOL(buf);
if (!*buf || buf[0] == '#')
{
*buf = '\0';
continue;
}
if (sscanf(buf, "%127s %31s %1023s %1023[^\r\n]", scope, weight, options, pat)!=4)
{
loge (("invalid filter line %d in file %s (ignored): %s", n, fi, buf));
*buf = '\0';
continue;
}
if (!f)
{
f = fc->filter = Scalloc (1, sizeof *f);
} else
{
f->next = Scalloc (1, sizeof *f);
f = f->next;
}
if (strEq(scope, "head"))
f->scope = sc_head;
else
if (strEq(scope, "ahead"))
f->scope = sc_ahead;
else
if (strEq(scope, "body"))
f->scope = sc_head;
else
if (strEq(scope, "article"))
f->scope = sc_article;
else
{
char *p=strchr(scope, ':');
if (!p)
{
loge (("bad filter header/scope %s in file %s: %s", scope, fi, buf));
Exit(1);
}
*p='\0';
f->scope = sc_header;
f->scope_header = Sstrdup(scope);
}
if (*weight=='/' || *weight=='*')
f->weight_op = *weight++;
else
f->weight_op = '\0';
if (sscanf(weight, "%d", &f->weight)!=1)
{
loge (("bad filter weight in %s line %d: '%s'", fi, n, buf));
Exit(1);
}
for (opt = strtok(options, ","); opt; opt = strtok(NULL, ","))
{
if (strCaseEq(options, "nocase"))
f->ignore_case = TRUE;
else
if (strCaseEq(options, "case"))
f->ignore_case = FALSE;
else
{
loge (("bad filter option in %s line %d: '%s'", fi, n, buf));
Exit(1);
}
}
strStripEOL(pat);
st = strStackAdd (NULL, pat);
for (; fgets(buf, sizeof buf, fp); n++)
{
char *p;
strStripEOL (buf);
for (p=buf; isspace(*p); p++);
if (p==buf || !*p)
break;
st = strStackAdd (st, p);
}
f->pat = Sstrdup(st->data);
strStackFree (st);
#ifdef USE_REGEX
if ((err_code = nn_regcomp(&f->preg, f->pat, REG_EXTENDED|REG_NEWLINE|REG_NOSUB|(f->ignore_case? REG_ICASE: 0)))!=0)
{
char errbuf[MAX_LINE];
regerror(err_code, &f->preg, errbuf, sizeof errbuf);
loge (("bad regular expression in %s line %d: %s", fi, n, errbuf));
Exit(1);
}
#endif
if (feof(fp) || ferror(fp))
break;
}
if (!fc->filter)
{
loge (("filter file %s contained no filters!", fi));
Exit(1);
}
if (!filter_list_index)
{
fli = filter_list_index = Scalloc (1, sizeof *filter_list_index);
} else
{
fli->next = filter_list_index = Scalloc (1, sizeof *filter_list_index);
fli = fli->next;
}
fli->name = fc->name = Sstrdup(fi);
fli->filter = fc->filter;
cont:
continue;
}
return head;
}
EXPORT bool authReadConfig (char *f)
/* [<][>][^][v][top][bottom][index][help] */
{
FILE *fp;
char buf[MAX_LINE];
struct authent *ll = NULL;
if (!(fp = fopen (f, "r")))
{
loge (("couldn't read access file '%s'", f));
return FALSE;
}
filter_list_index = NULL;
while (fgets (buf, sizeof buf, fp))
{
char host[128], group[128], perms[128], filters[1024]="", users[1024]="", junk[2];
char *p;
strStripEOL(buf);
if (!*buf || buf[0] == '#')
continue;
if (sscanf(buf, "%127s %127s %127s %1023s %1023s %1s", host, group, perms, filters, users, junk) <3)
{
loge (("bad access control in %s: '%s'", f, buf));
continue;
}
if (!ll)
authent = ll = Scalloc (1, sizeof *ll);
else
{
ll->next = Scalloc (1, sizeof *ll);
ll = ll->next;
}
ll->host = Sstrdup (host);
ll->group = Sstrdup (group);
ll->users = *users? Sstrdup (users): NULL;
p = strtok(perms, ",");
do
{
if (strCaseEq(p, "read"))
ll->read = TRUE;
else
if (strCaseEq(p, "post"))
ll->post = TRUE;
else
if (strCaseEq(p, "deny"))
ll->deny = TRUE;
else
if (strnCaseEq(p, "auth", 4))
{
ll->authinfo = authinfo_get(f, p + 4);
ll->auth = TRUE;
}
else
if (strCaseEq(p, "filter"))
ll->filter = TRUE;
else
if (strCaseEq(p, "censor"))
ll->censor = TRUE;
else
if (strCaseEq(p, "ihave"))
ll->ihave = TRUE;
else
if (strCaseEq(p, "quick"))
ll->quick = TRUE;
else
if (strCaseEq(p, "nocem"))
ll->nocem = TRUE;
else
if (strCaseEq(p, "http"))
ll->nocem = TRUE;
else
if (strCaseEq(p, "strip"))
{
if (!strEq (host, "*"))
loge (("bad access control in %s: strip host must be '*': '%s'", f, buf));
ll->strip = TRUE;
}
else
loge (("bad access control in %s: '%s'", f, buf));
} while ((p=strtok(NULL, ",")));
if (*filters && *filters != '*')
ll->filter_chain = filterReadConfigs(filters);
}
fclose (fp);
if (ll)
ll->next = NULL;
return TRUE;
}
static bool striplocaldomain (char *remotehost, char *localhost)
/* [<][>][^][v][top][bottom][index][help] */
{
int n1, n2;
char *p;
n1 = strlen (localhost) - 1;
n2 = strlen (remotehost) - 1;
for (; n1 && n2 && (localhost[n1] == remotehost[n2]); n1--, n2--) ;
if (!(p = strchr (remotehost + n2, '.')))
{
*remotehost = '\0';
return TRUE;
}
*p = '\0';
return TRUE;
}
/*
* hosts, group or both must be specified.
*/
EXPORT struct authent *authorise (char **hosts, char *group)
/* [<][>][^][v][top][bottom][index][help] */
{
struct authent *ll, *ll_last_match = NULL;
for (ll = authent; ll; ll = ll->next)
{
char **hp;
if (hosts)
{
for (hp = hosts; *hp; hp++)
{
if (matchExp (ll->host, *hp, 1, 0))
{
if (!group)
ll_last_match = ll;
else
{
if (!matchExp (ll->group, group, 1, 0))
continue;
ll_last_match = ll;
}
}
}
} else
{
/*
* only look for match all hosts
*/
if (strEq (ll->host, "*") &&
matchExp (ll->group, group, 1, 0))
ll_last_match = ll;
}
if (ll_last_match && ll_last_match->quick)
return ll_last_match;
}
return ll_last_match;
}
EXPORT bool getAuth (int sock, char *themgood, char *them, char *themlocal, char *rfc931them, char *rfc931themlocal, char *themaddr, char *rfc931themaddr, int themlen, char *us, struct sockaddr_in *themsockaddr)
/* [<][>][^][v][top][bottom][index][help] */
{
struct sockaddr_in our_sin, remote_sin;
struct hostent *hp;
int sinlen = sizeof (struct sockaddr_in);
char *user = NULL;
char buf[MAX_LINE];
char *host1 = NULL;
*themgood = *them = *themlocal = *rfc931them = *rfc931themlocal = *themaddr = *rfc931themaddr = '\0';
if (getpeername (sock, (struct sockaddr *) &remote_sin, &sinlen) < 0)
{
if (isatty (sock)) /* debugging */
{
strcpy (themgood, "<STDIN>");
return TRUE;
} else
{
strcpy (themgood, "unknown");
logw (("couldn't getpeername()"));
loginne (("? cant getpeername"));
return FALSE;
}
}
else if (remote_sin.sin_family != AF_INET)
{
logen (("remote connection not an AF_INET connection"));
emitf ("%d Not an INET connection. Goodbye", NNTP_ACCESS_VAL);
strcpy (themgood, "unknown");
return FALSE;
}
if (getsockname (sock, (struct sockaddr *) &our_sin, &sinlen) < 0)
{
logw (("couldn't getsockname()"));
} else
{
if (con->rfc931)
{
user = rfc931_lookup(&our_sin, &remote_sin);
if (user)
user[64] = '\0';
}
if (!user)
user = "unknown";
}
*themsockaddr = remote_sin;
if ((hp = gethostbyaddr ((char *) &remote_sin.sin_addr, sizeof (remote_sin.sin_addr), AF_INET)))
{
struct hostent *h;
host1 = Sstrdup ((char *) hp->h_name);
if (strspn(host1, ".abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-") != strlen(host1))
{
logwn (("suspect address ignored: gethostbyaddr(\"%s\") = '%.128s'", inet_ntoa (remote_sin.sin_addr), host1));
goto ret;
}
h = gethostbyname (host1);
if (h)
{
char **addr;
if (h->h_length != 4)
{
logen (("gethostbyname('%.128s')->h_length = %d != 4", host1, h->h_length));
goto ret;
}
if (h->h_addrtype != AF_INET)
{
logen (("gethostbyname('%.128s')->h_addrtype = %d != AF_INET", host1, h->h_addrtype));
goto ret;
}
for (addr = h->h_addr_list; *addr; addr++)
{
if (memcmp(&remote_sin.sin_addr, *addr, 4) == 0)
{
if (strlen (host1) >= themlen)
{
logwn (("host name length limited reached. ignored."));
continue;
}
strcpy (them, host1);
strLower (them);
strcpy (themlocal, them);
striplocaldomain (themlocal, us);
sprintf (buf, "%.127s@%.127s", user, them);
strcpy (rfc931them, buf);
if (*themlocal)
sprintf (buf, "%.127s@%.127s", user, themlocal);
break;
}
}
if (!*addr)
{
loginnw (("gethostbyaddr: %.128s != %s", host1, inet_ntoa (remote_sin.sin_addr)));
}
}
} else
{
loginn (("? cant gethostbyaddr %s %s", inet_ntoa (remote_sin.sin_addr), strerror(errno)));
}
ret:
strcpy (themaddr, inet_ntoa (remote_sin.sin_addr));
if (!*them)
strcpy(them, themaddr);
sprintf (rfc931themaddr, "%.127s@%.127s", user, themaddr);
if (host1)
free (host1);
if (*rfc931them)
strcpy(themgood, rfc931them);
else
strcpy(themgood, rfc931themaddr);
return TRUE;
}
static bool
authWrap(int fd)
/* [<][>][^][v][top][bottom][index][help] */
{
#ifdef HAVE_LIBWRAP
struct request_info req;
request_init(&req, RQ_DAEMON, "nntpcached", RQ_FILE, fd, NULL);
fromhost(&req);
return (hosts_access(&req) != 0);
#else
return TRUE;
#endif
}
EXPORT bool fillAuth (int fd, char *what)
/* [<][>][^][v][top][bottom][index][help] */
{
if (con->useLibWrap && !authWrap(fd))
return FALSE;
if (!getAuth (fd, ClientHost, ClientHostNormal, ClientHostLocal, ClientHostRFC931, ClientHostLocalRFC931, ClientHostAddr, ClientHostAddrRFC931, sizeof (ClientHost), Host, &ClientRemoteAddr))
return FALSE;
strncpy (Task->ti_client_host, ClientHost, sizeof Task->ti_client_host);
if (!(ConnectAuth = authorise (RemoteHosts, what)) || ConnectAuth->deny)
return FALSE;
return TRUE;
}