diff --git a/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj b/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj index b6e51c7f..d3098f4d 100644 --- a/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj +++ b/contrib/MacOSX/ngIRCd.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ FAA3D27B0F139CDC00B2447E /* conn-ssl.c in Sources */ = {isa = PBXBuildFile; fileRef = FAA3D2790F139CDC00B2447E /* conn-ssl.c */; }; FAA97C57124A271400D5BBA9 /* sighandlers.c in Sources */ = {isa = PBXBuildFile; fileRef = FAA97C55124A271400D5BBA9 /* sighandlers.c */; }; FAACD5F514A6099C006ED74F /* class.c in Sources */ = {isa = PBXBuildFile; fileRef = FAACD5F314A6099C006ED74F /* class.c */; }; + FAD5853815272C2600328741 /* login.c in Sources */ = {isa = PBXBuildFile; fileRef = FAD5853615272C2500328741 /* login.c */; }; FAE5CC2E0CF2308A007D69B6 /* numeric.c in Sources */ = {isa = PBXBuildFile; fileRef = FAE5CC2D0CF2308A007D69B6 /* numeric.c */; }; /* End PBXBuildFile section */ @@ -231,6 +232,8 @@ FAA97C56124A271400D5BBA9 /* sighandlers.h */ = {isa = PBXFileReference; fileEncoding = 5; lastKnownFileType = sourcecode.c.h; path = sighandlers.h; sourceTree = ""; }; FAACD5F314A6099C006ED74F /* class.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = class.c; sourceTree = ""; }; FAACD5F414A6099C006ED74F /* class.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = class.h; sourceTree = ""; }; + FAD5853615272C2500328741 /* login.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = login.c; sourceTree = ""; }; + FAD5853715272C2500328741 /* login.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = login.h; sourceTree = ""; }; FAE22BD215270EA300F1A5AB /* Bopm.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = Bopm.txt; sourceTree = ""; }; FAE22BD415270EA300F1A5AB /* Contributing.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = Contributing.txt; sourceTree = ""; }; FAE22BD515270EB500F1A5AB /* HowToRelease.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = HowToRelease.txt; sourceTree = ""; }; @@ -351,6 +354,8 @@ FA322CFF0CEF74B1001761B3 /* lists.h */, FA322D000CEF74B1001761B3 /* log.c */, FA322D010CEF74B1001761B3 /* log.h */, + FAD5853615272C2500328741 /* login.c */, + FAD5853715272C2500328741 /* login.h */, FA322D030CEF74B1001761B3 /* match.c */, FA322D040CEF74B1001761B3 /* match.h */, FA322D050CEF74B1001761B3 /* messages.h */, @@ -730,6 +735,7 @@ FA2D564A11EA158B00D37A35 /* pam.c in Sources */, FAA97C57124A271400D5BBA9 /* sighandlers.c in Sources */, FAACD5F514A6099C006ED74F /* class.c in Sources */, + FAD5853815272C2600328741 /* login.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/ngircd/Makefile.am b/src/ngircd/Makefile.am index cacd1c3c..e96d14be 100644 --- a/src/ngircd/Makefile.am +++ b/src/ngircd/Makefile.am @@ -42,6 +42,7 @@ ngircd_SOURCES = \ irc-write.c \ lists.c \ log.c \ + login.c \ match.c \ numeric.c \ op.c \ @@ -81,6 +82,7 @@ noinst_HEADERS = \ irc-write.h \ lists.h \ log.h \ + login.h \ match.h \ messages.h \ numeric.h \ diff --git a/src/ngircd/irc-login.c b/src/ngircd/irc-login.c index 53069f00..bf3254c9 100644 --- a/src/ngircd/irc-login.c +++ b/src/ngircd/irc-login.c @@ -18,22 +18,16 @@ #include "imp.h" #include -#include #include -#include #include -#include -#include -#include "ngircd.h" #include "conn-func.h" #include "class.h" #include "conf.h" #include "channel.h" -#include "io.h" #include "log.h" +#include "login.h" #include "messages.h" -#include "pam.h" #include "parse.h" #include "irc.h" #include "irc-info.h" @@ -42,13 +36,7 @@ #include "exp.h" #include "irc-login.h" - -static bool Hello_User PARAMS(( CLIENT *Client )); -static bool Hello_User_PostAuth PARAMS(( CLIENT *Client )); static void Kill_Nick PARAMS(( char *Nick, char *Reason )); -#ifdef PAM -static void cb_Read_Auth_Result PARAMS((int r_fd, UNUSED short events)); -#endif /** * Handler for the IRC "PASS" command. @@ -280,7 +268,7 @@ IRC_NICK( CLIENT *Client, REQUEST *Req ) /* If we received a valid USER command already then * register the new client! */ if( Client_Type( Client ) == CLIENT_GOTUSER ) - return Hello_User( Client ); + return Login_User( Client ); else Client_SetType( Client, CLIENT_GOTNICK ); } else { @@ -452,7 +440,7 @@ IRC_USER(CLIENT * Client, REQUEST * Req) LogDebug("Connection %d: got valid USER command ...", Client_Conn(Client)); if (Client_Type(Client) == CLIENT_GOTNICK) - return Hello_User(Client); + return Login_User(Client); else Client_SetType(Client, CLIENT_GOTUSER); return CONNECTED; @@ -875,7 +863,7 @@ IRC_PONG(CLIENT *Client, REQUEST *Req) if (auth_ping == atoi(Req->argv[0])) { Conn_SetAuthPing(conn, 0); if (Client_Type(Client) == CLIENT_WAITAUTHPING) - Hello_User(Client); + Login_User(Client); } else if (!IRC_WriteStrClient(Client, "To connect, type /QUOTE PONG %ld", @@ -898,196 +886,6 @@ IRC_PONG(CLIENT *Client, REQUEST *Req) } /* IRC_PONG */ -/** - * Initiate client registration. - * - * 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. - */ -static bool -Hello_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 - -#ifdef PAM - if (!Conf_PAM) { - /* Don't do any PAM authentication at all, instead emulate - * the beahiour of the daemon compiled without PAM support: - * because there can't be any "server password", all - * passwords supplied are classified as "wrong". */ - if(Client_Password(Client)[0] == '\0') - return Hello_User_PostAuth(Client); - Client_Reject(Client, "Non-empty password", false); - return DISCONNECTED; - } - - if (Conf_PAMIsOptional && strcmp(Client_Password(Client), "") == 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 Hello_User_PostAuth(Client); - } - - /* 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 - /* Check global server password ... */ - if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0) { - /* Bad password! */ - Client_Reject(Client, "Bad server password", false); - return DISCONNECTED; - } - return Hello_User_PostAuth(Client); -#endif -} - - -#ifdef PAM - -/** - * Read result of the authenticatior 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) -{ - 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) { - Client_SetUser(client, Client_OrigUser(client), true); - (void)Hello_User_PostAuth(client); - } else - Client_Reject(client, "Bad password", false); -} - -#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. - * @returns CONNECTED or DISCONNECTED. - */ -static bool -Hello_User_PostAuth(CLIENT *Client) -{ - 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, TARGET_CPU, - TARGET_VENDOR, TARGET_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; - - /* Suspend the client for a second ... */ - IRC_SetPenalty(Client, 1); - - return CONNECTED; -} - - /** * Kill all users with a specific nick name in the network. * diff --git a/src/ngircd/login.c b/src/ngircd/login.c new file mode 100644 index 00000000..2c305402 --- /dev/null +++ b/src/ngircd/login.c @@ -0,0 +1,234 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * Copyright (c)2001-2012 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 "imp.h" +#include +#include +#include +#include + +#include "defines.h" +#include "conn.h" +#include "class.h" +#include "client.h" +#include "channel.h" +#include "conf.h" +#include "io.h" +#include "parse.h" +#include "log.h" +#include "messages.h" +#include "ngircd.h" +#include "pam.h" +#include "irc-info.h" +#include "irc-write.h" + +#include "exp.h" +#include "login.h" + +#ifdef PAM +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 + +#ifdef PAM + if (!Conf_PAM) { + /* Don't do any PAM authentication at all, instead emulate + * the beahiour of the daemon compiled without PAM support: + * because there can't be any "server password", all + * passwords supplied are classified as "wrong". */ + if(Client_Password(Client)[0] == '\0') + return Login_User_PostAuth(Client); + Client_Reject(Client, "Non-empty password", false); + return DISCONNECTED; + } + + if (Conf_PAMIsOptional && strcmp(Client_Password(Client), "") == 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); + } + + /* 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 + /* Check global server password ... */ + if (strcmp(Client_Password(Client), 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) +{ + 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, TARGET_CPU, + TARGET_VENDOR, TARGET_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; + + /* Suspend the client for a second ... */ + IRC_SetPenalty(Client, 1); + + return CONNECTED; +} + +#ifdef PAM + +/** + * Read result of the authenticatior 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) +{ + 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) { + Client_SetUser(client, Client_OrigUser(client), true); + (void)Login_User_PostAuth(client); + } else + Client_Reject(client, "Bad password", false); +} + +#endif + +/* -eof- */ diff --git a/src/ngircd/login.h b/src/ngircd/login.h new file mode 100644 index 00000000..6e3a21d6 --- /dev/null +++ b/src/ngircd/login.h @@ -0,0 +1,25 @@ +/* + * ngIRCd -- The Next Generation IRC Daemon + * Copyright (c)2001-2012 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. + */ + +#ifndef __login_h__ +#define __login_h__ + +/** + * @file + * Functions to deal with client logins (header) + */ + +GLOBAL bool Login_User PARAMS((CLIENT * Client)); +GLOBAL bool Login_User_PostAuth PARAMS((CLIENT *Client)); + +#endif + +/* -eof- */