1 |
/** |
2 |
* access.C: Changes and shows nickname access lists. |
3 |
* |
4 |
* Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team |
5 |
* Rights to this code are as documented in COPYING. |
6 |
* |
7 |
* |
8 |
* Portions of this file were derived from sources bearing the following license: |
9 |
* Copyright © 2006-2007 Atheme Development Group |
10 |
* Rights to this code are as documented in doc/pod/license.pod. |
11 |
* |
12 |
* $Id: access.C,v 1.8 2007-09-16 18:54:43 pippijn Exp $ |
13 |
*/ |
14 |
|
15 |
#include "atheme.h" |
16 |
#include <ermyth/module.h> |
17 |
#include <account/myuser.h> |
18 |
#include <account/mynick.h> |
19 |
|
20 |
static char const rcsid[] = "$Id: access.C,v 1.8 2007-09-16 18:54:43 pippijn Exp $"; |
21 |
|
22 |
REGISTER_MODULE ("nickserv/access", false, "The Ermyth Team <http://ermyth.xinutec.org>"); |
23 |
|
24 |
static void ns_cmd_access (sourceinfo_t *si, int parc, char *parv[]); |
25 |
|
26 |
command_t const ns_access = { "ACCESS", N_("Changes and shows your nickname access list."), AC_NONE, 2, ns_cmd_access }; |
27 |
|
28 |
E cmdvec ns_cmdtree; |
29 |
E helpvec ns_helptree; |
30 |
|
31 |
bool |
32 |
_modinit (module *m) |
33 |
{ |
34 |
ns_cmdtree << ns_access; |
35 |
help_addentry (ns_helptree, "ACCESS", "help/nickserv/access", NULL); |
36 |
|
37 |
use_myuser_access++; |
38 |
|
39 |
return true; |
40 |
} |
41 |
|
42 |
void |
43 |
_moddeinit () |
44 |
{ |
45 |
ns_cmdtree >> ns_access; |
46 |
help_delentry (ns_helptree, "ACCESS"); |
47 |
|
48 |
use_myuser_access--; |
49 |
} |
50 |
|
51 |
static bool |
52 |
username_is_random (char const * const name) |
53 |
{ |
54 |
char const *p = name; |
55 |
int lower = 0, upper = 0, digit = 0; |
56 |
|
57 |
if (*p == '~') |
58 |
p++; |
59 |
if (strlen (p) < 9) |
60 |
return false; |
61 |
while (*p != '\0') |
62 |
{ |
63 |
if (isdigit (*p)) |
64 |
digit++; |
65 |
else if (isupper (*p)) |
66 |
upper++; |
67 |
else if (islower (*p)) |
68 |
lower++; |
69 |
p++; |
70 |
} |
71 |
if (digit >= 4 && lower + upper > 1) |
72 |
return true; |
73 |
if (lower == 0 || upper == 0 || (upper <= 2 && isupper (*name))) |
74 |
return false; |
75 |
return true; |
76 |
} |
77 |
|
78 |
static char * |
79 |
construct_mask (user_t *u) |
80 |
{ |
81 |
static char mask[USERLEN + HOSTLEN]; |
82 |
char const * const dynhosts[] = { "*dyn*.*", "*dial*.*.*", "*dhcp*.*.*", |
83 |
"*.t-online.??", "*.t-online.???", |
84 |
"*.t-dialin.??", "*.t-dialin.???", |
85 |
"*.t-ipconnect.??", "*.t-ipconnect.???", |
86 |
"*.ipt.aol.com", NULL |
87 |
}; |
88 |
int i; |
89 |
bool hostisdyn = false, havedigits; |
90 |
char const *p, *prevdot, *lastdot; |
91 |
|
92 |
for (i = 0; dynhosts[i] != NULL; i++) |
93 |
if (!match (dynhosts[i], u->host)) |
94 |
hostisdyn = true; |
95 |
if (hostisdyn) |
96 |
{ |
97 |
/* note that all dyn patterns contain a dot */ |
98 |
p = u->host; |
99 |
prevdot = u->host; |
100 |
lastdot = strrchr (u->host, '.'); |
101 |
havedigits = true; |
102 |
while (*p) |
103 |
{ |
104 |
if (*p == '.') |
105 |
{ |
106 |
if (!havedigits || p == lastdot || !strcasecmp (p, ".Level3.net")) |
107 |
break; |
108 |
prevdot = p; |
109 |
havedigits = false; |
110 |
} |
111 |
else if (isdigit (*p)) |
112 |
havedigits = true; |
113 |
p++; |
114 |
} |
115 |
snprintf (mask, sizeof mask, "%s@*%s", u->user, prevdot); |
116 |
} |
117 |
else if (username_is_random (u->user)) |
118 |
snprintf (mask, sizeof mask, "*@%s", u->host); |
119 |
else if (!strcmp (u->host, u->ip) && (p = strrchr (u->ip, '.')) != NULL) |
120 |
snprintf (mask, sizeof mask, "%s@%.*s.0/24", u->user, (int) (p - u->ip), u->ip); |
121 |
else |
122 |
snprintf (mask, sizeof mask, "%s@%s", u->user, u->host); |
123 |
return mask; |
124 |
} |
125 |
|
126 |
static bool |
127 |
mangle_wildcard_to_cidr (char const * const host, char *dest, int destlen) |
128 |
{ |
129 |
int i; |
130 |
char const *p = host; |
131 |
|
132 |
if ((p[0] != '0' || p[1] != '.') && ((i = atoi (p)) < 1 || i > 255)) |
133 |
return false; |
134 |
while (isdigit (*p)) |
135 |
p++; |
136 |
if (*p++ != '.') |
137 |
return false; |
138 |
if (p[0] == '*' && p[1] == '\0') |
139 |
{ |
140 |
snprintf (dest, destlen, "%.*s0.0.0/8", (int) (p - host), host); |
141 |
return true; |
142 |
} |
143 |
|
144 |
if ((p[0] != '0' || p[1] != '.') && ((i = atoi (p)) < 1 || i > 255)) |
145 |
return false; |
146 |
while (isdigit (*p)) |
147 |
p++; |
148 |
if (*p++ != '.') |
149 |
return false; |
150 |
if (p[0] == '*' && (p[1] == '\0' || (p[1] == '.' && p[2] == '*' && p[3] == '\0'))) |
151 |
{ |
152 |
snprintf (dest, destlen, "%.*s0.0/16", (int) (p - host), host); |
153 |
return true; |
154 |
} |
155 |
|
156 |
if ((p[0] != '0' || p[1] != '.') && ((i = atoi (p)) < 1 || i > 255)) |
157 |
return false; |
158 |
while (isdigit (*p)) |
159 |
p++; |
160 |
if (*p++ != '.') |
161 |
return false; |
162 |
if (p[0] == '*' && p[1] == '\0') |
163 |
{ |
164 |
snprintf (dest, destlen, "%.*s0/24", (int) (p - host), host); |
165 |
return true; |
166 |
} |
167 |
|
168 |
return false; |
169 |
} |
170 |
|
171 |
void |
172 |
myuser_access_delete_enforce (myuser_t *mu, char *mask) |
173 |
{ |
174 |
list_t l; |
175 |
node_t *n, *tn; |
176 |
mynick_t *mn; |
177 |
user_t *u; |
178 |
|
179 |
/* find users who get access via the access list */ |
180 |
LIST_FOREACH (n, mu->nicks.head) |
181 |
{ |
182 |
mn = static_cast<mynick_t *> (n->data); |
183 |
u = user_find_named (mn->nick); |
184 |
if (u != NULL && u->myuser != mu && mu->access_verify (u)) |
185 |
node_add (u, node_create (), &l); |
186 |
} |
187 |
/* remove mask */ |
188 |
mu->access_delete (mask); |
189 |
/* check if those users still have access */ |
190 |
LIST_FOREACH_SAFE (n, tn, l.head) |
191 |
{ |
192 |
u = static_cast<user_t *> (n->data); |
193 |
node_del (n, &l); |
194 |
node_free (n); |
195 |
if (!mu->access_verify (u)) |
196 |
{ |
197 |
mn = mynick_t::find (u->nick); |
198 |
if (mn != NULL) |
199 |
mn->callback.enforce (mn, u); |
200 |
} |
201 |
} |
202 |
} |
203 |
|
204 |
static void |
205 |
ns_cmd_access (sourceinfo_t *si, int parc, char *parv[]) |
206 |
{ |
207 |
myuser_t *mu; |
208 |
node_t *n; |
209 |
char *mask; |
210 |
char *host; |
211 |
char *p; |
212 |
char mangledmask[NICKLEN + HOSTLEN + 10]; |
213 |
|
214 |
if (parc < 1) |
215 |
{ |
216 |
command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "ACCESS"); |
217 |
command_fail (si, fault::needmoreparams, _("Syntax: ACCESS ADD|DEL|LIST [mask]")); |
218 |
return; |
219 |
} |
220 |
|
221 |
if (!strcasecmp (parv[0], "LIST")) |
222 |
{ |
223 |
if (parc < 2) |
224 |
{ |
225 |
mu = si->smu; |
226 |
if (mu == NULL) |
227 |
{ |
228 |
command_fail (si, fault::noprivs, _("You are not logged in.")); |
229 |
return; |
230 |
} |
231 |
} |
232 |
else |
233 |
{ |
234 |
if (!has_priv (si, PRIV_USER_AUSPEX)) |
235 |
{ |
236 |
command_fail (si, fault::noprivs, _("You are not authorized to use the target argument.")); |
237 |
return; |
238 |
} |
239 |
|
240 |
if (!(mu = myuser_t::find_ext (parv[1]))) |
241 |
{ |
242 |
command_fail (si, fault::badparams, _("\2%s\2 is not registered."), parv[1]); |
243 |
return; |
244 |
} |
245 |
} |
246 |
|
247 |
if (mu != si->smu) |
248 |
logcommand (si, CMDLOG_ADMIN, "ACCESS LIST %s", mu->name); |
249 |
else |
250 |
logcommand (si, CMDLOG_GET, "ACCESS LIST"); |
251 |
|
252 |
command_success_nodata (si, _("Access list for \2%s\2:"), mu->name); |
253 |
|
254 |
LIST_FOREACH (n, mu->access_list.head) |
255 |
{ |
256 |
mask = static_cast<char *> (n->data); |
257 |
command_success_nodata (si, "- %s", mask); |
258 |
} |
259 |
|
260 |
command_success_nodata (si, _("End of \2%s\2 access list."), mu->name); |
261 |
} |
262 |
else if (!strcasecmp (parv[0], "ADD")) |
263 |
{ |
264 |
mu = si->smu; |
265 |
if (parc < 2) |
266 |
{ |
267 |
if (si->su == NULL) |
268 |
{ |
269 |
command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "ACCESS ADD"); |
270 |
command_fail (si, fault::needmoreparams, _("Syntax: ACCESS ADD <mask>")); |
271 |
return; |
272 |
} |
273 |
else |
274 |
mask = construct_mask (si->su); |
275 |
} |
276 |
else |
277 |
mask = parv[1]; |
278 |
if (mu == NULL) |
279 |
{ |
280 |
command_fail (si, fault::noprivs, _("You are not logged in.")); |
281 |
return; |
282 |
} |
283 |
if (mask[0] == '*' && mask[1] == '!') |
284 |
mask += 2; |
285 |
if (strlen (mask) >= USERLEN + HOSTLEN) |
286 |
{ |
287 |
command_fail (si, fault::badparams, _("Invalid mask \2%s\2."), parv[1]); |
288 |
return; |
289 |
} |
290 |
p = mask; |
291 |
while (*p != '\0') |
292 |
{ |
293 |
if (!isprint (*p) || *p == ' ' || *p == '!') |
294 |
{ |
295 |
command_fail (si, fault::badparams, _("Invalid mask \2%s\2."), parv[1]); |
296 |
return; |
297 |
} |
298 |
p++; |
299 |
} |
300 |
host = strchr (mask, '@'); |
301 |
if (host == NULL) /* account name access masks? */ |
302 |
{ |
303 |
command_fail (si, fault::badparams, _("Invalid mask \2%s\2."), parv[1]); |
304 |
return; |
305 |
} |
306 |
host++; |
307 |
/* try mangling to cidr */ |
308 |
strlcpy (mangledmask, mask, sizeof mangledmask); |
309 |
if (mangle_wildcard_to_cidr (host, mangledmask + (host - mask), sizeof mangledmask - (host - mask))) |
310 |
host = mangledmask + (host - mask), mask = mangledmask; |
311 |
/* more checks */ |
312 |
if (si->su != NULL && (!strcasecmp (host, si->su->host) || !strcasecmp (host, si->su->vhost))) |
313 |
; /* it's their host, allow it */ |
314 |
else if (host[0] == '.' || host[0] == ':' || host[0] == '\0' || host[1] == '\0' || host == mask + 1 || strchr (host, '@') || strstr (host, "..")) |
315 |
{ |
316 |
command_fail (si, fault::badparams, _("Invalid mask \2%s\2."), parv[1]); |
317 |
return; |
318 |
} |
319 |
else if ((strchr (host, '*') || strchr (host, '?')) && (mask[0] == '*' && mask[1] == '@')) |
320 |
{ |
321 |
/* can't use * username and wildcarded host */ |
322 |
command_fail (si, fault::badparams, _("Too wide mask \2%s\2."), parv[1]); |
323 |
return; |
324 |
} |
325 |
else if ((p = strrchr (host, '/')) != NULL) |
326 |
{ |
327 |
if (isdigit (p[1]) && (atoi (p + 1) < 16 || (mask[0] == '*' && mask[1] == '@'))) |
328 |
{ |
329 |
command_fail (si, fault::badparams, _("Too wide mask \2%s\2."), parv[1]); |
330 |
return; |
331 |
} |
332 |
if (host[0] == '*') |
333 |
{ |
334 |
command_fail (si, fault::badparams, _("Too wide mask \2%s\2."), parv[1]); |
335 |
return; |
336 |
} |
337 |
} |
338 |
else |
339 |
{ |
340 |
p = strrchr (host, '.'); |
341 |
if (p != NULL) |
342 |
{ |
343 |
/* No wildcarded IPs */ |
344 |
if (isdigit (p[1]) && (strchr (host, '*') || strchr (host, '?'))) |
345 |
{ |
346 |
command_fail (si, fault::badparams, _("Too wide mask \2%s\2."), parv[1]); |
347 |
return; |
348 |
} |
349 |
/* Require non-wildcard top and second level |
350 |
* domain */ |
351 |
if (strchr (p, '?') || strchr (p, '*')) |
352 |
{ |
353 |
command_fail (si, fault::badparams, _("Too wide mask \2%s\2."), parv[1]); |
354 |
return; |
355 |
} |
356 |
p--; |
357 |
while (p >= host && *p != '.') |
358 |
{ |
359 |
if (*p == '?' || *p == '*') |
360 |
{ |
361 |
command_fail (si, fault::badparams, _("Too wide mask \2%s\2."), parv[1]); |
362 |
return; |
363 |
} |
364 |
p--; |
365 |
} |
366 |
} |
367 |
else if (strchr (host, ':')) |
368 |
{ |
369 |
/* No wildcarded IPs */ |
370 |
if (strchr (host, '?') || strchr (host, '*')) |
371 |
{ |
372 |
command_fail (si, fault::badparams, _("Too wide mask \2%s\2."), parv[1]); |
373 |
return; |
374 |
} |
375 |
} |
376 |
else /* no '.' or ':' */ |
377 |
{ |
378 |
command_fail (si, fault::badparams, _("Invalid mask \2%s\2."), parv[1]); |
379 |
return; |
380 |
} |
381 |
} |
382 |
if (mu->access_find (mask)) |
383 |
{ |
384 |
command_fail (si, fault::nochange, _("Mask \2%s\2 is already on your access list."), mask); |
385 |
return; |
386 |
} |
387 |
if (mu->access_add (mask)) |
388 |
{ |
389 |
command_success_nodata (si, _("Added mask \2%s\2 to your access list."), mask); |
390 |
logcommand (si, CMDLOG_SET, "ACCESS ADD %s", mask); |
391 |
} |
392 |
else |
393 |
command_fail (si, fault::toomany, _("Your access list is full.")); |
394 |
} |
395 |
else if (!strcasecmp (parv[0], "DEL")) |
396 |
{ |
397 |
if (parc < 2) |
398 |
{ |
399 |
command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "ACCESS DEL"); |
400 |
command_fail (si, fault::needmoreparams, _("Syntax: ACCESS DEL <mask>")); |
401 |
return; |
402 |
} |
403 |
mu = si->smu; |
404 |
if (mu == NULL) |
405 |
{ |
406 |
command_fail (si, fault::noprivs, _("You are not logged in.")); |
407 |
return; |
408 |
} |
409 |
if ((mask = mu->access_find (parv[1])) == NULL) |
410 |
{ |
411 |
command_fail (si, fault::nochange, _("Mask \2%s\2 is not on your access list."), parv[1]); |
412 |
return; |
413 |
} |
414 |
command_success_nodata (si, _("Deleted mask \2%s\2 from your access list."), mask); |
415 |
logcommand (si, CMDLOG_SET, "ACCESS DEL %s", mask); |
416 |
myuser_access_delete_enforce (mu, mask); |
417 |
} |
418 |
else |
419 |
{ |
420 |
command_fail (si, fault::needmoreparams, STR_INVALID_PARAMS, "ACCESS"); |
421 |
command_fail (si, fault::needmoreparams, _("Syntax: ACCESS ADD|DEL|LIST [mask]")); |
422 |
return; |
423 |
} |
424 |
} |