ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/nickserv/enforce.C
Revision: 1.2
Committed: Sat Jul 21 01:29:09 2007 UTC (19 years, 2 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.1: +1 -1 lines
Log Message:
- moved to new documentation system
- fixed small build error

File Contents

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