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