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