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