]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/login.c
64cc81251c6594cbd129a56270262fa9fede6c19
[ngircd-alex.git] / src / ngircd / login.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001-2014 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  * Functions to deal with client logins
17  */
18
19 #include <assert.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <unistd.h>
24
25 #include "conn.h"
26 #include "class.h"
27 #include "client-cap.h"
28 #include "channel.h"
29 #include "conf.h"
30 #include "parse.h"
31 #include "log.h"
32 #include "messages.h"
33 #include "ngircd.h"
34 #include "irc-info.h"
35 #include "irc-mode.h"
36 #include "irc-write.h"
37
38 #include "login.h"
39
40 #ifdef PAM
41
42 #include "io.h"
43 #include "pam.h"
44
45 static void cb_Read_Auth_Result PARAMS((int r_fd, UNUSED short events));
46
47 #endif
48
49 /**
50  * Initiate client login.
51  *
52  * This function is called after the daemon received the required NICK and
53  * USER commands of a new client. If the daemon is compiled with support for
54  * PAM, the authentication sub-processs is forked; otherwise the global server
55  * password is checked.
56  *
57  * @param Client The client logging in.
58  * @returns CONNECTED or DISCONNECTED.
59  */
60 GLOBAL bool
61 Login_User(CLIENT * Client)
62 {
63 #ifdef PAM
64         int pipefd[2], result;
65         pid_t pid;
66 #endif
67         CONN_ID conn;
68
69         assert(Client != NULL);
70         conn = Client_Conn(Client);
71
72 #ifndef STRICT_RFC
73         if (Conf_AuthPing) {
74                 /* Did we receive the "auth PONG" already? */
75                 if (Conn_GetAuthPing(conn)) {
76                         Client_SetType(Client, CLIENT_WAITAUTHPING);
77                         LogDebug("Connection %d: Waiting for AUTH PONG ...", conn);
78                         return CONNECTED;
79                 }
80         }
81 #endif
82
83         /* Still waiting for "CAP END" command? */
84         if (Client_Cap(Client) & CLIENT_CAP_PENDING) {
85                 Client_SetType(Client, CLIENT_WAITCAPEND);
86                 LogDebug("Connection %d: Waiting for CAP END ...", conn);
87                 return CONNECTED;
88         }
89
90 #ifdef PAM
91         if (!Conf_PAM) {
92                 /* Don't do any PAM authentication at all if PAM is not
93                  * enabled, instead emulate the behavior of the daemon
94                  * compiled without PAM support. */
95                 if (strcmp(Conn_Password(conn), Conf_ServerPwd) == 0) 
96                         return Login_User_PostAuth(Client);
97                 Client_Reject(Client, "Bad server password", false);
98                 return DISCONNECTED;
99         }
100
101         if (Conf_PAMIsOptional &&
102             strcmp(Conn_Password(conn), "") == 0) {
103                 /* Clients are not required to send a password and to be PAM-
104                  * authenticated at all. If not, they won't become "identified"
105                  * and keep the "~" in their supplied user name.
106                  * Therefore it is sensible to either set Conf_PAMisOptional or
107                  * to enable IDENT lookups -- not both. */
108                 return Login_User_PostAuth(Client);
109         }
110
111         if (Conf_PAM) {
112                 /* Fork child process for PAM authentication; and make sure that the
113                  * process timeout is set higher than the login timeout! */
114                 pid = Proc_Fork(Conn_GetProcStat(conn), pipefd,
115                                 cb_Read_Auth_Result, Conf_PongTimeout + 1);
116                 if (pid > 0) {
117                         LogDebug("Authenticator for connection %d created (PID %d).",
118                                  conn, pid);
119                         return CONNECTED;
120                 } else {
121                         /* Sub process */
122                         Log_Init_Subprocess("Auth");
123                         Conn_CloseAllSockets(NONE);
124                         result = PAM_Authenticate(Client);
125                         if (write(pipefd[1], &result, sizeof(result)) != sizeof(result))
126                                 Log_Subprocess(LOG_ERR,
127                                                "Failed to pipe result to parent!");
128                         Log_Exit_Subprocess("Auth");
129                         exit(0);
130                 }
131         } else return CONNECTED;
132 #else
133         /* Check global server password ... */
134         if (strcmp(Conn_Password(conn), Conf_ServerPwd) != 0) {
135                 /* Bad password! */
136                 Client_Reject(Client, "Bad server password", false);
137                 return DISCONNECTED;
138         }
139         return Login_User_PostAuth(Client);
140 #endif
141 }
142
143 /**
144  * Finish client registration.
145  *
146  * Introduce the new client to the network and send all "hello messages"
147  * to it after authentication has been succeeded.
148  *
149  * @param Client The client logging in.
150  * @return CONNECTED or DISCONNECTED.
151  */
152 GLOBAL bool
153 Login_User_PostAuth(CLIENT *Client)
154 {
155         REQUEST Req;
156         char modes[CLIENT_MODE_LEN + 1];
157
158         assert(Client != NULL);
159
160         if (Class_HandleServerBans(Client) != CONNECTED)
161                 return DISCONNECTED;
162
163         Client_Introduce(NULL, Client, CLIENT_USER);
164
165         if (!IRC_WriteStrClient
166             (Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client)))
167                 return false;
168         if (!IRC_WriteStrClient
169             (Client, RPL_YOURHOST_MSG, Client_ID(Client),
170              Client_ID(Client_ThisServer()), PACKAGE_VERSION, HOST_CPU,
171              HOST_VENDOR, HOST_OS))
172                 return false;
173         if (!IRC_WriteStrClient
174             (Client, RPL_CREATED_MSG, Client_ID(Client), NGIRCd_StartStr))
175                 return false;
176         if (!IRC_WriteStrClient
177             (Client, RPL_MYINFO_MSG, Client_ID(Client),
178              Client_ID(Client_ThisServer()), PACKAGE_VERSION, USERMODES,
179              CHANMODES))
180                 return false;
181
182         /* Features supported by this server (005 numeric, ISUPPORT),
183          * see <http://www.irc.org/tech_docs/005.html> for details. */
184         if (!IRC_Send_ISUPPORT(Client))
185                 return DISCONNECTED;
186
187         if (!IRC_Send_LUSERS(Client))
188                 return DISCONNECTED;
189         if (!IRC_Show_MOTD(Client))
190                 return DISCONNECTED;
191
192         /* Set default user modes */
193         if (Conf_DefaultUserModes[0]) {
194                 snprintf(modes, sizeof(modes), "+%s", Conf_DefaultUserModes);
195                 Req.prefix = Client_ID(Client_ThisServer());
196                 Req.command = "MODE";
197                 Req.argc = 2;
198                 Req.argv[0] = Client_ID(Client);
199                 Req.argv[1] = modes;
200                 IRC_MODE(Client, &Req);
201         } else
202                 IRC_SetPenalty(Client, 1);
203
204         return CONNECTED;
205 }
206
207 #ifdef PAM
208
209 /**
210  * Read result of the authenticator sub-process from pipe
211  *
212  * @param r_fd          File descriptor of the pipe.
213  * @param events        (ignored IO specification)
214  */
215 static void
216 cb_Read_Auth_Result(int r_fd, UNUSED short events)
217 {
218         char user[CLIENT_USER_LEN], *ptr;
219         CONN_ID conn;
220         CLIENT *client;
221         int result;
222         size_t len;
223         PROC_STAT *proc;
224
225         LogDebug("Auth: Got callback on fd %d, events %d", r_fd, events);
226         conn = Conn_GetFromProc(r_fd);
227         if (conn == NONE) {
228                 /* Ops, none found? Probably the connection has already
229                  * been closed!? We'll ignore that ... */
230                 io_close(r_fd);
231                 LogDebug("Auth: Got callback for unknown connection!?");
232                 return;
233         }
234         proc = Conn_GetProcStat(conn);
235         client = Conn_GetClient(conn);
236
237         /* Read result from pipe */
238         len = Proc_Read(proc, &result, sizeof(result));
239         Proc_Close(proc);
240         if (len == 0)
241                 return;
242
243         if (len != sizeof(result)) {
244                 Log(LOG_CRIT, "Auth: Got malformed result!");
245                 Client_Reject(client, "Internal error", false);
246                 return;
247         }
248
249         if (result == true) {
250                 /* Authentication succeeded, now set the correct user name
251                  * supplied by the client (without prepended '~' for exmaple),
252                  * but cut it at the first '@' character: */
253                 strlcpy(user, Client_OrigUser(client), sizeof(user));
254                 ptr = strchr(user, '@');
255                 if (ptr)
256                         *ptr = '\0';
257                 Client_SetUser(client, user, true);
258                 (void)Login_User_PostAuth(client);
259         } else
260                 Client_Reject(client, "Bad password", false);
261 }
262
263 #endif
264
265 /* -eof- */