1 |
/* |
2 |
* ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd). |
3 |
* access.c: Common code for user@host access control blocks. |
4 |
* |
5 |
* Copyright (C) 2006 by the Hybrid Development Team. |
6 |
* |
7 |
* This program is free software; you can redistribute it and/or modify |
8 |
* it under the terms of the GNU General Public License as published by |
9 |
* the Free Software Foundation; either version 2 of the License, or |
10 |
* (at your option) any later version. |
11 |
* |
12 |
* This program is distributed in the hope that it will be useful, |
13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
* GNU General Public License for more details. |
16 |
* |
17 |
* You should have received a copy of the GNU General Public License |
18 |
* along with this program; if not, write to the Free Software |
19 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
20 |
* USA |
21 |
* |
22 |
* $Id$ |
23 |
*/ |
24 |
|
25 |
#include "stdinc.h" |
26 |
#include "conf/conf.h" |
27 |
|
28 |
struct AccessConf *atable[ATABLE_SIZE] = {0}; |
29 |
struct Callback *cb_expire_confs = NULL; |
30 |
|
31 |
static ACB_FREE_HANDLER *acb_types[MAX_ACB_TYPES] = {0}; |
32 |
static dlink_node *hreset; |
33 |
|
34 |
/* |
35 |
* hash_ipv4() |
36 |
* |
37 |
* Hash algorithm for IPv4. |
38 |
* |
39 |
* inputs: the IP address |
40 |
* output: hash value |
41 |
*/ |
42 |
static unsigned int |
43 |
hash_ipv4(const struct irc_ssaddr *addr, int bits) |
44 |
{ |
45 |
if (bits != 0) |
46 |
{ |
47 |
const struct sockaddr_in *v4 = (const struct sockaddr_in *) addr; |
48 |
unsigned int av = ntohl(v4->sin_addr.s_addr) & ~((1 << (32 - bits)) - 1); |
49 |
|
50 |
return (av ^ (av >> 12) ^ (av >> 24)) % ATABLE_SIZE; |
51 |
} |
52 |
|
53 |
return 0; |
54 |
} |
55 |
|
56 |
/* |
57 |
* hash_ipv6() |
58 |
* |
59 |
* Hash algorithm for IPv6. |
60 |
* |
61 |
* inputs: the IP address |
62 |
* output: hash value |
63 |
*/ |
64 |
#ifdef IPV6 |
65 |
static unsigned int |
66 |
hash_ipv6(const struct irc_ssaddr *addr, int bits) |
67 |
{ |
68 |
const struct sockaddr_in6 *v6 = (const struct sockaddr_in6 *) addr; |
69 |
unsigned int av = 0, n; |
70 |
|
71 |
for (n = 0; n < 16; n++) |
72 |
if (bits >= 8) |
73 |
{ |
74 |
av ^= v6->sin6_addr.s6_addr[n]; |
75 |
bits -= 8; |
76 |
} |
77 |
else |
78 |
{ |
79 |
if (bits > 0) |
80 |
av ^= v6->sin6_addr.s6_addr[n] & ~((1 << (8 - bits)) - 1); |
81 |
break; |
82 |
} |
83 |
|
84 |
return av % ATABLE_SIZE; |
85 |
} |
86 |
#endif |
87 |
|
88 |
/* |
89 |
* hash_text() |
90 |
* |
91 |
* Hash algorithm for DNS names. |
92 |
* |
93 |
* inputs: the start of text to hash |
94 |
* output: hash value |
95 |
*/ |
96 |
static unsigned int |
97 |
hash_text(const char *p) |
98 |
{ |
99 |
unsigned int av = 0; |
100 |
|
101 |
for (; *p; p++) |
102 |
av = (av << 4) - (av + ToLower(*p)); |
103 |
|
104 |
return av % ATABLE_SIZE; |
105 |
} |
106 |
|
107 |
/* |
108 |
* hash_hostmask() |
109 |
* |
110 |
* Calculates the hash value for a given hostmask. |
111 |
* |
112 |
* inputs: |
113 |
* p - the hostname to hash |
114 |
* addr - where to store mask info |
115 |
* output: hash value |
116 |
*/ |
117 |
static unsigned int |
118 |
hash_hostmask(const char *p, struct irc_ssaddr *addr) |
119 |
{ |
120 |
int bits; |
121 |
unsigned int hv; |
122 |
const char *seg; |
123 |
|
124 |
switch (parse_netmask(p, addr, &bits)) |
125 |
{ |
126 |
case HM_IPV4: |
127 |
hv = hash_ipv4(addr, bits - bits % 8); |
128 |
break; |
129 |
#ifdef IPV6 |
130 |
case HM_IPV6: |
131 |
hv = hash_ipv6(addr, bits - bits % 16); |
132 |
break; |
133 |
#endif |
134 |
default: |
135 |
// |
136 |
// Choose the longest domain substring which contains no wildcards. |
137 |
// This way hash value will be as precise as possible. |
138 |
// |
139 |
for (seg = p; *p; p++) |
140 |
if (*p == '.' && !seg) |
141 |
seg = p + 1; |
142 |
else if (IsMWildChar(*p)) |
143 |
seg = NULL; |
144 |
|
145 |
hv = seg ? hash_text(seg) : 0; |
146 |
} |
147 |
|
148 |
addr->ss_port = (in_port_t) bits; |
149 |
|
150 |
return hv; |
151 |
} |
152 |
|
153 |
/* |
154 |
* add_access_conf() |
155 |
* |
156 |
* Adds an AccessConf entry to the hash table. |
157 |
* |
158 |
* inputs: pointer to AccessConf struct |
159 |
* output: none |
160 |
*/ |
161 |
void |
162 |
add_access_conf(struct AccessConf *conf) |
163 |
{ |
164 |
unsigned int hv = hash_hostmask(conf->host, &conf->ip); |
165 |
static uint64_t curprec = ~0; |
166 |
|
167 |
conf->precedence = curprec--; |
168 |
conf->hnext = atable[hv]; |
169 |
atable[hv] = conf; |
170 |
} |
171 |
|
172 |
/* |
173 |
* destroy_access_conf() |
174 |
* |
175 |
* Deletes an AccessConf entry from the hash table and frees its memory. |
176 |
* |
177 |
* inputs: pointer to AccessConf struct |
178 |
* output: none |
179 |
*/ |
180 |
void |
181 |
destroy_access_conf(struct AccessConf *conf) |
182 |
{ |
183 |
struct irc_ssaddr addr; |
184 |
unsigned int hv = hash_hostmask(conf->host, &addr); |
185 |
struct AccessConf *prev; |
186 |
|
187 |
if (conf == atable[hv]) |
188 |
atable[hv] = conf->hnext; |
189 |
else |
190 |
{ |
191 |
for (prev = atable[hv]; prev->hnext != conf; prev = prev->hnext) |
192 |
; // let it core if not found |
193 |
prev->hnext = conf->hnext; |
194 |
} |
195 |
|
196 |
acb_types[conf->type](conf); |
197 |
} |
198 |
|
199 |
/* |
200 |
* acb_generic_free() |
201 |
* |
202 |
* Generic free procedure for ACB's. |
203 |
* |
204 |
* inputs: pointer to AccessConf struct |
205 |
* output: none |
206 |
*/ |
207 |
void |
208 |
acb_generic_free(struct AccessConf *conf) |
209 |
{ |
210 |
MyFree(conf->user); |
211 |
MyFree(conf->host); |
212 |
MyFree(conf); |
213 |
} |
214 |
|
215 |
/* |
216 |
* enum_access_confs() |
217 |
* |
218 |
* Enumerates all AccessConfs. Calls the supplied routine on each one, |
219 |
* if 1 is returned then this one is deleted and freed. |
220 |
* Any number of AccessConfs can be deleted by this function. |
221 |
* |
222 |
* inputs: pointer to checking routine |
223 |
* output: none |
224 |
*/ |
225 |
void |
226 |
enum_access_confs(ACB_EXAMINE_HANDLER *examine, void *param) |
227 |
{ |
228 |
unsigned int hv; |
229 |
struct AccessConf *prev, *conf; |
230 |
|
231 |
for (hv = 0; hv < ATABLE_SIZE; hv++) |
232 |
for (prev = NULL, conf = atable[hv]; conf != NULL; |
233 |
conf = (prev ? prev->hnext : atable[hv])) |
234 |
{ |
235 |
if (examine(conf, param)) |
236 |
{ |
237 |
if (prev != NULL) |
238 |
prev->hnext = conf->hnext; |
239 |
else |
240 |
atable[hv] = conf->hnext; |
241 |
|
242 |
acb_types[conf->type](conf); |
243 |
} |
244 |
else |
245 |
prev = conf; |
246 |
} |
247 |
} |
248 |
|
249 |
static int is_acb_permanent(struct AccessConf *conf, void *unused) |
250 |
{ |
251 |
return !conf->expires; |
252 |
} |
253 |
|
254 |
static int is_acb_expired(struct AccessConf *conf, void *unused) |
255 |
{ |
256 |
return conf->expires && conf->expires <= CurrentTime; |
257 |
} |
258 |
|
259 |
static int is_acb_orphaned(struct AccessConf *conf, void *unused) |
260 |
{ |
261 |
return !acb_types[conf->type]; |
262 |
} |
263 |
|
264 |
/* |
265 |
* register_acb_type() |
266 |
* |
267 |
* Allocates a unique value for AccessConf.type field to be used by modules. |
268 |
* |
269 |
* inputs: pointer to routine which frees a conf entry of this type |
270 |
* output: requested value |
271 |
*/ |
272 |
int |
273 |
register_acb_type(void *fh) |
274 |
{ |
275 |
int i; |
276 |
|
277 |
for (i = 0; i < MAX_ACB_TYPES; i++) |
278 |
if (acb_types[i] == NULL) |
279 |
{ |
280 |
acb_types[i] = (ACB_FREE_HANDLER *) fh; |
281 |
return i; |
282 |
} |
283 |
|
284 |
return -1; |
285 |
} |
286 |
|
287 |
/* |
288 |
* unregister_acb_type() |
289 |
* |
290 |
* Deallocates an AccessConf.type specifier. |
291 |
* |
292 |
* inputs: type value |
293 |
* output: none |
294 |
*/ |
295 |
void |
296 |
unregister_acb_type(int id) |
297 |
{ |
298 |
acb_types[id] = NULL; |
299 |
enum_access_confs(is_acb_orphaned, NULL); |
300 |
} |
301 |
|
302 |
/* |
303 |
* reset_access() |
304 |
* |
305 |
* Deletes all non-expiring ACB's before a rehash. |
306 |
* |
307 |
* inputs: none |
308 |
* output: none |
309 |
*/ |
310 |
static void * |
311 |
reset_access(va_list args) |
312 |
{ |
313 |
enum_access_confs(is_acb_permanent, NULL); |
314 |
return pass_callback(hreset); |
315 |
} |
316 |
|
317 |
/* |
318 |
* find_access_conf() |
319 |
* |
320 |
* Looks for an access conf matching given criteria. |
321 |
* |
322 |
* inputs: |
323 |
* type - only check confs of this type |
324 |
* user - if non-NULL, limit to confs matching given username |
325 |
* host - limit to confs matching given hostname... |
326 |
* ip - ...or IP address (either host or ip must be non-NULL) |
327 |
* func - function which verifies additional criteria, can be NULL |
328 |
* output: pointer to struct AccessConf or NULL if not found |
329 |
*/ |
330 |
struct AccessConf * |
331 |
find_access_conf(int type, const char *user, const char *host, |
332 |
const struct irc_ssaddr *ip, ACB_EXAMINE_HANDLER *func, |
333 |
void *param) |
334 |
{ |
335 |
struct AccessConf *conf, *best = NULL; |
336 |
int bits; |
337 |
|
338 |
if (ip != NULL) |
339 |
if (ip->ss.sin_family == AF_INET) |
340 |
for (bits = 32; bits >= 0; bits -= 8) |
341 |
for (conf = atable[hash_ipv4(ip, bits)]; conf; conf = conf->hnext) |
342 |
if ((best == NULL || conf->precedence > best->precedence) && |
343 |
conf->type == type && match_ipv4(ip, &conf->ip, conf->ip.ss_port) |
344 |
&& (EmptyString(user) || match(conf->user, user)) && |
345 |
(!func || func(conf, param))) |
346 |
best = conf; |
347 |
#ifdef IPV6 |
348 |
else if (ip->ss.sin_family == AF_INET6) |
349 |
for (bits = 128; bits >= 0; bits -= 16) |
350 |
for (conf = atable[hash_ipv6(ip, bits)]; conf; conf = conf->hnext) |
351 |
if ((best == NULL || conf->precedence > best->precedence) && |
352 |
conf->type == type && match_ipv6(ip, &conf->ip, conf->ip.ss_port) |
353 |
&& (EmptyString(user) || match(conf->user, user)) && |
354 |
(!func || func(conf, param))) |
355 |
best = conf; |
356 |
#endif |
357 |
|
358 |
if (host != NULL) |
359 |
while (1) |
360 |
{ |
361 |
for (conf = atable[hash_text(host)]; conf; conf = conf->hnext) |
362 |
if ((best == NULL || conf->precedence > best->precedence) && |
363 |
conf->type == type && match(conf->host, host) && |
364 |
(EmptyString(user) || match(conf->user, user)) && |
365 |
(!func || func(conf, param))) |
366 |
best = conf; |
367 |
|
368 |
if (*host) |
369 |
do { |
370 |
if (*host++ == '.') |
371 |
break; |
372 |
} |
373 |
while (*host); |
374 |
else |
375 |
break; |
376 |
} |
377 |
|
378 |
return best; |
379 |
} |
380 |
|
381 |
/* |
382 |
* expire_confs() |
383 |
* |
384 |
* Called periodically to remove expired conf entries. |
385 |
* |
386 |
* inputs: none |
387 |
* output: none |
388 |
*/ |
389 |
static void |
390 |
expire_confs(void *unused) |
391 |
{ |
392 |
enum_access_confs(is_acb_expired, NULL); |
393 |
execute_callback(cb_expire_confs); |
394 |
} |
395 |
|
396 |
/* |
397 |
* init_access() |
398 |
* |
399 |
* Initializes ACB's support. |
400 |
* |
401 |
* inputs: none |
402 |
* output: none |
403 |
*/ |
404 |
void |
405 |
init_access(void) |
406 |
{ |
407 |
hreset = install_hook(reset_conf, reset_access); |
408 |
|
409 |
cb_expire_confs = register_callback(NULL, NULL); |
410 |
eventAddIsh("expire_confs", expire_confs, NULL, EXPIRE_FREQUENCY); |
411 |
} |