ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/nickserv/identify.C
Revision: 1.1
Committed: Thu Jul 19 08:24:54 2007 UTC (19 years, 2 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Log Message:
initial import. the most important changes since Atheme are:
- fixed many memory leaks
- fixed many bugs
- converted to C++ and use more STL containers
- added a (not very enhanced yet) perl module
- greatly improved XML-RPC speed
- added a JSON-RPC module with code from json-cpp
- added a valgrind memcheck module to operserv
- added a more object oriented base64 implementation
- added a specialised unit test framework
- improved stability
- use gettimeofday() if available
- reworked adding/removing commands
- MemoServ IGNORE DEL can now remove indices

File Contents

# User Rev Content
1 pippijn 1.1 /*
2     * Copyright © 2005-2006 William Pitcock, et al.
3     * Rights to this code are as documented in doc/LICENSE.
4     *
5     * This file contains code for the NickServ IDENTIFY and LOGIN functions.
6     *
7     * $Id: identify.c 7969 2007-03-23 19:19:38Z jilles $
8     */
9    
10     #include "atheme.h"
11     #include <account/mychan.h>
12     #include <account/chanacs.h>
13     #include <account/mynick.h>
14     #include <account/myuser.h>
15    
16     /* Check whether we are compiling IDENTIFY or LOGIN */
17     #ifdef NICKSERV_LOGIN
18     #define COMMAND_UC "LOGIN"
19     #define COMMAND_LC "login"
20     #else
21     #define COMMAND_UC "IDENTIFY"
22     #define COMMAND_LC "identify"
23     #endif
24    
25     static char const rcsid[] = "$Id";
26    
27     struct ns_identify_listeners : callback::has_listeners
28     {
29     };
30    
31     ns_identify_listeners listeners;
32    
33     DECLARE_MODULE_V1 ("nickserv/" COMMAND_LC, false, _modinit, _moddeinit, rcsid, "The Ermyth Team <http://ermyth.schmorp.de>");
34    
35     static void ns_cmd_login (sourceinfo_t *si, int parc, char *parv[]);
36    
37     #ifdef NICKSERV_LOGIN
38     command_t ns_login = { "LOGIN", N_("Authenticates to a services account."), AC_NONE, 2, ns_cmd_login };
39     #else
40     command_t ns_identify = { "IDENTIFY", N_("Identifies to services for a nickname."), AC_NONE, 2, ns_cmd_login };
41     command_t ns_id = { "ID", N_("Alias for IDENTIFY"), AC_NONE, 2, ns_cmd_login };
42     #endif
43    
44     cmdvec *ns_cmdtree;
45     helpvec *ns_helptree;
46    
47     void
48     _modinit (module_t *m)
49     {
50     MODULE_USE_SYMBOL_T (ns_cmdtree, cmdvec, "nickserv/main", "ns_cmdtree");
51     MODULE_USE_SYMBOL_T (ns_helptree, helpvec, "nickserv/main", "ns_helptree");
52    
53     #ifdef NICKSERV_LOGIN
54     ns_cmdtree << ns_login;
55     help_addentry (ns_helptree, "LOGIN", "help/nickserv/login", NULL);
56     #else
57     ns_cmdtree << ns_identify;
58     ns_cmdtree << ns_id;
59     help_addentry (ns_helptree, "IDENTIFY", "help/nickserv/identify", NULL);
60     help_addentry (ns_helptree, "ID", "help/nickserv/identify", NULL);
61     #endif
62     }
63    
64     void
65     _moddeinit ()
66     {
67     #ifdef NICKSERV_LOGIN
68     ns_cmdtree >> ns_login;
69     help_delentry (ns_helptree, "LOGIN");
70     #else
71     ns_cmdtree >> ns_identify;
72     ns_cmdtree >> ns_id;
73     help_delentry (ns_helptree, "IDENTIFY");
74     help_delentry (ns_helptree, "ID");
75     #endif
76     }
77    
78     static void
79     ns_cmd_login (sourceinfo_t *si, int parc, char *parv[])
80     {
81     user_t *u = si->su;
82     myuser_t *mu;
83     mynick_t *mn;
84     chanuser_t *cu;
85     chanacs_t *ca;
86     node_t *n;
87     char *target = parv[0];
88     char *password = parv[1];
89     char buf[BUFSIZE], strfbuf[32];
90     char lau[BUFSIZE], lao[BUFSIZE];
91     struct tm tm;
92     metadata *md_failnum;
93    
94     if (si->su == NULL)
95     {
96     command_fail (si, fault_noprivs, _("\2%s\2 can only be executed via IRC."), COMMAND_UC);
97     return;
98     }
99    
100     #ifndef NICKSERV_LOGIN
101     if (!nicksvs.no_nick_ownership && target && !password)
102     {
103     password = target;
104     target = si->su->nick;
105     }
106     #endif
107    
108     if (!target || !password)
109     {
110     command_fail (si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, COMMAND_UC);
111     command_fail (si, fault_needmoreparams, nicksvs.no_nick_ownership ? "Syntax: " COMMAND_UC " <account> <password>" : "Syntax: " COMMAND_UC " [nick] <password>");
112     return;
113     }
114    
115     if (nicksvs.no_nick_ownership)
116     mu = myuser_find (target);
117     else
118     {
119     mn = mynick_find (target);
120     mu = mn != NULL ? mn->owner : NULL;
121     }
122    
123     if (!mu)
124     {
125     command_fail (si, fault_nosuch_target, _("\2%s\2 is not a registered nickname."), target);
126     return;
127     }
128    
129     if (mu->find_metadata ("private:freeze:freezer"))
130     {
131     command_fail (si, fault_authfail, nicksvs.no_nick_ownership ? "You cannot login as \2%s\2 because the account has been frozen." : "You cannot identify to \2%s\2 because the nickname has been frozen.", mu->name);
132     logcommand (si, CMDLOG_LOGIN, "failed " COMMAND_UC " to %s (frozen)", mu->name);
133     return;
134     }
135    
136     if (u->myuser == mu)
137     {
138     command_fail (si, fault_nochange, _("You are already logged in as \2%s\2."), u->myuser->name);
139     return;
140     }
141     else if (u->myuser != NULL && !si->service->cmdtree->find ("LOGOUT"))
142     {
143     command_fail (si, fault_alreadyexists, _("You are already logged in as \2%s\2."), u->myuser->name);
144     return;
145     }
146     else if (u->myuser != NULL && ircd_on_logout (u->nick, u->myuser->name, NULL))
147     /* logout killed the user... */
148     return;
149    
150     /* we use this in both cases, so set it up here. may be NULL. */
151     md_failnum = mu->find_metadata ("private:loginfail:failnum");
152    
153     if (verify_password (mu, password))
154     {
155     if (mu->logins.size () >= me.maxlogins)
156     {
157     command_fail (si, fault_toomany, _("There are already \2%d\2 sessions logged in to \2%s\2 (maximum allowed: %d)."), mu->logins.size (), mu->name, me.maxlogins);
158     logcommand (si, CMDLOG_LOGIN, "failed " COMMAND_UC " to %s (too many logins)", mu->name);
159     return;
160     }
161    
162     /* if they are identified to another account, nuke their session first */
163     if (u->myuser)
164     {
165     myuser_t::login_vector::iterator it, it_end;
166     u->myuser->lastlogin = NOW;
167     for (it = u->myuser->logins.begin (), it_end = u->myuser->logins.end (); it != it_end; ++it)
168     {
169     if (*it == u)
170     {
171     u->myuser->logins.erase (u);
172     break;
173     }
174     }
175     u->myuser = NULL;
176     }
177    
178     if (is_soper (mu))
179     snoop ("SOPER: \2%s\2 as \2%s\2", u->nick, mu->name);
180    
181     myuser_notice (nicksvs.nick, mu, "%s!%s@%s has just authenticated as you (%s)", u->nick, u->user, u->vhost, mu->name);
182    
183     u->myuser = mu;
184     mu->logins.insert (u);
185    
186     /* keep track of login address for users */
187     strlcpy (lau, u->user, BUFSIZE);
188     strlcat (lau, "@", BUFSIZE);
189     strlcat (lau, u->vhost, BUFSIZE);
190     mu->add_metadata ("private:host:vhost", lau);
191    
192     /* and for opers */
193     strlcpy (lao, u->user, BUFSIZE);
194     strlcat (lao, "@", BUFSIZE);
195     strlcat (lao, u->host, BUFSIZE);
196     mu->add_metadata ("private:host:actual", lao);
197    
198     logcommand (si, CMDLOG_LOGIN, COMMAND_UC);
199    
200     command_success_nodata (si, nicksvs.no_nick_ownership ? "You are now logged in as \2%s\2." : "You are now identified for \2%s\2.", u->myuser->name);
201    
202     /* check for failed attempts and let them know */
203     if (md_failnum && (atoi (md_failnum->value) > 0))
204     {
205     metadata *md_failtime, *md_failaddr;
206     time_t ts;
207    
208     tm = *localtime (&mu->lastlogin);
209     strftime (strfbuf, sizeof (strfbuf) - 1, "%b %d %H:%M:%S %Y", &tm);
210    
211     command_success_nodata (si, _("\2%d\2 failed %s since %s."), atoi (md_failnum->value), (atoi (md_failnum->value) == 1) ? "login" : "logins", strfbuf);
212    
213     md_failtime = mu->find_metadata ("private:loginfail:lastfailtime");
214     ts = atol (md_failtime->value);
215     md_failaddr = mu->find_metadata ("private:loginfail:lastfailaddr");
216    
217     tm = *localtime (&ts);
218     strftime (strfbuf, sizeof (strfbuf) - 1, "%b %d %H:%M:%S %Y", &tm);
219    
220     command_success_nodata (si, _("Last failed attempt from: \2%s\2 on %s."), md_failaddr->value, strfbuf);
221    
222     mu->del_metadata ("private:loginfail:failnum"); /* md_failnum now invalid */
223     mu->del_metadata ("private:loginfail:lastfailtime");
224     mu->del_metadata ("private:loginfail:lastfailaddr");
225     }
226    
227     mu->lastlogin = NOW;
228     mn = mynick_find (u->nick);
229     if (mn != NULL && mn->owner == mu)
230     mn->lastseen = NOW;
231    
232     /* XXX: ircd_on_login supports hostmasking, we just dont have it yet. */
233     /* don't allow them to join regonly chans until their
234     * email is verified */
235     if (!(mu->flags & MU_WAITAUTH))
236     ircd_on_login (si->su->nick, mu->name, NULL);
237    
238     hook_call_event ("user_identify", u);
239    
240     /* now we get to check for xOP */
241     /* we don't check for host access yet (could match different
242     * entries because of services cloaks) */
243     LIST_FOREACH (n, mu->chanacs.head)
244     {
245     ca = (chanacs_t *) n->data;
246    
247     cu = chanuser_find (ca->mychan->chan, u);
248     if (cu && chansvs.me != NULL)
249     {
250     if (ca->level & CA_AKICK && !(ca->level & CA_REMOVE))
251     {
252     /* Stay on channel if this would empty it -- jilles */
253     if (ca->mychan->chan->nummembers <= (ca->mychan->flags & MC_GUARD ? 2 : 1))
254     {
255     ca->mychan->flags |= MC_INHABIT;
256     if (!(ca->mychan->flags & MC_GUARD))
257     join (cu->chan->name, chansvs.nick);
258     }
259     ban (chansvs.me->me, ca->mychan->chan, u);
260     remove_ban_exceptions (chansvs.me->me, ca->mychan->chan, u);
261     kick (chansvs.nick, ca->mychan->name, u->nick, "User is banned from this channel");
262     continue;
263     }
264    
265     if (ca->level & CA_USEDUPDATE)
266     ca->mychan->used = NOW;
267    
268     if (ca->mychan->flags & MC_NOOP || mu->flags & MU_NOOP)
269     continue;
270    
271     if (ircd->uses_owner && !(cu->modes & ircd->owner_mode) && ca->level & CA_AUTOOP && should_owner (ca->mychan, ca->myuser))
272     {
273     modestack_mode_param (chansvs.nick, ca->mychan->chan, MTYPE_ADD, ircd->owner_mchar[1], CLIENT_NAME (u));
274     cu->modes |= ircd->owner_mode;
275     }
276    
277     if (ircd->uses_protect && !(cu->modes & ircd->protect_mode) && ca->level & CA_AUTOOP && should_protect (ca->mychan, ca->myuser))
278     {
279     modestack_mode_param (chansvs.nick, ca->mychan->chan, MTYPE_ADD, ircd->protect_mchar[1], CLIENT_NAME (u));
280     cu->modes |= ircd->protect_mode;
281     }
282    
283     if (!(cu->modes & CMODE_OP) && ca->level & CA_AUTOOP)
284     {
285     modestack_mode_param (chansvs.nick, ca->mychan->chan, MTYPE_ADD, 'o', CLIENT_NAME (u));
286     cu->modes |= CMODE_OP;
287     }
288    
289     if (ircd->uses_halfops && !(cu->modes & (CMODE_OP | ircd->halfops_mode)) && ca->level & CA_AUTOHALFOP)
290     {
291     modestack_mode_param (chansvs.nick, ca->mychan->chan, MTYPE_ADD, 'h', CLIENT_NAME (u));
292     cu->modes |= ircd->halfops_mode;
293     }
294    
295     if (!(cu->modes & (CMODE_OP | ircd->halfops_mode | CMODE_VOICE)) && ca->level & CA_AUTOVOICE)
296     {
297     modestack_mode_param (chansvs.nick, ca->mychan->chan, MTYPE_ADD, 'v', CLIENT_NAME (u));
298     cu->modes |= CMODE_VOICE;
299     }
300     }
301     }
302    
303     return;
304     }
305    
306     logcommand (si, CMDLOG_LOGIN, "failed " COMMAND_UC " to %s (bad password)", mu->name);
307    
308     command_fail (si, fault_authfail, _("Invalid password for \2%s\2."), mu->name);
309    
310     /* record the failed attempts */
311     /* note that we reuse this buffer later when warning opers about failed logins */
312     snprintf (buf, sizeof buf, "%s!%s@%s", u->nick, u->user, u->vhost);
313    
314     /* increment fail count */
315     if (md_failnum && (atoi (md_failnum->value) > 0))
316     md_failnum = mu->add_metadata ("private:loginfail:failnum", itoa (atoi (md_failnum->value) + 1));
317     else
318     md_failnum = mu->add_metadata ("private:loginfail:failnum", "1");
319     mu->add_metadata ("private:loginfail:lastfailaddr", buf);
320     mu->add_metadata ("private:loginfail:lastfailtime", itoa (NOW));
321    
322     if (atoi (md_failnum->value) == 10)
323     {
324     time_t ts = NOW;
325     tm = *localtime (&ts);
326     strftime (strfbuf, sizeof (strfbuf) - 1, "%b %d %H:%M:%S %Y", &tm);
327    
328     wallops ("Warning: Numerous failed login attempts to \2%s\2. Last attempt received from \2%s\2 on %s.", mu->name, buf, strfbuf);
329     }
330     }