ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/nickserv/identify.C
Revision: 1.10
Committed: Sat Sep 22 14:27:27 2007 UTC (16 years, 8 months 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

# Content
1 /**
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 * Copyright © 2005-2006 William Pitcock, et al.
10 * Rights to this code are as documented in doc/pod/license.pod.
11 *
12 * $Id: identify.C,v 1.9 2007-09-16 18:54:43 pippijn Exp $
13 */
14
15 #include "atheme.h"
16 #include <util/numeric.h>
17 #include <libermyth.h>
18 #include <ermyth/module.h>
19 #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 static char const rcsid[] = "$Id: identify.C,v 1.9 2007-09-16 18:54:43 pippijn Exp $";
34
35 REGISTER_MODULE ("nickserv/" COMMAND_LC, false, "The Ermyth Team <http://ermyth.xinutec.org>");
36
37 static void ns_cmd_login (sourceinfo_t *si, int parc, char *parv[]);
38
39 #ifdef NICKSERV_LOGIN
40 command_t const ns_login = { "LOGIN", N_("Authenticates to a services account."), AC_NONE, 2, ns_cmd_login };
41 #else
42 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 #endif
45
46 E cmdvec ns_cmdtree;
47 E helpvec ns_helptree;
48
49 bool
50 _modinit (module *m)
51 {
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
62 return true;
63 }
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 metadata *md_failnum;
94
95 if (si->su == NULL)
96 {
97 command_fail (si, fault::noprivs, _("\2%s\2 can only be executed via IRC."), COMMAND_UC);
98 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 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 return;
114 }
115
116 if (nicksvs.no_nick_ownership)
117 mu = myuser_t::find (target);
118 else
119 {
120 mn = mynick_t::find (target);
121 mu = mn != NULL ? mn->owner : NULL;
122 }
123
124 if (!mu)
125 {
126 command_fail (si, fault::nosuch_target, _("\2%s\2 is not a registered nickname."), target);
127 return;
128 }
129
130 if (mu->find_metadata ("private:freeze:freezer"))
131 {
132 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 logcommand (si, CMDLOG_LOGIN, "failed " COMMAND_UC " to %s (frozen)", mu->name);
134 return;
135 }
136
137 if (u->myuser == mu)
138 {
139 command_fail (si, fault::nochange, _("You are already logged in as \2%s\2."), u->myuser->name);
140 return;
141 }
142 else if (u->myuser != NULL && !si->service->cmdtree->find ("LOGOUT"))
143 {
144 command_fail (si, fault::alreadyexists, _("You are already logged in as \2%s\2."), u->myuser->name);
145 return;
146 }
147 else if (u->myuser != NULL && phandler->ircd_on_logout (u->nick, u->myuser->name, NULL))
148 /* 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 if (mu->verify_password (password))
155 {
156 if (mu->logins.size () >= me.maxlogins)
157 {
158 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 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 mu->notice (nicksvs.nick, "%s!%s@%s has just authenticated as you (%s)", u->nick, u->user, u->vhost, mu->name);
183
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 metadata *md_failtime, *md_failaddr;
207 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 mn = mynick_t::find (u->nick);
230 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 phandler->ircd_on_login (si->su->nick, mu->name, NULL);
238
239 u->callback.identify (u);
240
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 phandler->kick (chansvs.nick, ca->mychan->name, u->nick, "User is banned from this channel");
263 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 if (ircd->uses_owner && !(cu->modes & ircd->owner_mode) && ca->level & CA_AUTOOP && ca->myuser->should_owner (ca->mychan))
273 {
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 if (ircd->uses_protect && !(cu->modes & ircd->protect_mode) && ca->level & CA_AUTOOP && ca->myuser->should_protect (ca->mychan))
279 {
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 command_fail (si, fault::authfail, _("Invalid password for \2%s\2."), mu->name);
310
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 }