www.gusucode.com > VC++版的邮件服务器源程序源码程序 > VC++版的邮件服务器源程序源码程序\code\SMTPSvr.cpp
//Download by http://www.NewXing.com /* * XMail by Davide Libenzi ( Intranet and Internet mail server ) * Copyright (C) 1999,..,2004 Davide Libenzi * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Davide Libenzi <davidel@xmailserver.org> * */ #include "SysInclude.h" #include "SysDep.h" #include "SvrDefines.h" #include "SList.h" #include "ShBlocks.h" #include "BuffSock.h" #include "ResLocks.h" #include "StrUtils.h" #include "UsrUtils.h" #include "SvrUtils.h" #include "MessQueue.h" #include "SMAILUtils.h" #include "QueueUtils.h" #include "MiscUtils.h" #include "Base64Enc.h" #include "MD5.h" #include "UsrMailList.h" #include "SMTPSvr.h" #include "SMTPUtils.h" #include "MailDomains.h" #include "POP3Utils.h" #include "MailConfig.h" #include "AppDefines.h" #include "MailSvr.h" #define SMTP_MAX_LINE_SIZE 2048 #define SMTPSRV_ACCEPT_TIMEOUT 4 #define STD_SMTP_TIMEOUT 30 #define SMTP_LISTEN_SIZE 64 #define SMTP_WAIT_SLEEP 2 #define MAX_CLIENTS_WAIT 300 #define SMTP_IPMAP_FILE "smtp.ipmap.tab" #define SMTP_IPPROP_FILE "smtp.ipprop.tab" #define SMTP_LOG_FILE "smtp" #define SMTP_SERVER_NAME "[" APP_NAME_VERSION_STR " ESMTP Server]" #define SMTP_PRE_DATA_FILTER "pre-data" #define SMTP_POST_DATA_FILTER "post-data" #define SMTP_FILTER_REJECT_CODE 3 #define PLAIN_AUTH_PARAM_SIZE 1024 #define LOGIN_AUTH_USERNAME "Username:" #define LOGIN_AUTH_PASSWORD "Password:" #define SVR_SMTP_AUTH_FILE "smtpauth.tab" #define SVR_SMTPAUTH_LINE_MAX 512 #define SVR_SMTP_EXTAUTH_FILE "smtpextauth.tab" #define SVR_SMTP_EXTAUTH_LINE_MAX 1024 #define SVR_SMTP_EXTAUTH_TIMEOUT 60 #define SVR_SMTP_EXTAUTH_PRIORITY SYS_PRIORITY_NORMAL #define SVR_SMTP_EXTAUTH_SUCCESS 0 #define SMTPF_RELAY_ENABLED (1 << 0) #define SMTPF_MAIL_LOCKED (1 << 1) #define SMTPF_MAIL_UNLOCKED (1 << 2) #define SMTPF_AUTHENTICATED (1 << 3) #define SMTPF_VRFY_ENABLED (1 << 4) #define SMTPF_MAPPED_IP (1 << 5) #define SMTPF_NORDNS_IP (1 << 6) #define SMTPF_ETRN_ENABLED (1 << 7) #define SMTPF_NOEMIT_AUTH (1 << 8) #define SMTPF_WHITE_LISTED (1 << 9) #define SMTPF_BLOCKED_IP (1 << 10) #define SMTPF_STATIC_MASK (SMTPF_MAPPED_IP | SMTPF_NORDNS_IP | SMTPF_WHITE_LISTED | SMTPF_BLOCKED_IP) #define SMTPF_AUTH_MASK (SMTPF_RELAY_ENABLED | SMTPF_MAIL_UNLOCKED | SMTPF_AUTHENTICATED | \ SMTPF_VRFY_ENABLED | SMTPF_ETRN_ENABLED) #define SMTPF_RESET_MASK (SMTPF_AUTH_MASK | SMTPF_STATIC_MASK | SMTPF_NOEMIT_AUTH) #define SMTP_FILTER_FL_BREAK (1 << 4) #define SMTP_FILTER_FL_MASK SMTP_FILTER_FL_BREAK enum SMTPStates { stateInit = 0, stateHelo, stateAuthenticated, stateMail, stateRcpt, stateExit }; struct SMTPSession { int iSMTPState; SHB_HANDLE hShbSMTP; SMTPConfig *pSMTPCfg; SVRCFG_HANDLE hSvrConfig; SYS_INET_ADDR PeerInfo; SYS_INET_ADDR SockInfo; int iCmdDelay; unsigned long ulMaxMsgSize; char szSvrFQDN[MAX_ADDR_NAME]; char szSvrDomain[MAX_ADDR_NAME]; char szClientFQDN[MAX_ADDR_NAME]; char szClientDomain[MAX_ADDR_NAME]; char szDestDomain[MAX_ADDR_NAME]; char szLogonUser[128]; char szMsgFile[SYS_MAX_PATH]; FILE *pMsgFile; char *pszFrom; char *pszRcpt; char *pszSendRcpt; int iRcptCount; SYS_UINT64 ullMessageID; char szMessageID[128]; char szTimeStamp[256]; unsigned long ulSetupFlags; unsigned long ulFlags; char *pszCustMsg; char szRejMapName[256]; }; enum SmtpAuthFields { smtpaUsername = 0, smtpaPassword, smtpaPerms, smtpaMax }; static SMTPConfig *SMTPGetConfigCopy(SHB_HANDLE hShbSMTP); static int SMTPLogEnabled(SHB_HANDLE hShbSMTP, SMTPConfig * pSMTPCfg = NULL); static int SMTPCheckPeerIP(SYS_SOCKET SockFD); static int SMTPThreadCountAdd(long lCount, SHB_HANDLE hShbSMTP, SMTPConfig * pSMTPCfg = NULL); static unsigned int SMTPClientThread(void *pThreadData); static int SMTPCheckSysResources(SVRCFG_HANDLE hSvrConfig); static int SMTPCheckMapsList(SYS_INET_ADDR const &PeerInfo, char const *pszMapList, char *pszMapName, int iMaxMapName, int &iMapCode); static int SMTPApplyIPProps(SMTPSession & SMTPS); static int SMTPDoIPBasedInit(SMTPSession & SMTPS, char *&pszSMTPError); static int SMTPInitSession(SHB_HANDLE hShbSMTP, BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char *&pszSMTPError); static int SMTPLoadConfig(SMTPSession & SMTPS, char const *pszSvrConfig); static int SMTPApplyPerms(SMTPSession & SMTPS, char const *pszPerms); static int SMTPApplyUserConfig(SMTPSession & SMTPS, UserInfo * pUI); static int SMTPLogSession(SMTPSession & SMTPS, char const *pszSender, char const *pszRecipient, char const *pszStatus, unsigned long ulMsgSize); static int SMTPSendError(BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char const *pszFormat, ...); static int SMTPHandleSession(SHB_HANDLE hShbSMTP, BSOCK_HANDLE hBSock); static void SMTPClearSession(SMTPSession & SMTPS); static void SMTPResetSession(SMTPSession & SMTPS); static int SMTPHandleCommand(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS); static int SMTPCheckReturnPath(const char *pszCommand, char **ppszRetDomains, SMTPSession & SMTPS, char *&pszSMTPError); static int SMTPTryPopAuthIpCheck(SMTPSession & SMTPS, char const *pszUser, char const *pszDomain); static int SMTPAddMessageInfo(SMTPSession & SMTPS); static int SMTPCheckMailParams(const char *pszCommand, char **ppszRetDomains, SMTPSession & SMTPS, char *&pszSMTPError); static int SMTPHandleCmd_MAIL(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS); static int SMTPCheckRelayCapability(SMTPSession & SMTPS, char const *pszDestDomain); static int SMTPCheckForwardPath(char **ppszFwdDomains, SMTPSession & SMTPS, char *&pszRealRcpt, char *&pszSMTPError); static int SMTPHandleCmd_RCPT(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS); static int SMTPGetFilterFile(const char *pszFiltID, char *pszFileName, int iMaxName); static int SMTPFilterMacroSubstitutes(char **ppszCmdTokens, SMTPSession & SMTPS); static char *SMTPGetFilterRejMessage(char const *pszMsgFilePath); static int SMTPRunFilters(SMTPSession & SMTPS, char const *pszFilterPath, char *&pszError); static int SMTPFilterMessage(SMTPSession & SMTPS, const char *pszFiltID, char *&pszError); static int SMTPHandleCmd_DATA(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS); static int SMTPAddReceived(int iType, char const *pszAuth, char const *const *ppszMsgInfo, char const *pszMailFrom, char const *pszRcptTo, char const *pszMessageID, FILE * pMailFile); static char *SMTPTrimRcptLine(char *pszRcptLn); static int SMTPSubmitPackedFile(SMTPSession & SMTPS, const char *pszPkgFile); static int SMTPHandleCmd_HELO(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS); static int SMTPHandleCmd_EHLO(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS); static int SMTPListExtAuths(FILE * pRespFile, SMTPSession & SMTPS); static int SMTPExternalAuthSubstitute(char **ppszAuthTokens, char const *pszChallenge, char const *pszDigest, char const *pszSecretsFile); static int SMTPCreateSecretsFile(char const *pszSecretsFile); static int SMTPExternalAuthenticate(BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char **ppszAuthTokens); static int SMTPDoAuthExternal(BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char const *pszAuthType); static int SMTPDoAuthPlain(BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char const *pszAuthParam); static int SMTPDoAuthLogin(BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char const *pszAuthParam); static char *SMTPGetAuthFilePath(char *pszFilePath, int iMaxPath); static char *SMTPGetExtAuthFilePath(char *pszFilePath, int iMaxPath); static int SMTPTryApplyLocalAuth(SMTPSession & SMTPS, char const *pszUsername, char const *pszPassword); static int SMTPGetUserSmtpPerms(UserInfo * pUI, SVRCFG_HANDLE hSvrConfig, char *pszPerms, int iMaxPerms); static int SMTPTryApplyLocalCMD5Auth(SMTPSession & SMTPS, char const *pszChallenge, char const *pszUsername, char const *pszDigest); static int SMTPTryApplyUsrPwdAuth(SMTPSession & SMTPS, char const *pszUsername, char const *pszPassword); static int SMTPTryApplyCMD5Auth(SMTPSession & SMTPS, char const *pszChallenge, char const *pszUsername, char const *pszDigest); static int SMTPDoAuthCramMD5(BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char const *pszAuthParam); static int SMTPHandleCmd_AUTH(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS); static int SMTPSendMultilineResponse(BSOCK_HANDLE hBSock, int iTimeout, FILE * pRespFile); static int SMTPHandleCmd_RSET(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS); static int SMTPHandleCmd_NOOP(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS); static int SMTPHandleCmd_HELP(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS); static int SMTPHandleCmd_QUIT(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS); static int SMTPHandleCmd_VRFY(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS); static int SMTPHandleCmd_ETRN(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS); static SMTPConfig *SMTPGetConfigCopy(SHB_HANDLE hShbSMTP) { SMTPConfig *pSMTPCfg = (SMTPConfig *) ShbLock(hShbSMTP); if (pSMTPCfg == NULL) return (NULL); SMTPConfig *pSMTPCfgCopy = (SMTPConfig *) SysAlloc(sizeof(SMTPConfig)); if (pSMTPCfgCopy != NULL) memcpy(pSMTPCfgCopy, pSMTPCfg, sizeof(SMTPConfig)); ShbUnlock(hShbSMTP); return (pSMTPCfgCopy); } static int SMTPLogEnabled(SHB_HANDLE hShbSMTP, SMTPConfig * pSMTPCfg) { int iDoUnlock = 0; if (pSMTPCfg == NULL) { if ((pSMTPCfg = (SMTPConfig *) ShbLock(hShbSMTP)) == NULL) return (ErrGetErrorCode()); ++iDoUnlock; } unsigned long ulFlags = pSMTPCfg->ulFlags; if (iDoUnlock) ShbUnlock(hShbSMTP); return ((ulFlags & SMTPF_LOG_ENABLED) ? 1 : 0); } static int SMTPCheckPeerIP(SYS_SOCKET SockFD) { char szIPMapFile[SYS_MAX_PATH] = ""; CfgGetRootPath(szIPMapFile, sizeof(szIPMapFile)); StrNCat(szIPMapFile, SMTP_IPMAP_FILE, sizeof(szIPMapFile)); if (SysExistFile(szIPMapFile)) { SYS_INET_ADDR PeerInfo; if (SysGetPeerInfo(SockFD, PeerInfo) < 0) return (ErrGetErrorCode()); if (MscCheckAllowedIP(szIPMapFile, PeerInfo, true) < 0) return (ErrGetErrorCode()); } return (0); } static int SMTPThreadCountAdd(long lCount, SHB_HANDLE hShbSMTP, SMTPConfig * pSMTPCfg) { int iDoUnlock = 0; if (pSMTPCfg == NULL) { if ((pSMTPCfg = (SMTPConfig *) ShbLock(hShbSMTP)) == NULL) return (ErrGetErrorCode()); ++iDoUnlock; } if ((pSMTPCfg->lThreadCount + lCount) > pSMTPCfg->lMaxThreads) { if (iDoUnlock) ShbUnlock(hShbSMTP); ErrSetErrorCode(ERR_SERVER_BUSY); return (ERR_SERVER_BUSY); } pSMTPCfg->lThreadCount += lCount; if (iDoUnlock) ShbUnlock(hShbSMTP); return (0); } static unsigned int SMTPClientThread(void *pThreadData) { SYS_SOCKET SockFD = (SYS_SOCKET) (unsigned int) pThreadData; /////////////////////////////////////////////////////////////////////////////// // Link socket to the bufferer /////////////////////////////////////////////////////////////////////////////// BSOCK_HANDLE hBSock = BSckAttach(SockFD); if (hBSock == INVALID_BSOCK_HANDLE) { ErrorPush(); SysCloseSocket(SockFD); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Check IP permission /////////////////////////////////////////////////////////////////////////////// if (SMTPCheckPeerIP(SockFD) < 0) { ErrorPush(); BSckVSendString(hBSock, STD_SMTP_TIMEOUT, "421 %s - %s", SMTP_SERVER_NAME, ErrGetErrorString(ErrorFetch())); BSckDetach(hBSock, 1); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Increase threads count /////////////////////////////////////////////////////////////////////////////// if (SMTPThreadCountAdd(+1, hShbSMTP) < 0) { ErrorPush(); BSckVSendString(hBSock, STD_SMTP_TIMEOUT, "421 %s - %s", SMTP_SERVER_NAME, ErrGetErrorString(ErrorFetch())); BSckDetach(hBSock, 1); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Handle client session /////////////////////////////////////////////////////////////////////////////// SMTPHandleSession(hShbSMTP, hBSock); /////////////////////////////////////////////////////////////////////////////// // Decrease threads count /////////////////////////////////////////////////////////////////////////////// SMTPThreadCountAdd(-1, hShbSMTP); /////////////////////////////////////////////////////////////////////////////// // Unlink socket from the bufferer and close it /////////////////////////////////////////////////////////////////////////////// BSckDetach(hBSock, 1); return (0); } unsigned int SMTPThreadProc(void *pThreadData) { SMTPConfig *pSMTPCfg = (SMTPConfig *) ShbLock(hShbSMTP); if (pSMTPCfg == NULL) { ErrorPush(); SysLogMessage(LOG_LEV_ERROR, "%s\n", ErrGetErrorString()); return (ErrorPop()); } int iNumSockFDs = 0; SYS_SOCKET SockFDs[MAX_SMTP_ACCEPT_ADDRESSES]; if (MscCreateServerSockets(pSMTPCfg->iNumAddr, pSMTPCfg->SvrAddr, pSMTPCfg->iPort, SMTP_LISTEN_SIZE, SockFDs, iNumSockFDs) < 0) { ErrorPush(); SysLogMessage(LOG_LEV_ERROR, "%s\n", ErrGetErrorString()); ShbUnlock(hShbSMTP); return (ErrorPop()); } ShbUnlock(hShbSMTP); SysLogMessage(LOG_LEV_MESSAGE, "%s started\n", SMTP_SERVER_NAME); for (;;) { int iNumConnSockFD = 0; SYS_SOCKET ConnSockFD[MAX_SMTP_ACCEPT_ADDRESSES]; if (MscAcceptServerConnection(SockFDs, iNumSockFDs, ConnSockFD, iNumConnSockFD, SMTPSRV_ACCEPT_TIMEOUT) < 0) { unsigned long ulFlags = SMTPF_STOP_SERVER; pSMTPCfg = (SMTPConfig *) ShbLock(hShbSMTP); if (pSMTPCfg != NULL) ulFlags = pSMTPCfg->ulFlags; ShbUnlock(hShbSMTP); if (ulFlags & SMTPF_STOP_SERVER) break; else continue; } for (int ss = 0; ss < iNumConnSockFD; ss++) { SYS_THREAD hClientThread = SysCreateServiceThread(SMTPClientThread, ConnSockFD[ss]); if (hClientThread != SYS_INVALID_THREAD) SysCloseThread(hClientThread, 0); else SysCloseSocket(ConnSockFD[ss]); } } for (int ss = 0; ss < iNumSockFDs; ss++) SysCloseSocket(SockFDs[ss]); /////////////////////////////////////////////////////////////////////////////// // Wait for client completion /////////////////////////////////////////////////////////////////////////////// for (int iTotalWait = 0; (iTotalWait < MAX_CLIENTS_WAIT); iTotalWait += SMTP_WAIT_SLEEP) { pSMTPCfg = (SMTPConfig *) ShbLock(hShbSMTP); if (pSMTPCfg == NULL) break; long lThreadCount = pSMTPCfg->lThreadCount; ShbUnlock(hShbSMTP); if (lThreadCount == 0) break; SysSleep(SMTP_WAIT_SLEEP); } SysLogMessage(LOG_LEV_MESSAGE, "%s stopped\n", SMTP_SERVER_NAME); return (0); } static int SMTPCheckSysResources(SVRCFG_HANDLE hSvrConfig) { /////////////////////////////////////////////////////////////////////////////// // Check disk space /////////////////////////////////////////////////////////////////////////////// int iMinValue = SvrGetConfigInt("SmtpMinDiskSpace", -1, hSvrConfig); if ((iMinValue > 0) && (SvrCheckDiskSpace(1024 * (unsigned long) iMinValue) < 0)) return (ErrGetErrorCode()); /////////////////////////////////////////////////////////////////////////////// // Check virtual memory /////////////////////////////////////////////////////////////////////////////// if (((iMinValue = SvrGetConfigInt("SmtpMinVirtMemSpace", -1, hSvrConfig)) > 0) && (SvrCheckVirtMemSpace(1024 * (unsigned long) iMinValue) < 0)) return (ErrGetErrorCode()); return (0); } static int SMTPCheckMapsList(SYS_INET_ADDR const &PeerInfo, char const *pszMapList, char *pszMapName, int iMaxMapName, int &iMapCode) { for (;;) { char const *pszColon = strchr(pszMapList, ':'); if (pszColon == NULL) break; int iRetCode = atoi(pszColon + 1); int iMapLength = Min((int) (pszColon - pszMapList), MAX_HOST_NAME - 1); char szMapName[MAX_HOST_NAME] = ""; strncpy(szMapName, pszMapList, iMapLength); szMapName[iMapLength] = '\0'; if (USmtpDnsMapsContained(PeerInfo, szMapName)) { if (pszMapName != NULL) StrNCpy(pszMapName, szMapName, iMaxMapName); iMapCode = iRetCode; char szIP[128] = "???.???.???.???"; char szMapSpec[MAX_HOST_NAME + 128] = ""; SysInetNToA(PeerInfo, szIP); SysSNPrintf(szMapSpec, sizeof(szMapSpec) - 1, "%s:%s", szMapName, szIP); ErrSetErrorCode(ERR_MAPS_CONTAINED, szMapSpec); return (ERR_MAPS_CONTAINED); } if ((pszMapList = strchr(pszColon, ',')) == NULL) break; ++pszMapList; } return (0); } static int SMTPApplyIPProps(SMTPSession & SMTPS) { char szIPPropFile[SYS_MAX_PATH] = ""; CfgGetRootPath(szIPPropFile, sizeof(szIPPropFile)); StrNCat(szIPPropFile, SMTP_IPPROP_FILE, sizeof(szIPPropFile)); int ii; char **ppszProps; if ((ppszProps = MscGetIPProperties(szIPPropFile, SMTPS.PeerInfo)) == NULL) return (0); for (ii = 1; ppszProps[ii] != NULL; ii++) { int iNameLen; char *pszName = ppszProps[ii]; char *pszVal = strchr(pszName, '='); if (pszVal == NULL) continue; iNameLen = pszVal - pszName; pszVal++; if (strncmp(pszName, "WhiteList", CStringSize("WhiteList")) == 0) { if (atoi(pszVal)) SMTPS.ulFlags |= SMTPF_WHITE_LISTED; } } StrFreeStrings(ppszProps); return (0); } static int SMTPDoIPBasedInit(SMTPSession & SMTPS, char *&pszSMTPError) { /////////////////////////////////////////////////////////////////////////////// // Check if SMTP client is in "spammers.tab" file /////////////////////////////////////////////////////////////////////////////// char *pszChkInfo = NULL; if (USmtpSpammerCheck(SMTPS.PeerInfo, pszChkInfo) < 0) { int iMapCode = 1, iErrorCode = ErrGetErrorCode(); if (pszChkInfo != NULL) { char szMapCode[32] = ""; if (StrParamGet(pszChkInfo, "code", szMapCode, sizeof(szMapCode) - 1)) iMapCode = atoi(szMapCode); SysFree(pszChkInfo); } if (iMapCode > 0) { if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, "", "", "SNDRIP=EIPSPAM", 0); pszSMTPError = SvrGetConfigVar(SMTPS.hSvrConfig, "SmtpMsgIPBanSpammers"); ErrSetErrorCode(iErrorCode); return (iErrorCode); } else if (iMapCode == 0) SMTPS.ulFlags |= SMTPF_BLOCKED_IP; else SMTPS.iCmdDelay = Max(SMTPS.iCmdDelay, Abs(iMapCode)); } /////////////////////////////////////////////////////////////////////////////// // Custom maps checking /////////////////////////////////////////////////////////////////////////////// char *pszMapsList = SvrGetConfigVar(SMTPS.hSvrConfig, "CustMapsList"); if (pszMapsList != NULL) { int iMapCode = 0; char *pszCfgError = NULL; if (SMTPCheckMapsList(SMTPS.PeerInfo, pszMapsList, SMTPS.szRejMapName, sizeof(SMTPS.szRejMapName) - 1, iMapCode) < 0) { if (iMapCode == 1) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) { char *pszError = StrSprint("SNDRIP=EIPMAP (%s)", SMTPS.szRejMapName); SMTPLogSession(SMTPS, "", "", (pszError != NULL) ? pszError : "SNDRIP=EIPMAP", 0); if (pszError != NULL) SysFree(pszError); } if ((pszCfgError = SvrGetConfigVar(SMTPS.hSvrConfig, "SmtpMsgIPBanMaps")) != NULL) { pszSMTPError = StrSprint("%s (%s)", pszCfgError, SMTPS.szRejMapName); SysFree(pszCfgError); } else pszSMTPError = StrSprint ("550 Denied due inclusion of your IP inside (%s)", SMTPS.szRejMapName); SysFree(pszMapsList); return (ErrorPop()); } if (iMapCode == 0) SMTPS.ulFlags |= SMTPF_MAPPED_IP; else SMTPS.iCmdDelay = Max(SMTPS.iCmdDelay, Abs(iMapCode)); } SysFree(pszMapsList); } /////////////////////////////////////////////////////////////////////////////// // RDNS client check /////////////////////////////////////////////////////////////////////////////// int iCheckValue = SvrGetConfigInt("SMTP-RDNSCheck", 0, SMTPS.hSvrConfig); if ((iCheckValue != 0) && (SysGetHostByAddr(SMTPS.PeerInfo, SMTPS.szClientFQDN) < 0)) { if (iCheckValue > 0) SMTPS.ulFlags |= SMTPF_NORDNS_IP; else SMTPS.iCmdDelay = Max(SMTPS.iCmdDelay, -iCheckValue); } return (0); } static int SMTPInitSession(SHB_HANDLE hShbSMTP, BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char *&pszSMTPError) { ZeroData(SMTPS); SMTPS.iSMTPState = stateInit; SMTPS.hShbSMTP = hShbSMTP; SMTPS.hSvrConfig = INVALID_SVRCFG_HANDLE; SMTPS.pSMTPCfg = NULL; SMTPS.pMsgFile = NULL; SMTPS.pszFrom = NULL; SMTPS.pszRcpt = NULL; SMTPS.pszSendRcpt = NULL; SMTPS.iRcptCount = 0; SMTPS.iCmdDelay = 0; SMTPS.ulMaxMsgSize = 0; SetEmptyString(SMTPS.szMessageID); SetEmptyString(SMTPS.szDestDomain); SetEmptyString(SMTPS.szClientFQDN); SetEmptyString(SMTPS.szClientDomain); SetEmptyString(SMTPS.szLogonUser); SetEmptyString(SMTPS.szRejMapName); SMTPS.ulFlags = 0; SMTPS.ulSetupFlags = 0; SMTPS.pszCustMsg = NULL; SysGetTmpFile(SMTPS.szMsgFile); if ((SMTPS.hSvrConfig = SvrGetConfigHandle()) == INVALID_SVRCFG_HANDLE) return (ErrGetErrorCode()); if ((SMTPCheckSysResources(SMTPS.hSvrConfig) < 0) || (SysGetPeerInfo(BSckGetAttachedSocket(hBSock), SMTPS.PeerInfo) < 0) || (SysGetSockInfo(BSckGetAttachedSocket(hBSock), SMTPS.SockInfo) < 0) || (SMTPApplyIPProps(SMTPS) < 0)) { ErrorPush(); SvrReleaseConfigHandle(SMTPS.hSvrConfig); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // If the remote IP is not white-listed, do all IP based checks /////////////////////////////////////////////////////////////////////////////// if (((SMTPS.ulFlags & SMTPF_WHITE_LISTED) == 0) && (SMTPDoIPBasedInit(SMTPS, pszSMTPError) < 0)) { ErrorPush(); SvrReleaseConfigHandle(SMTPS.hSvrConfig); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Setup SMTP domain /////////////////////////////////////////////////////////////////////////////// char *pszSvrDomain = SvrGetConfigVar(SMTPS.hSvrConfig, "SmtpServerDomain"); char szIP[128] = "???.???.???.???"; if (pszSvrDomain != NULL) { StrSNCpy(SMTPS.szSvrDomain, pszSvrDomain); SysFree(pszSvrDomain); } else { if (MscGetSockHost(BSckGetAttachedSocket(hBSock), SMTPS.szSvrFQDN) < 0) StrSNCpy(SMTPS.szSvrFQDN, SysInetNToA(SMTPS.SockInfo, szIP)); else { /////////////////////////////////////////////////////////////////////////////// // Try to get a valid domain from the FQDN /////////////////////////////////////////////////////////////////////////////// if (MDomGetClientDomain(SMTPS.szSvrFQDN, SMTPS.szSvrDomain, sizeof(SMTPS.szSvrDomain) - 1) < 0) StrSNCpy(SMTPS.szSvrDomain, SMTPS.szSvrFQDN); } /////////////////////////////////////////////////////////////////////////////// // Last attempt, try fetch the "RootDomain" variable ... /////////////////////////////////////////////////////////////////////////////// if (IsEmptyString(SMTPS.szSvrDomain)) { if ((pszSvrDomain = SvrGetConfigVar(SMTPS.hSvrConfig, "RootDomain")) == NULL) { SvrReleaseConfigHandle(SMTPS.hSvrConfig); ErrSetErrorCode(ERR_NO_DOMAIN); return (ERR_NO_DOMAIN); } StrSNCpy(SMTPS.szSvrDomain, pszSvrDomain); SysFree(pszSvrDomain); } } /////////////////////////////////////////////////////////////////////////////// // Create timestamp /////////////////////////////////////////////////////////////////////////////// sprintf(SMTPS.szTimeStamp, "<%lu.%lu@%s>", (unsigned long) time(NULL), SysGetCurrentThreadId(), SMTPS.szSvrDomain); if ((SMTPS.pSMTPCfg = SMTPGetConfigCopy(hShbSMTP)) == NULL) { ErrorPush(); SvrReleaseConfigHandle(SMTPS.hSvrConfig); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Get maximum accepted message size /////////////////////////////////////////////////////////////////////////////// SMTPS.ulMaxMsgSize = 1024 * (unsigned long) SvrGetConfigInt("MaxMessageSize", 0, SMTPS.hSvrConfig); /////////////////////////////////////////////////////////////////////////////// // Check if the emission of "X-Auth-User:" is diabled /////////////////////////////////////////////////////////////////////////////// if (SvrGetConfigInt("DisableEmitAuthUser", 0, SMTPS.hSvrConfig)) SMTPS.ulFlags |= SMTPF_NOEMIT_AUTH; /////////////////////////////////////////////////////////////////////////////// // Try to load specific configuration /////////////////////////////////////////////////////////////////////////////// char szConfigName[128] = ""; sprintf(szConfigName, "SmtpConfig-%s", SysInetNToA(SMTPS.SockInfo, szIP)); char *pszSvrConfig = SvrGetConfigVar(SMTPS.hSvrConfig, szConfigName); if ((pszSvrConfig != NULL) || ((pszSvrConfig = SvrGetConfigVar(SMTPS.hSvrConfig, "SmtpConfig")) != NULL)) { SMTPLoadConfig(SMTPS, pszSvrConfig); SysFree(pszSvrConfig); } SMTPS.ulFlags |= SMTPS.ulSetupFlags; /////////////////////////////////////////////////////////////////////////////// // Get custom message to append to the SMTP response /////////////////////////////////////////////////////////////////////////////// SMTPS.pszCustMsg = SvrGetConfigVar(SMTPS.hSvrConfig, "CustomSMTPMessage"); return (0); } static int SMTPLoadConfig(SMTPSession & SMTPS, char const *pszSvrConfig) { char **ppszCfgTokens = StrTokenize(pszSvrConfig, ","); if (ppszCfgTokens == NULL) return (ErrGetErrorCode()); for (int ii = 0; ppszCfgTokens[ii] != NULL; ii++) { if (stricmp(ppszCfgTokens[ii], "mail-auth") == 0) SMTPS.ulSetupFlags |= SMTPF_MAIL_LOCKED; } StrFreeStrings(ppszCfgTokens); return (0); } static int SMTPApplyPerms(SMTPSession & SMTPS, char const *pszPerms) { for (int ii = 0; pszPerms[ii] != '\0'; ii++) { switch (pszPerms[ii]) { case ('M'): SMTPS.ulFlags |= SMTPF_MAIL_UNLOCKED; break; case ('R'): SMTPS.ulFlags |= SMTPF_RELAY_ENABLED; break; case ('V'): SMTPS.ulFlags |= SMTPF_VRFY_ENABLED; break; case ('T'): SMTPS.ulFlags |= SMTPF_ETRN_ENABLED; break; case ('Z'): SMTPS.ulMaxMsgSize = 0; break; } } /////////////////////////////////////////////////////////////////////////////// // Clear bad ip mask and command delay /////////////////////////////////////////////////////////////////////////////// SMTPS.ulFlags &= ~(SMTPF_MAPPED_IP | SMTPF_NORDNS_IP | SMTPF_BLOCKED_IP); SMTPS.iCmdDelay = 0; return (0); } static int SMTPApplyUserConfig(SMTPSession & SMTPS, UserInfo * pUI) { /////////////////////////////////////////////////////////////////////////////// // Retrieve and apply permissions /////////////////////////////////////////////////////////////////////////////// char *pszValue = NULL; char szPerms[128] = ""; if (SMTPGetUserSmtpPerms(pUI, SMTPS.hSvrConfig, szPerms, sizeof(szPerms) - 1) < 0) return (ErrGetErrorCode()); SMTPApplyPerms(SMTPS, szPerms); /////////////////////////////////////////////////////////////////////////////// // Check "MaxMessageSize" override /////////////////////////////////////////////////////////////////////////////// if ((pszValue = UsrGetUserInfoVar(pUI, "MaxMessageSize")) != NULL) { SMTPS.ulMaxMsgSize = 1024 * (unsigned long) atol(pszValue); SysFree(pszValue); } /////////////////////////////////////////////////////////////////////////////// // Check if the emission of "X-Auth-User:" is diabled /////////////////////////////////////////////////////////////////////////////// if ((pszValue = UsrGetUserInfoVar(pUI, "DisableEmitAuthUser")) != NULL) { if (atoi(pszValue)) SMTPS.ulFlags |= SMTPF_NOEMIT_AUTH; else SMTPS.ulFlags &= ~SMTPF_NOEMIT_AUTH; SysFree(pszValue); } return (0); } static int SMTPLogSession(SMTPSession & SMTPS, char const *pszSender, char const *pszRecipient, char const *pszStatus, unsigned long ulMsgSize) { char szTime[256] = ""; MscGetTimeNbrString(szTime, sizeof(szTime) - 1); RLCK_HANDLE hResLock = RLckLockEX(SVR_LOGS_DIR SYS_SLASH_STR SMTP_LOG_FILE); if (hResLock == INVALID_RLCK_HANDLE) return (ErrGetErrorCode()); char szIP[128] = "???.???.???.???"; MscFileLog(SMTP_LOG_FILE, "\"%s\"" "\t\"%s\"" "\t\"%s\"" "\t\"%s\"" "\t\"%s\"" "\t\"%s\"" "\t\"%s\"" "\t\"%s\"" "\t\"%s\"" "\t\"%s\"" "\t\"%s\"" "\t\"%lu\"" "\t\"%s\"" "\n", SMTPS.szSvrFQDN, SMTPS.szSvrDomain, SysInetNToA(SMTPS.PeerInfo, szIP), szTime, SMTPS.szClientDomain, SMTPS.szDestDomain, pszSender, pszRecipient, SMTPS.szMessageID, pszStatus, SMTPS.szLogonUser, ulMsgSize, SMTPS.szClientFQDN); RLckUnlockEX(hResLock); return (0); } static int SMTPSendError(BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char const *pszFormat, ...) { char *pszBuffer = NULL; STRSPRINTF(pszBuffer, pszFormat, pszFormat); if (pszBuffer == NULL) return (ErrGetErrorCode()); if (SMTPS.pszCustMsg == NULL) { if (BSckSendString(hBSock, pszBuffer, SMTPS.pSMTPCfg->iTimeout) < 0) { ErrorPush(); SysFree(pszBuffer); return (ErrorPop()); } } else { if (BSckVSendString(hBSock, SMTPS.pSMTPCfg->iTimeout, "%s - %s", pszBuffer, SMTPS.pszCustMsg) < 0) { ErrorPush(); SysFree(pszBuffer); return (ErrorPop()); } } SysFree(pszBuffer); return (0); } static int SMTPHandleSession(SHB_HANDLE hShbSMTP, BSOCK_HANDLE hBSock) { /////////////////////////////////////////////////////////////////////////////// // Session structure declaration and init /////////////////////////////////////////////////////////////////////////////// char *pszSMTPError = NULL; SMTPSession SMTPS; if (SMTPInitSession(hShbSMTP, hBSock, SMTPS, pszSMTPError) < 0) { ErrorPush(); if (pszSMTPError != NULL) { BSckSendString(hBSock, pszSMTPError, STD_SMTP_TIMEOUT); SysFree(pszSMTPError); } else BSckVSendString(hBSock, STD_SMTP_TIMEOUT, "421 %s service not available (%d), closing transmission channel", SMTP_SERVER_NAME, ErrorFetch()); return (ErrorPop()); } char szIP[128] = "???.???.???.???"; SysLogMessage(LOG_LEV_MESSAGE, "SMTP client connection from [%s]\n", SysInetNToA(SMTPS.PeerInfo, szIP)); /////////////////////////////////////////////////////////////////////////////// // Send welcome message /////////////////////////////////////////////////////////////////////////////// char szTime[256] = ""; MscGetTimeStr(szTime, sizeof(szTime) - 1); if (BSckVSendString(hBSock, SMTPS.pSMTPCfg->iTimeout, "220 %s %s service ready; %s", SMTPS.szTimeStamp, SMTP_SERVER_NAME, szTime) < 0) { ErrorPush(); SMTPClearSession(SMTPS); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Command loop /////////////////////////////////////////////////////////////////////////////// char szCommand[1024] = ""; while (!SvrInShutdown() && (SMTPS.iSMTPState != stateExit) && (BSckGetString(hBSock, szCommand, sizeof(szCommand) - 1, SMTPS.pSMTPCfg->iSessionTimeout) != NULL) && (MscCmdStringCheck(szCommand) == 0)) { /////////////////////////////////////////////////////////////////////////////// // Retrieve a fresh new configuration copy and test shutdown flag /////////////////////////////////////////////////////////////////////////////// SysFree(SMTPS.pSMTPCfg); SMTPS.pSMTPCfg = SMTPGetConfigCopy(hShbSMTP); if ((SMTPS.pSMTPCfg == NULL) || (SMTPS.pSMTPCfg->ulFlags & SMTPF_STOP_SERVER)) break; /////////////////////////////////////////////////////////////////////////////// // Handle command /////////////////////////////////////////////////////////////////////////////// SMTPHandleCommand(szCommand, hBSock, SMTPS); } SysLogMessage(LOG_LEV_MESSAGE, "SMTP client exit [%s]\n", SysInetNToA(SMTPS.PeerInfo, szIP)); SMTPClearSession(SMTPS); return (0); } static void SMTPClearSession(SMTPSession & SMTPS) { if (SMTPS.pMsgFile != NULL) fclose(SMTPS.pMsgFile), SMTPS.pMsgFile = NULL; SysRemove(SMTPS.szMsgFile); if (SMTPS.hSvrConfig != INVALID_SVRCFG_HANDLE) SvrReleaseConfigHandle(SMTPS.hSvrConfig), SMTPS.hSvrConfig = INVALID_SVRCFG_HANDLE; if (SMTPS.pSMTPCfg != NULL) SysFree(SMTPS.pSMTPCfg), SMTPS.pSMTPCfg = NULL; if (SMTPS.pszFrom != NULL) SysFree(SMTPS.pszFrom), SMTPS.pszFrom = NULL; if (SMTPS.pszRcpt != NULL) SysFree(SMTPS.pszRcpt), SMTPS.pszRcpt = NULL; if (SMTPS.pszSendRcpt != NULL) SysFree(SMTPS.pszSendRcpt), SMTPS.pszSendRcpt = NULL; if (SMTPS.pszCustMsg != NULL) SysFree(SMTPS.pszCustMsg), SMTPS.pszCustMsg = NULL; } static void SMTPResetSession(SMTPSession & SMTPS) { SMTPS.ulFlags = SMTPS.ulSetupFlags | (SMTPS.ulFlags & SMTPF_RESET_MASK); SMTPS.ullMessageID = 0; SMTPS.iRcptCount = 0; SetEmptyString(SMTPS.szMessageID); if (SMTPS.pMsgFile != NULL) fclose(SMTPS.pMsgFile), SMTPS.pMsgFile = NULL; SysRemove(SMTPS.szMsgFile); SetEmptyString(SMTPS.szDestDomain); if (SMTPS.pszFrom != NULL) SysFree(SMTPS.pszFrom), SMTPS.pszFrom = NULL; if (SMTPS.pszRcpt != NULL) SysFree(SMTPS.pszRcpt), SMTPS.pszRcpt = NULL; if (SMTPS.pszSendRcpt != NULL) SysFree(SMTPS.pszSendRcpt), SMTPS.pszSendRcpt = NULL; SMTPS.iSMTPState = (SMTPS.ulFlags & SMTPF_AUTHENTICATED) ? stateAuthenticated : Min(SMTPS.iSMTPState, stateHelo); } static int SMTPHandleCommand(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS) { /////////////////////////////////////////////////////////////////////////////// // Delay protection against massive spammers /////////////////////////////////////////////////////////////////////////////// if (SMTPS.iCmdDelay > 0) SysSleep(SMTPS.iCmdDelay); /////////////////////////////////////////////////////////////////////////////// // Command parsing and processing /////////////////////////////////////////////////////////////////////////////// int iCmdResult = -1; if (StrINComp(pszCommand, MAIL_FROM_STR) == 0) iCmdResult = SMTPHandleCmd_MAIL(pszCommand, hBSock, SMTPS); else if (StrINComp(pszCommand, RCPT_TO_STR) == 0) iCmdResult = SMTPHandleCmd_RCPT(pszCommand, hBSock, SMTPS); else if (StrCmdMatch(pszCommand, "DATA")) iCmdResult = SMTPHandleCmd_DATA(pszCommand, hBSock, SMTPS); else if (StrCmdMatch(pszCommand, "HELO")) iCmdResult = SMTPHandleCmd_HELO(pszCommand, hBSock, SMTPS); else if (StrCmdMatch(pszCommand, "EHLO")) iCmdResult = SMTPHandleCmd_EHLO(pszCommand, hBSock, SMTPS); else if (StrCmdMatch(pszCommand, "AUTH")) iCmdResult = SMTPHandleCmd_AUTH(pszCommand, hBSock, SMTPS); else if (StrCmdMatch(pszCommand, "RSET")) iCmdResult = SMTPHandleCmd_RSET(pszCommand, hBSock, SMTPS); else if (StrCmdMatch(pszCommand, "VRFY")) iCmdResult = SMTPHandleCmd_VRFY(pszCommand, hBSock, SMTPS); else if (StrCmdMatch(pszCommand, "ETRN")) iCmdResult = SMTPHandleCmd_ETRN(pszCommand, hBSock, SMTPS); else if (StrCmdMatch(pszCommand, "NOOP")) iCmdResult = SMTPHandleCmd_NOOP(pszCommand, hBSock, SMTPS); else if (StrCmdMatch(pszCommand, "HELP")) iCmdResult = SMTPHandleCmd_HELP(pszCommand, hBSock, SMTPS); else if (StrCmdMatch(pszCommand, "QUIT")) iCmdResult = SMTPHandleCmd_QUIT(pszCommand, hBSock, SMTPS); else SMTPSendError(hBSock, SMTPS, "500 Syntax error, command unrecognized"); return (iCmdResult); } static int SMTPTryPopAuthIpCheck(SMTPSession & SMTPS, char const *pszUser, char const *pszDomain) { /////////////////////////////////////////////////////////////////////////////// // Load user info /////////////////////////////////////////////////////////////////////////////// UserInfo *pUI = UsrGetUserByNameOrAlias(pszDomain, pszUser); if (pUI == NULL) return (ErrGetErrorCode()); /////////////////////////////////////////////////////////////////////////////// // Perform IP file checking /////////////////////////////////////////////////////////////////////////////// if (UPopUserIpCheck(pUI, &SMTPS.PeerInfo, SMTPS.pSMTPCfg->uPopAuthExpireTime) < 0) { ErrorPush(); UsrFreeUserInfo(pUI); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Apply user configuration /////////////////////////////////////////////////////////////////////////////// if (SMTPApplyUserConfig(SMTPS, pUI) < 0) { ErrorPush(); UsrFreeUserInfo(pUI); return (ErrorPop()); } UsrFreeUserInfo(pUI); return (0); } static int SMTPCheckReturnPath(const char *pszCommand, char **ppszRetDomains, SMTPSession & SMTPS, char *&pszSMTPError) { int iDomainCount = StrStringsCount(ppszRetDomains); if (iDomainCount == 0) { if (!SvrTestConfigFlag("AllowNullSender", true, SMTPS.hSvrConfig)) { if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, "", "", "SNDR=EEMPTY", 0); pszSMTPError = SysStrDup("501 Syntax error in return path"); ErrSetErrorCode(ERR_BAD_RETURN_PATH); return (ERR_BAD_RETURN_PATH); } if (SMTPS.pszFrom != NULL) SysFree(SMTPS.pszFrom); SMTPS.pszFrom = SysStrDup(""); return (0); } char szMailerUser[MAX_ADDR_NAME] = ""; char szMailerDomain[MAX_ADDR_NAME] = ""; if (USmtpSplitEmailAddr(ppszRetDomains[0], szMailerUser, szMailerDomain) < 0) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, ppszRetDomains[0], "", "SNDR=ESYNTAX", 0); pszSMTPError = SysStrDup("501 Syntax error in return path"); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Check mailer domain for DNS/MX entries /////////////////////////////////////////////////////////////////////////////// if (SvrTestConfigFlag("CheckMailerDomain", false, SMTPS.hSvrConfig) && (USmtpCheckMailDomain(SMTPS.hSvrConfig, szMailerDomain) < 0)) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, ppszRetDomains[0], "", "SNDR=ENODNS", 0); pszSMTPError = SysStrDup("505 Your domain has not DNS/MX entries"); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Check if mail come from a spammer address ( spam-address.tab ) /////////////////////////////////////////////////////////////////////////////// if (USmtpSpamAddressCheck(ppszRetDomains[0]) < 0) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, ppszRetDomains[0], "", "SNDR=ESPAM", 0); if ((pszSMTPError = SvrGetConfigVar(SMTPS.hSvrConfig, "SmtpMsgIPBanSpamAddress")) == NULL) pszSMTPError = SysStrDup("504 You are registered as spammer"); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Check SMTP after POP3 authentication /////////////////////////////////////////////////////////////////////////////// if (SvrTestConfigFlag("EnableAuthSMTP-POP3", true, SMTPS.hSvrConfig)) SMTPTryPopAuthIpCheck(SMTPS, szMailerUser, szMailerDomain); /////////////////////////////////////////////////////////////////////////////// // Check extended mail from parameters /////////////////////////////////////////////////////////////////////////////// if (SMTPCheckMailParams(pszCommand, ppszRetDomains, SMTPS, pszSMTPError) < 0) return (ErrGetErrorCode()); /////////////////////////////////////////////////////////////////////////////// // Setup From string /////////////////////////////////////////////////////////////////////////////// if (SMTPS.pszFrom != NULL) SysFree(SMTPS.pszFrom); SMTPS.pszFrom = SysStrDup(ppszRetDomains[0]); return (0); } static int SMTPAddMessageInfo(SMTPSession & SMTPS) { return (USmtpAddMessageInfo(SMTPS.pMsgFile, SMTPS.szClientDomain, SMTPS.PeerInfo, SMTPS.szSvrDomain, SMTPS.SockInfo, SMTP_SERVER_NAME)); } static int SMTPCheckMailParams(const char *pszCommand, char **ppszRetDomains, SMTPSession & SMTPS, char *&pszSMTPError) { char const *pszParams = strrchr(pszCommand, '>'); if (pszParams == NULL) pszParams = pszCommand; /////////////////////////////////////////////////////////////////////////////// // Check the SIZE parameter /////////////////////////////////////////////////////////////////////////////// if (SMTPS.ulMaxMsgSize != 0) { char const *pszSize = pszParams; while ((pszSize = StrIStr(pszSize, " SIZE=")) != NULL) { pszSize += CStringSize(" SIZE="); if (isdigit(*pszSize) && (SMTPS.ulMaxMsgSize < (unsigned long) atol(pszSize))) { if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, (ppszRetDomains[0] != NULL) ? ppszRetDomains[0] : "", "", "SIZE=EBIG", (unsigned long) atol(pszSize)); pszSMTPError = SysStrDup("552 Message exceeds fixed maximum message size"); ErrSetErrorCode(ERR_MESSAGE_SIZE); return (ERR_MESSAGE_SIZE); } } } return (0); } static int SMTPHandleCmd_MAIL(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS) { if ((SMTPS.iSMTPState != stateHelo) && (SMTPS.iSMTPState != stateAuthenticated)) { SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "503 Bad sequence of commands"); ErrSetErrorCode(ERR_SMTP_BAD_CMD_SEQUENCE); return (ERR_SMTP_BAD_CMD_SEQUENCE); } /////////////////////////////////////////////////////////////////////////////// // Split the RETURN PATH /////////////////////////////////////////////////////////////////////////////// char **ppszRetDomains = USmtpGetPathStrings(pszCommand); if (ppszRetDomains == NULL) { ErrorPush(); SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrorFetch()); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Check RETURN PATH /////////////////////////////////////////////////////////////////////////////// char *pszSMTPError = NULL; if (SMTPCheckReturnPath(pszCommand, ppszRetDomains, SMTPS, pszSMTPError) < 0) { ErrorPush(); StrFreeStrings(ppszRetDomains); SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "%s", pszSMTPError); SysFree(pszSMTPError); return (ErrorPop()); } StrFreeStrings(ppszRetDomains); /////////////////////////////////////////////////////////////////////////////// // If the incoming IP is "mapped" stop here /////////////////////////////////////////////////////////////////////////////// if (SMTPS.ulFlags & (SMTPF_MAPPED_IP | SMTPF_NORDNS_IP | SMTPF_BLOCKED_IP)) { char *pszSMTPError = NULL; if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) { if (SMTPS.ulFlags & SMTPF_MAPPED_IP) { char *pszError = StrSprint("SNDRIP=EIPMAP (%s)", SMTPS.szRejMapName); SMTPLogSession(SMTPS, SMTPS.pszFrom, "", (pszError != NULL) ? pszError : "SNDRIP=EIPMAP", 0); if (pszError != NULL) SysFree(pszError); } else if (SMTPS.ulFlags & SMTPF_NORDNS_IP) SMTPLogSession(SMTPS, SMTPS.pszFrom, "", "SNDRIP=ERDNS", 0); else SMTPLogSession(SMTPS, SMTPS.pszFrom, "", "SNDRIP=EIPSPAM", 0); } if (SMTPS.ulFlags & SMTPF_BLOCKED_IP) pszSMTPError = SvrGetConfigVar(SMTPS.hSvrConfig, "SmtpMsgIPBanSpammers"); SMTPResetSession(SMTPS); if (pszSMTPError == NULL) SMTPSendError(hBSock, SMTPS, "551 Server access forbidden by your IP"); else { SMTPSendError(hBSock, SMTPS, "%s", pszSMTPError); SysFree(pszSMTPError); } ErrSetErrorCode(ERR_SMTP_USE_FORBIDDEN); return (ERR_SMTP_USE_FORBIDDEN); } /////////////////////////////////////////////////////////////////////////////// // If MAIL command is locked stop here /////////////////////////////////////////////////////////////////////////////// if ((SMTPS.ulFlags & SMTPF_MAIL_LOCKED) && !(SMTPS.ulFlags & SMTPF_MAIL_UNLOCKED)) { SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "551 Server use forbidden"); ErrSetErrorCode(ERR_SMTP_USE_FORBIDDEN); return (ERR_SMTP_USE_FORBIDDEN); } /////////////////////////////////////////////////////////////////////////////// // Prepare mail file /////////////////////////////////////////////////////////////////////////////// if ((SMTPS.pMsgFile = fopen(SMTPS.szMsgFile, "w+b")) == NULL) { SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ERR_FILE_CREATE); ErrSetErrorCode(ERR_FILE_CREATE, SMTPS.szMsgFile); return (ERR_FILE_CREATE); } /////////////////////////////////////////////////////////////////////////////// // Write message infos ( 1st row of the smtp-mail file ) /////////////////////////////////////////////////////////////////////////////// if (SMTPAddMessageInfo(SMTPS) < 0) { ErrorPush(); SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrorFetch()); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Write domain ( 2nd row of the smtp-mail file ) /////////////////////////////////////////////////////////////////////////////// if (StrWriteCRLFString(SMTPS.pMsgFile, SMTPS.szSvrDomain) < 0) { ErrorPush(); SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrorFetch()); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Get SMTP message ID and write it to file ( 3th row of the smtp-mail file ) /////////////////////////////////////////////////////////////////////////////// if (SvrGetMessageID(&SMTPS.ullMessageID) < 0) { ErrorPush(); SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrorFetch()); return (ErrorPop()); } sprintf(SMTPS.szMessageID, "S" SYS_LLX_FMT, SMTPS.ullMessageID); if (StrWriteCRLFString(SMTPS.pMsgFile, SMTPS.szMessageID) < 0) { ErrorPush(); SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrorFetch()); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Write MAIL FROM ( 4th row of the smtp-mail file ) /////////////////////////////////////////////////////////////////////////////// if (StrWriteCRLFString(SMTPS.pMsgFile, pszCommand) < 0) { ErrorPush(); SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrorFetch()); return (ErrorPop()); } BSckSendString(hBSock, "250 OK", SMTPS.pSMTPCfg->iTimeout); SMTPS.iSMTPState = stateMail; return (0); } static int SMTPCheckRelayCapability(SMTPSession & SMTPS, char const *pszDestDomain) { /////////////////////////////////////////////////////////////////////////////// // OK if enabled ( authentication ) /////////////////////////////////////////////////////////////////////////////// if (SMTPS.ulFlags & SMTPF_RELAY_ENABLED) return (0); /////////////////////////////////////////////////////////////////////////////// // OK if destination domain is a custom-handled domain /////////////////////////////////////////////////////////////////////////////// if (USmlCustomizedDomain(pszDestDomain) == 0) return (0); /////////////////////////////////////////////////////////////////////////////// // IP based relay check /////////////////////////////////////////////////////////////////////////////// return (USmtpIsAllowedRelay(SMTPS.PeerInfo, SMTPS.hSvrConfig)); } static int SMTPCheckForwardPath(char **ppszFwdDomains, SMTPSession & SMTPS, char *&pszRealRcpt, char *&pszSMTPError) { int iDomainCount = StrStringsCount(ppszFwdDomains); if (iDomainCount == 0) { if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, "", "RCPT=ESYNTAX", 0); pszSMTPError = SysStrDup("501 Syntax error in forward path"); ErrSetErrorCode(ERR_BAD_FORWARD_PATH); return (ERR_BAD_FORWARD_PATH); } char szDestUser[MAX_ADDR_NAME] = ""; char szDestDomain[MAX_ADDR_NAME] = ""; char szRealUser[MAX_ADDR_NAME] = ""; if (USmtpSplitEmailAddr(ppszFwdDomains[iDomainCount - 1], szDestUser, szDestDomain) < 0) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, ppszFwdDomains[0], "RCPT=ESYNTAX", 0); pszSMTPError = SysStrDup("501 Syntax error in forward path"); return (ErrorPop()); } if (iDomainCount == 1) { if (USmlIsCmdAliasAccount(szDestDomain, szDestUser) == 0) { /////////////////////////////////////////////////////////////////////////////// // The recipient is handled with cmdaliases /////////////////////////////////////////////////////////////////////////////// } else if (MDomIsHandledDomain(szDestDomain) == 0) { /////////////////////////////////////////////////////////////////////////////// // Check user existance /////////////////////////////////////////////////////////////////////////////// UserInfo *pUI = UsrGetUserByNameOrAlias(szDestDomain, szDestUser); if (pUI != NULL) { /////////////////////////////////////////////////////////////////////////////// // Check if the account is enabled for receiving /////////////////////////////////////////////////////////////////////////////// if (!UsrGetUserInfoVarInt(pUI, "ReceiveEnable", 1)) { UsrFreeUserInfo(pUI); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, ppszFwdDomains[0], "RCPT=EDSBL", 0); pszSMTPError = StrSprint("550 Account disabled <%s@%s>", szDestUser, szDestDomain); ErrSetErrorCode(ERR_USER_DISABLED); return (ERR_USER_DISABLED); } if (UsrGetUserType(pUI) == usrTypeUser) { /////////////////////////////////////////////////////////////////////////////// // Target is a normal user /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Check user mailbox size /////////////////////////////////////////////////////////////////////////////// if (UPopCheckMailboxSize(pUI) < 0) { ErrorPush(); UsrFreeUserInfo(pUI); if (SMTPLogEnabled (SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, ppszFwdDomains[0], "RCPT=EFULL", 0); pszSMTPError = StrSprint ("552 Requested mail action aborted: exceeded storage allocation - <%s@%s>", szDestUser, szDestDomain); return (ErrorPop()); } } else { /////////////////////////////////////////////////////////////////////////////// // Target is a mailing list /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Check if client can post to this mailing list /////////////////////////////////////////////////////////////////////////////// if (UsrMLCheckUserPost(pUI, SMTPS.pszFrom, IsEmptyString(SMTPS. szLogonUser) ? NULL : SMTPS.szLogonUser) < 0) { ErrorPush(); UsrFreeUserInfo(pUI); if (SMTPLogEnabled (SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, ppszFwdDomains[0], "RCPT=EACCESS", 0); pszSMTPError = StrSprint ("557 Access denied <%s@%s> for user <%s>", szDestUser, szDestDomain, SMTPS.pszFrom); return (ErrorPop()); } } /////////////////////////////////////////////////////////////////////////////// // Extract the real user address /////////////////////////////////////////////////////////////////////////////// UsrGetAddress(pUI, szRealUser); UsrFreeUserInfo(pUI); } else { /////////////////////////////////////////////////////////////////////////////// // Recipient domain is local but no account is found inside the standard // users/aliases database and the account is not handled with cmdaliases. // It's pretty much time to report a recipient error /////////////////////////////////////////////////////////////////////////////// if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, ppszFwdDomains[0], "RCPT=EAVAIL", 0); pszSMTPError = StrSprint("550 Mailbox unavailable <%s@%s>", szDestUser, szDestDomain); ErrSetErrorCode(ERR_USER_NOT_LOCAL); return (ERR_USER_NOT_LOCAL); } } else { /////////////////////////////////////////////////////////////////////////////// // Check relay permission /////////////////////////////////////////////////////////////////////////////// if (SMTPCheckRelayCapability(SMTPS, szDestDomain) < 0) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, ppszFwdDomains[0], "RCPT=ERELAY", 0); pszSMTPError = SysStrDup("550 Relay denied"); return (ErrorPop()); } } } else { /////////////////////////////////////////////////////////////////////////////// // Check relay permission /////////////////////////////////////////////////////////////////////////////// if (SMTPCheckRelayCapability(SMTPS, szDestDomain) < 0) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, ppszFwdDomains[0], "RCPT=ERELAY", 0); pszSMTPError = SysStrDup("550 Relay denied"); return (ErrorPop()); } } /////////////////////////////////////////////////////////////////////////////// // Retrieve destination domain /////////////////////////////////////////////////////////////////////////////// if (USmtpSplitEmailAddr(ppszFwdDomains[0], NULL, SMTPS.szDestDomain) < 0) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, ppszFwdDomains[0], "RCPT=ESYNTAX", 0); pszSMTPError = SysStrDup("501 Syntax error in forward path"); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Setup SendRcpt string ( it'll be used to build "RCPT TO:<>" line into // the message file /////////////////////////////////////////////////////////////////////////////// if (SMTPS.pszSendRcpt != NULL) SysFree(SMTPS.pszSendRcpt); if ((SMTPS.pszSendRcpt = USmtpBuildRcptPath(ppszFwdDomains, SMTPS.hSvrConfig)) == NULL) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, ppszFwdDomains[0], "RCPT=ESYNTAX", 0); pszSMTPError = SysStrDup("501 Syntax error in forward path"); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Setup the Real Rcpt string /////////////////////////////////////////////////////////////////////////////// if (!IsEmptyString(szRealUser)) pszRealRcpt = SysStrDup(szRealUser); /////////////////////////////////////////////////////////////////////////////// // Setup Rcpt string /////////////////////////////////////////////////////////////////////////////// if (SMTPS.pszRcpt != NULL) SysFree(SMTPS.pszRcpt); SMTPS.pszRcpt = SysStrDup(ppszFwdDomains[0]); return (0); } static int SMTPHandleCmd_RCPT(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS) { if ((SMTPS.iSMTPState != stateMail) && (SMTPS.iSMTPState != stateRcpt)) { SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "503 Bad sequence of commands"); ErrSetErrorCode(ERR_SMTP_BAD_CMD_SEQUENCE); return (ERR_SMTP_BAD_CMD_SEQUENCE); } /////////////////////////////////////////////////////////////////////////////// // Check recipients count /////////////////////////////////////////////////////////////////////////////// if (SMTPS.iRcptCount >= SMTPS.pSMTPCfg->iMaxRcpts) { if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, "", "RCPT=ENBR", 0); SMTPSendError(hBSock, SMTPS, "552 Too many recipients"); ErrSetErrorCode(ERR_SMTP_TOO_MANY_RECIPIENTS); return (ERR_SMTP_TOO_MANY_RECIPIENTS); } char **ppszFwdDomains = USmtpGetPathStrings(pszCommand); if (ppszFwdDomains == NULL) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, "", "RCPT=ESYNTAX", 0); SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrorFetch()); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Check FORWARD PATH /////////////////////////////////////////////////////////////////////////////// char *pszRealRcpt = NULL; char *pszSMTPError = NULL; if (SMTPCheckForwardPath(ppszFwdDomains, SMTPS, pszRealRcpt, pszSMTPError) < 0) { ErrorPush(); StrFreeStrings(ppszFwdDomains); SMTPSendError(hBSock, SMTPS, "%s", pszSMTPError); SysFree(pszSMTPError); return (ErrorPop()); } StrFreeStrings(ppszFwdDomains); /////////////////////////////////////////////////////////////////////////////// // Log SMTP session /////////////////////////////////////////////////////////////////////////////// if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, SMTPS.pszRcpt, "RCPT=OK", 0); /////////////////////////////////////////////////////////////////////////////// // Write RCPT TO ( 5th[,...] row(s) of the smtp-mail file ) /////////////////////////////////////////////////////////////////////////////// fprintf(SMTPS.pMsgFile, "RCPT TO:<%s> {ra=%s}\r\n", SMTPS.pszSendRcpt, (pszRealRcpt != NULL) ? pszRealRcpt: SMTPS.pszSendRcpt); if (pszRealRcpt != NULL) SysFree(pszRealRcpt); BSckSendString(hBSock, "250 OK", SMTPS.pSMTPCfg->iTimeout); ++SMTPS.iRcptCount; SMTPS.iSMTPState = stateRcpt; return (0); } static int SMTPGetFilterFile(const char *pszFiltID, char *pszFileName, int iMaxName) { char szMailRoot[SYS_MAX_PATH] = ""; CfgGetRootPath(szMailRoot, sizeof(szMailRoot)); SysSNPrintf(pszFileName, iMaxName - 1, "%sfilters.%s.tab", szMailRoot, pszFiltID); return (SysExistFile(pszFileName)); } static int SMTPFilterMacroSubstitutes(char **ppszCmdTokens, SMTPSession & SMTPS) { for (int ii = 0; ppszCmdTokens[ii] != NULL; ii++) { if (strcmp(ppszCmdTokens[ii], "@@FILE") == 0) { char *pszNewValue = SysStrDup(SMTPS.szMsgFile); if (pszNewValue == NULL) return (ErrGetErrorCode()); SysFree(ppszCmdTokens[ii]); ppszCmdTokens[ii] = pszNewValue; } else if (strcmp(ppszCmdTokens[ii], "@@USERAUTH") == 0) { char *pszNewValue = SysStrDup(IsEmptyString(SMTPS.szLogonUser) ? "-": SMTPS.szLogonUser); if (pszNewValue == NULL) return (ErrGetErrorCode()); SysFree(ppszCmdTokens[ii]); ppszCmdTokens[ii] = pszNewValue; } else if (strcmp(ppszCmdTokens[ii], "@@REMOTEADDR") == 0) { char szIP[128] = "???.???.???.???"; char *pszNewValue = SysStrDup(SysInetNToA(SMTPS.PeerInfo, szIP)); if (pszNewValue == NULL) return (ErrGetErrorCode()); SysFree(ppszCmdTokens[ii]); ppszCmdTokens[ii] = pszNewValue; } else if (strcmp(ppszCmdTokens[ii], "@@LOCALADDR") == 0) { char szIP[128] = "???.???.???.???"; char *pszNewValue = SysStrDup(SysInetNToA(SMTPS.SockInfo, szIP)); if (pszNewValue == NULL) return (ErrGetErrorCode()); SysFree(ppszCmdTokens[ii]); ppszCmdTokens[ii] = pszNewValue; } } return (0); } static char *SMTPGetFilterRejMessage(char const *pszMsgFilePath) { FILE *pFile; char szRejFilePath[SYS_MAX_PATH] = ""; char szRejMsg[512] = ""; SysSNPrintf(szRejFilePath, sizeof(szRejFilePath) - 1, "%s.rej", pszMsgFilePath); if ((pFile = fopen(szRejFilePath, "rb")) == NULL) return NULL; MscFGets(szRejMsg, sizeof(szRejMsg) - 1, pFile); fclose(pFile); SysRemove(szRejFilePath); return (SysStrDup(szRejMsg)); } static int SMTPRunFilters(SMTPSession & SMTPS, char const *pszFilterPath, char *&pszError) { /////////////////////////////////////////////////////////////////////////////// // Share lock the filter file /////////////////////////////////////////////////////////////////////////////// char szResLock[SYS_MAX_PATH] = ""; RLCK_HANDLE hResLock = RLckLockSH(CfgGetBasedPath(pszFilterPath, szResLock, sizeof(szResLock))); if (hResLock == INVALID_RLCK_HANDLE) return (ErrGetErrorCode()); /////////////////////////////////////////////////////////////////////////////// // This should not happen but if it happens we let the message pass through /////////////////////////////////////////////////////////////////////////////// FILE *pFiltFile = fopen(pszFilterPath, "rt"); if (pFiltFile == NULL) { RLckUnlockSH(hResLock); return (0); } /////////////////////////////////////////////////////////////////////////////// // Filter this message /////////////////////////////////////////////////////////////////////////////// int iFieldsCount, iExitCode, iExitFlags, iExecResult; char szFiltLine[1024] = ""; while (MscGetConfigLine(szFiltLine, sizeof(szFiltLine) - 1, pFiltFile) != NULL) { char **ppszCmdTokens = StrGetTabLineStrings(szFiltLine); if (ppszCmdTokens == NULL) continue; if ((iFieldsCount = StrStringsCount(ppszCmdTokens)) < 1) { StrFreeStrings(ppszCmdTokens); continue; } SMTPFilterMacroSubstitutes(ppszCmdTokens, SMTPS); iExitCode = iExitFlags = 0; iExecResult = SysExec(ppszCmdTokens[0], &ppszCmdTokens[0], iFilterTimeout, SYS_PRIORITY_NORMAL, &iExitCode); if (iExecResult == 0) { SysLogMessage(LOG_LEV_MESSAGE, "SMTP filter run: Filter = \"%s\" Retcode = %d\n", ppszCmdTokens[0], iExitCode); iExitFlags = iExitCode & SMTP_FILTER_FL_MASK; iExitCode &= ~SMTP_FILTER_FL_MASK; if (iExitCode == SMTP_FILTER_REJECT_CODE) { StrFreeStrings(ppszCmdTokens); fclose(pFiltFile); RLckUnlockSH(hResLock); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, SMTPS.pszRcpt, "DATA=EFILTER", 0); pszError = SMTPGetFilterRejMessage(SMTPS.szMsgFile); ErrSetErrorCode(ERR_FILTERED_MESSAGE); return (ERR_FILTERED_MESSAGE); } } else { SysLogMessage(LOG_LEV_ERROR, "SMTP filter error (%d): Filter = \"%s\"\n", iExecResult, ppszCmdTokens[0]); } StrFreeStrings(ppszCmdTokens); if (iExitFlags & SMTP_FILTER_FL_BREAK) break; } fclose(pFiltFile); RLckUnlockSH(hResLock); return (0); } static int SMTPFilterMessage(SMTPSession & SMTPS, const char *pszFiltID, char *&pszError) { int iReOpen = 0; char szFilterFile[SYS_MAX_PATH] = ""; if (!SMTPGetFilterFile(pszFiltID, szFilterFile, sizeof(szFilterFile) - 1)) return (0); if (SMTPS.pMsgFile != NULL) { if (fflush(SMTPS.pMsgFile)) { ErrSetErrorCode(ERR_FILE_WRITE, SMTPS.szMsgFile); return (ERR_FILE_WRITE); } if (fclose(SMTPS.pMsgFile)) { SMTPS.pMsgFile = NULL; ErrSetErrorCode(ERR_FILE_WRITE, SMTPS.szMsgFile); return (ERR_FILE_WRITE); } SMTPS.pMsgFile = NULL; iReOpen++; } if (SMTPRunFilters(SMTPS, szFilterFile, pszError) < 0) return (ErrGetErrorCode()); if (iReOpen) { if ((SMTPS.pMsgFile = fopen(SMTPS.szMsgFile, "r+b")) == NULL) { ErrSetErrorCode(ERR_FILE_OPEN, SMTPS.szMsgFile); return (ERR_FILE_OPEN); } fseek(SMTPS.pMsgFile, 0, SEEK_END); } return (0); } static int SMTPHandleCmd_DATA(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS) { char *pszError; if (SMTPS.iSMTPState != stateRcpt) { SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "503 Bad sequence of commands"); ErrSetErrorCode(ERR_SMTP_BAD_CMD_SEQUENCE); return (ERR_SMTP_BAD_CMD_SEQUENCE); } /////////////////////////////////////////////////////////////////////////////// // Run the pre-DATA filter /////////////////////////////////////////////////////////////////////////////// pszError = NULL; if (SMTPFilterMessage(SMTPS, SMTP_PRE_DATA_FILTER, pszError) < 0) { ErrorPush(); SMTPResetSession(SMTPS); if (pszError != NULL) { SMTPSendError(hBSock, SMTPS, "%s", pszError); SysFree(pszError); } else BSckSendString(hBSock, "554 Transaction failed", SMTPS.pSMTPCfg->iTimeout); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Write data begin marker /////////////////////////////////////////////////////////////////////////////// if (StrWriteCRLFString(SMTPS.pMsgFile, SPOOL_FILE_DATA_START) < 0) { ErrorPush(); SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrorFetch()); return (ErrorPop()); } BSckSendString(hBSock, "354 Start mail input; end with <CRLF>.<CRLF>", SMTPS.pSMTPCfg->iTimeout); /////////////////////////////////////////////////////////////////////////////// // Write data /////////////////////////////////////////////////////////////////////////////// int iErrorCode = 0; int iLineLength; int iGotNL; int iGotNLPrev = 1; unsigned long ulMessageSize = 0; unsigned long ulMaxMsgSize = SMTPS.ulMaxMsgSize; char const *pszSmtpError = NULL; char szBuffer[SMTP_MAX_LINE_SIZE + 4]; for (;;) { if (BSckGetString (hBSock, szBuffer, sizeof(szBuffer) - 3, SMTPS.pSMTPCfg->iTimeout, &iLineLength, &iGotNL) == NULL) { ErrorPush(); SMTPResetSession(SMTPS); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Check end of data condition /////////////////////////////////////////////////////////////////////////////// if (iGotNL && iGotNLPrev && (strcmp(szBuffer, ".") == 0)) break; /////////////////////////////////////////////////////////////////////////////// // Correctly terminate the line /////////////////////////////////////////////////////////////////////////////// if (iGotNL) memcpy(szBuffer + iLineLength, "\r\n", 3), iLineLength += 2; if (iErrorCode == 0) { /////////////////////////////////////////////////////////////////////////////// // Write data on disk /////////////////////////////////////////////////////////////////////////////// if (!fwrite(szBuffer, iLineLength, 1, SMTPS.pMsgFile)) { ErrSetErrorCode(iErrorCode = ERR_FILE_WRITE, SMTPS.szMsgFile); } } ulMessageSize += (unsigned long) iLineLength; /////////////////////////////////////////////////////////////////////////////// // Check the message size /////////////////////////////////////////////////////////////////////////////// if ((ulMaxMsgSize != 0) && (ulMaxMsgSize < ulMessageSize)) { pszSmtpError = "552 Message exceeds fixed maximum message size"; ErrSetErrorCode(iErrorCode = ERR_MESSAGE_SIZE); } if (SvrInShutdown()) { SMTPResetSession(SMTPS); ErrSetErrorCode(ERR_SERVER_SHUTDOWN); return (ERR_SERVER_SHUTDOWN); } iGotNLPrev = iGotNL; } /////////////////////////////////////////////////////////////////////////////// // Check fclose() return value coz data might be buffered and fail to flush /////////////////////////////////////////////////////////////////////////////// if (fclose(SMTPS.pMsgFile)) ErrSetErrorCode(iErrorCode = ERR_FILE_WRITE, SMTPS.szMsgFile); SMTPS.pMsgFile = NULL; if (iErrorCode == 0) { /////////////////////////////////////////////////////////////////////////////// // Run the post-DATA filter /////////////////////////////////////////////////////////////////////////////// pszError = NULL; if (SMTPFilterMessage(SMTPS, SMTP_POST_DATA_FILTER, pszError) < 0) { ErrorPush(); SMTPResetSession(SMTPS); if (pszError != NULL) { SMTPSendError(hBSock, SMTPS, "%s", pszError); SysFree(pszError); } else BSckSendString(hBSock, "554 Transaction failed", SMTPS.pSMTPCfg->iTimeout); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Transfer spool file /////////////////////////////////////////////////////////////////////////////// if (SMTPSubmitPackedFile(SMTPS, SMTPS.szMsgFile) < 0) SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrGetErrorCode()); else { /////////////////////////////////////////////////////////////////////////////// // Log the message receive /////////////////////////////////////////////////////////////////////////////// if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, SMTPS.pszFrom, SMTPS.pszRcpt, "RECV=OK", ulMessageSize); /////////////////////////////////////////////////////////////////////////////// // Send the ack only when everything is OK /////////////////////////////////////////////////////////////////////////////// BSckVSendString(hBSock, SMTPS.pSMTPCfg->iTimeout, "250 OK <%s>", SMTPS.szMessageID); } } else { /////////////////////////////////////////////////////////////////////////////// // Notify the client the error condition /////////////////////////////////////////////////////////////////////////////// if (pszSmtpError == NULL) SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrGetErrorCode()); else SMTPSendError(hBSock, SMTPS, "%s", pszSmtpError); } SMTPResetSession(SMTPS); return (iErrorCode); } static int SMTPAddReceived(int iType, char const *pszAuth, char const *const *ppszMsgInfo, char const *pszMailFrom, char const *pszRcptTo, char const *pszMessageID, FILE * pMailFile) { char *pszReceived = USmtpGetReceived(iType, pszAuth, ppszMsgInfo, pszMailFrom, pszRcptTo, pszMessageID); if (pszReceived == NULL) return (ErrGetErrorCode()); /////////////////////////////////////////////////////////////////////////////// // Write "Received:" tag /////////////////////////////////////////////////////////////////////////////// fputs(pszReceived, pMailFile); SysFree(pszReceived); return (0); } static char *SMTPTrimRcptLine(char *pszRcptLn) { char *pszTrim = strchr(pszRcptLn, '>'); if (pszTrim != NULL) pszTrim[1] = '\0'; return (pszRcptLn); } static int SMTPSubmitPackedFile(SMTPSession & SMTPS, const char *pszPkgFile) { FILE *pPkgFile = fopen(pszPkgFile, "rb"); if (pPkgFile == NULL) { ErrSetErrorCode(ERR_FILE_OPEN); return (ERR_FILE_OPEN); } char szSpoolLine[MAX_SPOOL_LINE] = ""; while ((MscGetString(pPkgFile, szSpoolLine, sizeof(szSpoolLine) - 1) != NULL) && (strncmp(szSpoolLine, SPOOL_FILE_DATA_START, CStringSize(SPOOL_FILE_DATA_START)) != 0)); if (strncmp(szSpoolLine, SPOOL_FILE_DATA_START, CStringSize(SPOOL_FILE_DATA_START)) != 0) { fclose(pPkgFile); ErrSetErrorCode(ERR_INVALID_SPOOL_FILE); return (ERR_INVALID_SPOOL_FILE); } /////////////////////////////////////////////////////////////////////////////// // Get the offset at which the message data begin and rewind the file /////////////////////////////////////////////////////////////////////////////// unsigned long ulMsgOffset = (unsigned long) ftell(pPkgFile); rewind(pPkgFile); /////////////////////////////////////////////////////////////////////////////// // Read SMTP message info ( 1st row of the smtp-mail file ) /////////////////////////////////////////////////////////////////////////////// char **ppszMsgInfo = NULL; if ((MscGetString(pPkgFile, szSpoolLine, sizeof(szSpoolLine) - 1) == NULL) || ((ppszMsgInfo = StrTokenize(szSpoolLine, ";")) == NULL) || (StrStringsCount(ppszMsgInfo) < smsgiMax)) { if (ppszMsgInfo != NULL) StrFreeStrings(ppszMsgInfo); fclose(pPkgFile); ErrSetErrorCode(ERR_INVALID_SPOOL_FILE); return (ERR_INVALID_SPOOL_FILE); } /////////////////////////////////////////////////////////////////////////////// // Read SMTP domain ( 2nd row of the smtp-mail file ) /////////////////////////////////////////////////////////////////////////////// char szSMTPDomain[256] = ""; if (MscGetString(pPkgFile, szSMTPDomain, sizeof(szSMTPDomain) - 1) == NULL) { StrFreeStrings(ppszMsgInfo); fclose(pPkgFile); ErrSetErrorCode(ERR_INVALID_SPOOL_FILE); return (ERR_INVALID_SPOOL_FILE); } /////////////////////////////////////////////////////////////////////////////// // Read message ID ( 3th row of the smtp-mail file ) /////////////////////////////////////////////////////////////////////////////// char szMessageID[128] = ""; if (MscGetString(pPkgFile, szMessageID, sizeof(szMessageID) - 1) == NULL) { StrFreeStrings(ppszMsgInfo); fclose(pPkgFile); ErrSetErrorCode(ERR_INVALID_SPOOL_FILE); return (ERR_INVALID_SPOOL_FILE); } /////////////////////////////////////////////////////////////////////////////// // Read "MAIL FROM:" ( 4th row of the smtp-mail file ) /////////////////////////////////////////////////////////////////////////////// char szMailFrom[MAX_SPOOL_LINE] = ""; if ((MscGetString(pPkgFile, szMailFrom, sizeof(szMailFrom) - 1) == NULL) || (StrINComp(szMailFrom, MAIL_FROM_STR) != 0)) { StrFreeStrings(ppszMsgInfo); fclose(pPkgFile); ErrSetErrorCode(ERR_INVALID_SPOOL_FILE); return (ERR_INVALID_SPOOL_FILE); } /////////////////////////////////////////////////////////////////////////////// // Get the Received: header type to emit /////////////////////////////////////////////////////////////////////////////// int iReceivedType = SvrGetConfigInt("ReceivedHdrType", RECEIVED_TYPE_STD, SMTPS.hSvrConfig); /////////////////////////////////////////////////////////////////////////////// // Read "RCPT TO:" ( 5th[,...] row(s) of the smtp-mail file ) /////////////////////////////////////////////////////////////////////////////// while ((MscGetString(pPkgFile, szSpoolLine, sizeof(szSpoolLine) - 1) != NULL) && (StrINComp(szSpoolLine, RCPT_TO_STR) == 0)) { /////////////////////////////////////////////////////////////////////////////// // Cleanup the RCPT line from extra info /////////////////////////////////////////////////////////////////////////////// SMTPTrimRcptLine(szSpoolLine); /////////////////////////////////////////////////////////////////////////////// // Get message handle /////////////////////////////////////////////////////////////////////////////// QMSG_HANDLE hMessage = QueCreateMessage(hSpoolQueue); if (hMessage == INVALID_QMSG_HANDLE) { ErrorPush(); StrFreeStrings(ppszMsgInfo); fclose(pPkgFile); return (ErrorPop()); } char szQueueFilePath[SYS_MAX_PATH] = ""; QueGetFilePath(hSpoolQueue, hMessage, szQueueFilePath); FILE *pSpoolFile = fopen(szQueueFilePath, "wb"); if (pSpoolFile == NULL) { QueCleanupMessage(hSpoolQueue, hMessage); QueCloseMessage(hSpoolQueue, hMessage); StrFreeStrings(ppszMsgInfo); fclose(pPkgFile); ErrSetErrorCode(ERR_FILE_CREATE); return (ERR_FILE_CREATE); } /////////////////////////////////////////////////////////////////////////////// // Write info line /////////////////////////////////////////////////////////////////////////////// USmtpWriteInfoLine(pSpoolFile, ppszMsgInfo[smsgiClientAddr], ppszMsgInfo[smsgiServerAddr], ppszMsgInfo[smsgiTime]); /////////////////////////////////////////////////////////////////////////////// // Write SMTP domain /////////////////////////////////////////////////////////////////////////////// fprintf(pSpoolFile, "%s\r\n", szSMTPDomain); /////////////////////////////////////////////////////////////////////////////// // Write message ID /////////////////////////////////////////////////////////////////////////////// fprintf(pSpoolFile, "%s\r\n", szMessageID); /////////////////////////////////////////////////////////////////////////////// // Write "MAIL FROM:" /////////////////////////////////////////////////////////////////////////////// fprintf(pSpoolFile, "%s\r\n", szMailFrom); /////////////////////////////////////////////////////////////////////////////// // Write "RCPT TO:" /////////////////////////////////////////////////////////////////////////////// fprintf(pSpoolFile, "%s\r\n", szSpoolLine); /////////////////////////////////////////////////////////////////////////////// // Write SPOOL_FILE_DATA_START /////////////////////////////////////////////////////////////////////////////// fprintf(pSpoolFile, "%s\r\n", SPOOL_FILE_DATA_START); /////////////////////////////////////////////////////////////////////////////// // Write "X-AuthUser:" tag /////////////////////////////////////////////////////////////////////////////// if (!IsEmptyString(SMTPS.szLogonUser) && !(SMTPS.ulFlags & SMTPF_NOEMIT_AUTH)) fprintf(pSpoolFile, "X-AuthUser: %s\r\n", SMTPS.szLogonUser); /////////////////////////////////////////////////////////////////////////////// // Write "Received:" tag /////////////////////////////////////////////////////////////////////////////// SMTPAddReceived(iReceivedType, IsEmptyString(SMTPS.szLogonUser) ? NULL : SMTPS.szLogonUser, ppszMsgInfo, szMailFrom, szSpoolLine, szMessageID, pSpoolFile); /////////////////////////////////////////////////////////////////////////////// // Write mail data, saving and restoring the current file pointer /////////////////////////////////////////////////////////////////////////////// unsigned long ulCurrOffset = (unsigned long) ftell(pPkgFile); if (MscCopyFile(pSpoolFile, pPkgFile, ulMsgOffset, (unsigned long) -1) < 0) { ErrorPush(); fclose(pSpoolFile); QueCleanupMessage(hSpoolQueue, hMessage); QueCloseMessage(hSpoolQueue, hMessage); StrFreeStrings(ppszMsgInfo); fclose(pPkgFile); return (ErrorPop()); } if (SysFileSync(pSpoolFile) < 0) { ErrorPush(); fclose(pSpoolFile); QueCleanupMessage(hSpoolQueue, hMessage); QueCloseMessage(hSpoolQueue, hMessage); StrFreeStrings(ppszMsgInfo); fclose(pPkgFile); return (ErrorPop()); } if (fclose(pSpoolFile)) { QueCleanupMessage(hSpoolQueue, hMessage); QueCloseMessage(hSpoolQueue, hMessage); StrFreeStrings(ppszMsgInfo); fclose(pPkgFile); ErrSetErrorCode(ERR_FILE_WRITE, szQueueFilePath); return (ERR_FILE_WRITE); } fseek(pPkgFile, ulCurrOffset, SEEK_SET); /////////////////////////////////////////////////////////////////////////////// // Transfer file to the spool /////////////////////////////////////////////////////////////////////////////// if (QueCommitMessage(hSpoolQueue, hMessage) < 0) { ErrorPush(); QueCleanupMessage(hSpoolQueue, hMessage); QueCloseMessage(hSpoolQueue, hMessage); StrFreeStrings(ppszMsgInfo); fclose(pPkgFile); return (ErrorPop()); } } StrFreeStrings(ppszMsgInfo); fclose(pPkgFile); return (0); } static int SMTPHandleCmd_HELO(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS) { if ((SMTPS.iSMTPState != stateInit) && (SMTPS.iSMTPState != stateHelo)) { SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "503 Bad sequence of commands"); ErrSetErrorCode(ERR_SMTP_BAD_CMD_SEQUENCE); return (ERR_SMTP_BAD_CMD_SEQUENCE); } char **ppszTokens = StrTokenize(pszCommand, " "); if ((ppszTokens == NULL) || (StrStringsCount(ppszTokens) != 2)) { if (ppszTokens != NULL) StrFreeStrings(ppszTokens); SMTPSendError(hBSock, SMTPS, "501 Syntax error in parameters or arguments"); return (-1); } StrSNCpy(SMTPS.szClientDomain, ppszTokens[1]); StrFreeStrings(ppszTokens); char *pszDomain = SvrGetConfigVar(SMTPS.hSvrConfig, "RootDomain"); if (pszDomain == NULL) { SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ERR_NO_ROOT_DOMAIN_VAR); ErrSetErrorCode(ERR_NO_ROOT_DOMAIN_VAR); return (ERR_NO_ROOT_DOMAIN_VAR); } BSckVSendString(hBSock, SMTPS.pSMTPCfg->iTimeout, "250 %s", pszDomain); SysFree(pszDomain); SMTPS.iSMTPState = stateHelo; return (0); } static int SMTPHandleCmd_EHLO(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS) { if ((SMTPS.iSMTPState != stateInit) && (SMTPS.iSMTPState != stateHelo)) { SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "503 Bad sequence of commands"); ErrSetErrorCode(ERR_SMTP_BAD_CMD_SEQUENCE); return (ERR_SMTP_BAD_CMD_SEQUENCE); } char **ppszTokens = StrTokenize(pszCommand, " "); if ((ppszTokens == NULL) || (StrStringsCount(ppszTokens) != 2)) { if (ppszTokens != NULL) StrFreeStrings(ppszTokens); SMTPSendError(hBSock, SMTPS, "501 Syntax error in parameters or arguments"); return (-1); } StrSNCpy(SMTPS.szClientDomain, ppszTokens[1]); StrFreeStrings(ppszTokens); /////////////////////////////////////////////////////////////////////////////// // Create response file /////////////////////////////////////////////////////////////////////////////// char szRespFile[SYS_MAX_PATH] = ""; SysGetTmpFile(szRespFile); FILE *pRespFile = fopen(szRespFile, "w+b"); if (pRespFile == NULL) { CheckRemoveFile(szRespFile); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ERR_FILE_CREATE); ErrSetErrorCode(ERR_FILE_CREATE); return (ERR_FILE_CREATE); } /////////////////////////////////////////////////////////////////////////////// // Get root domain ( "RootDomain" ) /////////////////////////////////////////////////////////////////////////////// char *pszDomain = SvrGetConfigVar(SMTPS.hSvrConfig, "RootDomain"); if (pszDomain == NULL) { fclose(pRespFile); SysRemove(szRespFile); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ERR_NO_ROOT_DOMAIN_VAR); ErrSetErrorCode(ERR_NO_ROOT_DOMAIN_VAR); return (ERR_NO_ROOT_DOMAIN_VAR); } /////////////////////////////////////////////////////////////////////////////// // Build EHLO response file ( domain + auths ) /////////////////////////////////////////////////////////////////////////////// fprintf(pRespFile, "250 %s\r\n", pszDomain); SysFree(pszDomain); /////////////////////////////////////////////////////////////////////////////// // Emit extended SMTP command and internal auths /////////////////////////////////////////////////////////////////////////////// fprintf(pRespFile, "250 VRFY\r\n" "250 ETRN\r\n" "250 8BITMIME\r\n" "250 PIPELINING\r\n" "250 AUTH LOGIN PLAIN CRAM-MD5"); /////////////////////////////////////////////////////////////////////////////// // Emit external authentication methods /////////////////////////////////////////////////////////////////////////////// SMTPListExtAuths(pRespFile, SMTPS); fprintf(pRespFile, "\r\n"); /////////////////////////////////////////////////////////////////////////////// // Emit maximum message size ( if set ) /////////////////////////////////////////////////////////////////////////////// if (SMTPS.ulMaxMsgSize != 0) fprintf(pRespFile, "250 SIZE %lu\r\n", SMTPS.ulMaxMsgSize); else fprintf(pRespFile, "250 SIZE\r\n"); /////////////////////////////////////////////////////////////////////////////// // Send EHLO response file /////////////////////////////////////////////////////////////////////////////// if (SMTPSendMultilineResponse(hBSock, SMTPS.pSMTPCfg->iTimeout, pRespFile) < 0) { ErrorPush(); fclose(pRespFile); SysRemove(szRespFile); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrorFetch()); return (ErrorPop()); } fclose(pRespFile); SysRemove(szRespFile); SMTPS.iSMTPState = stateHelo; return (0); } static int SMTPListExtAuths(FILE * pRespFile, SMTPSession & SMTPS) { char szExtAuthFilePath[SYS_MAX_PATH] = ""; SMTPGetExtAuthFilePath(szExtAuthFilePath, sizeof(szExtAuthFilePath)); FILE *pExtAuthFile = fopen(szExtAuthFilePath, "rt"); if (pExtAuthFile != NULL) { char szExtAuthLine[SVR_SMTP_EXTAUTH_LINE_MAX] = ""; while (MscGetConfigLine(szExtAuthLine, sizeof(szExtAuthLine) - 1, pExtAuthFile) != NULL) { char **ppszStrings = StrGetTabLineStrings(szExtAuthLine); if (ppszStrings == NULL) continue; if (StrStringsCount(ppszStrings) > 0) fprintf(pRespFile, " %s", ppszStrings[0]); StrFreeStrings(ppszStrings); } fclose(pExtAuthFile); } return (0); } static int SMTPExternalAuthSubstitute(char **ppszAuthTokens, char const *pszChallenge, char const *pszDigest, char const *pszSecretsFile) { for (int ii = 0; ppszAuthTokens[ii] != NULL; ii++) { if (strcmp(ppszAuthTokens[ii], "@@FSECRT") == 0) { char *pszNewValue = SysStrDup(pszSecretsFile); if (pszNewValue == NULL) return (ErrGetErrorCode()); SysFree(ppszAuthTokens[ii]); ppszAuthTokens[ii] = pszNewValue; } else if (strcmp(ppszAuthTokens[ii], "@@CHALL") == 0) { char *pszNewValue = SysStrDup(pszChallenge); if (pszNewValue == NULL) return (ErrGetErrorCode()); SysFree(ppszAuthTokens[ii]); ppszAuthTokens[ii] = pszNewValue; } else if (strcmp(ppszAuthTokens[ii], "@@DGEST") == 0) { char *pszNewValue = SysStrDup(pszDigest); if (pszNewValue == NULL) return (ErrGetErrorCode()); SysFree(ppszAuthTokens[ii]); ppszAuthTokens[ii] = pszNewValue; } } return (0); } static int SMTPCreateSecretsFile(char const *pszSecretsFile) { char szAuthFilePath[SYS_MAX_PATH] = ""; SMTPGetAuthFilePath(szAuthFilePath, sizeof(szAuthFilePath)); FILE *pAuthFile = fopen(szAuthFilePath, "rt"); if (pAuthFile == NULL) { ErrSetErrorCode(ERR_FILE_OPEN, szAuthFilePath); return (ERR_FILE_OPEN); } FILE *pSecretFile = fopen(pszSecretsFile, "wt"); if (pSecretFile == NULL) { fclose(pAuthFile); ErrSetErrorCode(ERR_FILE_CREATE, pszSecretsFile); return (ERR_FILE_CREATE); } char szAuthLine[SVR_SMTPAUTH_LINE_MAX] = ""; while (MscGetConfigLine(szAuthLine, sizeof(szAuthLine) - 1, pAuthFile) != NULL) { char **ppszStrings = StrGetTabLineStrings(szAuthLine); if (ppszStrings == NULL) continue; int iFieldsCount = StrStringsCount(ppszStrings); if (iFieldsCount >= smtpaMax) fprintf(pSecretFile, "%s:%s\n", ppszStrings[smtpaUsername], ppszStrings[smtpaPassword]); StrFreeStrings(ppszStrings); } fclose(pSecretFile); fclose(pAuthFile); return (0); } static int SMTPExternalAuthenticate(BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char **ppszAuthTokens) { /////////////////////////////////////////////////////////////////////////////// // Emit encoded ( base64 ) challenge ( param1 + ':' + timestamp ) // and get client response /////////////////////////////////////////////////////////////////////////////// unsigned int uEnc64Length = 0; char szChallenge[1024] = ""; char szDigest[1024] = ""; SysSNPrintf(szDigest, sizeof(szDigest) - 1, "%s:%s", ppszAuthTokens[1], SMTPS.szTimeStamp); encode64(szDigest, strlen(szDigest), szChallenge, sizeof(szChallenge) - 1, &uEnc64Length); if ((BSckVSendString(hBSock, SMTPS.pSMTPCfg->iTimeout, "334 %s", szChallenge) < 0) || (BSckGetString(hBSock, szChallenge, sizeof(szChallenge) - 1, SMTPS.pSMTPCfg->iTimeout) == NULL)) return (ErrGetErrorCode()); /////////////////////////////////////////////////////////////////////////////// // Decode ( base64 ) client response /////////////////////////////////////////////////////////////////////////////// unsigned int uDec64Length = 0; if (decode64(szChallenge, strlen(szChallenge), szDigest, &uDec64Length) != 0) { SMTPSendError(hBSock, SMTPS, "501 Syntax error in parameters or arguments"); ErrSetErrorCode(ERR_BAD_SMTP_CMD_SYNTAX); return (ERR_BAD_SMTP_CMD_SYNTAX); } SysSNPrintf(szChallenge, sizeof(szChallenge) - 1, "%s:%s", ppszAuthTokens[1], SMTPS.szTimeStamp); /////////////////////////////////////////////////////////////////////////////// // Create secrets file ( username + ':' + password ) /////////////////////////////////////////////////////////////////////////////// char szSecretsFile[SYS_MAX_PATH] = ""; SysGetTmpFile(szSecretsFile); if (SMTPCreateSecretsFile(szSecretsFile) < 0) { ErrorPush(); CheckRemoveFile(szSecretsFile); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Do macro substitution /////////////////////////////////////////////////////////////////////////////// SMTPExternalAuthSubstitute(ppszAuthTokens, szChallenge, szDigest, szSecretsFile); /////////////////////////////////////////////////////////////////////////////// // Call external program to compute the response /////////////////////////////////////////////////////////////////////////////// int iExitCode = -1; if (SysExec(ppszAuthTokens[2], &ppszAuthTokens[2], SVR_SMTP_EXTAUTH_TIMEOUT, SVR_SMTP_EXTAUTH_PRIORITY, &iExitCode) < 0) { ErrorPush(); SysRemove(szSecretsFile); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrorFetch()); return (ErrorPop()); } if (iExitCode != SVR_SMTP_EXTAUTH_SUCCESS) { SysRemove(szSecretsFile); SMTPSendError(hBSock, SMTPS, "503 Authentication failed"); ErrSetErrorCode(ERR_BAD_EXTRNPRG_EXITCODE); return (ERR_BAD_EXTRNPRG_EXITCODE); } /////////////////////////////////////////////////////////////////////////////// // Load response file containing the matching secret /////////////////////////////////////////////////////////////////////////////// unsigned int uRespSize = 0; char *pMatchSecret = (char *) MscLoadFile(szSecretsFile, uRespSize); CheckRemoveFile(szSecretsFile); if (pMatchSecret == NULL) { ErrorPush(); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrorFetch()); return (ErrorPop()); } while ((uRespSize > 0) && ((pMatchSecret[uRespSize - 1] == '\r') || (pMatchSecret[uRespSize - 1] == '\n'))) --uRespSize; pMatchSecret[uRespSize] = '\0'; /////////////////////////////////////////////////////////////////////////////// // Try to extract username and password tokens ( username + ':' + password ) /////////////////////////////////////////////////////////////////////////////// char **ppszTokens = StrTokenize(pMatchSecret, ":"); SysFree(pMatchSecret); if (StrStringsCount(ppszTokens) != 2) { StrFreeStrings(ppszTokens); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ERR_BAD_SMTP_EXTAUTH_RESPONSE_FILE); ErrSetErrorCode(ERR_BAD_SMTP_EXTAUTH_RESPONSE_FILE); return (ERR_BAD_SMTP_EXTAUTH_RESPONSE_FILE); } /////////////////////////////////////////////////////////////////////////////// // Lookup smtp auth file /////////////////////////////////////////////////////////////////////////////// if (SMTPTryApplyUsrPwdAuth(SMTPS, ppszTokens[0], ppszTokens[1]) < 0) { ErrorPush(); StrFreeStrings(ppszTokens); SMTPSendError(hBSock, SMTPS, "503 Authentication failed"); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Set the logon user /////////////////////////////////////////////////////////////////////////////// StrSNCpy(SMTPS.szLogonUser, ppszTokens[0]); StrFreeStrings(ppszTokens); SMTPS.ulFlags |= SMTPF_AUTHENTICATED; SMTPS.iSMTPState = stateAuthenticated; BSckSendString(hBSock, "235 Authentication successful", SMTPS.pSMTPCfg->iTimeout); return (0); } static int SMTPDoAuthExternal(BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char const *pszAuthType) { char szExtAuthFilePath[SYS_MAX_PATH] = ""; SMTPGetExtAuthFilePath(szExtAuthFilePath, sizeof(szExtAuthFilePath)); FILE *pExtAuthFile = fopen(szExtAuthFilePath, "rt"); if (pExtAuthFile == NULL) { ErrSetErrorCode(ERR_FILE_OPEN, szExtAuthFilePath); return (ERR_FILE_OPEN); } char szExtAuthLine[SVR_SMTP_EXTAUTH_LINE_MAX] = ""; while (MscGetConfigLine(szExtAuthLine, sizeof(szExtAuthLine) - 1, pExtAuthFile) != NULL) { char **ppszStrings = StrGetTabLineStrings(szExtAuthLine); if (ppszStrings == NULL) continue; int iFieldsCount = StrStringsCount(ppszStrings); if ((iFieldsCount > 5) && (stricmp(ppszStrings[0], pszAuthType) == 0)) { fclose(pExtAuthFile); int iAuthResult = SMTPExternalAuthenticate(hBSock, SMTPS, ppszStrings); StrFreeStrings(ppszStrings); return (iAuthResult); } StrFreeStrings(ppszStrings); } fclose(pExtAuthFile); SMTPSendError(hBSock, SMTPS, "504 Unknown authentication"); ErrSetErrorCode(ERR_UNKNOWN_SMTP_AUTH, pszAuthType); return (ERR_UNKNOWN_SMTP_AUTH); } static int SMTPDoAuthPlain(BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char const *pszAuthParam) { /////////////////////////////////////////////////////////////////////////////// // Parameter validation /////////////////////////////////////////////////////////////////////////////// if ((pszAuthParam == NULL) || (strlen(pszAuthParam) == 0)) { SMTPSendError(hBSock, SMTPS, "501 Syntax error in parameters or arguments"); ErrSetErrorCode(ERR_BAD_SMTP_CMD_SYNTAX); return (ERR_BAD_SMTP_CMD_SYNTAX); } /////////////////////////////////////////////////////////////////////////////// // Decode ( base64 ) auth parameter /////////////////////////////////////////////////////////////////////////////// unsigned int uDec64Length = 0; char szClientAuth[PLAIN_AUTH_PARAM_SIZE] = ""; ZeroData(szClientAuth); if (decode64(pszAuthParam, strlen(pszAuthParam), szClientAuth, &uDec64Length) != 0) { SMTPSendError(hBSock, SMTPS, "501 Syntax error in parameters or arguments"); ErrSetErrorCode(ERR_BAD_SMTP_CMD_SYNTAX); return (ERR_BAD_SMTP_CMD_SYNTAX); } /////////////////////////////////////////////////////////////////////////////// // Extract plain auth params ( unused + 0 + username + 0 + password ) /////////////////////////////////////////////////////////////////////////////// char *pszUsername = szClientAuth + strlen(szClientAuth) + 1; char *pszPassword = pszUsername + strlen(pszUsername) + 1; /////////////////////////////////////////////////////////////////////////////// // Validate client response /////////////////////////////////////////////////////////////////////////////// if ((SMTPTryApplyLocalAuth(SMTPS, pszUsername, pszPassword) < 0) && (SMTPTryApplyUsrPwdAuth(SMTPS, pszUsername, pszPassword) < 0)) { ErrorPush(); SMTPSendError(hBSock, SMTPS, "503 Authentication failed"); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Set the logon user /////////////////////////////////////////////////////////////////////////////// StrSNCpy(SMTPS.szLogonUser, pszUsername); SMTPS.ulFlags |= SMTPF_AUTHENTICATED; SMTPS.iSMTPState = stateAuthenticated; BSckSendString(hBSock, "235 Authentication successful", SMTPS.pSMTPCfg->iTimeout); return (0); } static int SMTPDoAuthLogin(BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char const *pszAuthParam) { /////////////////////////////////////////////////////////////////////////////// // Emit encoded64 username request /////////////////////////////////////////////////////////////////////////////// unsigned int uEnc64Length = 0; char szUsername[512] = ""; encode64(LOGIN_AUTH_USERNAME, strlen(LOGIN_AUTH_USERNAME), szUsername, sizeof(szUsername), &uEnc64Length); if ((BSckVSendString(hBSock, SMTPS.pSMTPCfg->iTimeout, "334 %s", szUsername) < 0) || (BSckGetString(hBSock, szUsername, sizeof(szUsername) - 1, SMTPS.pSMTPCfg->iTimeout) == NULL)) return (ErrGetErrorCode()); /////////////////////////////////////////////////////////////////////////////// // Emit encoded64 password request /////////////////////////////////////////////////////////////////////////////// char szPassword[512] = ""; encode64(LOGIN_AUTH_PASSWORD, strlen(LOGIN_AUTH_PASSWORD), szPassword, sizeof(szPassword), &uEnc64Length); if ((BSckVSendString(hBSock, SMTPS.pSMTPCfg->iTimeout, "334 %s", szPassword) < 0) || (BSckGetString(hBSock, szPassword, sizeof(szPassword) - 1, SMTPS.pSMTPCfg->iTimeout) == NULL)) return (ErrGetErrorCode()); /////////////////////////////////////////////////////////////////////////////// // Decode ( base64 ) username /////////////////////////////////////////////////////////////////////////////// unsigned int uDec64Length = 0; char szDecodeBuffer[512] = ""; if (decode64(szUsername, strlen(szUsername), szDecodeBuffer, &uDec64Length) != 0) { SMTPSendError(hBSock, SMTPS, "501 Syntax error in parameters or arguments"); ErrSetErrorCode(ERR_BAD_SMTP_CMD_SYNTAX); return (ERR_BAD_SMTP_CMD_SYNTAX); } StrSNCpy(szUsername, szDecodeBuffer); /////////////////////////////////////////////////////////////////////////////// // Decode ( base64 ) password /////////////////////////////////////////////////////////////////////////////// if (decode64(szPassword, strlen(szPassword), szDecodeBuffer, &uDec64Length) != 0) { SMTPSendError(hBSock, SMTPS, "501 Syntax error in parameters or arguments"); ErrSetErrorCode(ERR_BAD_SMTP_CMD_SYNTAX); return (ERR_BAD_SMTP_CMD_SYNTAX); } StrSNCpy(szPassword, szDecodeBuffer); /////////////////////////////////////////////////////////////////////////////// // Validate client response /////////////////////////////////////////////////////////////////////////////// if ((SMTPTryApplyLocalAuth(SMTPS, szUsername, szPassword) < 0) && (SMTPTryApplyUsrPwdAuth(SMTPS, szUsername, szPassword) < 0)) { ErrorPush(); SMTPSendError(hBSock, SMTPS, "503 Authentication failed"); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Set the logon user /////////////////////////////////////////////////////////////////////////////// StrSNCpy(SMTPS.szLogonUser, szUsername); SMTPS.ulFlags |= SMTPF_AUTHENTICATED; SMTPS.iSMTPState = stateAuthenticated; BSckSendString(hBSock, "235 Authentication successful", SMTPS.pSMTPCfg->iTimeout); return (0); } static char *SMTPGetAuthFilePath(char *pszFilePath, int iMaxPath) { CfgGetRootPath(pszFilePath, iMaxPath); StrNCat(pszFilePath, SVR_SMTP_AUTH_FILE, iMaxPath); return (pszFilePath); } static char *SMTPGetExtAuthFilePath(char *pszFilePath, int iMaxPath) { CfgGetRootPath(pszFilePath, iMaxPath); StrNCat(pszFilePath, SVR_SMTP_EXTAUTH_FILE, iMaxPath); return (pszFilePath); } static int SMTPTryApplyLocalAuth(SMTPSession & SMTPS, char const *pszUsername, char const *pszPassword) { /////////////////////////////////////////////////////////////////////////////// // First try to lookup mailusers.tab /////////////////////////////////////////////////////////////////////////////// char szAccountUser[MAX_ADDR_NAME] = ""; char szAccountDomain[MAX_HOST_NAME] = ""; if (StrSplitString(pszUsername, POP3_USER_SPLITTERS, szAccountUser, sizeof(szAccountUser), szAccountDomain, sizeof(szAccountDomain)) < 0) return (ErrGetErrorCode()); UserInfo *pUI = UsrGetUserByName(szAccountDomain, szAccountUser); if (pUI != NULL) { if (strcmp(pUI->pszPassword, pszPassword) == 0) { /////////////////////////////////////////////////////////////////////////////// // Apply user configuration /////////////////////////////////////////////////////////////////////////////// if (SMTPApplyUserConfig(SMTPS, pUI) < 0) { ErrorPush(); UsrFreeUserInfo(pUI); return (ErrorPop()); } UsrFreeUserInfo(pUI); return (0); } UsrFreeUserInfo(pUI); } ErrSetErrorCode(ERR_SMTP_AUTH_FAILED); return (ERR_SMTP_AUTH_FAILED); } static int SMTPGetUserSmtpPerms(UserInfo * pUI, SVRCFG_HANDLE hSvrConfig, char *pszPerms, int iMaxPerms) { char *pszUserPerms = UsrGetUserInfoVar(pUI, "SmtpPerms"); if (pszUserPerms != NULL) { StrNCpy(pszPerms, pszUserPerms, iMaxPerms); SysFree(pszUserPerms); } else { /////////////////////////////////////////////////////////////////////////////// // Match found, get the default permissions /////////////////////////////////////////////////////////////////////////////// char *pszDefultPerms = SvrGetConfigVar(hSvrConfig, "DefaultSmtpPerms", "MR"); if (pszDefultPerms != NULL) { StrNCpy(pszPerms, pszDefultPerms, iMaxPerms); SysFree(pszDefultPerms); } else SetEmptyString(pszPerms); } return (0); } static int SMTPTryApplyLocalCMD5Auth(SMTPSession & SMTPS, char const *pszChallenge, char const *pszUsername, char const *pszDigest) { /////////////////////////////////////////////////////////////////////////////// // First try to lookup mailusers.tab /////////////////////////////////////////////////////////////////////////////// char szAccountUser[MAX_ADDR_NAME] = ""; char szAccountDomain[MAX_HOST_NAME] = ""; if (StrSplitString(pszUsername, POP3_USER_SPLITTERS, szAccountUser, sizeof(szAccountUser), szAccountDomain, sizeof(szAccountDomain)) < 0) return (ErrGetErrorCode()); UserInfo *pUI = UsrGetUserByName(szAccountDomain, szAccountUser); if (pUI != NULL) { /////////////////////////////////////////////////////////////////////////////// // Compute MD5 response ( secret , challenge , digest ) /////////////////////////////////////////////////////////////////////////////// char szCurrDigest[512] = ""; if (MscCramMD5(pUI->pszPassword, pszChallenge, szCurrDigest) < 0) { UsrFreeUserInfo(pUI); return (ErrGetErrorCode()); } if (stricmp(szCurrDigest, pszDigest) == 0) { /////////////////////////////////////////////////////////////////////////////// // Apply user configuration /////////////////////////////////////////////////////////////////////////////// if (SMTPApplyUserConfig(SMTPS, pUI) < 0) { ErrorPush(); UsrFreeUserInfo(pUI); return (ErrorPop()); } UsrFreeUserInfo(pUI); return (0); } UsrFreeUserInfo(pUI); } ErrSetErrorCode(ERR_SMTP_AUTH_FAILED); return (ERR_SMTP_AUTH_FAILED); } static int SMTPTryApplyUsrPwdAuth(SMTPSession & SMTPS, char const *pszUsername, char const *pszPassword) { char szAuthFilePath[SYS_MAX_PATH] = ""; SMTPGetAuthFilePath(szAuthFilePath, sizeof(szAuthFilePath)); char szResLock[SYS_MAX_PATH] = ""; RLCK_HANDLE hResLock = RLckLockSH(CfgGetBasedPath(szAuthFilePath, szResLock, sizeof(szResLock))); if (hResLock == INVALID_RLCK_HANDLE) return (ErrGetErrorCode()); FILE *pAuthFile = fopen(szAuthFilePath, "rt"); if (pAuthFile == NULL) { RLckUnlockSH(hResLock); ErrSetErrorCode(ERR_FILE_OPEN, szAuthFilePath); return (ERR_FILE_OPEN); } char szAuthLine[SVR_SMTPAUTH_LINE_MAX] = ""; while (MscGetConfigLine(szAuthLine, sizeof(szAuthLine) - 1, pAuthFile) != NULL) { char **ppszStrings = StrGetTabLineStrings(szAuthLine); if (ppszStrings == NULL) continue; int iFieldsCount = StrStringsCount(ppszStrings); if ((iFieldsCount >= smtpaMax) && (strcmp(ppszStrings[smtpaUsername], pszUsername) == 0) && (strcmp(ppszStrings[smtpaPassword], pszPassword) == 0)) { /////////////////////////////////////////////////////////////////////////////// // Apply user perms to SMTP config /////////////////////////////////////////////////////////////////////////////// SMTPApplyPerms(SMTPS, ppszStrings[smtpaPerms]); StrFreeStrings(ppszStrings); fclose(pAuthFile); RLckUnlockSH(hResLock); return (0); } StrFreeStrings(ppszStrings); } fclose(pAuthFile); RLckUnlockSH(hResLock); ErrSetErrorCode(ERR_SMTP_AUTH_FAILED); return (ERR_SMTP_AUTH_FAILED); } static int SMTPTryApplyCMD5Auth(SMTPSession & SMTPS, char const *pszChallenge, char const *pszUsername, char const *pszDigest) { char szAuthFilePath[SYS_MAX_PATH] = ""; SMTPGetAuthFilePath(szAuthFilePath, sizeof(szAuthFilePath)); FILE *pAuthFile = fopen(szAuthFilePath, "rt"); if (pAuthFile == NULL) { ErrSetErrorCode(ERR_FILE_OPEN, szAuthFilePath); return (ERR_FILE_OPEN); } char szAuthLine[SVR_SMTPAUTH_LINE_MAX] = ""; while (MscGetConfigLine(szAuthLine, sizeof(szAuthLine) - 1, pAuthFile) != NULL) { char **ppszStrings = StrGetTabLineStrings(szAuthLine); if (ppszStrings == NULL) continue; int iFieldsCount = StrStringsCount(ppszStrings); if ((iFieldsCount >= smtpaMax) && (strcmp(ppszStrings[smtpaUsername], pszUsername) == 0)) { char szCurrDigest[512] = ""; /////////////////////////////////////////////////////////////////////////////// // Compute MD5 response ( secret , challenge , digest ) /////////////////////////////////////////////////////////////////////////////// if (MscCramMD5(ppszStrings[smtpaPassword], pszChallenge, szCurrDigest) < 0) { StrFreeStrings(ppszStrings); fclose(pAuthFile); return (ErrGetErrorCode()); } if (stricmp(szCurrDigest, pszDigest) == 0) { /////////////////////////////////////////////////////////////////////////////// // Apply user perms to SMTP config /////////////////////////////////////////////////////////////////////////////// SMTPApplyPerms(SMTPS, ppszStrings[smtpaPerms]); StrFreeStrings(ppszStrings); fclose(pAuthFile); return (0); } } StrFreeStrings(ppszStrings); } fclose(pAuthFile); ErrSetErrorCode(ERR_SMTP_AUTH_FAILED); return (ERR_SMTP_AUTH_FAILED); } static int SMTPDoAuthCramMD5(BSOCK_HANDLE hBSock, SMTPSession & SMTPS, char const *pszAuthParam) { /////////////////////////////////////////////////////////////////////////////// // Emit encoded64 challenge and get client response /////////////////////////////////////////////////////////////////////////////// unsigned int uEnc64Length = 0; char szChallenge[512] = ""; encode64(SMTPS.szTimeStamp, strlen(SMTPS.szTimeStamp), szChallenge, sizeof(szChallenge), &uEnc64Length); if ((BSckVSendString(hBSock, SMTPS.pSMTPCfg->iTimeout, "334 %s", szChallenge) < 0) || (BSckGetString(hBSock, szChallenge, sizeof(szChallenge) - 1, SMTPS.pSMTPCfg->iTimeout) == NULL)) return (ErrGetErrorCode()); /////////////////////////////////////////////////////////////////////////////// // Decode ( base64 ) client response /////////////////////////////////////////////////////////////////////////////// unsigned int uDec64Length = 0; char szClientResp[512] = ""; if (decode64(szChallenge, strlen(szChallenge), szClientResp, &uDec64Length) != 0) { SMTPSendError(hBSock, SMTPS, "501 Syntax error in parameters or arguments"); ErrSetErrorCode(ERR_BAD_SMTP_CMD_SYNTAX); return (ERR_BAD_SMTP_CMD_SYNTAX); } /////////////////////////////////////////////////////////////////////////////// // Extract the username and client digest /////////////////////////////////////////////////////////////////////////////// char *pszUsername = szClientResp; char *pszDigest = strchr(szClientResp, ' '); if (pszDigest == NULL) { SMTPSendError(hBSock, SMTPS, "501 Syntax error in parameters or arguments"); ErrSetErrorCode(ERR_BAD_SMTP_CMD_SYNTAX); return (ERR_BAD_SMTP_CMD_SYNTAX); } *pszDigest++ = '\0'; /////////////////////////////////////////////////////////////////////////////// // Validate client response /////////////////////////////////////////////////////////////////////////////// if ((SMTPTryApplyLocalCMD5Auth(SMTPS, SMTPS.szTimeStamp, pszUsername, pszDigest) < 0) && (SMTPTryApplyCMD5Auth(SMTPS, SMTPS.szTimeStamp, pszUsername, pszDigest) < 0)) { ErrorPush(); SMTPSendError(hBSock, SMTPS, "503 Authentication failed"); return (ErrorPop()); } /////////////////////////////////////////////////////////////////////////////// // Set the logon user /////////////////////////////////////////////////////////////////////////////// StrSNCpy(SMTPS.szLogonUser, pszUsername); SMTPS.ulFlags |= SMTPF_AUTHENTICATED; SMTPS.iSMTPState = stateAuthenticated; BSckSendString(hBSock, "235 Authentication successful", SMTPS.pSMTPCfg->iTimeout); return (0); } static int SMTPHandleCmd_AUTH(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS) { if (SMTPS.iSMTPState != stateHelo) { SMTPResetSession(SMTPS); SMTPSendError(hBSock, SMTPS, "503 Bad sequence of commands"); ErrSetErrorCode(ERR_SMTP_BAD_CMD_SEQUENCE); return (ERR_SMTP_BAD_CMD_SEQUENCE); } int iTokensCount; char **ppszTokens = StrTokenize(pszCommand, " "); if ((ppszTokens == NULL) || ((iTokensCount = StrStringsCount(ppszTokens)) < 2)) { if (ppszTokens != NULL) StrFreeStrings(ppszTokens); SMTPSendError(hBSock, SMTPS, "501 Syntax error in parameters or arguments"); return (-1); } /////////////////////////////////////////////////////////////////////////////// // Decode AUTH command params /////////////////////////////////////////////////////////////////////////////// char szAuthType[128] = ""; char szAuthParam[PLAIN_AUTH_PARAM_SIZE] = ""; StrSNCpy(szAuthType, ppszTokens[1]); if (iTokensCount > 2) StrSNCpy(szAuthParam, ppszTokens[2]); StrFreeStrings(ppszTokens); /////////////////////////////////////////////////////////////////////////////// // Handle authentication methods /////////////////////////////////////////////////////////////////////////////// if (stricmp(szAuthType, "plain") == 0) { if (SMTPDoAuthPlain(hBSock, SMTPS, szAuthParam) < 0) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, "", "", "AUTH=EFAIL:TYPE=PLAIN", 0); return (ErrorPop()); } } else if (stricmp(szAuthType, "login") == 0) { if (SMTPDoAuthLogin(hBSock, SMTPS, szAuthParam) < 0) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, "", "", "AUTH=EFAIL:TYPE=LOGIN", 0); return (ErrorPop()); } } else if (stricmp(szAuthType, "cram-md5") == 0) { if (SMTPDoAuthCramMD5(hBSock, SMTPS, szAuthParam) < 0) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, "", "", "AUTH=EFAIL:TYPE=CRAM-MD5", 0); return (ErrorPop()); } } else { /////////////////////////////////////////////////////////////////////////////// // Handle external authentication methods /////////////////////////////////////////////////////////////////////////////// if (SMTPDoAuthExternal(hBSock, SMTPS, szAuthType) < 0) { ErrorPush(); if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, "", "", "AUTH=EFAIL:TYPE=EXTRN", 0); return (ErrorPop()); } } return (0); } static int SMTPSendMultilineResponse(BSOCK_HANDLE hBSock, int iTimeout, FILE * pRespFile) { rewind(pRespFile); char szCurrLine[1024] = ""; char szPrevLine[1024] = ""; if (MscGetString(pRespFile, szPrevLine, sizeof(szPrevLine) - 1) != NULL) { while (MscGetString(pRespFile, szCurrLine, sizeof(szCurrLine) - 1) != NULL) { szPrevLine[3] = '-'; if (BSckSendString(hBSock, szPrevLine, iTimeout) < 0) return (ErrGetErrorCode()); StrSNCpy(szPrevLine, szCurrLine); } if (BSckSendString(hBSock, szPrevLine, iTimeout) < 0) return (ErrGetErrorCode()); } return (0); } static int SMTPHandleCmd_RSET(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS) { SMTPResetSession(SMTPS); BSckSendString(hBSock, "250 OK", SMTPS.pSMTPCfg->iTimeout); return (0); } static int SMTPHandleCmd_NOOP(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS) { BSckSendString(hBSock, "250 OK", SMTPS.pSMTPCfg->iTimeout); return (0); } static int SMTPHandleCmd_HELP(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS) { BSckVSendString(hBSock, SMTPS.pSMTPCfg->iTimeout, "250-HELO EHLO MAIL RCPT DATA AUTH\r\n" "250-RSET VRFY ETRN NOOP HELP QUIT\r\n" "250 For more information please visit : %s", APP_URL); return (0); } static int SMTPHandleCmd_QUIT(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS) { SMTPS.iSMTPState = stateExit; BSckVSendString(hBSock, SMTPS.pSMTPCfg->iTimeout, "221 %s service closing transmission channel", SMTP_SERVER_NAME); return (0); } static int SMTPHandleCmd_VRFY(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS) { /////////////////////////////////////////////////////////////////////////////// // Check if VRFY is enabled /////////////////////////////////////////////////////////////////////////////// if (((SMTPS.ulFlags & SMTPF_VRFY_ENABLED) == 0) && !SvrTestConfigFlag("AllowSmtpVRFY", false, SMTPS.hSvrConfig)) { if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, "", "", "VRFY=EACCESS", 0); SMTPSendError(hBSock, SMTPS, "252 Argument not checked"); return (-1); } char **ppszTokens = StrTokenize(pszCommand, " "); if ((ppszTokens == NULL) || (StrStringsCount(ppszTokens) != 2)) { if (ppszTokens != NULL) StrFreeStrings(ppszTokens); SMTPSendError(hBSock, SMTPS, "501 Syntax error in parameters or arguments"); return (-1); } char szVrfyUser[MAX_ADDR_NAME] = ""; char szVrfyDomain[MAX_ADDR_NAME] = ""; if (USmtpSplitEmailAddr(ppszTokens[1], szVrfyUser, szVrfyDomain) < 0) { ErrorPush(); StrFreeStrings(ppszTokens); SMTPSendError(hBSock, SMTPS, "501 Syntax error in parameters or arguments"); return (ErrorPop()); } StrFreeStrings(ppszTokens); UserInfo *pUI = UsrGetUserByNameOrAlias(szVrfyDomain, szVrfyUser); if (pUI != NULL) { char *pszRealName = UsrGetUserInfoVar(pUI, "RealName", "Unknown"); BSckVSendString(hBSock, SMTPS.pSMTPCfg->iTimeout, "250 %s <%s@%s>", pszRealName, pUI->pszName, pUI->pszDomain); SysFree(pszRealName); UsrFreeUserInfo(pUI); } else { if (USmlIsCmdAliasAccount(szVrfyDomain, szVrfyUser) < 0) { SMTPSendError(hBSock, SMTPS, "550 String does not match anything"); ErrSetErrorCode(ERR_USER_NOT_LOCAL); return (ERR_USER_NOT_LOCAL); } BSckVSendString(hBSock, SMTPS.pSMTPCfg->iTimeout, "250 Local account <%s@%s>", szVrfyUser, szVrfyDomain); } return (0); } static int SMTPHandleCmd_ETRN(const char *pszCommand, BSOCK_HANDLE hBSock, SMTPSession & SMTPS) { /////////////////////////////////////////////////////////////////////////////// // Check if ETRN is enabled /////////////////////////////////////////////////////////////////////////////// if (((SMTPS.ulFlags & SMTPF_ETRN_ENABLED) == 0) && !SvrTestConfigFlag("AllowSmtpETRN", false, SMTPS.hSvrConfig)) { if (SMTPLogEnabled(SMTPS.hShbSMTP, SMTPS.pSMTPCfg)) SMTPLogSession(SMTPS, "", "", "ETRN=EACCESS", 0); SMTPSendError(hBSock, SMTPS, "501 Command not accepted"); return (-1); } char **ppszTokens = StrTokenize(pszCommand, " "); if ((ppszTokens == NULL) || (StrStringsCount(ppszTokens) != 2)) { if (ppszTokens != NULL) StrFreeStrings(ppszTokens); SMTPSendError(hBSock, SMTPS, "501 Syntax error in parameters or arguments"); return (-1); } /////////////////////////////////////////////////////////////////////////////// // Do a matched flush of the rsnd arena /////////////////////////////////////////////////////////////////////////////// if (QueFlushRsndArena(hSpoolQueue, ppszTokens[1]) < 0) { ErrorPush(); StrFreeStrings(ppszTokens); SMTPSendError(hBSock, SMTPS, "451 Requested action aborted: (%d) local error in processing", ErrorFetch()); return (ErrorPop()); } BSckVSendString(hBSock, SMTPS.pSMTPCfg->iTimeout, "250 Queueing for '%s' has been started", ppszTokens[1]); StrFreeStrings(ppszTokens); return (0); }