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

File Contents

# User Rev Content
1 pippijn 1.9 /**
2     * enforce.C: This file contains code for the NickServ RELEASE/ENFORCE functions.
3 pippijn 1.1 *
4 pippijn 1.9 * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
5     * Rights to this code are as documented in COPYING.
6 pippijn 1.1 *
7     * This does nickserv enforcement on registered nicks if the ENFORCE option
8     * has been enabled. Users who do not identify within 30-60 seconds have
9     * their nick changed to Guest<num>.
10     * If the ircd or protocol module do not support forced nick changes,
11     * they are killed instead.
12     * Enforcement of the nick is only supported for ircds that support
13     * holdnick_sts(), currently bahamut, charybdis, hybrid, inspircd11,
14     * solidircd, ratbox and unreal (i.e. making sure they can't change back
15     * immediately). Consequently this module is of little use for other ircds.
16     * Note: For hybrid and ratbox, and charybdis before 2.1, the
17     * RELEASE command to remove an enforcer prematurely is not supported,
18     * although it pretends to be successful.
19 pippijn 1.9 *
20     *
21     * Portions of this file were derived from sources bearing the following license:
22     * Copyright © 2005-2007 Atheme Development Group
23     * Rights to this code are as documented in doc/pod/license.pod.
24     *
25 pippijn 1.10 * $Id: enforce.C,v 1.9 2007-09-16 18:54:43 pippijn Exp $
26 pippijn 1.1 */
27    
28     #include "atheme.h"
29 pippijn 1.10 #include <libermyth.h>
30 pippijn 1.4 #include <ermyth/module.h>
31 pippijn 1.1 #include <account/mynick.h>
32     #include <account/myuser.h>
33 pippijn 1.10 #include <util/random.h>
34 pippijn 1.1
35 pippijn 1.10 static char const rcsid[] = "$Id: enforce.C,v 1.9 2007-09-16 18:54:43 pippijn Exp $";
36 pippijn 1.1
37 pippijn 1.10 REGISTER_MODULE ("nickserv/enforce", false, "The Ermyth Team <http://ermyth.xinutec.org>");
38 pippijn 1.1
39 pippijn 1.4 #define SHOW_CORRECT_TIMEOUT_BUT_BE_SLOW 0
40 pippijn 1.1
41     #define ENFORCE_TIMEOUT 30
42     #define ENFORCE_CHECK_FREQ 5
43    
44 pippijn 1.4 struct enforce_timeout_t : zero_initialised
45 pippijn 1.1 {
46     char nick[NICKLEN];
47     char host[HOSTLEN];
48     time_t timelimit;
49     node_t node;
50 pippijn 1.4 };
51 pippijn 1.1
52     list_t enforce_list;
53    
54     static void guest_nickname (user_t *u);
55    
56     static void ns_cmd_set_enforce (sourceinfo_t *si, int parc, char *parv[]);
57     static void ns_cmd_release (sourceinfo_t *si, int parc, char *parv[]);
58    
59     static void enforce_timeout_check (void *arg);
60 pippijn 1.4
61     command_t const ns_set_enforce = { "ENFORCE", N_("Enables or disables automatic protection of a nickname."), AC_NONE, 1, ns_cmd_set_enforce };
62     command_t const ns_release = { "RELEASE", N_("Releases a services enforcer."), AC_NONE, 2, ns_cmd_release };
63    
64     E cmdvec ns_cmdtree;
65     E cmdvec ns_set_cmdtree;
66     E helpvec ns_helptree;
67 pippijn 1.1
68     /* sends an FNC for the given user */
69     static void
70     guest_nickname (user_t *u)
71     {
72     char gnick[NICKLEN];
73     int tries;
74    
75     /* Generate a new guest nickname and check if it already exists
76     * This will try to generate a new nickname 30 different times
77     * if nicks are in use. If it runs into 30 nicks in use, maybe
78     * you shouldn't use this module. */
79     for (tries = 0; tries < 30; tries++)
80     {
81 pippijn 1.4 snprintf (gnick, sizeof gnick, "Guest%d", gen_rand32 () % 100000);
82 pippijn 1.1 if (!user_find_named (gnick))
83     break;
84     }
85 pippijn 1.4 phandler->fnc_sts (nicksvs.me->me, u, gnick, FNC_FORCE);
86 pippijn 1.1 }
87    
88     static void
89     ns_cmd_set_enforce (sourceinfo_t *si, int parc, char *parv[])
90     {
91     char *setting = parv[0];
92    
93     if (!setting)
94     {
95 pippijn 1.6 command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "ENFORCE");
96     command_fail (si, fault::needmoreparams, _("Syntax: SET ENFORCE ON|OFF"));
97 pippijn 1.1 return;
98     }
99    
100     if (!si->smu)
101     {
102 pippijn 1.6 command_fail (si, fault::noprivs, _("You are not logged in."));
103 pippijn 1.1 return;
104     }
105    
106     if (strcasecmp (setting, "ON") == 0)
107     {
108     if (si->smu->find_metadata ("private:doenforce"))
109 pippijn 1.6 command_fail (si, fault::nochange, _("ENFORCE is already enabled."));
110 pippijn 1.1 else
111     {
112     si->smu->add_metadata ("private:doenforce", "1");
113     command_success_nodata (si, _("ENFORCE is now enabled."));
114     }
115     }
116     else if (strcasecmp (setting, "OFF") == 0)
117     {
118     if (si->smu->del_metadata ("private:doenforce"))
119     command_success_nodata (si, _("ENFORCE is now disabled."));
120     else
121 pippijn 1.6 command_fail (si, fault::nochange, _("ENFORCE is already disabled."));
122 pippijn 1.1 }
123     else
124 pippijn 1.6 command_fail (si, fault::badparams, _("Unknown value for ENFORCE. Expected values are ON or OFF."));
125 pippijn 1.1 }
126    
127     static void
128     ns_cmd_release (sourceinfo_t *si, int parc, char *parv[])
129     {
130     mynick_t *mn;
131     char *target = parv[0];
132     char *password = parv[1];
133     user_t *u;
134     node_t *n, *tn;
135     enforce_timeout_t *timeout;
136    
137     /* Absolutely do not do anything like this if nicks
138     * are not considered owned */
139     if (nicksvs.no_nick_ownership)
140     {
141 pippijn 1.6 command_fail (si, fault::noprivs, _("RELEASE is disabled."));
142 pippijn 1.1 return;
143     }
144    
145     if (!target && si->smu != NULL)
146     target = si->smu->name;
147     if (!target)
148     {
149 pippijn 1.6 command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "RELEASE");
150     command_fail (si, fault::needmoreparams, _("Syntax: RELEASE <nick> [password]"));
151 pippijn 1.1 return;
152     }
153    
154     u = user_find_named (target);
155 pippijn 1.8 mn = mynick_t::find (target);
156 pippijn 1.1
157     if (!mn)
158     {
159 pippijn 1.6 command_fail (si, fault::nosuch_target, _("\2%s\2 is not a registered nickname."), target);
160 pippijn 1.1 return;
161     }
162    
163     if (u == si->su)
164     {
165 pippijn 1.6 command_fail (si, fault::noprivs, _("You cannot RELEASE yourself."));
166 pippijn 1.1 return;
167     }
168 pippijn 1.4 if ((si->smu == mn->owner) || mn->owner->verify_password (password))
169 pippijn 1.1 {
170     /* if this (nick, host) is waiting to be enforced, remove it */
171     LIST_FOREACH_SAFE (n, tn, enforce_list.head)
172     {
173     timeout = static_cast<enforce_timeout_t *> (n->data);
174     if (!irccasecmp (mn->nick, timeout->nick) && (!strcmp (u->host, timeout->host) || !strcmp (u->vhost, timeout->host)))
175     {
176     node_del (&timeout->node, &enforce_list);
177 pippijn 1.4 delete timeout;
178 pippijn 1.1 }
179     }
180     if (u == NULL || is_internal_client (u))
181     {
182     logcommand (si, CMDLOG_DO, "RELEASE %s", target);
183 pippijn 1.4 phandler->holdnick_sts (si->service->me, 0, target, mn->owner);
184 pippijn 1.1 command_success_nodata (si, _("\2%s\2 has been released."), target);
185     }
186     else
187     {
188     notice (nicksvs.nick, target, "%s has released your nickname.", get_source_mask (si));
189     guest_nickname (u);
190     command_success_nodata (si, _("%s has been released."), target);
191     logcommand (si, CMDLOG_DO, "RELEASE %s!%s@%s", u->nick, u->user, u->vhost);
192     }
193     return;
194     }
195     if (!password)
196     {
197 pippijn 1.6 command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "RELEASE");
198     command_fail (si, fault::needmoreparams, _("Syntax: RELEASE <nickname> [password]"));
199 pippijn 1.1 return;
200     }
201     else
202     {
203     logcommand (si, CMDLOG_DO, "failed RELEASE %s (bad password)", target);
204 pippijn 1.6 command_fail (si, fault::authfail, _("Invalid password for \2%s\2."), target);
205 pippijn 1.1 }
206     }
207    
208     void
209     enforce_timeout_check (void *arg)
210     {
211     node_t *n, *tn;
212     enforce_timeout_t *timeout;
213     user_t *u;
214     mynick_t *mn;
215     bool valid;
216    
217     LIST_FOREACH_SAFE (n, tn, enforce_list.head)
218     {
219     timeout = static_cast<enforce_timeout_t *> (n->data);
220     if (timeout->timelimit > NOW)
221     break; /* assume sorted list */
222     u = user_find_named (timeout->nick);
223 pippijn 1.8 mn = mynick_t::find (timeout->nick);
224 pippijn 1.1 valid = u != NULL && mn != NULL && (!strcmp (u->host, timeout->host) || !strcmp (u->vhost, timeout->host));
225     node_del (&timeout->node, &enforce_list);
226 pippijn 1.4 delete timeout;
227 pippijn 1.1 if (!valid)
228     continue;
229     if (is_internal_client (u))
230     continue;
231     if (u->myuser == mn->owner)
232     continue;
233 pippijn 1.5 if (mn->owner->access_verify (u))
234 pippijn 1.1 continue;
235     if (!mn->owner->find_metadata ("private:doenforce"))
236     continue;
237    
238     notice (nicksvs.nick, u->nick, "You failed to identify in time for the nickname %s", mn->nick);
239     guest_nickname (u);
240 pippijn 1.4 phandler->holdnick_sts (nicksvs.me->me, 3600, u->nick, mn->owner);
241 pippijn 1.1 }
242     }
243    
244     static void
245 pippijn 1.4 show_enforce (mynick_t *mn, myuser_t *mu, sourceinfo_t *si)
246 pippijn 1.1 {
247 pippijn 1.4 if (!mu->find_metadata ("private:doenforce"))
248     command_success_nodata (si, "%s has enabled nick protection", mu->name);
249 pippijn 1.1 }
250    
251 pippijn 1.4 static bool
252     check_registration (sourceinfo_t *si, char const * const account, char const * const email)
253 pippijn 1.1 {
254 pippijn 1.4 if (!strncasecmp (account, "Guest", 5) && isdigit (account[5]))
255 pippijn 1.1 {
256 pippijn 1.6 command_fail (si, fault::badparams, "The nick \2%s\2 is reserved and cannot be registered.", account);
257 pippijn 1.4 return false;
258 pippijn 1.1 }
259 pippijn 1.4
260     return true;
261 pippijn 1.1 }
262    
263     static void
264 pippijn 1.4 check_enforce (mynick_t *mn, user_t *u)
265 pippijn 1.1 {
266     enforce_timeout_t *timeout;
267 pippijn 1.4 #if SHOW_CORRECT_TIMEOUT_BUT_BE_SLOW
268 pippijn 1.1 enforce_timeout_t *timeout2;
269     #endif
270    
271     /* nick is a service, ignore it */
272 pippijn 1.4 if (is_internal_client (u))
273 pippijn 1.1 return;
274    
275 pippijn 1.4 if (!mn->owner->find_metadata ("private:doenforce"))
276 pippijn 1.1 return;
277    
278     /* check if it's already in enforce_list */
279     timeout = NULL;
280 pippijn 1.4 #if SHOW_CORRECT_TIMEOUT_BUT_BE_SLOW
281 pippijn 1.1 /* don't do this now, it's O(n^2) in the number of users using
282     * a nick without access at a time */
283     LIST_FOREACH (n, enforce_list.head)
284     {
285     timeout2 = n->data;
286 pippijn 1.4 if (!irccasecmp (mn->nick, timeout2->nick) && (!strcmp (u->host, timeout2->host) || !strcmp (u->vhost, timeout2->host)))
287 pippijn 1.1 {
288     timeout = timeout2;
289     break;
290     }
291     }
292     #endif
293    
294     if (timeout == NULL)
295     {
296 pippijn 1.4 timeout = new enforce_timeout_t;
297     strlcpy (timeout->nick, mn->nick, sizeof timeout->nick);
298     strlcpy (timeout->host, u->host, sizeof timeout->host);
299 pippijn 1.1 /* the following ENFORCE_TIMEOUT must be constant,
300     * otherwise the timeouts will not be sorted and
301     * enforce_timeout_check() will break */
302     timeout->timelimit = NOW + ENFORCE_TIMEOUT;
303     node_add (timeout, &timeout->node, &enforce_list);
304     }
305    
306 pippijn 1.4 notice (nicksvs.nick, u->nick, "You have %d seconds to identify to your nickname before it is changed.", timeout->timelimit - NOW);
307 pippijn 1.1 }
308    
309 pippijn 1.4 static void
310 pippijn 1.7 idcheck_foreach_cb (myuser_t::pair_type &it)
311 pippijn 1.1 {
312 pippijn 1.4 myuser_t *mu = it.second;
313 pippijn 1.1
314     mu->del_metadata ("private:idcheck");
315     mu->del_metadata ("private:enforcer");
316     }
317    
318 pippijn 1.4 bool
319     _modinit (module *m)
320 pippijn 1.1 {
321     /* Leave this for compatibility with old versions of this code
322     * -- jilles
323     */
324 pippijn 1.7 std::for_each (myuser_t::map.begin (), myuser_t::map.end (), idcheck_foreach_cb);
325 pippijn 1.1
326     /* Absolutely do not do anything like this if nicks
327     * are not considered owned */
328     if (nicksvs.no_nick_ownership)
329     {
330 pippijn 1.4 slog (LG_ERROR, "%s: nicks are not configured to be owned", m->name);
331     return false;
332 pippijn 1.1 }
333    
334     event_add ("enforce_timeout_check", enforce_timeout_check, NULL, ENFORCE_CHECK_FREQ);
335     /*event_add("manage_bots", manage_bots, NULL, 30); */
336     ns_cmdtree << ns_release;
337     ns_set_cmdtree << ns_set_enforce;
338     help_addentry (ns_helptree, "RELEASE", "help/nickserv/release", NULL);
339     help_addentry (ns_helptree, "SET ENFORCE", "help/nickserv/set_enforce", NULL);
340 pippijn 1.4
341     mynick_t::callback.info.attach (show_enforce);
342     user_t::callback.can_register.attach (check_registration);
343     mynick_t::callback.enforce.attach (check_enforce);
344    
345     return true;
346 pippijn 1.1 }
347    
348     void
349     _moddeinit ()
350     {
351     event_delete (enforce_timeout_check, NULL);
352     ns_cmdtree >> ns_release;
353     ns_set_cmdtree >> ns_set_enforce;
354     help_delentry (ns_helptree, "RELEASE");
355     help_delentry (ns_helptree, "SET ENFORCE");
356 pippijn 1.4
357     mynick_t::callback.info.detach (show_enforce);
358     user_t::callback.can_register.detach (check_registration);
359     mynick_t::callback.enforce.detach (check_enforce);
360 pippijn 1.1 }