www.pudn.com > warsrc.rar > IPchkExt.cpp
// IPchkExt.cpp : Defines the initialization routines for the DLL. // #include "stdafx.h" #include "WarDaemon.h" #include "IPchkExt.h" #include#include "resource.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif static AFX_EXTENSION_MODULE IPchkExtDLL = { NULL, NULL }; int CallOnConnect(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam); int CallOnVerifyIPAddress(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam); int CallOnBadPassword(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam); int CallOnHasLoggedOn(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam); int CallOnVerifyLogin(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam); int CallOnSocketIsDestroyed(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam); //////////////////////////////////////// // ADDED: This is required for plugin's static CIPchkExt *pMe; extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { if (dwReason == DLL_PROCESS_ATTACH) { TRACE0("IPCHKEXT.DLL Initializing!\n"); // Extension DLL one-time initialization AfxInitExtensionModule(IPchkExtDLL, hInstance); // Insert this DLL into the resource chain new CDynLinkLibrary(IPchkExtDLL); pMe = new CIPchkExt; if (pMe->Register("IPchkExt") != 0) return 0; // Failure } else if (dwReason == DLL_PROCESS_DETACH) { TRACE0("IPCHKEXT.DLL Terminating!\n"); //////////////////////////////////////// // ADDED: This is required for plugin's if (pMe) delete pMe; } return 1; // ok } // Initialize the extended COptions variables void CIPchkExt::InitializeCOptions() { DeclOpt("Connection delay", m_ConnectionDelay, 1, 1, DATATYPE_INT); DeclOpt("Passwd retries", m_MaxPasswdRetries, 3, 2, DATATYPE_INT); DeclOpt("Passwd retry delay", m_PasswdRetryDelay, 3, 3, DATATYPE_INT); DeclOpt("Hack pswd attmpt", m_MaxPasswdHacks, 9, 4, DATATYPE_INT); DeclOpt("Hack delay", m_HackDely, 6, 5, DATATYPE_INT); } CIPchkExt::CIPchkExt() { } CIPchkExt::~CIPchkExt() { CIPConnList::KillAll(this); } int CIPchkSock::OnConnect(int Event, WPARAM wParam, LPARAM lParam) { CIPConnList *pConn = NULL; CString cBuf("- No message -"), cFmt; m_SessionBadPwdCnt = 0; if (Event) return 0; // We don't want to handle socket errors... if (!CIPConnList::VerifyConnection(pSock, &pConn)) { if (pSock->IsKindOf(RUNTIME_CLASS(CTextSock))) { CTextSock *pTextSock = (CTextSock *)pSock; if (pConn->m_IsHacker) { cBuf.LoadString(IDS_FUCKHACKERS); } else { cFmt.LoadString(IDS_TIMEOUTACTIVE); DWORD TimeLeft = pConn->m_Suspend.TimeLeft(pConn->m_SuspendVal); TimeLeft /= (1000 * 60); if (!TimeLeft) TimeLeft = 1; cBuf.Format(cFmt, TimeLeft ); } // We need to send the message directly to bypass the framework's // processing. (The connection is not yet ready for normal control messages). cFmt.Format("421 %s\r\n", cBuf); cBuf = cFmt; pTextSock->CAsyncSocket::Send(cBuf, cBuf.GetLength()); } pMe->LogMsg(LOGF_SECURITY, "CIPchkSock::OnConnect() Access denied. Told client: %s", cBuf); CSocketException::Throw(pSock, "CIPchkSock::OnConnect()", -1, cBuf); } return 0; } int CIPchkSock::OnBadPassword(int Event, WPARAM wParam, LPARAM lParam) { CIPConnList *pConn = NULL; CString Name; if (pConn = CIPConnList::Find(pSock, Name)) { ++m_SessionBadPwdCnt; ++pConn->m_BadPwdCnt; if (pConn->m_BadPwdCnt >= pMe->m_MaxPasswdHacks) { // Hacker! pConn->m_IsHacker = TRUE; pConn->m_SuspendVal = pMe->m_HackDely * 1000 * 60 * 60; pConn->m_ExpiryVal = max(pConn->m_ExpiryVal, pConn->m_SuspendVal); pConn->m_BadPwdCnt = 0; // Reset for the next user on this IP, - in a couple of hours... // Say gently goodbye... ASSERT(pSock->IsKindOf(RUNTIME_CLASS(CTextSock))); CTextSock *pTextSock = (CTextSock *)pSock; pTextSock->LogMsg(LOGF_SECURITY,"CIPchkSock::OnBadPassword() - HACKER detected. Turning on full protection."); pTextSock->SendMsg(530,"Hacking is a very bad habbit... Goodbye."); CSocketException::Throw(pSock, "CIPchkSock::OnBadPassword()", -1, "Hacker detected."); } if (m_SessionBadPwdCnt >= pMe->m_MaxPasswdRetries) { pConn->m_SuspendVal = pMe->m_PasswdRetryDelay * 1000 * 60; pConn->m_ExpiryVal = max(pConn->m_ExpiryVal, pConn->m_SuspendVal); // Say gently goodbye... ASSERT(pSock->IsKindOf(RUNTIME_CLASS(CTextSock))); CTextSock *pTextSock = (CTextSock *)pSock; pTextSock->LogMsg(LOGF_SECURITY,"CIPchkSock::OnBadPassword() - Too many bad passwords. Disconnecting user."); pTextSock->SendMsg(530,"You better check your login name and password. Goodbye."); CSocketException::Throw(pSock, "CIPchkSock::OnBadPassword()", -1, "Too many login attempts"); } } return 0; } int CIPchkSock::OnVerifyLogin(int Event, WPARAM wParam, LPARAM lParam) { USER *pUser = (USER *)wParam; LOGINPRMS *pLP = (LOGINPRMS *)lParam; CIPConnList *pConn = NULL; CString Name; if (CUsr::IsAdmin(*pUser)) return 0; // Don't mess with admins... if (pConn = CIPConnList::Find(pSock, Name)) { // Check IP/Domain shitlist for the user if (pConn->IsBanned(*pUser, pSock)) { pLP->Sock->LogMsg(LOGF_SECURITY,"CIPchkSock::OnVerifyLogin() - User %s from %s is shitlisted on IP/domain level. Access is denied.", pLP->UserID, pLP->Sock->m_DNSName); pLP->Sock->SendMsg(530,"Your IP address or domain name is shitlisted. Goodbye."); CSocketException::Throw(pSock, "CIPchkSock::OnVerifyLogin()", -1, "Shitlisted"); } // Check max logins on the IP for the user account int MaxConnections = CUsr::GetRecursiveParam(*pUser, "IP MaxConn", 0); if (MaxConnections > 0) { int Count = 0; for(CLinkedListItem *Item = CSock::m_SocketList.First() ; Item ; Item = CSock::m_SocketList.Next(Item)) { CSock *pSck = (CSock *)CSock::m_SocketList.Ptr(Item); if (!pSck->IsKindOf(RUNTIME_CLASS(CTextSock))) continue; if (pSck->m_PeerName == pConn->m_Name) ++Count; } if (Count > MaxConnections) { pLP->Sock->LogMsg(LOGF_SECURITY,"CIPchkSock::OnVerifyLogin() - To many connections from IP %s. Access is denied.", pLP->UserID, pConn->m_Name); pLP->Sock->SendMsg(530,"Your have exeeded your limit of %d concurrent sessions. Goodbye.", MaxConnections); CSocketException::Throw(pSock, "CIPchkSock::OnVerifyLogin()", -1, "To many connections from IP"); } } } return 0; } int CIPchkSock::OnHasLoggedOn(int Event, WPARAM wParam, LPARAM lParam) { CIPConnList *pConn = NULL; CString Name; if (pConn = CIPConnList::Find(pSock, Name)) { // Reset counters and flags. pConn->m_BadPwdCnt = 0; pConn->m_SuspendVal = ((DWORD)pMe->m_ConnectionDelay * 1000 * 60); pConn->m_IsHacker = FALSE; } return 0; } int CIPchkSock::OnVerifyIPAddress(int Event, WPARAM wParam, LPARAM lParam) { return 0; } int CallOnConnect(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) { return ((CIPchkSock *)Origin)->OnConnect(Event, wParam, lParam); } int CallOnVerifyIPAddress(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) { return ((CIPchkSock *)Origin)->OnVerifyIPAddress(Event, wParam, lParam); } int CallOnBadPassword(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) { return ((CIPchkSock *)Origin)->OnBadPassword(Event, wParam, lParam); } int CallOnHasLoggedOn(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) { return ((CIPchkSock *)Origin)->OnHasLoggedOn(Event, wParam, lParam); } int CallOnVerifyLogin(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) { return ((CIPchkSock *)Origin)->OnVerifyLogin(Event, wParam, lParam); } // Reqired function if sockets extentions are used // Creates a new socket derived int CallOnSocketIsDestroyed(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) { delete (CIPchkSock *)Origin; // CSocketAPI destructor will delete references return 0; } // Required function int CallApiInitInstance(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) { return ((CIPchkExt *)Origin)->ApiInitInstance(Event, wParam, lParam); } // Required function int CallApiExitInstance(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) { return ((CIPchkExt *)Origin)->ApiExitInstance(Event, wParam, lParam); } void CIPchkExt::LogMsg(int flag, LPCSTR Format, ...) { ASSERT(AfxIsValidAddress(this,sizeof(CIPchkExt))); ASSERT(m_pLog != NULL); ASSERT(AfxIsValidAddress(m_pLog, sizeof(CLog))); if (!ShouldLog(m_pLog, flag)) return; { CString cBuf; ASSERT(AfxIsValidString(Format, FALSE)); cBuf.Format("(CIPchkExt) %s", Format); va_list argList; va_start(argList, Format); m_pLog->LogMsgV(flag, cBuf, argList); va_end(argList); } } // We check _all_ sockets. This to prevent server to server transfers to // banned sites. int CallOnNewSocket(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) { CSock *pSock = (CSock *)lParam; // Create a new CNTFTPConn object and link it to the calls we will use. CDllInfo *pDLL = pMe->GetDLLInfo(); ASSERT(pDLL != NULL); if (pDLL == NULL) return 0; CIPchkSock *pConn = new CIPchkSock; pConn->pSock = pSock; // Required pSock->m_Funcs[CSock::iOnSocketIsDestroyed].AddLast(pDLL, CSock::iOnSocketIsDestroyed, pConn, CallOnSocketIsDestroyed); pSock->m_Funcs[CSock::iOnConnect].AddLast(pDLL, CSock::iOnConnect, pConn, CallOnConnect); pSock->m_Funcs[CSock::iOnVerifyIPAddress].AddLast(pDLL, CSock::iOnVerifyIPAddress, pConn, CallOnVerifyIPAddress); pSock->m_Funcs[CSock::iOnBadPassword].AddLast(pDLL, CSock::iOnBadPassword, pConn, CallOnBadPassword); pSock->m_Funcs[CSock::iOnHasLoggedOn].AddLast(pDLL, CSock::iOnHasLoggedOn, pConn, CallOnHasLoggedOn); pSock->m_Funcs[CSock::iOnVerifyLogin].AddLast(pDLL, CSock::iOnVerifyLogin, pConn, CallOnVerifyLogin); return 0; } ////////////////////////////////////////////////////////////////////////////////////////// // CIPConnList CIPConnList::CIPConnList(LPCSTR Name) { m_Name = Name; m_BadPwdCnt = 0; m_ExpiryVal = 1000 * 60 * 60; // Remember a connection for 1 hours m_SuspendVal = 0; m_IsHacker = FALSE; } CIPConnList::~CIPConnList() { } void CIPConnList::KillAll(CIPchkExt *pExt) { CIPConnList *pConn; while(pConn = (CIPConnList *)pExt->m_History.GetAndDeleteFirst()) delete pConn; } // Verify if a connection can be accepted. // Called from OnConnect() BOOL CIPConnList::VerifyConnection(CSock *pSock, CIPConnList **ppConn) { CIPConnList *pConn; CString Name(""); BOOL Rval = TRUE; if (pConn = Find(pSock, Name)) { if (pConn->m_IsHacker || pSock->IsKindOf(RUNTIME_CLASS(CTextSock))) { if (!pConn->m_Suspend.TimeOut(pConn->m_SuspendVal)) { Rval = FALSE; } else pConn->m_IsHacker = FALSE; // Only hacker until timeout... if (pSock->IsKindOf(RUNTIME_CLASS(CTextSock))) { // We always reset the timeout periods on CTextSock connection // or attempted connections... pConn->m_Suspend.Reset(); pConn->m_Expiry.Reset(); } } } else { // Just add the socket if (Name.IsEmpty()) return FALSE; // Find() is supposed to initialize 'Name' pConn = new CIPConnList(Name); pMe->m_History.AddFirst((LPVOID)pConn); } ASSERT(pConn != NULL); // Set the suspend length. if (!pConn->m_IsHacker && !pConn->m_BadPwdCnt) { pConn->m_SuspendVal = ((DWORD)pMe->m_ConnectionDelay * 1000 * 60); } // Make sure that the expery val is larger or equal to than the suspend val pConn->m_ExpiryVal = max(pConn->m_ExpiryVal, pConn->m_SuspendVal); if (ppConn) *ppConn = pConn; return Rval; } CIPConnList *CIPConnList::Find(CSock *pSock, CString& Name) { UINT Dummy; if (!pMe) return FALSE; // Dll not loaded. Something bad is going on... if (!pSock->GetPeerName(Name, Dummy)) { pMe->LogMsg(LOGF_WARNINGS,"CIPConnList::Find() - Can't resolve IP name. Connection refused."); return FALSE; } return Find(Name); } CIPConnList *CIPConnList::Find(LPCSTR Name) { if (!pMe) return NULL; // Dll not loaded. Something bad is going on... for(CLinkedListItem *Item = pMe->m_History.First(); Item;) { CLinkedListItem *Curr = Item; Item = pMe->m_History.Next(Item); CIPConnList *pConn = (CIPConnList *)pMe->m_History.Ptr(Curr); if (pConn->m_Expiry.TimeOut(pConn->m_ExpiryVal)) { pMe->m_History.DeleteItem(Curr); delete pConn; continue; } if (pConn->m_Name == Name) return pConn; } return NULL; } // Check if a connection is banned. // Verification is done on both IP name and DNS name. // Exceptions override denials. // Scan order: Server, class, group, user BOOL CIPConnList::IsBanned(USER User, CSock *pSock) { USER ParseOrder[5]; memset(&ParseOrder, 0, sizeof(ParseOrder)); int Index; BOOL Denied = FALSE; if (User == INVALID_USER_VALUE) { // Use the system list only ParseOrder[0] = CUsr::FindUser(UT_SYSTEM, "*"); // Don't do anything if the shitlist is disabled if (CUsr::GetRecursiveParam(ParseOrder[0], "Disable IP Shitlist", FALSE) == TRUE) return FALSE; } else { // Don't do anything if the shitlist is disabled if (CUsr::GetRecursiveParam(User, "Disable IP Shitlist", FALSE) == TRUE) return FALSE; // Full scan Index = 0; USER Test; if ((Test = CUsr::GetUserSystem(User)) != INVALID_USER_VALUE) ParseOrder[Index++] = Test; if ((Test = CUsr::GetUserClass(User)) != INVALID_USER_VALUE) ParseOrder[Index++] = Test; if ((Test = CUsr::GetUserGroup(User)) != INVALID_USER_VALUE) ParseOrder[Index++] = Test; ParseOrder[Index] = User; } // Check shitlist for(Index = 0; ParseOrder[Index]; Index++) { CString AccessList; CUsr::GetParam(ParseOrder[Index], "IP Shitlist", "", AccessList); if (!AccessList.IsEmpty()) { TranslateList(AccessList); Denied = IPGenericIsIpInList(m_Name, AccessList); if (!Denied && (m_Name != pSock->m_DNSName)) Denied = IPGenericIsIpInList(pSock->m_DNSName, AccessList); if (Denied) break; } } if (Denied) { // Check viplist for(Index = 0; ParseOrder[Index]; Index++) { CString AccessList; CUsr::GetParam(ParseOrder[Index], "IP Viplist", "", AccessList); if (!AccessList.IsEmpty()) { TranslateList(AccessList); Denied = (IPGenericIsIpInList(m_Name, AccessList) == FALSE); if (Denied && (m_Name != pSock->m_DNSName)) Denied = (IPGenericIsIpInList(pSock->m_DNSName, AccessList) == FALSE); if (!Denied) break; } } } return Denied; } ///////////////////////////////////////////////////////////////////////////////////// // IP mask based pattern matching // Parse the rule and return the mask BOOL IPVerifyRule(LPCSTR Rule,int **IPmask) { int Index = 0; int FromTo = 0; int MyMask[4][2]; if (!Rule || !*Rule) return FALSE; // 1.2.3.4 // *.2.3.4 // 1-4.200-203.*.4 // Initialize memset(MyMask,0,sizeof(MyMask)); while(*Rule) { // Determine token type if (*Rule == '*') { if (MyMask[Index][0]) return FALSE; MyMask[Index][0] = 0; MyMask[Index][1] = 255; FromTo = 1; ++Rule; if (*Rule && (*Rule != '.')) return FALSE; } else if (*Rule == ' ') { ++Rule; // Ignore space } else if (*Rule == '-') { if (FromTo) return FALSE; ++FromTo; if (!isdigit(*++Rule)) return FALSE; } else if (*Rule == '.') { if (!FromTo) MyMask[Index][1] = MyMask[Index][0]; FromTo = 0; if (!isdigit(*++Rule) && (*Rule != '*')) return FALSE; if ((MyMask[Index][0] > 255) || (MyMask[Index][1] > 255)) return FALSE; if (++Index == 4) return FALSE; } else { MyMask[Index][FromTo] *= 10; MyMask[Index][FromTo] += *(Rule++) - '0'; if (*Rule == '*') return FALSE; } } if (Index != 3) return FALSE; if (!FromTo) MyMask[Index][1] = MyMask[Index][0]; if ((MyMask[Index][0] > 255) || (MyMask[Index][1] > 255)) return FALSE; if (IPmask) memcpy(IPmask,MyMask,sizeof(MyMask)); return TRUE; } BOOL IPGenericIsIpInList(LPCSTR IPaddess,CString &cList) { int MyMask[4][2]; int MyIp[4]; int Index; CString Rules = cList; char *p = Rules.GetBuffer(1); int Match; if (!*p) return FALSE; // No rule if (inet_addr(IPaddess) == INADDR_NONE) goto name_lookup; memset(MyIp,0,sizeof(MyIp)); // Parse IPaddess and get the ip address numbers if (sscanf(IPaddess,"%d.%d.%d.%d", &MyIp[0], &MyIp[1], &MyIp[2], &MyIp[3]) != 4) goto name_lookup; for(p = strtok(p,";\r\n"); p && *p; p = strtok(NULL, ";\r\n")) { if (IPVerifyRule(p,(int **)MyMask)) { Match = 0; for(Index = 0; Index < 4; Index++) { if ((MyIp[Index] >= MyMask[Index][0]) && (MyIp[Index] <= MyMask[Index][1])) ++Match; } if (Match == 4) return TRUE; } else { name_lookup: if (PatternMatchesName(p, IPaddess)) return TRUE; } } return FALSE; } void TranslateList(CString &Val) { CString cBuf = Val; LPSTR p = cBuf.GetBuffer(1); Val = ""; for(p = strtok(p, ";\r\n"); p; p = strtok(NULL, ";\r\n")) { if (!Val.IsEmpty()) Val += "\r\n"; Val += p; } }