ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/nickserv/enforce.C
Revision: 1.4
Committed: Tue Aug 28 17:08:09 2007 UTC (19 years, 1 month ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.3: +60 -85 lines
Log Message:
- changed name
- updated the example config to the new system
- added more documentation
- enhanced documentation generators
- added a link to the pdf to the website
- added an RSS feed generator
- transitioned hooks to c++ callbacks
- did various merges with upstream along the way
- added const where appropriate
- removed the old block allocator
- fixed most memory leaks
- transitioned some dictionaries to std::map
- transitioned some lists to std::vector
- made some free functions members where appropriate
- renamed string to dynstr and added a static string ststr
- use NOW instead of time (NULL) if possible
- completely reworked database backends, crypto handlers and protocol handlers
  to use an object factory
- removed the old module system. ermyth does not do any dynamic loading anymore
- fixed most of the build system
- reworked how protocol commands work

File Contents

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