]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/login.c
Only log "IDENT ... no result" when IDENT was looked up
[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, instead emulate
95                  * the behavior of the daemon compiled without PAM support:
96                  * because there can't be any "server password", all
97                  * passwords supplied are classified as "wrong". */
98                 if(Conn_Password(conn)[0] == '\0')
99                         return Login_User_PostAuth(Client);
100                 Client_Reject(Client, "Non-empty password", false);
101                 return DISCONNECTED;
102         }
103
104         if (Conf_PAMIsOptional &&
105             strcmp(Conn_Password(conn), "") == 0) {
106                 /* Clients are not required to send a password and to be PAM-
107                  * authenticated at all. If not, they won't become "identified"
108                  * and keep the "~" in their supplied user name.
109                  * Therefore it is sensible to either set Conf_PAMisOptional or
110                  * to enable IDENT lookups -- not both. */
111                 return Login_User_PostAuth(Client);
112         }
113
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
134         /* Check global server password ... */
135         if (strcmp(Conn_Password(conn), Conf_ServerPwd) != 0) {
136                 /* Bad password! */
137                 Client_Reject(Client, "Bad server password", false);
138                 return DISCONNECTED;
139         }
140         return Login_User_PostAuth(Client);
141 #endif
142 }
143
144 /**
145  * Finish client registration.
146  *
147  * Introduce the new client to the network and send all "hello messages"
148  * to it after authentication has been succeeded.
149  *
150  * @param Client The client logging in.
151  * @return CONNECTED or DISCONNECTED.
152  */
153 GLOBAL bool
154 Login_User_PostAuth(CLIENT *Client)
155 {
156         REQUEST Req;
157         char modes[CLIENT_MODE_LEN + 1];
158
159         assert(Client != NULL);
160
161         if (Class_HandleServerBans(Client) != CONNECTED)
162                 return DISCONNECTED;
163
164         Client_Introduce(NULL, Client, CLIENT_USER);
165
166         if (!IRC_WriteStrClient
167             (Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client)))
168                 return false;
169         if (!IRC_WriteStrClient
170             (Client, RPL_YOURHOST_MSG, Client_ID(Client),
171              Client_ID(Client_ThisServer()), PACKAGE_VERSION, HOST_CPU,
172              HOST_VENDOR, HOST_OS))
173                 return false;
174         if (!IRC_WriteStrClient
175             (Client, RPL_CREATED_MSG, Client_ID(Client), NGIRCd_StartStr))
176                 return false;
177         if (!IRC_WriteStrClient
178             (Client, RPL_MYINFO_MSG, Client_ID(Client),
179              Client_ID(Client_ThisServer()), PACKAGE_VERSION, USERMODES,
180              CHANMODES))
181                 return false;
182
183         /* Features supported by this server (005 numeric, ISUPPORT),
184          * see <http://www.irc.org/tech_docs/005.html> for details. */
185         if (!IRC_Send_ISUPPORT(Client))
186                 return DISCONNECTED;
187
188         if (!IRC_Send_LUSERS(Client))
189                 return DISCONNECTED;
190         if (!IRC_Show_MOTD(Client))
191                 return DISCONNECTED;
192
193         /* Set default user modes */
194         if (Conf_DefaultUserModes[0]) {
195                 snprintf(modes, sizeof(modes), "+%s", Conf_DefaultUserModes);
196                 Req.prefix = Client_ThisServer();
197                 Req.command = "MODE";
198                 Req.argc = 2;
199                 Req.argv[0] = Client_ID(Client);
200                 Req.argv[1] = modes;
201                 IRC_MODE(Client, &Req);
202         } else
203                 IRC_SetPenalty(Client, 1);
204
205         return CONNECTED;
206 }
207
208 #ifdef PAM
209
210 /**
211  * Read result of the authenticator sub-process from pipe
212  *
213  * @param r_fd          File descriptor of the pipe.
214  * @param events        (ignored IO specification)
215  */
216 static void
217 cb_Read_Auth_Result(int r_fd, UNUSED short events)
218 {
219         char user[CLIENT_USER_LEN], *ptr;
220         CONN_ID conn;
221         CLIENT *client;
222         int result;
223         size_t len;
224         PROC_STAT *proc;
225
226         LogDebug("Auth: Got callback on fd %d, events %d", r_fd, events);
227         conn = Conn_GetFromProc(r_fd);
228         if (conn == NONE) {
229                 /* Ops, none found? Probably the connection has already
230                  * been closed!? We'll ignore that ... */
231                 io_close(r_fd);
232                 LogDebug("Auth: Got callback for unknown connection!?");
233                 return;
234         }
235         proc = Conn_GetProcStat(conn);
236         client = Conn_GetClient(conn);
237
238         /* Read result from pipe */
239         len = Proc_Read(proc, &result, sizeof(result));
240         Proc_Close(proc);
241         if (len == 0)
242                 return;
243
244         if (len != sizeof(result)) {
245                 Log(LOG_CRIT, "Auth: Got malformed result!");
246                 Client_Reject(client, "Internal error", false);
247                 return;
248         }
249
250         if (result == true) {
251                 /* Authentication succeeded, now set the correct user name
252                  * supplied by the client (without prepended '~' for exmaple),
253                  * but cut it at the first '@' character: */
254                 strlcpy(user, Client_OrigUser(client), sizeof(user));
255                 ptr = strchr(user, '@');
256                 if (ptr)
257                         *ptr = '\0';
258                 Client_SetUser(client, user, true);
259                 (void)Login_User_PostAuth(client);
260         } else
261                 Client_Reject(client, "Bad password", false);
262 }
263
264 #endif
265
266 /* -eof- */