/* * ngIRCd -- The Next Generation IRC Daemon * Copyright (c)2001-2014 Alexander Barton (alex@barton.de) and Contributors. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * Please read the file COPYING, README and AUTHORS for more information. */ #include "portab.h" /** * @file * Functions to deal with client logins */ #include #include #include #include #include #include "conn.h" #include "class.h" #include "client-cap.h" #include "channel.h" #include "conf.h" #include "parse.h" #include "log.h" #include "messages.h" #include "ngircd.h" #include "irc-info.h" #include "irc-mode.h" #include "irc-write.h" #include "login.h" #ifdef PAM #include "io.h" #include "pam.h" static void cb_Read_Auth_Result PARAMS((int r_fd, UNUSED short events)); #endif /** * Initiate client login. * * This function is called after the daemon received the required NICK and * USER commands of a new client. If the daemon is compiled with support for * PAM, the authentication sub-processs is forked; otherwise the global server * password is checked. * * @param Client The client logging in. * @returns CONNECTED or DISCONNECTED. */ GLOBAL bool Login_User(CLIENT * Client) { #ifdef PAM int pipefd[2], result; pid_t pid; #endif CONN_ID conn; assert(Client != NULL); conn = Client_Conn(Client); #ifndef STRICT_RFC if (Conf_AuthPing) { /* Did we receive the "auth PONG" already? */ if (Conn_GetAuthPing(conn)) { Client_SetType(Client, CLIENT_WAITAUTHPING); LogDebug("Connection %d: Waiting for AUTH PONG ...", conn); return CONNECTED; } } #endif /* Still waiting for "CAP END" command? */ if (Client_Cap(Client) & CLIENT_CAP_PENDING) { Client_SetType(Client, CLIENT_WAITCAPEND); LogDebug("Connection %d: Waiting for CAP END ...", conn); return CONNECTED; } #ifdef PAM if (!Conf_PAM) { /* Don't do any PAM authentication at all if PAM is not * enabled, instead emulate the behavior of the daemon * compiled without PAM support. */ if (strcmp(Conn_Password(conn), Conf_ServerPwd) == 0) return Login_User_PostAuth(Client); Client_Reject(Client, "Bad server password", false); return DISCONNECTED; } if (Conf_PAMIsOptional && strcmp(Conn_Password(conn), "") == 0) { /* Clients are not required to send a password and to be PAM- * authenticated at all. If not, they won't become "identified" * and keep the "~" in their supplied user name. * Therefore it is sensible to either set Conf_PAMisOptional or * to enable IDENT lookups -- not both. */ return Login_User_PostAuth(Client); } if (Conf_PAM) { /* Fork child process for PAM authentication; and make sure that the * process timeout is set higher than the login timeout! */ pid = Proc_Fork(Conn_GetProcStat(conn), pipefd, cb_Read_Auth_Result, Conf_PongTimeout + 1); if (pid > 0) { LogDebug("Authenticator for connection %d created (PID %d).", conn, pid); return CONNECTED; } else { /* Sub process */ Log_Init_Subprocess("Auth"); Conn_CloseAllSockets(NONE); result = PAM_Authenticate(Client); if (write(pipefd[1], &result, sizeof(result)) != sizeof(result)) Log_Subprocess(LOG_ERR, "Failed to pipe result to parent!"); Log_Exit_Subprocess("Auth"); exit(0); } } else return CONNECTED; #else /* Check global server password ... */ if (strcmp(Conn_Password(conn), Conf_ServerPwd) != 0) { /* Bad password! */ Client_Reject(Client, "Bad server password", false); return DISCONNECTED; } return Login_User_PostAuth(Client); #endif } /** * Finish client registration. * * Introduce the new client to the network and send all "hello messages" * to it after authentication has been succeeded. * * @param Client The client logging in. * @return CONNECTED or DISCONNECTED. */ GLOBAL bool Login_User_PostAuth(CLIENT *Client) { REQUEST Req; char modes[CLIENT_MODE_LEN + 1]; assert(Client != NULL); if (Class_HandleServerBans(Client) != CONNECTED) return DISCONNECTED; Client_Introduce(NULL, Client, CLIENT_USER); if (!IRC_WriteStrClient (Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client))) return false; if (!IRC_WriteStrClient (Client, RPL_YOURHOST_MSG, Client_ID(Client), Client_ID(Client_ThisServer()), PACKAGE_VERSION, HOST_CPU, HOST_VENDOR, HOST_OS)) return false; if (!IRC_WriteStrClient (Client, RPL_CREATED_MSG, Client_ID(Client), NGIRCd_StartStr)) return false; if (!IRC_WriteStrClient (Client, RPL_MYINFO_MSG, Client_ID(Client), Client_ID(Client_ThisServer()), PACKAGE_VERSION, USERMODES, CHANMODES)) return false; /* Features supported by this server (005 numeric, ISUPPORT), * see for details. */ if (!IRC_Send_ISUPPORT(Client)) return DISCONNECTED; if (!IRC_Send_LUSERS(Client)) return DISCONNECTED; if (!IRC_Show_MOTD(Client)) return DISCONNECTED; /* Set default user modes */ if (Conf_DefaultUserModes[0]) { snprintf(modes, sizeof(modes), "+%s", Conf_DefaultUserModes); Req.prefix = Client_ID(Client_ThisServer()); Req.command = "MODE"; Req.argc = 2; Req.argv[0] = Client_ID(Client); Req.argv[1] = modes; IRC_MODE(Client, &Req); } else IRC_SetPenalty(Client, 1); return CONNECTED; } #ifdef PAM /** * Read result of the authenticator sub-process from pipe * * @param r_fd File descriptor of the pipe. * @param events (ignored IO specification) */ static void cb_Read_Auth_Result(int r_fd, UNUSED short events) { char user[CLIENT_USER_LEN], *ptr; CONN_ID conn; CLIENT *client; int result; size_t len; PROC_STAT *proc; LogDebug("Auth: Got callback on fd %d, events %d", r_fd, events); conn = Conn_GetFromProc(r_fd); if (conn == NONE) { /* Ops, none found? Probably the connection has already * been closed!? We'll ignore that ... */ io_close(r_fd); LogDebug("Auth: Got callback for unknown connection!?"); return; } proc = Conn_GetProcStat(conn); client = Conn_GetClient(conn); /* Read result from pipe */ len = Proc_Read(proc, &result, sizeof(result)); Proc_Close(proc); if (len == 0) return; if (len != sizeof(result)) { Log(LOG_CRIT, "Auth: Got malformed result!"); Client_Reject(client, "Internal error", false); return; } if (result == true) { /* Authentication succeeded, now set the correct user name * supplied by the client (without prepended '~' for exmaple), * but cut it at the first '@' character: */ strlcpy(user, Client_OrigUser(client), sizeof(user)); ptr = strchr(user, '@'); if (ptr) *ptr = '\0'; Client_SetUser(client, user, true); (void)Login_User_PostAuth(client); } else Client_Reject(client, "Bad password", false); } #endif /* -eof- */