]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/irc-login.c
Introduce_Client() => Client_Introduce(), and move it to client.c
[ngircd-alex.git] / src / ngircd / irc-login.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2011 Alexander Barton (alex@barton.de) and Contributors.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  */
11
12 #include "portab.h"
13
14 /**
15  * @file
16  * Login and logout
17  */
18
19 #include "imp.h"
20 #include <assert.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <strings.h>
25 #include <signal.h>
26 #include <unistd.h>
27
28 #include "ngircd.h"
29 #include "conn-func.h"
30 #include "class.h"
31 #include "conf.h"
32 #include "channel.h"
33 #include "io.h"
34 #include "log.h"
35 #include "messages.h"
36 #include "pam.h"
37 #include "parse.h"
38 #include "irc.h"
39 #include "irc-info.h"
40 #include "irc-write.h"
41
42 #include "exp.h"
43 #include "irc-login.h"
44
45
46 static bool Hello_User PARAMS(( CLIENT *Client ));
47 static bool Hello_User_PostAuth PARAMS(( CLIENT *Client ));
48 static void Kill_Nick PARAMS(( char *Nick, char *Reason ));
49 #ifdef PAM
50 static void cb_Read_Auth_Result PARAMS((int r_fd, UNUSED short events));
51 #endif
52
53 /**
54  * Handler for the IRC "PASS" command.
55  *
56  * See RFC 2813 section 4.1.1, and RFC 2812 section 3.1.1.
57  *
58  * @param Client        The client from which this command has been received.
59  * @param Req           Request structure with prefix and all parameters.
60  * @returns             CONNECTED or DISCONNECTED.
61  */
62 GLOBAL bool
63 IRC_PASS( CLIENT *Client, REQUEST *Req )
64 {
65         char *type, *orig_flags;
66         int protohigh, protolow;
67
68         assert( Client != NULL );
69         assert( Req != NULL );
70
71         /* Return an error if this is not a local client */
72         if (Client_Conn(Client) <= NONE)
73                 return IRC_WriteStrClient(Client, ERR_UNKNOWNCOMMAND_MSG,
74                                           Client_ID(Client), Req->command);
75
76         if (Client_Type(Client) == CLIENT_UNKNOWN && Req->argc == 1) {
77                 /* Not yet registered "unknown" connection, PASS with one
78                  * argument: either a regular client, service, or server
79                  * using the old RFC 1459 section 4.1.1 syntax. */
80                 LogDebug("Connection %d: got PASS command (RFC 1459) ...",
81                          Client_Conn(Client));
82         } else if ((Client_Type(Client) == CLIENT_UNKNOWN ||
83                     Client_Type(Client) == CLIENT_UNKNOWNSERVER) &&
84                    (Req->argc == 3 || Req->argc == 4)) {
85                 /* Not yet registered "unknown" connection or outgoing server
86                  * link, PASS with three or four argument: server using the
87                  * RFC 2813 section 4.1.1 syntax. */
88                 LogDebug("Connection %d: got PASS command (RFC 2813, new server link) ...",
89                          Client_Conn(Client));
90         } else if (Client_Type(Client) == CLIENT_UNKNOWN ||
91                    Client_Type(Client) == CLIENT_UNKNOWNSERVER) {
92                 /* Unregistered connection, but wrong number of arguments: */
93                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
94                                           Client_ID(Client), Req->command);
95         } else {
96                 /* Registered connection, PASS command is not allowed! */
97                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
98                                           Client_ID(Client));
99         }
100
101         Client_SetPassword(Client, Req->argv[0]);
102
103         /* Protocol version */
104         if (Req->argc >= 2 && strlen(Req->argv[1]) >= 4) {
105                 int c2, c4;
106
107                 c2 = Req->argv[1][2];
108                 c4 = Req->argv[1][4];
109
110                 Req->argv[1][4] = '\0';
111                 protolow = atoi(&Req->argv[1][2]);
112                 Req->argv[1][2] = '\0';
113                 protohigh = atoi(Req->argv[1]);
114
115                 Req->argv[1][2] = c2;
116                 Req->argv[1][4] = c4;
117
118                 Client_SetType(Client, CLIENT_GOTPASS_2813);
119         } else {
120                 protohigh = protolow = 0;
121                 Client_SetType(Client, CLIENT_GOTPASS);
122         }
123
124         /* Protocol type, see doc/Protocol.txt */
125         if (Req->argc >= 2 && strlen(Req->argv[1]) > 4)
126                 type = &Req->argv[1][4];
127         else
128                 type = NULL;
129
130         /* Protocol flags/options */
131         if (Req->argc >= 4)
132                 orig_flags = Req->argv[3];
133         else
134                 orig_flags = "";
135
136         /* Implementation, version and IRC+ flags */
137         if (Req->argc >= 3) {
138                 char *impl, *ptr, *serverver, *flags;
139
140                 impl = Req->argv[2];
141                 ptr = strchr(impl, '|');
142                 if (ptr)
143                         *ptr = '\0';
144
145                 if (type && strcmp(type, PROTOIRCPLUS) == 0) {
146                         /* The peer seems to be a server which supports the
147                          * IRC+ protocol (see doc/Protocol.txt). */
148                         serverver = ptr ? ptr + 1 : "?";
149                         flags = strchr(ptr ? serverver : impl, ':');
150                         if (flags) {
151                                 *flags = '\0';
152                                 flags++;
153                         } else
154                                 flags = "";
155                         Log(LOG_INFO,
156                             "Peer on conenction %d announces itself as %s-%s using protocol %d.%d/IRC+ (flags: \"%s\").",
157                             Client_Conn(Client), impl, serverver,
158                             protohigh, protolow, flags);
159                 } else {
160                         /* The peer seems to be a server supporting the
161                          * "original" IRC protocol (RFC 2813). */
162                         if (strchr(orig_flags, 'Z'))
163                                 flags = "Z";
164                         else
165                                 flags = "";
166                         Log(LOG_INFO,
167                             "Peer on connection %d announces itself as \"%s\" using protocol %d.%d (flags: \"%s\").",
168                             Client_Conn(Client), impl,
169                             protohigh, protolow, flags);
170                 }
171                 Client_SetFlags(Client, flags);
172         }
173
174         return CONNECTED;
175 } /* IRC_PASS */
176
177
178 /**
179  * Handler for the IRC "NICK" command.
180  *
181  * See RFC 2812, 3.1.2 "Nick message", and RFC 2813, 4.1.3 "Nick".
182  *
183  * This function implements the IRC command "NICK" which is used to register
184  * with the server, to change already registered nicknames and to introduce
185  * new users which are connected to other servers.
186  *
187  * @param Client        The client from which this command has been received.
188  * @param Req           Request structure with prefix and all parameters.
189  * @returns             CONNECTED or DISCONNECTED.
190  */
191 GLOBAL bool
192 IRC_NICK( CLIENT *Client, REQUEST *Req )
193 {
194         CLIENT *intr_c, *target, *c;
195         char *nick, *user, *hostname, *modes, *info;
196         int token, hops;
197
198         assert( Client != NULL );
199         assert( Req != NULL );
200
201         /* Some IRC clients, for example BitchX, send the NICK and USER
202          * commands in the wrong order ... */
203         if(Client_Type(Client) == CLIENT_UNKNOWN
204             || Client_Type(Client) == CLIENT_GOTPASS
205             || Client_Type(Client) == CLIENT_GOTNICK
206 #ifndef STRICT_RFC
207             || Client_Type(Client) == CLIENT_GOTUSER
208 #endif
209             || Client_Type(Client) == CLIENT_USER
210             || Client_Type(Client) == CLIENT_SERVICE
211             || (Client_Type(Client) == CLIENT_SERVER && Req->argc == 1))
212         {
213                 /* User registration or change of nickname */
214
215                 /* Wrong number of arguments? */
216                 if( Req->argc != 1 )
217                         return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG,
218                                                    Client_ID( Client ),
219                                                    Req->command );
220
221                 /* Search "target" client */
222                 if( Client_Type( Client ) == CLIENT_SERVER )
223                 {
224                         target = Client_Search( Req->prefix );
225                         if( ! target )
226                                 return IRC_WriteStrClient( Client,
227                                                            ERR_NOSUCHNICK_MSG,
228                                                            Client_ID( Client ),
229                                                            Req->argv[0] );
230                 }
231                 else
232                 {
233                         /* Is this a restricted client? */
234                         if( Client_HasMode( Client, 'r' ))
235                                 return IRC_WriteStrClient( Client,
236                                                            ERR_RESTRICTED_MSG,
237                                                            Client_ID( Client ));
238
239                         target = Client;
240                 }
241
242 #ifndef STRICT_RFC
243                 /* If the clients tries to change to its own nickname we won't
244                  * do anything. This is how the original ircd behaves and some
245                  * clients (for example Snak) expect it to be like this.
246                  * But I doubt that this is "really the right thing" ... */
247                 if( strcmp( Client_ID( target ), Req->argv[0] ) == 0 )
248                         return CONNECTED;
249 #endif
250
251                 /* Check that the new nickname is available. Special case:
252                  * the client only changes from/to upper to lower case. */
253                 if( strcasecmp( Client_ID( target ), Req->argv[0] ) != 0 )
254                 {
255                         if( ! Client_CheckNick( target, Req->argv[0] ))
256                                 return CONNECTED;
257                 }
258
259                 if (Client_Type(target) != CLIENT_USER &&
260                     Client_Type(target) != CLIENT_SERVICE &&
261                     Client_Type(target) != CLIENT_SERVER) {
262                         /* New client */
263                         LogDebug("Connection %d: got valid NICK command ...",
264                              Client_Conn( Client ));
265
266                         /* Register new nickname of this client */
267                         Client_SetID( target, Req->argv[0] );
268
269 #ifndef STRICT_RFC
270                         if (Conf_AuthPing) {
271                                 Conn_SetAuthPing(Client_Conn(Client), rand());
272                                 IRC_WriteStrClient(Client, "PING :%ld",
273                                         Conn_GetAuthPing(Client_Conn(Client)));
274                                 LogDebug("Connection %d: sent AUTH PING %ld ...",
275                                         Client_Conn(Client),
276                                         Conn_GetAuthPing(Client_Conn(Client)));
277                         }
278 #endif
279
280                         /* If we received a valid USER command already then
281                          * register the new client! */
282                         if( Client_Type( Client ) == CLIENT_GOTUSER )
283                                 return Hello_User( Client );
284                         else
285                                 Client_SetType( Client, CLIENT_GOTNICK );
286                 } else {
287                         /* Nickname change */
288                         if (Client_Conn(target) > NONE) {
289                                 /* Local client */
290                                 Log(LOG_INFO,
291                                     "%s \"%s\" changed nick (connection %d): \"%s\" -> \"%s\".",
292                                     Client_TypeText(target), Client_Mask(target),
293                                     Client_Conn(target), Client_ID(target),
294                                     Req->argv[0]);
295                                 Conn_UpdateIdle(Client_Conn(target));
296                         } else {
297                                 /* Remote client */
298                                 LogDebug("%s \"%s\" changed nick: \"%s\" -> \"%s\".",
299                                          Client_TypeText(target),
300                                          Client_Mask(target), Client_ID(target),
301                                          Req->argv[0]);
302                         }
303
304                         /* Inform all users and servers (which have to know)
305                          * of this nickname change */
306                         if( Client_Type( Client ) == CLIENT_USER )
307                                 IRC_WriteStrClientPrefix( Client, Client,
308                                                           "NICK :%s",
309                                                           Req->argv[0] );
310                         IRC_WriteStrServersPrefix( Client, target,
311                                                    "NICK :%s", Req->argv[0] );
312                         IRC_WriteStrRelatedPrefix( target, target, false,
313                                                    "NICK :%s", Req->argv[0] );
314
315                         /* Register old nickname for WHOWAS queries */
316                         Client_RegisterWhowas( target );
317
318                         /* Save new nickname */
319                         Client_SetID( target, Req->argv[0] );
320
321                         IRC_SetPenalty( target, 2 );
322                 }
323
324                 return CONNECTED;
325         } else if(Client_Type(Client) == CLIENT_SERVER ||
326                   Client_Type(Client) == CLIENT_SERVICE) {
327                 /* Server or service introduces new client */
328
329                 /* Bad number of parameters? */
330                 if (Req->argc != 2 && Req->argc != 7)
331                         return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
332                                                   Client_ID(Client), Req->command);
333
334                 if (Req->argc >= 7) {
335                         /* RFC 2813 compatible syntax */
336                         nick = Req->argv[0];
337                         hops = atoi(Req->argv[1]);
338                         user = Req->argv[2];
339                         hostname = Req->argv[3];
340                         token = atoi(Req->argv[4]);
341                         modes = Req->argv[5] + 1;
342                         info = Req->argv[6];
343                 } else {
344                         /* RFC 1459 compatible syntax */
345                         nick = Req->argv[0];
346                         hops = 1;
347                         user = Req->argv[0];
348                         hostname = Client_ID(Client);
349                         token = atoi(Req->argv[1]);
350                         modes = "";
351                         info = Req->argv[0];
352                 }
353
354                 c = Client_Search(nick);
355                 if(c) {
356                         /*
357                          * the new nick is already present on this server:
358                          * the new and the old one have to be disconnected now.
359                          */
360                         Log( LOG_ERR, "Server %s introduces already registered nick \"%s\"!", Client_ID( Client ), Req->argv[0] );
361                         Kill_Nick( Req->argv[0], "Nick collision" );
362                         return CONNECTED;
363                 }
364
365                 /* Find the Server this client is connected to */
366                 intr_c = Client_GetFromToken(Client, token);
367                 if( ! intr_c )
368                 {
369                         Log( LOG_ERR, "Server %s introduces nick \"%s\" on unknown server!?", Client_ID( Client ), Req->argv[0] );
370                         Kill_Nick( Req->argv[0], "Unknown server" );
371                         return CONNECTED;
372                 }
373
374                 c = Client_NewRemoteUser(intr_c, nick, hops, user, hostname,
375                                          token, modes, info, true);
376                 if( ! c )
377                 {
378                         /* out of memory, need to disconnect client to keep network state consistent */
379                         Log( LOG_ALERT, "Can't create client structure! (on connection %d)", Client_Conn( Client ));
380                         Kill_Nick( Req->argv[0], "Server error" );
381                         return CONNECTED;
382                 }
383
384                 /* RFC 2813: client is now fully registered, inform all the
385                  * other servers about the new user.
386                  * RFC 1459: announce the new client only after receiving the
387                  * USER command, first we need more information! */
388                 if (Req->argc < 7) {
389                         LogDebug("Client \"%s\" is being registered (RFC 1459) ...",
390                                  Client_Mask(c));
391                         Client_SetType(c, CLIENT_GOTNICK);
392                 } else
393                         Client_Introduce(Client, c, CLIENT_USER);
394
395                 return CONNECTED;
396         }
397         else return IRC_WriteStrClient( Client, ERR_ALREADYREGISTRED_MSG, Client_ID( Client ));
398 } /* IRC_NICK */
399
400
401 /**
402  * Handler for the IRC "USER" command.
403  *
404  * See RFC 2812, 3.1.3 "User message".
405  *
406  * @param Client        The client from which this command has been received.
407  * @param Req           Request structure with prefix and all parameters.
408  * @returns             CONNECTED or DISCONNECTED.
409  */
410 GLOBAL bool
411 IRC_USER(CLIENT * Client, REQUEST * Req)
412 {
413         CLIENT *c;
414 #ifdef IDENTAUTH
415         char *ptr;
416 #endif
417
418         assert(Client != NULL);
419         assert(Req != NULL);
420
421         if (Client_Type(Client) == CLIENT_GOTNICK ||
422 #ifndef STRICT_RFC
423             Client_Type(Client) == CLIENT_UNKNOWN ||
424 #endif
425             Client_Type(Client) == CLIENT_GOTPASS)
426         {
427                 /* New connection */
428                 if (Req->argc != 4)
429                         return IRC_WriteStrClient(Client,
430                                                   ERR_NEEDMOREPARAMS_MSG,
431                                                   Client_ID(Client),
432                                                   Req->command);
433
434                 /* User name */
435 #ifdef IDENTAUTH
436                 ptr = Client_User(Client);
437                 if (!ptr || !*ptr || *ptr == '~')
438                         Client_SetUser(Client, Req->argv[0], false);
439 #else
440                 Client_SetUser(Client, Req->argv[0], false);
441 #endif
442                 Client_SetOrigUser(Client, Req->argv[0]);
443
444                 /* "Real name" or user info text: Don't set it to the empty
445                  * string, the original ircd can't deal with such "real names"
446                  * (e. g. "USER user * * :") ... */
447                 if (*Req->argv[3])
448                         Client_SetInfo(Client, Req->argv[3]);
449                 else
450                         Client_SetInfo(Client, "-");
451
452                 LogDebug("Connection %d: got valid USER command ...",
453                     Client_Conn(Client));
454                 if (Client_Type(Client) == CLIENT_GOTNICK)
455                         return Hello_User(Client);
456                 else
457                         Client_SetType(Client, CLIENT_GOTUSER);
458                 return CONNECTED;
459
460         } else if (Client_Type(Client) == CLIENT_SERVER ||
461                    Client_Type(Client) == CLIENT_SERVICE) {
462                 /* Server/service updating an user */
463                 if (Req->argc != 4)
464                         return IRC_WriteStrClient(Client,
465                                                   ERR_NEEDMOREPARAMS_MSG,
466                                                   Client_ID(Client),
467                                                   Req->command);
468                 c = Client_Search(Req->prefix);
469                 if (!c)
470                         return IRC_WriteStrClient(Client, ERR_NOSUCHNICK_MSG,
471                                                   Client_ID(Client),
472                                                   Req->prefix);
473
474                 Client_SetUser(c, Req->argv[0], true);
475                 Client_SetOrigUser(c, Req->argv[0]);
476                 Client_SetHostname(c, Req->argv[1]);
477                 Client_SetInfo(c, Req->argv[3]);
478
479                 LogDebug("Connection %d: got valid USER command for \"%s\".",
480                          Client_Conn(Client), Client_Mask(c));
481
482                 /* RFC 1459 style user registration?
483                  * Introduce client to network: */
484                 if (Client_Type(c) == CLIENT_GOTNICK)
485                         Client_Introduce(Client, c, CLIENT_USER);
486
487                 return CONNECTED;
488         } else if (Client_Type(Client) == CLIENT_USER) {
489                 /* Already registered connection */
490                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
491                                           Client_ID(Client));
492         } else {
493                 /* Unexpected/invalid connection state? */
494                 return IRC_WriteStrClient(Client, ERR_NOTREGISTERED_MSG,
495                                           Client_ID(Client));
496         }
497 } /* IRC_USER */
498
499
500 /**
501  * Handler for the IRC "SERVICE" command.
502  *
503  * This function implements IRC Services registration using the SERVICE command
504  * defined in RFC 2812 3.1.6 and RFC 2813 4.1.4.
505  *
506  * At the moment ngIRCd doesn't support directly linked services, so this
507  * function returns ERR_ERRONEUSNICKNAME when the SERVICE command has not been
508  * received from a peer server.
509  *
510  * @param Client        The client from which this command has been received.
511  * @param Req           Request structure with prefix and all parameters.
512  * @returns             CONNECTED or DISCONNECTED..
513  */
514 GLOBAL bool
515 IRC_SERVICE(CLIENT *Client, REQUEST *Req)
516 {
517         CLIENT *c, *intr_c;
518         char *nick, *user, *host, *info, *modes, *ptr;
519         int token, hops;
520
521         assert(Client != NULL);
522         assert(Req != NULL);
523
524         if (Client_Type(Client) != CLIENT_GOTPASS &&
525             Client_Type(Client) != CLIENT_SERVER)
526                 return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG,
527                                           Client_ID(Client));
528
529         if (Req->argc != 6)
530                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
531                                           Client_ID(Client), Req->command);
532
533         if (Client_Type(Client) != CLIENT_SERVER)
534                 return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG,
535                                   Client_ID(Client), Req->argv[0]);
536
537         /* Bad number of parameters? */
538         if (Req->argc != 6)
539                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
540                                           Client_ID(Client), Req->command);
541
542         nick = Req->argv[0];
543         user = NULL; host = NULL;
544         token = atoi(Req->argv[1]);
545         hops = atoi(Req->argv[4]);
546         info = Req->argv[5];
547
548         /* Validate service name ("nick name") */
549         c = Client_Search(nick);
550         if(c) {
551                 /* Nick name collission: disconnect (KILL) both clients! */
552                 Log(LOG_ERR, "Server %s introduces already registered service \"%s\"!",
553                     Client_ID(Client), nick);
554                 Kill_Nick(nick, "Nick collision");
555                 return CONNECTED;
556         }
557
558         /* Get the server to which the service is connected */
559         intr_c = Client_GetFromToken(Client, token);
560         if (! intr_c) {
561                 Log(LOG_ERR, "Server %s introduces service \"%s\" on unknown server!?",
562                     Client_ID(Client), nick);
563                 Kill_Nick(nick, "Unknown server");
564                 return CONNECTED;
565         }
566
567         /* Get user and host name */
568         ptr = strchr(nick, '@');
569         if (ptr) {
570                 *ptr = '\0';
571                 host = ++ptr;
572         }
573         if (!host)
574                 host = Client_Hostname(intr_c);
575         ptr = strchr(nick, '!');
576         if (ptr) {
577                 *ptr = '\0';
578                 user = ++ptr;
579         }
580         if (!user)
581                 user = nick;
582
583         /* According to RFC 2812/2813 parameter 4 <type> "is currently reserved
584          * for future usage"; but we use it to transfer the modes and check
585          * that the first character is a '+' sign and ignore it otherwise. */
586         modes = (Req->argv[3][0] == '+') ? ++Req->argv[3] : "";
587
588         c = Client_NewRemoteUser(intr_c, nick, hops, user, host,
589                                  token, modes, info, true);
590         if (! c) {
591                 /* Couldn't create client structure, so KILL the service to
592                  * keep network status consistent ... */
593                 Log(LOG_ALERT, "Can't create client structure! (on connection %d)",
594                     Client_Conn(Client));
595                 Kill_Nick(nick, "Server error");
596                 return CONNECTED;
597         }
598
599         Client_Introduce(Client, c, CLIENT_SERVICE);
600         return CONNECTED;
601 } /* IRC_SERVICE */
602
603
604 /**
605  * Handler for the IRC "WEBIRC" command.
606  *
607  * See doc/Protocol.txt, section II.4:
608  * "Update webchat/proxy client information".
609  *
610  * @param Client        The client from which this command has been received.
611  * @param Req           Request structure with prefix and all parameters.
612  * @returns             CONNECTED or DISCONNECTED.
613  */
614 GLOBAL bool
615 IRC_WEBIRC(CLIENT *Client, REQUEST *Req)
616 {
617         /* Exactly 4 parameters are requited */
618         if (Req->argc != 4)
619                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
620                                           Client_ID(Client), Req->command);
621
622         if (!Conf_WebircPwd[0] || strcmp(Req->argv[0], Conf_WebircPwd) != 0)
623                 return IRC_WriteStrClient(Client, ERR_PASSWDMISMATCH_MSG,
624                                           Client_ID(Client));
625
626         LogDebug("Connection %d: got valid WEBIRC command: user=%s, host=%s, ip=%s",
627                  Client_Conn(Client), Req->argv[1], Req->argv[2], Req->argv[3]);
628
629         Client_SetUser(Client, Req->argv[1], true);
630         Client_SetOrigUser(Client, Req->argv[1]);
631         Client_SetHostname(Client, Req->argv[2]);
632         return CONNECTED;
633 } /* IRC_WEBIRC */
634
635
636 /**
637  * Handler for the IRC "QUIT" command.
638  *
639  * See RFC 2812, 3.1.7 "Quit", and RFC 2813, 4.1.5 "Quit".
640  *
641  * @param Client        The client from which this command has been received.
642  * @param Req           Request structure with prefix and all parameters.
643  * @returns             CONNECTED or DISCONNECTED.
644  */
645 GLOBAL bool
646 IRC_QUIT( CLIENT *Client, REQUEST *Req )
647 {
648         CLIENT *target;
649         char quitmsg[LINE_LEN];
650
651         assert(Client != NULL);
652         assert(Req != NULL);
653
654         /* Wrong number of arguments? */
655         if (Req->argc > 1)
656                 return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
657                                           Client_ID(Client), Req->command);
658
659         if (Req->argc == 1)
660                 strlcpy(quitmsg, Req->argv[0], sizeof quitmsg);
661
662         if (Client_Type(Client) == CLIENT_SERVER) {
663                 /* Server */
664                 target = Client_Search(Req->prefix);
665                 if (!target) {
666                         Log(LOG_WARNING,
667                             "Got QUIT from %s for unknown client!?",
668                             Client_ID(Client));
669                         return CONNECTED;
670                 }
671
672                 if (target != Client) {
673                         Client_Destroy(target, "Got QUIT command.",
674                                        Req->argc == 1 ? quitmsg : NULL, true);
675                         return CONNECTED;
676                 } else {
677                         Conn_Close(Client_Conn(Client), "Got QUIT command.",
678                                    Req->argc == 1 ? quitmsg : NULL, true);
679                         return DISCONNECTED;
680                 }
681         } else {
682                 if (Req->argc == 1 && quitmsg[0] != '\"') {
683                         /* " " to avoid confusion */
684                         strlcpy(quitmsg, "\"", sizeof quitmsg);
685                         strlcat(quitmsg, Req->argv[0], sizeof quitmsg-1);
686                         strlcat(quitmsg, "\"", sizeof quitmsg );
687                 }
688
689                 /* User, Service, or not yet registered */
690                 Conn_Close(Client_Conn(Client), "Got QUIT command.",
691                            Req->argc == 1 ? quitmsg : NULL, true);
692
693                 return DISCONNECTED;
694         }
695 } /* IRC_QUIT */
696
697
698 #ifndef STRICT_RFC
699
700 /**
701  * Handler for HTTP command, e.g. GET and POST
702  *
703  * We handle these commands here to avoid the quite long timeout when
704  * some user tries to access this IRC daemon using an web browser ...
705  *
706  * @param Client        The client from which this command has been received.
707  * @param Req           Request structure with prefix and all parameters.
708  * @returns             CONNECTED or DISCONNECTED.
709  */
710 GLOBAL bool
711 IRC_QUIT_HTTP( CLIENT *Client, REQUEST *Req )
712 {
713         Req->argc = 1;
714         Req->argv[0] = "Oops, HTTP request received? This is IRC!";
715         return IRC_QUIT(Client, Req);
716 } /* IRC_QUIT_HTTP */
717
718 #endif
719
720
721 /**
722  * Handler for the IRC "PING" command.
723  *
724  * See RFC 2812, 3.7.2 "Ping message".
725  *
726  * @param Client        The client from which this command has been received.
727  * @param Req           Request structure with prefix and all parameters.
728  * @returns             CONNECTED or DISCONNECTED.
729  */
730 GLOBAL bool
731 IRC_PING(CLIENT *Client, REQUEST *Req)
732 {
733         CLIENT *target, *from;
734
735         assert(Client != NULL);
736         assert(Req != NULL);
737
738         if (Req->argc < 1)
739                 return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
740                                           Client_ID(Client));
741 #ifdef STRICT_RFC
742         /* Don't ignore additional arguments when in "strict" mode */
743         if (Req->argc > 2)
744                  return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG,
745                                            Client_ID(Client), Req->command);
746 #endif
747
748         if (Req->argc > 1) {
749                 /* A target has been specified ... */
750                 target = Client_Search(Req->argv[1]);
751
752                 if (!target || Client_Type(target) != CLIENT_SERVER)
753                         return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
754                                         Client_ID(Client), Req->argv[1]);
755
756                 if (target != Client_ThisServer()) {
757                         /* Ok, we have to forward the PING */
758                         if (Client_Type(Client) == CLIENT_SERVER)
759                                 from = Client_Search(Req->prefix);
760                         else
761                                 from = Client;
762                         if (!from)
763                                 return IRC_WriteStrClient(Client,
764                                                 ERR_NOSUCHSERVER_MSG,
765                                                 Client_ID(Client), Req->prefix);
766
767                         return IRC_WriteStrClientPrefix(target, from,
768                                         "PING %s :%s", Req->argv[0],
769                                         Req->argv[1] );
770                 }
771         }
772
773         if (Client_Type(Client) == CLIENT_SERVER) {
774                 if (Req->prefix)
775                         from = Client_Search(Req->prefix);
776                 else
777                         from = Client;
778         } else
779                 from = Client_ThisServer();
780         if (!from)
781                 return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
782                                         Client_ID(Client), Req->prefix);
783
784         Log(LOG_DEBUG, "Connection %d: got PING, sending PONG ...",
785             Client_Conn(Client));
786
787 #ifdef STRICT_RFC
788         return IRC_WriteStrClient(Client, "PONG %s :%s",
789                 Client_ID(from), Client_ID(Client));
790 #else
791         /* Some clients depend on the argument being returned in the PONG
792          * reply (not mentioned in any RFC, though) */
793         return IRC_WriteStrClient(Client, "PONG %s :%s",
794                 Client_ID(from), Req->argv[0]);
795 #endif
796 } /* IRC_PING */
797
798
799 /**
800  * Handler for the IRC "PONG" command.
801  *
802  * See RFC 2812, 3.7.3 "Pong message".
803  *
804  * @param Client        The client from which this command has been received.
805  * @param Req           Request structure with prefix and all parameters.
806  * @returns             CONNECTED or DISCONNECTED.
807  */
808 GLOBAL bool
809 IRC_PONG(CLIENT *Client, REQUEST *Req)
810 {
811         CLIENT *target, *from;
812         CONN_ID conn;
813 #ifndef STRICT_RFC
814         long auth_ping;
815 #endif
816         char *s;
817
818         assert(Client != NULL);
819         assert(Req != NULL);
820
821         /* Wrong number of arguments? */
822         if (Req->argc < 1) {
823                 if (Client_Type(Client) == CLIENT_USER)
824                         return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
825                                                   Client_ID(Client));
826                 else
827                         return CONNECTED;
828         }
829         if (Req->argc > 2) {
830                 if (Client_Type(Client) == CLIENT_USER)
831                         return IRC_WriteStrClient(Client,
832                                                   ERR_NEEDMOREPARAMS_MSG,
833                                                   Client_ID(Client),
834                                                   Req->command);
835                 else
836                         return CONNECTED;
837         }
838
839         /* Forward? */
840         if (Req->argc == 2 && Client_Type(Client) == CLIENT_SERVER) {
841                 target = Client_Search(Req->argv[0]);
842                 if (!target)
843                         return IRC_WriteStrClient(Client, ERR_NOSUCHSERVER_MSG,
844                                         Client_ID(Client), Req->argv[0]);
845
846                 from = Client_Search(Req->prefix);
847
848                 if (target != Client_ThisServer() && target != from) {
849                         /* Ok, we have to forward the message. */
850                         if (!from)
851                                 return IRC_WriteStrClient(Client,
852                                                 ERR_NOSUCHSERVER_MSG,
853                                                 Client_ID(Client), Req->prefix);
854
855                         if (Client_Type(Client_NextHop(target)) != CLIENT_SERVER)
856                                 s = Client_ID(from);
857                         else
858                                 s = Req->argv[0];
859                         return IRC_WriteStrClientPrefix(target, from,
860                                  "PONG %s :%s", s, Req->argv[1]);
861                 }
862         }
863
864         /* The connection timestamp has already been updated when the data has
865          * been read from so socket, so we don't need to update it here. */
866
867         conn = Client_Conn(Client);
868
869 #ifndef STRICT_RFC
870         /* Check authentication PING-PONG ... */
871         auth_ping = Conn_GetAuthPing(conn);
872         if (auth_ping) {
873                 LogDebug("AUTH PONG: waiting for token \"%ld\", got \"%s\" ...",
874                          auth_ping, Req->argv[0]);
875                 if (auth_ping == atoi(Req->argv[0])) {
876                         Conn_SetAuthPing(conn, 0);
877                         if (Client_Type(Client) == CLIENT_WAITAUTHPING)
878                                 Hello_User(Client);
879                 } else
880                         if (!IRC_WriteStrClient(Client,
881                                         "To connect, type /QUOTE PONG %ld",
882                                         auth_ping))
883                                 return DISCONNECTED;
884         }
885 #endif
886
887         if (Client_Type(Client) == CLIENT_SERVER && Conn_LastPing(conn) == 0) {
888                 Log(LOG_INFO,
889                     "Synchronization with \"%s\" done (connection %d): %ld seconds [%ld users, %ld channels]",
890                     Client_ID(Client), conn, time(NULL) - Conn_GetSignon(conn),
891                     Client_UserCount(), Channel_CountVisible(NULL));
892                 Conn_UpdatePing(conn);
893         } else
894                 LogDebug("Connection %d: received PONG. Lag: %ld seconds.",
895                          conn, time(NULL) - Conn_LastPing(conn));
896
897         return CONNECTED;
898 } /* IRC_PONG */
899
900
901 /**
902  * Initiate client registration.
903  *
904  * This function is called after the daemon received the required NICK and
905  * USER commands of a new client. If the daemon is compiled with support for
906  * PAM, the authentication sub-processs is forked; otherwise the global server
907  * password is checked.
908  *
909  * @param Client        The client logging in.
910  * @returns             CONNECTED or DISCONNECTED.
911  */
912 static bool
913 Hello_User(CLIENT * Client)
914 {
915 #ifdef PAM
916         int pipefd[2], result;
917         pid_t pid;
918 #endif
919         CONN_ID conn;
920
921         assert(Client != NULL);
922         conn = Client_Conn(Client);
923
924 #ifndef STRICT_RFC
925         if (Conf_AuthPing) {
926                 /* Did we receive the "auth PONG" already? */
927                 if (Conn_GetAuthPing(conn)) {
928                         Client_SetType(Client, CLIENT_WAITAUTHPING);
929                         LogDebug("Connection %d: Waiting for AUTH PONG ...", conn);
930                         return CONNECTED;
931                 }
932         }
933 #endif
934
935 #ifdef PAM
936         if (!Conf_PAM) {
937                 /* Don't do any PAM authentication at all, instead emulate
938                  * the beahiour of the daemon compiled without PAM support:
939                  * because there can't be any "server password", all
940                  * passwords supplied are classified as "wrong". */
941                 if(Client_Password(Client)[0] == '\0')
942                         return Hello_User_PostAuth(Client);
943                 Client_Reject(Client, "Non-empty password", false);
944                 return DISCONNECTED;
945         }
946
947         if (Conf_PAMIsOptional && strcmp(Client_Password(Client), "") == 0) {
948                 /* Clients are not required to send a password and to be PAM-
949                  * authenticated at all. If not, they won't become "identified"
950                  * and keep the "~" in their supplied user name.
951                  * Therefore it is sensible to either set Conf_PAMisOptional or
952                  * to enable IDENT lookups -- not both. */
953                 return Hello_User_PostAuth(Client);
954         }
955
956         /* Fork child process for PAM authentication; and make sure that the
957          * process timeout is set higher than the login timeout! */
958         pid = Proc_Fork(Conn_GetProcStat(conn), pipefd,
959                         cb_Read_Auth_Result, Conf_PongTimeout + 1);
960         if (pid > 0) {
961                 LogDebug("Authenticator for connection %d created (PID %d).",
962                          conn, pid);
963                 return CONNECTED;
964         } else {
965                 /* Sub process */
966                 Log_Init_Subprocess("Auth");
967                 Conn_CloseAllSockets(NONE);
968                 result = PAM_Authenticate(Client);
969                 if (write(pipefd[1], &result, sizeof(result)) != sizeof(result))
970                         Log_Subprocess(LOG_ERR,
971                                        "Failed to pipe result to parent!");
972                 Log_Exit_Subprocess("Auth");
973                 exit(0);
974         }
975 #else
976         /* Check global server password ... */
977         if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0) {
978                 /* Bad password! */
979                 Client_Reject(Client, "Bad server password", false);
980                 return DISCONNECTED;
981         }
982         return Hello_User_PostAuth(Client);
983 #endif
984 }
985
986
987 #ifdef PAM
988
989 /**
990  * Read result of the authenticatior sub-process from pipe
991  *
992  * @param r_fd          File descriptor of the pipe.
993  * @param events        (ignored IO specification)
994  */
995 static void
996 cb_Read_Auth_Result(int r_fd, UNUSED short events)
997 {
998         CONN_ID conn;
999         CLIENT *client;
1000         int result;
1001         size_t len;
1002         PROC_STAT *proc;
1003
1004         LogDebug("Auth: Got callback on fd %d, events %d", r_fd, events);
1005         conn = Conn_GetFromProc(r_fd);
1006         if (conn == NONE) {
1007                 /* Ops, none found? Probably the connection has already
1008                  * been closed!? We'll ignore that ... */
1009                 io_close(r_fd);
1010                 LogDebug("Auth: Got callback for unknown connection!?");
1011                 return;
1012         }
1013         proc = Conn_GetProcStat(conn);
1014         client = Conn_GetClient(conn);
1015
1016         /* Read result from pipe */
1017         len = Proc_Read(proc, &result, sizeof(result));
1018         Proc_Close(proc);
1019         if (len == 0)
1020                 return;
1021
1022         if (len != sizeof(result)) {
1023                 Log(LOG_CRIT, "Auth: Got malformed result!");
1024                 Client_Reject(client, "Internal error", false);
1025                 return;
1026         }
1027
1028         if (result == true) {
1029                 Client_SetUser(client, Client_OrigUser(client), true);
1030                 (void)Hello_User_PostAuth(client);
1031         } else
1032                 Client_Reject(client, "Bad password", false);
1033 }
1034
1035 #endif
1036
1037
1038 /**
1039  * Finish client registration.
1040  *
1041  * Introduce the new client to the network and send all "hello messages"
1042  * to it after authentication has been succeeded.
1043  *
1044  * @param Client        The client logging in.
1045  * @returns             CONNECTED or DISCONNECTED.
1046  */
1047 static bool
1048 Hello_User_PostAuth(CLIENT *Client)
1049 {
1050         assert(Client != NULL);
1051
1052         if (Class_HandleServerBans(Client) != CONNECTED)
1053                 return DISCONNECTED;
1054
1055         Client_Introduce(NULL, Client, CLIENT_USER);
1056
1057         if (!IRC_WriteStrClient
1058             (Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client)))
1059                 return false;
1060         if (!IRC_WriteStrClient
1061             (Client, RPL_YOURHOST_MSG, Client_ID(Client),
1062              Client_ID(Client_ThisServer()), PACKAGE_VERSION, TARGET_CPU,
1063              TARGET_VENDOR, TARGET_OS))
1064                 return false;
1065         if (!IRC_WriteStrClient
1066             (Client, RPL_CREATED_MSG, Client_ID(Client), NGIRCd_StartStr))
1067                 return false;
1068         if (!IRC_WriteStrClient
1069             (Client, RPL_MYINFO_MSG, Client_ID(Client),
1070              Client_ID(Client_ThisServer()), PACKAGE_VERSION, USERMODES,
1071              CHANMODES))
1072                 return false;
1073
1074         /* Features supported by this server (005 numeric, ISUPPORT),
1075          * see <http://www.irc.org/tech_docs/005.html> for details. */
1076         if (!IRC_Send_ISUPPORT(Client))
1077                 return DISCONNECTED;
1078
1079         if (!IRC_Send_LUSERS(Client))
1080                 return DISCONNECTED;
1081         if (!IRC_Show_MOTD(Client))
1082                 return DISCONNECTED;
1083
1084         /* Suspend the client for a second ... */
1085         IRC_SetPenalty(Client, 1);
1086
1087         return CONNECTED;
1088 }
1089
1090
1091 /**
1092  * Kill all users with a specific nick name in the network.
1093  *
1094  * @param Nick          Nick name.
1095  * @param Reason        Reason for the KILL.
1096  */
1097 static void
1098 Kill_Nick(char *Nick, char *Reason)
1099 {
1100         REQUEST r;
1101
1102         assert (Nick != NULL);
1103         assert (Reason != NULL);
1104
1105         r.prefix = NULL;
1106         r.argv[0] = Nick;
1107         r.argv[1] = Reason;
1108         r.argc = 2;
1109
1110         Log(LOG_ERR, "User(s) with nick \"%s\" will be disconnected: %s",
1111             Nick, Reason);
1112
1113         IRC_KILL(Client_ThisServer(), &r);
1114 } /* Kill_Nick */
1115
1116
1117 /* -eof- */