From bf2bd3976979e3b01e4d543453cdd81306907527 Mon Sep 17 00:00:00 2001 From: psychon Date: Sun, 13 Jul 2008 15:32:27 +0000 Subject: [PATCH] Add supports for salted hashes to znc.conf These changes the format of the 'Pass' config option. The old format is still accepted. The new format is: Pass = plain# Pass = md5# Pass = md5### This also makes ZNC only write configs in the new format. znc --makeconf and znc --makepass now always generate salted hashes. git-svn-id: https://znc.svn.sourceforge.net/svnroot/znc/trunk@1127 726aef4b-f618-498e-8847-2d620e286838 --- User.cpp | 20 +++++++++++++++++--- User.h | 3 ++- Utils.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ Utils.h | 1 + main.cpp | 5 +++-- znc.cpp | 24 +++++++++++++++++++++--- 6 files changed, 87 insertions(+), 9 deletions(-) diff --git a/User.cpp b/User.cpp index a269a035..c3f5d36b 100644 --- a/User.cpp +++ b/User.cpp @@ -565,7 +565,15 @@ bool CUser::PrintLine(CFile& File, const CString& sName, const CString& sValue) bool CUser::WriteConfig(CFile& File) { File.Write("\n"); - PrintLine(File, "Pass", GetPass() + ((IsPassHashed()) ? " -" : "")); + if (IsPassHashed()) { + if (m_sPassSalt.empty()) { + PrintLine(File, "Pass", "md5#" + GetPass()); + } else { + PrintLine(File, "Pass", "md5#" + GetPass() + "#" + m_sPassSalt + "#"); + } + } else { + PrintLine(File, "Pass", "plain#" + GetPass()); + } PrintLine(File, "Nick", GetNick()); PrintLine(File, "AltNick", GetAltNick()); PrintLine(File, "Ident", GetIdent()); @@ -808,7 +816,9 @@ bool CUser::CheckPass(const CString& sPass) { return (sPass == m_sPass); } - return (m_sPass.CaseCmp(sPass.MD5()) == 0); + CString sSaltedPass = sPass + m_sPassSalt; + + return (m_sPass.CaseCmp(sSaltedPass.MD5()) == 0); } /*CClient* CUser::GetClient() { @@ -1023,7 +1033,11 @@ void CUser::SetAltNick(const CString& s) { m_sAltNick = s; } void CUser::SetIdent(const CString& s) { m_sIdent = s; } void CUser::SetRealName(const CString& s) { m_sRealName = s; } void CUser::SetVHost(const CString& s) { m_sVHost = s; } -void CUser::SetPass(const CString& s, bool bHashed) { m_sPass = s; m_bPassHashed = bHashed; } +void CUser::SetPass(const CString& s, bool bHashed, const CString& sSalt) { + m_sPass = s; + m_bPassHashed = bHashed; + m_sPassSalt = sSalt; +} void CUser::SetMultiClients(bool b) { m_bMultiClients = b; } void CUser::SetBounceDCCs(bool b) { m_bBounceDCCs = b; } void CUser::SetUseClientIP(bool b) { m_bUseClientIP = b; } diff --git a/User.h b/User.h index 02dca00a..ae4d1e6c 100644 --- a/User.h +++ b/User.h @@ -119,7 +119,7 @@ public: void SetIdent(const CString& s); void SetRealName(const CString& s); void SetVHost(const CString& s); - void SetPass(const CString& s, bool bHashed); + void SetPass(const CString& s, bool bHashed, const CString& sSalt = ""); void SetBounceDCCs(bool b); void SetMultiClients(bool b); void SetUseClientIP(bool b); @@ -207,6 +207,7 @@ protected: CString m_sRealName; CString m_sVHost; CString m_sPass; + CString m_sPassSalt; CString m_sStatusPrefix; CString m_sDefaultChanModes; CString m_sChanPrefixes; diff --git a/Utils.cpp b/Utils.cpp index ffbf27c2..8aea3a76 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -189,6 +189,49 @@ CString CUtils::GetHashPass() { return ""; } +CString CUtils::GetSaltedHashPass(CString& sSalt, unsigned int uiSaltLength) { + sSalt = CString::RandomString(uiSaltLength); + const char *pSalt = sSalt.c_str(); + + while (true) { + char* pass = CUtils::GetPass("Enter Password"); + char* pass1 = (char *) malloc(strlen(pass) + 1); + // Make a copy of this since it is stored in a static buffer and will be overwritten when we fill pass2 below + strcpy(pass1, pass); + // null out our pass so it doesn't sit in memory + memset(pass, 0, strlen(pass)); + char* pass2 = CUtils::GetPass("Confirm Password"); + int iLen = strlen(pass1); + + if (strcmp(pass1, pass2) != 0) { + CUtils::PrintError("The supplied passwords did not match"); + } else if (!iLen) { + CUtils::PrintError("You can not use an empty password"); + } else { + // Construct the salted pass + char *salted_pass = (char *) malloc(iLen + uiSaltLength + 1); + strcpy(salted_pass, pass1); + strcpy(salted_pass + iLen, pSalt); + + CString sRet((const char*) CMD5(salted_pass, iLen + uiSaltLength)); + // null out our pass so it doesn't sit in memory + memset(salted_pass, 0, iLen + uiSaltLength); + memset(pass1, 0, iLen); + memset(pass2, 0, strlen(pass2)); + free(salted_pass); + free(pass1); + + return sRet; + } + + memset((char*) pass1, 0, iLen); // null out our pass so it doesn't sit in memory + memset((char*) pass2, 0, strlen(pass2)); // null out our pass so it doesn't sit in memory + free(pass1); + } + + return ""; +} + char* CUtils::GetPass(const CString& sPrompt) { PrintPrompt(sPrompt); return getpass(""); diff --git a/Utils.h b/Utils.h index 20dcb77d..f1b456f8 100644 --- a/Utils.h +++ b/Utils.h @@ -52,6 +52,7 @@ public: static void PrintAction(const CString& sMessage); static void PrintStatus(bool bSuccess, const CString& sMessage = ""); static CString GetHashPass(); + static CString GetSaltedHashPass(CString& sSalt, unsigned int uiSaltLength = 20); static char* GetPass(const CString& sPrompt); static bool GetInput(const CString& sPrompt, CString& sRet, const CString& sDefault = "", const CString& sHint = ""); static bool GetBoolInput(const CString& sPrompt, bool bDefault); diff --git a/main.cpp b/main.cpp index 230878e2..59fa7d98 100644 --- a/main.cpp +++ b/main.cpp @@ -193,9 +193,10 @@ int main(int argc, char** argv) { #endif /* HAVE_LIBSSL */ if (bMakePass) { - CString sHash = CUtils::GetHashPass(); + CString sSalt; + CString sHash = CUtils::GetSaltedHashPass(sSalt); CUtils::PrintMessage("Use this in the section of your config:"); - CUtils::PrintMessage("Pass = " + sHash + " -"); + CUtils::PrintMessage("Pass = md5#" + sHash + "#" + sSalt + "#"); return 0; } diff --git a/znc.cpp b/znc.cpp index f8674d5f..d5d52d39 100644 --- a/znc.cpp +++ b/znc.cpp @@ -666,8 +666,9 @@ bool CZNC::WriteNewConfig(const CString& sConfig) { } while (!CUser::IsValidUserName(sUser)); vsLines.push_back(""); - sAnswer = CUtils::GetHashPass(); - vsLines.push_back("\tPass = " + sAnswer + " -"); + CString sSalt; + sAnswer = CUtils::GetSaltedHashPass(sSalt); + vsLines.push_back("\tPass = md5#" + sAnswer + "#" + sSalt + "#"); if (CUtils::GetBoolInput("Would you like this user to be an admin?", bFirstUser)) { vsLines.push_back("\tAdmin = true"); @@ -1180,12 +1181,29 @@ bool CZNC::DoRehash(CString& sError) CUtils::PrintMessage("WARNING: AwaySuffix has been depricated, instead try -> LoadModule = awaynick %nick%_" + sValue); continue; } else if (sName.CaseCmp("Pass") == 0) { + // There are different formats for this available: + // Pass = + // Pass = - + // Pass = plain# + // Pass = md5# + // Pass = md5### + // The last one is the md5 hash of 'password' + 'salt' if (sValue.Right(1) == "-") { sValue.RightChomp(); sValue.Trim(); pUser->SetPass(sValue, true); } else { - pUser->SetPass(sValue, false); + CString sMethod = sValue.Token(0, false, "#"); + CString sPass = sValue.Token(1, true, "#"); + if (sMethod == "md5") { + CString sSalt = sPass.Token(1, false, "#"); + sPass = sPass.Token(0, false, "#"); + pUser->SetPass(sPass, true, sSalt); + } else if (sMethod == "plain") { + pUser->SetPass(sPass, false); + } else { + pUser->SetPass(sValue, false); + } } continue;