1 |
michael |
3389 |
/* 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 |
|
|
*/ |