ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/nickserv/access.C
Revision: 1.9
Committed: Sat Sep 22 14:27:27 2007 UTC (16 years, 8 months ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.8: +3 -3 lines
Log Message:
split up ermyth into ermyth-modules, libermyth (currently just ermyth-util) and ermyth-core

File Contents

# Content
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 }