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