]> jfr.im git - solanum.git/blob - modules/m_signon.c
Complain to opers if a server that isn't a service tries to SU/RSFNC/NICKDELAY/SVSLOGIN.
[solanum.git] / modules / m_signon.c
1 /* modules/m_signon.c
2 * Copyright (C) 2006 Michael Tharp <gxti@partiallystapled.com>
3 * Copyright (C) 2006 charybdis development team
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * 1.Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * 2.Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3.The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * $Id: m_signon.c 1192 2006-04-21 16:21:02Z jilles $
30 */
31
32 #include "stdinc.h"
33
34 #include "send.h"
35 #include "channel.h"
36 #include "client.h"
37 #include "common.h"
38 #include "config.h"
39 #include "ircd.h"
40 #include "numeric.h"
41 #include "s_conf.h"
42 #include "s_serv.h"
43 #include "hash.h"
44 #include "msg.h"
45 #include "parse.h"
46 #include "modules.h"
47 #include "whowas.h"
48 #include "monitor.h"
49 #include "s_stats.h"
50 #include "snomask.h"
51 #include "match.h"
52 #include "s_user.h"
53
54 static int me_svslogin(struct Client *, struct Client *, int, const char **);
55 static int ms_signon(struct Client *, struct Client *, int, const char **);
56
57 static void send_signon(struct Client *, struct Client *, const char *, const char *, const char *, unsigned int, const char *);
58
59 struct Message svslogin_msgtab = {
60 "SVSLOGIN", 0, 0, 0, MFLG_SLOW,
61 {mg_ignore, mg_ignore, mg_ignore, mg_ignore, {me_svslogin, 6}, mg_ignore}
62 };
63 struct Message signon_msgtab = {
64 "SIGNON", 0, 0, 0, MFLG_SLOW,
65 {mg_ignore, mg_ignore, {ms_signon, 6}, mg_ignore, mg_ignore, mg_ignore}
66 };
67
68 mapi_clist_av1 signon_clist[] = {
69 &svslogin_msgtab, &signon_msgtab, NULL
70 };
71
72 DECLARE_MODULE_AV1(signon, NULL, NULL, signon_clist, NULL, NULL, "$Revision: 1192 $");
73
74 #define NICK_VALID 1
75 #define USER_VALID 2
76 #define HOST_VALID 4
77
78 static int
79 clean_nick(const char *nick)
80 {
81 int len = 0;
82
83 if(*nick == '-')
84 return 0;
85
86 /* This is used to check logins, which are often
87 * numeric. Don't check for leading digits, if
88 * services wants to set someone's nick to something
89 * starting with a number, let it try.
90 * --gxti
91 */
92
93 for (; *nick; nick++)
94 {
95 len++;
96 if(!IsNickChar(*nick))
97 return 0;
98 }
99
100 /* nicklen is +1 */
101 if(len >= NICKLEN)
102 return 0;
103
104 return 1;
105 }
106
107 static int
108 clean_username(const char *username)
109 {
110 int len = 0;
111
112 for (; *username; username++)
113 {
114 len++;
115
116 if(!IsUserChar(*username))
117 return 0;
118 }
119
120 if(len > USERLEN)
121 return 0;
122
123 return 1;
124 }
125
126 static int
127 clean_host(const char *host)
128 {
129 int len = 0;
130
131 for (; *host; host++)
132 {
133 len++;
134
135 if(!IsHostChar(*host))
136 return 0;
137 }
138
139 if(len > HOSTLEN)
140 return 0;
141
142 return 1;
143 }
144
145 static int
146 me_svslogin(struct Client *client_p, struct Client *source_p,
147 int parc, const char *parv[])
148 {
149 struct Client *target_p, *exist_p;
150 char nick[NICKLEN+1], login[NICKLEN+1];
151 char user[USERLEN+1], host[HOSTLEN+1];
152 int valid = 0;
153
154 if(!(source_p->flags & FLAGS_SERVICE))
155 {
156 sendto_realops_snomask(SNO_GENERAL, L_ALL,
157 "Non-service server %s attempting to execute services-only command SVSLOGIN", source_p->name);
158 return 0;
159 }
160
161 if((target_p = find_client(parv[1])) == NULL)
162 return 0;
163
164 if(!MyClient(target_p) && !IsUnknown(target_p))
165 return 0;
166
167 if(clean_nick(parv[2]))
168 {
169 rb_strlcpy(nick, parv[2], NICKLEN + 1);
170 valid |= NICK_VALID;
171 }
172 else if(*target_p->name)
173 rb_strlcpy(nick, target_p->name, NICKLEN + 1);
174 else
175 strcpy(nick, "*");
176
177 if(clean_username(parv[3]))
178 {
179 rb_strlcpy(user, parv[3], USERLEN + 1);
180 valid |= USER_VALID;
181 }
182 else
183 rb_strlcpy(user, target_p->username, USERLEN + 1);
184
185 if(clean_host(parv[4]))
186 {
187 rb_strlcpy(host, parv[4], HOSTLEN + 1);
188 valid |= HOST_VALID;
189 }
190 else
191 rb_strlcpy(host, target_p->host, HOSTLEN + 1);
192
193 if(*parv[5] == '*')
194 {
195 if(target_p->user)
196 rb_strlcpy(login, target_p->user->suser, NICKLEN + 1);
197 else
198 login[0] = '\0';
199 }
200 else if(!strcmp(parv[5], "0"))
201 login[0] = '\0';
202 else
203 rb_strlcpy(login, parv[5], NICKLEN + 1);
204
205 /* Login (mostly) follows nick rules. */
206 if(*login && !clean_nick(login))
207 return 0;
208
209 if((exist_p = find_person(nick)) && target_p != exist_p)
210 {
211 char buf[BUFSIZE];
212
213 if(MyClient(exist_p))
214 sendto_one(exist_p, ":%s KILL %s :(Nickname regained by services)",
215 me.name, exist_p->name);
216
217 exist_p->flags |= FLAGS_KILLED;
218 kill_client_serv_butone(NULL, exist_p, "%s (Nickname regained by services)",
219 me.name);
220 sendto_realops_snomask(SNO_SKILL, L_ALL,
221 "Nick collision due to SVSLOGIN on %s",
222 nick);
223
224 rb_snprintf(buf, sizeof(buf), "Killed (%s (Nickname regained by services))",
225 me.name);
226 exit_client(NULL, exist_p, &me, buf);
227 }else if((exist_p = find_client(nick)) && IsUnknown(exist_p) && exist_p != target_p) {
228 exit_client(NULL, exist_p, &me, "Overridden");
229 }
230
231 if(*login)
232 {
233 /* Strip leading digits, unless it's purely numeric. */
234 const char *p = login;
235 while(IsDigit(*p))
236 p++;
237 if(!*p)
238 p = login;
239
240 sendto_one(target_p, form_str(RPL_LOGGEDIN), me.name, EmptyString(target_p->name) ? "*" : target_p->name,
241 nick, user, host, p, p);
242 }
243 else
244 sendto_one(target_p, form_str(RPL_LOGGEDOUT), me.name, EmptyString(target_p->name) ? "*" : target_p->name,
245 nick, user, host);
246
247 if(IsUnknown(target_p))
248 {
249 struct User *user_p = make_user(target_p);
250
251 if(valid & NICK_VALID)
252 strcpy(target_p->preClient->spoofnick, nick);
253
254 if(valid & USER_VALID)
255 strcpy(target_p->preClient->spoofuser, user);
256
257 if(valid & HOST_VALID)
258 strcpy(target_p->preClient->spoofhost, host);
259
260 rb_strlcpy(user_p->suser, login, NICKLEN + 1);
261 }
262 else
263 {
264 char note[NICKLEN + 10];
265
266 send_signon(NULL, target_p, nick, user, host, rb_current_time(), login);
267
268 rb_snprintf(note, NICKLEN + 10, "Nick: %s", target_p->name);
269 rb_note(target_p->localClient->F, note);
270 }
271
272 return 0;
273 }
274
275 static int
276 ms_signon(struct Client *client_p, struct Client *source_p,
277 int parc, const char *parv[])
278 {
279 struct Client *target_p;
280 int newts, sameuser;
281 char login[NICKLEN+1];
282
283 if(!clean_nick(parv[1]))
284 {
285 ServerStats.is_kill++;
286 sendto_realops_snomask(SNO_DEBUG, L_ALL,
287 "Bad Nick from SIGNON: %s From: %s(via %s)",
288 parv[1], source_p->servptr->name, client_p->name);
289 /* if source_p has an id, kill_client_serv_butone() will
290 * send a kill to client_p, otherwise do it here */
291 if (!has_id(source_p))
292 sendto_one(client_p, ":%s KILL %s :%s (Bad nickname from SIGNON)",
293 get_id(&me, client_p), parv[1], me.name);
294 kill_client_serv_butone(client_p, source_p, "%s (Bad nickname from SIGNON)",
295 me.name);
296 source_p->flags |= FLAGS_KILLED;
297 exit_client(NULL, source_p, &me, "Bad nickname from SIGNON");
298 return 0;
299 }
300
301 if(!clean_username(parv[2]) || !clean_host(parv[3]))
302 {
303 ServerStats.is_kill++;
304 sendto_realops_snomask(SNO_DEBUG, L_ALL,
305 "Bad user@host from SIGNON: %s@%s From: %s(via %s)",
306 parv[2], parv[3], source_p->servptr->name, client_p->name);
307 /* if source_p has an id, kill_client_serv_butone() will
308 * send a kill to client_p, otherwise do it here */
309 if (!has_id(source_p))
310 sendto_one(client_p, ":%s KILL %s :%s (Bad user@host from SIGNON)",
311 get_id(&me, client_p), parv[1], me.name);
312 kill_client_serv_butone(client_p, source_p, "%s (Bad user@host from SIGNON)",
313 me.name);
314 source_p->flags |= FLAGS_KILLED;
315 exit_client(NULL, source_p, &me, "Bad user@host from SIGNON");
316 return 0;
317 }
318
319 newts = atol(parv[4]);
320
321 if(!strcmp(parv[5], "0"))
322 login[0] = '\0';
323 else if(*parv[5] != '*')
324 {
325 if (clean_nick(parv[5]))
326 rb_strlcpy(login, parv[5], NICKLEN + 1);
327 else
328 return 0;
329 }
330
331 target_p = find_named_client(parv[1]);
332 if(target_p != NULL && target_p != source_p)
333 {
334 /* In case of collision, follow NICK rules. */
335 /* XXX this is duplicated code and does not do SAVE */
336 if(IsUnknown(target_p))
337 exit_client(NULL, target_p, &me, "Overridden");
338 else
339 {
340 if(!newts || !target_p->tsinfo || (newts == target_p->tsinfo) || !source_p->user)
341 {
342 sendto_realops_snomask(SNO_GENERAL, L_ALL,
343 "Nick change collision from SIGNON from %s to %s(%s <- %s)(both killed)",
344 source_p->name, target_p->name, target_p->from->name,
345 client_p->name);
346
347 ServerStats.is_kill++;
348 sendto_one_numeric(target_p, ERR_NICKCOLLISION,
349 form_str(ERR_NICKCOLLISION), target_p->name);
350
351 kill_client_serv_butone(NULL, source_p, "%s (Nick change collision)", me.name);
352
353 ServerStats.is_kill++;
354
355 kill_client_serv_butone(NULL, target_p, "%s (Nick change collision)", me.name);
356
357 target_p->flags |= FLAGS_KILLED;
358 exit_client(NULL, target_p, &me, "Nick collision(new)");
359 source_p->flags |= FLAGS_KILLED;
360 exit_client(client_p, source_p, &me, "Nick collision(old)");
361 return 0;
362 }
363 else
364 {
365 sameuser = !irccmp(target_p->username, source_p->username) &&
366 !irccmp(target_p->host, source_p->host);
367
368 if((sameuser && newts < target_p->tsinfo) ||
369 (!sameuser && newts > target_p->tsinfo))
370 {
371 if(sameuser)
372 sendto_realops_snomask(SNO_GENERAL, L_ALL,
373 "Nick change collision from SIGNON from %s to %s(%s <- %s)(older killed)",
374 source_p->name, target_p->name,
375 target_p->from->name, client_p->name);
376 else
377 sendto_realops_snomask(SNO_GENERAL, L_ALL,
378 "Nick change collision from SIGNON from %s to %s(%s <- %s)(newer killed)",
379 source_p->name, target_p->name,
380 target_p->from->name, client_p->name);
381
382 ServerStats.is_kill++;
383
384 sendto_one_numeric(target_p, ERR_NICKCOLLISION,
385 form_str(ERR_NICKCOLLISION), target_p->name);
386
387 /* kill the client issuing the nickchange */
388 kill_client_serv_butone(client_p, source_p,
389 "%s (Nick change collision)", me.name);
390
391 source_p->flags |= FLAGS_KILLED;
392
393 if(sameuser)
394 exit_client(client_p, source_p, &me, "Nick collision(old)");
395 else
396 exit_client(client_p, source_p, &me, "Nick collision(new)");
397 return 0;
398 }
399 else
400 {
401 if(sameuser)
402 sendto_realops_snomask(SNO_GENERAL, L_ALL,
403 "Nick collision from SIGNON on %s(%s <- %s)(older killed)",
404 target_p->name, target_p->from->name,
405 client_p->name);
406 else
407 sendto_realops_snomask(SNO_GENERAL, L_ALL,
408 "Nick collision from SIGNON on %s(%s <- %s)(newer killed)",
409 target_p->name, target_p->from->name,
410 client_p->name);
411
412 sendto_one_numeric(target_p, ERR_NICKCOLLISION,
413 form_str(ERR_NICKCOLLISION), target_p->name);
414
415 /* kill the client who existed before hand */
416 kill_client_serv_butone(client_p, target_p,
417 "%s (Nick collision)", me.name);
418
419 ServerStats.is_kill++;
420
421 target_p->flags |= FLAGS_KILLED;
422 (void) exit_client(client_p, target_p, &me, "Nick collision");
423 }
424 }
425
426 }
427 }
428
429 send_signon(client_p, source_p, parv[1], parv[2], parv[3], newts, login);
430 return 0;
431 }
432
433 static void
434 send_signon(struct Client *client_p, struct Client *target_p,
435 const char *nick, const char *user, const char *host,
436 unsigned int newts, const char *login)
437 {
438 sendto_server(client_p, NULL, CAP_TS6, NOCAPS, ":%s SIGNON %s %s %s %ld %s",
439 use_id(target_p), nick, user, host,
440 (long) target_p->tsinfo, *login ? login : "0");
441
442 strcpy(target_p->user->suser, login);
443
444 change_nick_user_host(target_p, nick, user, host, newts, "Signing %s (%s)", *login ? "in" : "out", nick);
445 }
446