1 |
/* IP address authorization module for HTTP server. |
2 |
* |
3 |
* IRC Services is copyright (c) 1996-2009 Andrew Church. |
4 |
* E-mail: <achurch@achurch.org> |
5 |
* Parts written by Andrew Kempe and others. |
6 |
* This program is free but copyrighted software; see the file GPL.txt for |
7 |
* details. |
8 |
*/ |
9 |
|
10 |
#include "services.h" |
11 |
#include "modules.h" |
12 |
#include "conffile.h" |
13 |
#include "http.h" |
14 |
#include <netinet/in.h> |
15 |
#include <netdb.h> |
16 |
|
17 |
/*************************************************************************/ |
18 |
|
19 |
static Module *module_httpd; |
20 |
|
21 |
/* List of hosts to allow/deny */ |
22 |
typedef struct { |
23 |
char *path; |
24 |
int pathlen; /* for convenience */ |
25 |
uint32 ip, mask; /* network byte order */ |
26 |
int allow; /* 1 = allow, 0 = deny */ |
27 |
} DirInfo; |
28 |
static DirInfo *protected = NULL; |
29 |
static int protected_count = 0; |
30 |
|
31 |
/*************************************************************************/ |
32 |
/************************ Authorization callback *************************/ |
33 |
/*************************************************************************/ |
34 |
|
35 |
static int do_auth(Client *c, int *close_ptr) |
36 |
{ |
37 |
int i; |
38 |
|
39 |
ARRAY_FOREACH (i, protected) { |
40 |
if (strncmp(c->url, protected[i].path, protected[i].pathlen) != 0) |
41 |
continue; |
42 |
if ((c->ip & protected[i].mask) != protected[i].ip) |
43 |
continue; |
44 |
if (protected[i].allow) { |
45 |
return HTTP_AUTH_UNDECIDED; |
46 |
} else { |
47 |
module_log("Denying request for %s from %s", c->url, c->address); |
48 |
return HTTP_AUTH_DENY; |
49 |
} |
50 |
} |
51 |
return HTTP_AUTH_UNDECIDED; |
52 |
} |
53 |
|
54 |
/*************************************************************************/ |
55 |
/***************************** Module stuff ******************************/ |
56 |
/*************************************************************************/ |
57 |
|
58 |
static int do_prefix(const char *filename, int linenum, char *param); |
59 |
static int do_AllowHost(const char *filename, int linenum, char *param); |
60 |
static int do_DenyHost(const char *filename, int linenum, char *param); |
61 |
static int do_AllowDenyHost(const char *filename, int linenum, char *param, |
62 |
int allow); |
63 |
ConfigDirective module_config[] = { |
64 |
{ "AllowHost", { { CD_FUNC, 0, do_prefix }, |
65 |
{ CD_FUNC, 0, do_AllowHost } } }, |
66 |
{ "DenyHost", { { CD_FUNC, 0, do_prefix }, |
67 |
{ CD_FUNC, 0, do_DenyHost } } }, |
68 |
{ NULL } |
69 |
}; |
70 |
|
71 |
static char *prefix = NULL; |
72 |
|
73 |
/*************************************************************************/ |
74 |
|
75 |
static int do_prefix(const char *filename, int linenum, char *param) |
76 |
{ |
77 |
if (filename) { |
78 |
free(prefix); |
79 |
prefix = strdup(param); |
80 |
if (!prefix) { |
81 |
config_error(filename, linenum, "Out of memory"); |
82 |
return 0; |
83 |
} |
84 |
} |
85 |
return 1; |
86 |
} |
87 |
|
88 |
static int do_AllowHost(const char *filename, int linenum, char *param) |
89 |
{ |
90 |
return do_AllowDenyHost(filename, linenum, param, 1); |
91 |
} |
92 |
|
93 |
static int do_DenyHost(const char *filename, int linenum, char *param) |
94 |
{ |
95 |
return do_AllowDenyHost(filename, linenum, param, 0); |
96 |
} |
97 |
|
98 |
/*************************************************************************/ |
99 |
|
100 |
static int do_AllowDenyHost(const char *filename, int linenum, char *param, |
101 |
int allow) |
102 |
{ |
103 |
char *s; |
104 |
int mask = 32; |
105 |
const uint8 *ip; |
106 |
int recursing = 0, i; |
107 |
DirInfo di; |
108 |
static DirInfo *new_protected = NULL; |
109 |
static int new_protected_count = 0; |
110 |
|
111 |
if (!filename) { |
112 |
/* filename == NULL, special actions */ |
113 |
switch (linenum) { |
114 |
case CDFUNC_INIT: /* prepare for reading */ |
115 |
free(new_protected); |
116 |
new_protected = NULL; |
117 |
new_protected_count = 0; |
118 |
break; |
119 |
case CDFUNC_SET: /* store new values in config variables */ |
120 |
if (new_protected_count >= 0) { |
121 |
ARRAY_FOREACH (i, protected) |
122 |
free(protected[i].path); |
123 |
free(protected); |
124 |
protected = new_protected; |
125 |
protected_count = new_protected_count; |
126 |
new_protected = NULL; |
127 |
new_protected_count = -1; /* flag to say "don't copy again" */ |
128 |
} |
129 |
break; |
130 |
case CDFUNC_DECONFIG: /* clear out config variables */ |
131 |
ARRAY_FOREACH (i, protected) |
132 |
free(protected[i].path); |
133 |
free(protected); |
134 |
protected = NULL; |
135 |
protected_count = 0; |
136 |
break; |
137 |
} /* switch (linenum) */ |
138 |
return 1; |
139 |
} /* if (!filename) */ |
140 |
|
141 |
/* filename != NULL, process directive */ |
142 |
|
143 |
if (linenum < 0) { |
144 |
recursing = 1; |
145 |
linenum = -linenum; |
146 |
} |
147 |
di.path = prefix; |
148 |
di.pathlen = strlen(prefix); |
149 |
prefix = NULL; |
150 |
|
151 |
s = strchr(param, '/'); |
152 |
if (s) { |
153 |
*s++ = 0; |
154 |
mask = (int)atolsafe(s, 1, 31); |
155 |
if (mask < 1) { |
156 |
config_error(filename, linenum, "Invalid mask length `%s'", s); |
157 |
free(di.path); |
158 |
return 0; |
159 |
} |
160 |
} |
161 |
|
162 |
if (strcmp(param, "*") == 0) { |
163 |
/* All-hosts wildcard -> equivalent to 0.0.0.0/0 */ |
164 |
ip = (const uint8 *)"\0\0\0\0"; |
165 |
mask = 0; |
166 |
} else if ((ip = pack_ip(param)) != NULL) { |
167 |
/* IP address -> okay as is */ |
168 |
} else { |
169 |
/* hostname -> check for double recursion, then look up and |
170 |
* recursively add addresses */ |
171 |
#ifdef HAVE_GETHOSTBYNAME |
172 |
struct hostent *hp; |
173 |
#endif |
174 |
if (recursing) { |
175 |
config_error(filename, linenum, "BUG: double recursion (param=%s)", |
176 |
param); |
177 |
free(di.path); |
178 |
return 0; |
179 |
} |
180 |
#ifdef HAVE_GETHOSTBYNAME |
181 |
if ((hp = gethostbyname(param)) != NULL) { |
182 |
if (hp->h_addrtype == AF_INET) { |
183 |
for (i = 0; hp->h_addr_list[i]; i++) { |
184 |
char ipbuf[16]; |
185 |
ip = (const uint8 *)hp->h_addr_list[i]; |
186 |
snprintf(ipbuf, sizeof(ipbuf), "%u.%u.%u.%u", |
187 |
ip[0], ip[1], ip[2], ip[3]); |
188 |
if (strlen(ipbuf) > 15) { |
189 |
config_error(filename, linenum, |
190 |
"BUG: strlen(ipbuf) > 15 [%s]", ipbuf); |
191 |
free(di.path); |
192 |
return 0; |
193 |
} |
194 |
prefix = strdup(di.path); |
195 |
if (!prefix) { |
196 |
config_error(filename, linenum, "Out of memory"); |
197 |
free(di.path); |
198 |
return 0; |
199 |
} |
200 |
if (!do_AllowDenyHost(filename, -linenum, ipbuf, allow)) { |
201 |
free(di.path); |
202 |
return 0; |
203 |
} |
204 |
} |
205 |
free(di.path); |
206 |
return 1; /* Success */ |
207 |
} else { |
208 |
config_error(filename, linenum, "%s: no IPv4 addresses found", |
209 |
param); |
210 |
} |
211 |
} else { |
212 |
config_error(filename, linenum, "%s: %s", param, |
213 |
hstrerror(h_errno)); |
214 |
} |
215 |
#else |
216 |
config_error(filename, linenum, |
217 |
"gethostbyname() not available, hostnames may not be" |
218 |
" used"); |
219 |
#endif |
220 |
free(di.path); |
221 |
return 0; |
222 |
} |
223 |
|
224 |
di.ip = *((uint32 *)ip); |
225 |
di.mask = mask ? htonl(0xFFFFFFFFUL << (32-mask)) : 0; |
226 |
di.ip &= di.mask; |
227 |
di.allow = allow; |
228 |
ARRAY_EXTEND(new_protected); |
229 |
new_protected[new_protected_count-1] = di; |
230 |
return 1; |
231 |
} |
232 |
|
233 |
/*************************************************************************/ |
234 |
/*************************************************************************/ |
235 |
|
236 |
int init_module(void) |
237 |
{ |
238 |
module_httpd = find_module("httpd/main"); |
239 |
if (!module_httpd) { |
240 |
module_log("Main httpd module not loaded"); |
241 |
exit_module(0); |
242 |
return 0; |
243 |
} |
244 |
use_module(module_httpd); |
245 |
|
246 |
if (!add_callback(module_httpd, "auth", do_auth)) { |
247 |
module_log("Unable to add callback"); |
248 |
exit_module(0); |
249 |
return 0; |
250 |
} |
251 |
|
252 |
return 1; |
253 |
} |
254 |
|
255 |
/*************************************************************************/ |
256 |
|
257 |
int exit_module(int shutdown_unused) |
258 |
{ |
259 |
if (module_httpd) { |
260 |
remove_callback(module_httpd, "auth", do_auth); |
261 |
unuse_module(module_httpd); |
262 |
module_httpd = NULL; |
263 |
} |
264 |
|
265 |
return 1; |
266 |
} |
267 |
|
268 |
/*************************************************************************/ |
269 |
|
270 |
/* |
271 |
* Local variables: |
272 |
* c-file-style: "stroustrup" |
273 |
* c-file-offsets: ((case-label . *) (statement-case-intro . *)) |
274 |
* indent-tabs-mode: nil |
275 |
* End: |
276 |
* |
277 |
* vim: expandtab shiftwidth=4: |
278 |
*/ |