2 * ngIRCd -- The Next Generation IRC Daemon
3 * Copyright (c)2001-2014 Alexander Barton (alex@barton.de) and Contributors.
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.
16 * Functions to deal with client logins
27 #include "client-cap.h"
34 #include "irc-channel.h"
37 #include "irc-write.h"
46 static void cb_Read_Auth_Result PARAMS((int r_fd, UNUSED short events));
51 * Initiate client login.
53 * This function is called after the daemon received the required NICK and
54 * USER commands of a new client. If the daemon is compiled with support for
55 * PAM, the authentication sub-processs is forked; otherwise the global server
56 * password is checked.
58 * @param Client The client logging in.
59 * @returns CONNECTED or DISCONNECTED.
62 Login_User(CLIENT * Client)
65 int pipefd[2], result;
70 assert(Client != NULL);
71 conn = Client_Conn(Client);
75 /* Did we receive the "auth PONG" already? */
76 if (Conn_GetAuthPing(conn)) {
77 Client_SetType(Client, CLIENT_WAITAUTHPING);
78 LogDebug("Connection %d: Waiting for AUTH PONG ...", conn);
84 /* Still waiting for "CAP END" command? */
85 if (Client_Cap(Client) & CLIENT_CAP_PENDING) {
86 Client_SetType(Client, CLIENT_WAITCAPEND);
87 LogDebug("Connection %d: Waiting for CAP END ...", conn);
93 /* Don't do any PAM authentication at all if PAM is not
94 * enabled, instead emulate the behavior of the daemon
95 * compiled without PAM support. */
96 if (strcmp(Conn_Password(conn), Conf_ServerPwd) == 0)
97 return Login_User_PostAuth(Client);
98 Client_Reject(Client, "Bad server password", false);
102 if (Conf_PAMIsOptional &&
103 strcmp(Conn_Password(conn), "") == 0) {
104 /* Clients are not required to send a password and to be PAM-
105 * authenticated at all. If not, they won't become "identified"
106 * and keep the "~" in their supplied user name.
107 * Therefore it is sensible to either set Conf_PAMisOptional or
108 * to enable IDENT lookups -- not both. */
109 return Login_User_PostAuth(Client);
113 /* Fork child process for PAM authentication; and make sure that the
114 * process timeout is set higher than the login timeout! */
115 pid = Proc_Fork(Conn_GetProcStat(conn), pipefd,
116 cb_Read_Auth_Result, Conf_PongTimeout + 1);
118 LogDebug("Authenticator for connection %d created (PID %d).",
123 Log_Init_Subprocess("Auth");
124 Conn_CloseAllSockets(NONE);
125 result = PAM_Authenticate(Client);
126 if (write(pipefd[1], &result, sizeof(result)) != sizeof(result))
127 Log_Subprocess(LOG_ERR,
128 "Failed to pipe result to parent!");
129 Log_Exit_Subprocess("Auth");
132 } else return CONNECTED;
134 /* Check global server password ... */
135 if (strcmp(Conn_Password(conn), Conf_ServerPwd) != 0) {
137 Client_Reject(Client, "Bad server password", false);
140 return Login_User_PostAuth(Client);
145 * Finish client registration.
147 * Introduce the new client to the network and send all "hello messages"
148 * to it after authentication has been succeeded.
150 * @param Client The client logging in.
151 * @return CONNECTED or DISCONNECTED.
154 Login_User_PostAuth(CLIENT *Client)
157 char modes[CLIENT_MODE_LEN + 1];
159 assert(Client != NULL);
161 if (Class_HandleServerBans(Client) != CONNECTED)
164 Client_Introduce(NULL, Client, CLIENT_USER);
166 if (!IRC_WriteStrClient
167 (Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client)))
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))
174 if (!IRC_WriteStrClient
175 (Client, RPL_CREATED_MSG, Client_ID(Client), NGIRCd_StartStr))
177 if (!IRC_WriteStrClient
178 (Client, RPL_MYINFO_MSG, Client_ID(Client),
179 Client_ID(Client_ThisServer()), PACKAGE_VERSION, USERMODES,
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))
188 if (!IRC_Send_LUSERS(Client))
190 if (!IRC_Show_MOTD(Client))
193 /* Set default user modes */
194 if (Conf_DefaultUserModes[0]) {
195 snprintf(modes, sizeof(modes), "+%s", Conf_DefaultUserModes);
196 Req.prefix = Client_ID(Client_ThisServer());
197 Req.command = "MODE";
199 Req.argv[0] = Client_ID(Client);
201 IRC_MODE(Client, &Req);
203 IRC_SetPenalty(Client, 1);
205 /* Autojoin clients to the channels */
206 Login_Autojoin(Client);
212 * Autojoin clients to the channels set by administrator
214 * Do nothing if autojoin is not set in the configuration or the channel is not
215 * available (any more).
218 Login_Autojoin(CLIENT *Client)
221 const struct Conf_Channel *conf_chan;
222 size_t i, channel_count = array_length(&Conf_Channels, sizeof(*conf_chan));
224 conf_chan = array_start(&Conf_Channels);
225 assert(channel_count == 0 || conf_chan != NULL);
227 for (i = 0; i < channel_count; i++, conf_chan++) {
228 if(!conf_chan->autojoin)
230 if (!Channel_Search(conf_chan->name))
232 Req.prefix = Client_ID(Client_ThisServer());
233 Req.command = "JOIN";
235 Req.argv[0] = (char *)conf_chan->name;
236 IRC_JOIN(Client, &Req);
243 * Read result of the authenticator sub-process from pipe
245 * @param r_fd File descriptor of the pipe.
246 * @param events (ignored IO specification)
249 cb_Read_Auth_Result(int r_fd, UNUSED short events)
251 char user[CLIENT_USER_LEN], *ptr;
258 LogDebug("Auth: Got callback on fd %d, events %d", r_fd, events);
259 conn = Conn_GetFromProc(r_fd);
261 /* Ops, none found? Probably the connection has already
262 * been closed!? We'll ignore that ... */
264 LogDebug("Auth: Got callback for unknown connection!?");
267 proc = Conn_GetProcStat(conn);
268 client = Conn_GetClient(conn);
270 /* Read result from pipe */
271 len = Proc_Read(proc, &result, sizeof(result));
276 if (len != sizeof(result)) {
277 Log(LOG_CRIT, "Auth: Got malformed result!");
278 Client_Reject(client, "Internal error", false);
282 if (result == true) {
283 /* Authentication succeeded, now set the correct user name
284 * supplied by the client (without prepended '~' for example),
285 * but cut it at the first '@' character: */
286 strlcpy(user, Client_OrigUser(client), sizeof(user));
287 ptr = strchr(user, '@');
290 Client_SetUser(client, user, true);
291 (void)Login_User_PostAuth(client);
293 Client_Reject(client, "Bad password", false);