mirror of
https://github.com/NishiOwO/ircservices5.git
synced 2025-04-22 01:04:38 +00:00
279 lines
8.5 KiB
C
279 lines
8.5 KiB
C
/* IP address authorization module for HTTP server.
|
|
*
|
|
* IRC Services is copyright (c) 1996-2009 Andrew Church.
|
|
* E-mail: <achurch@achurch.org>
|
|
* Parts written by Andrew Kempe and others.
|
|
* This program is free but copyrighted software; see the file GPL.txt for
|
|
* details.
|
|
*/
|
|
|
|
#include "services.h"
|
|
#include "modules.h"
|
|
#include "conffile.h"
|
|
#include "http.h"
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
|
|
/*************************************************************************/
|
|
|
|
static Module *module_httpd;
|
|
|
|
/* List of hosts to allow/deny */
|
|
typedef struct {
|
|
char *path;
|
|
int pathlen; /* for convenience */
|
|
uint32 ip, mask; /* network byte order */
|
|
int allow; /* 1 = allow, 0 = deny */
|
|
} DirInfo;
|
|
static DirInfo *protected = NULL;
|
|
static int protected_count = 0;
|
|
|
|
/*************************************************************************/
|
|
/************************ Authorization callback *************************/
|
|
/*************************************************************************/
|
|
|
|
static int do_auth(Client *c, int *close_ptr)
|
|
{
|
|
int i;
|
|
|
|
ARRAY_FOREACH (i, protected) {
|
|
if (strncmp(c->url, protected[i].path, protected[i].pathlen) != 0)
|
|
continue;
|
|
if ((c->ip & protected[i].mask) != protected[i].ip)
|
|
continue;
|
|
if (protected[i].allow) {
|
|
return HTTP_AUTH_UNDECIDED;
|
|
} else {
|
|
module_log("Denying request for %s from %s", c->url, c->address);
|
|
return HTTP_AUTH_DENY;
|
|
}
|
|
}
|
|
return HTTP_AUTH_UNDECIDED;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/***************************** Module stuff ******************************/
|
|
/*************************************************************************/
|
|
|
|
static int do_prefix(const char *filename, int linenum, char *param);
|
|
static int do_AllowHost(const char *filename, int linenum, char *param);
|
|
static int do_DenyHost(const char *filename, int linenum, char *param);
|
|
static int do_AllowDenyHost(const char *filename, int linenum, char *param,
|
|
int allow);
|
|
ConfigDirective module_config[] = {
|
|
{ "AllowHost", { { CD_FUNC, 0, do_prefix },
|
|
{ CD_FUNC, 0, do_AllowHost } } },
|
|
{ "DenyHost", { { CD_FUNC, 0, do_prefix },
|
|
{ CD_FUNC, 0, do_DenyHost } } },
|
|
{ NULL }
|
|
};
|
|
|
|
static char *prefix = NULL;
|
|
|
|
/*************************************************************************/
|
|
|
|
static int do_prefix(const char *filename, int linenum, char *param)
|
|
{
|
|
if (filename) {
|
|
free(prefix);
|
|
prefix = strdup(param);
|
|
if (!prefix) {
|
|
config_error(filename, linenum, "Out of memory");
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int do_AllowHost(const char *filename, int linenum, char *param)
|
|
{
|
|
return do_AllowDenyHost(filename, linenum, param, 1);
|
|
}
|
|
|
|
static int do_DenyHost(const char *filename, int linenum, char *param)
|
|
{
|
|
return do_AllowDenyHost(filename, linenum, param, 0);
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static int do_AllowDenyHost(const char *filename, int linenum, char *param,
|
|
int allow)
|
|
{
|
|
char *s;
|
|
int mask = 32;
|
|
const uint8 *ip;
|
|
int recursing = 0, i;
|
|
DirInfo di;
|
|
static DirInfo *new_protected = NULL;
|
|
static int new_protected_count = 0;
|
|
|
|
if (!filename) {
|
|
/* filename == NULL, special actions */
|
|
switch (linenum) {
|
|
case CDFUNC_INIT: /* prepare for reading */
|
|
free(new_protected);
|
|
new_protected = NULL;
|
|
new_protected_count = 0;
|
|
break;
|
|
case CDFUNC_SET: /* store new values in config variables */
|
|
if (new_protected_count >= 0) {
|
|
ARRAY_FOREACH (i, protected)
|
|
free(protected[i].path);
|
|
free(protected);
|
|
protected = new_protected;
|
|
protected_count = new_protected_count;
|
|
new_protected = NULL;
|
|
new_protected_count = -1; /* flag to say "don't copy again" */
|
|
}
|
|
break;
|
|
case CDFUNC_DECONFIG: /* clear out config variables */
|
|
ARRAY_FOREACH (i, protected)
|
|
free(protected[i].path);
|
|
free(protected);
|
|
protected = NULL;
|
|
protected_count = 0;
|
|
break;
|
|
} /* switch (linenum) */
|
|
return 1;
|
|
} /* if (!filename) */
|
|
|
|
/* filename != NULL, process directive */
|
|
|
|
if (linenum < 0) {
|
|
recursing = 1;
|
|
linenum = -linenum;
|
|
}
|
|
di.path = prefix;
|
|
di.pathlen = strlen(prefix);
|
|
prefix = NULL;
|
|
|
|
s = strchr(param, '/');
|
|
if (s) {
|
|
*s++ = 0;
|
|
mask = (int)atolsafe(s, 1, 31);
|
|
if (mask < 1) {
|
|
config_error(filename, linenum, "Invalid mask length `%s'", s);
|
|
free(di.path);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (strcmp(param, "*") == 0) {
|
|
/* All-hosts wildcard -> equivalent to 0.0.0.0/0 */
|
|
ip = (const uint8 *)"\0\0\0\0";
|
|
mask = 0;
|
|
} else if ((ip = pack_ip(param)) != NULL) {
|
|
/* IP address -> okay as is */
|
|
} else {
|
|
/* hostname -> check for double recursion, then look up and
|
|
* recursively add addresses */
|
|
#ifdef HAVE_GETHOSTBYNAME
|
|
struct hostent *hp;
|
|
#endif
|
|
if (recursing) {
|
|
config_error(filename, linenum, "BUG: double recursion (param=%s)",
|
|
param);
|
|
free(di.path);
|
|
return 0;
|
|
}
|
|
#ifdef HAVE_GETHOSTBYNAME
|
|
if ((hp = gethostbyname(param)) != NULL) {
|
|
if (hp->h_addrtype == AF_INET) {
|
|
for (i = 0; hp->h_addr_list[i]; i++) {
|
|
char ipbuf[16];
|
|
ip = (const uint8 *)hp->h_addr_list[i];
|
|
snprintf(ipbuf, sizeof(ipbuf), "%u.%u.%u.%u",
|
|
ip[0], ip[1], ip[2], ip[3]);
|
|
if (strlen(ipbuf) > 15) {
|
|
config_error(filename, linenum,
|
|
"BUG: strlen(ipbuf) > 15 [%s]", ipbuf);
|
|
free(di.path);
|
|
return 0;
|
|
}
|
|
prefix = strdup(di.path);
|
|
if (!prefix) {
|
|
config_error(filename, linenum, "Out of memory");
|
|
free(di.path);
|
|
return 0;
|
|
}
|
|
if (!do_AllowDenyHost(filename, -linenum, ipbuf, allow)) {
|
|
free(di.path);
|
|
return 0;
|
|
}
|
|
}
|
|
free(di.path);
|
|
return 1; /* Success */
|
|
} else {
|
|
config_error(filename, linenum, "%s: no IPv4 addresses found",
|
|
param);
|
|
}
|
|
} else {
|
|
config_error(filename, linenum, "%s: %s", param,
|
|
hstrerror(h_errno));
|
|
}
|
|
#else
|
|
config_error(filename, linenum,
|
|
"gethostbyname() not available, hostnames may not be"
|
|
" used");
|
|
#endif
|
|
free(di.path);
|
|
return 0;
|
|
}
|
|
|
|
di.ip = *((uint32 *)ip);
|
|
di.mask = mask ? htonl(0xFFFFFFFFUL << (32-mask)) : 0;
|
|
di.ip &= di.mask;
|
|
di.allow = allow;
|
|
ARRAY_EXTEND(new_protected);
|
|
new_protected[new_protected_count-1] = di;
|
|
return 1;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
int init_module(void)
|
|
{
|
|
module_httpd = find_module("httpd/main");
|
|
if (!module_httpd) {
|
|
module_log("Main httpd module not loaded");
|
|
exit_module(0);
|
|
return 0;
|
|
}
|
|
use_module(module_httpd);
|
|
|
|
if (!add_callback(module_httpd, "auth", do_auth)) {
|
|
module_log("Unable to add callback");
|
|
exit_module(0);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
int exit_module(int shutdown_unused)
|
|
{
|
|
if (module_httpd) {
|
|
remove_callback(module_httpd, "auth", do_auth);
|
|
unuse_module(module_httpd);
|
|
module_httpd = NULL;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-file-style: "stroustrup"
|
|
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*
|
|
* vim: expandtab shiftwidth=4:
|
|
*/
|