From 2f093651c4edf10ba83088eb485b818a4a5e709f Mon Sep 17 00:00:00 2001 From: George Hazan Date: Mon, 2 Dec 2024 21:54:38 +0300 Subject: Jabber: upgrade tasks implementation --- protocols/JabberG/jabber.vcxproj | 2 +- protocols/JabberG/jabber.vcxproj.filters | 6 +- protocols/JabberG/src/jabber_auth.cpp | 44 ++++++------ protocols/JabberG/src/jabber_proto.cpp | 7 +- protocols/JabberG/src/jabber_proto.h | 34 +++++++++- protocols/JabberG/src/jabber_sasl2.cpp | 112 +++++++++++++++++++++++-------- protocols/JabberG/src/jabber_thread.cpp | 4 +- protocols/JabberG/src/stdafx.h | 3 +- 8 files changed, 152 insertions(+), 60 deletions(-) diff --git a/protocols/JabberG/jabber.vcxproj b/protocols/JabberG/jabber.vcxproj index ed81e479b2..c624c1bfa4 100644 --- a/protocols/JabberG/jabber.vcxproj +++ b/protocols/JabberG/jabber.vcxproj @@ -34,6 +34,7 @@ + @@ -70,7 +71,6 @@ - diff --git a/protocols/JabberG/jabber.vcxproj.filters b/protocols/JabberG/jabber.vcxproj.filters index ee4943caf3..b87ab85845 100644 --- a/protocols/JabberG/jabber.vcxproj.filters +++ b/protocols/JabberG/jabber.vcxproj.filters @@ -119,9 +119,6 @@ Source Files - - Source Files - Source Files @@ -167,6 +164,9 @@ Source Files + + Source Files + diff --git a/protocols/JabberG/src/jabber_auth.cpp b/protocols/JabberG/src/jabber_auth.cpp index 239586d58f..86c5495a1f 100644 --- a/protocols/JabberG/src/jabber_auth.cpp +++ b/protocols/JabberG/src/jabber_auth.cpp @@ -241,6 +241,24 @@ public: ///////////////////////////////////////////////////////////////////////////////////////// // SCRAM-SHA-1 authorization +void Hi(const EVP_MD *hashMethod, uint8_t *res, char *passw, size_t passwLen, char *salt, size_t saltLen, int iterations) +{ + size_t bufLen = saltLen + sizeof(UINT32); + uint8_t *u = (uint8_t *)_alloca(max(bufLen, EVP_MAX_MD_SIZE)); + memcpy(u, salt, saltLen); *(UINT32 *)(u + saltLen) = htonl(1); + + memset(res, 0, EVP_MAX_MD_SIZE); + + for (int i = 0; i < iterations; i++) { + unsigned int len; + HMAC(hashMethod, (uint8_t *)passw, (unsigned)passwLen, u, (unsigned)bufLen, u, &len); + bufLen = EVP_MD_size(hashMethod); + + for (size_t j = 0; j < bufLen; j++) + res[j] ^= u[j]; + } +} + class TScramAuth : public TJabberAuth { typedef TJabberAuth CSuper; @@ -294,7 +312,7 @@ public: { size_t chlLen, saltLen = 0; ptrA snonce, salt; - int ind = -1; + int iterations = -1; ptrA chl((char *)mir_base64_decode(challenge, &chlLen)), cbd; if (bindData.isEmpty()) @@ -313,16 +331,16 @@ public: else if (*p == 's' && p[1] == '=') // salt salt = (char *)mir_base64_decode(p + 2, &saltLen); else if (*p == 'i' && p[1] == '=') - ind = atoi(p + 2); + iterations = atoi(p + 2); } - if (snonce == nullptr || salt == nullptr || ind == -1) + if (snonce == nullptr || salt == nullptr || iterations == -1) return nullptr; int hashSize = EVP_MD_size(hashMethod); uint8_t saltedPassw[EVP_MAX_MD_SIZE]; - Hi(saltedPassw, info->conn.password, mir_strlen(info->conn.password), salt, saltLen, ind); + Hi(hashMethod, saltedPassw, info->conn.password, mir_strlen(info->conn.password), salt, saltLen, iterations); uint8_t clientKey[EVP_MAX_MD_SIZE]; unsigned int len; @@ -364,24 +382,6 @@ public: ptrA chl((char *)mir_base64_decode(challenge, &chlLen)); return chl && strncmp((char *)chl + 2, serverSignature, chlLen - 2) == 0; } - - void Hi(uint8_t *res, char *passw, size_t passwLen, char *salt, size_t saltLen, int iterations) - { - size_t bufLen = saltLen + sizeof(UINT32); - uint8_t *u = (uint8_t *)_alloca(max(bufLen, EVP_MAX_MD_SIZE)); - memcpy(u, salt, saltLen); *(UINT32 *)(u + saltLen) = htonl(1); - - memset(res, 0, EVP_MAX_MD_SIZE); - - for (int i = 0; i < iterations; i++) { - unsigned int len; - HMAC(hashMethod, (uint8_t *)passw, (unsigned)passwLen, u, (unsigned)bufLen, u, &len); - bufLen = EVP_MD_size(hashMethod); - - for (size_t j = 0; j < bufLen; j++) - res[j] ^= u[j]; - } - } }; ///////////////////////////////////////////////////////////////////////////////////////// diff --git a/protocols/JabberG/src/jabber_proto.cpp b/protocols/JabberG/src/jabber_proto.cpp index 0ec19dfc69..0245cfa2db 100644 --- a/protocols/JabberG/src/jabber_proto.cpp +++ b/protocols/JabberG/src/jabber_proto.cpp @@ -57,13 +57,18 @@ static int compareAuth(const TJabberAuth *p1, const TJabberAuth *p2) return p2->getPriority() - p1->getPriority(); // reverse sorting order } +static int compareTasks(const TUpgradeTask *p1, const TUpgradeTask *p2) +{ + return p2->getPriority() - p1->getPriority(); // reverse sorting order +} + CJabberProto::CJabberProto(const char *aProtoName, const wchar_t *aUserName) : PROTO(aProtoName, aUserName), m_impl(*this), m_omemo(this), m_arChatMarks(50, NumericKeySortT), m_arAuthMechs(1, compareAuth), - m_arSaslUpgrade(1, compareAuth), + m_arSaslUpgrade(1, compareTasks), m_lstTransports(50, compareTransports), m_lstRoster(50, compareListItems), m_iqManager(this), diff --git a/protocols/JabberG/src/jabber_proto.h b/protocols/JabberG/src/jabber_proto.h index 8ec6d498be..969f59ee9b 100644 --- a/protocols/JabberG/src/jabber_proto.h +++ b/protocols/JabberG/src/jabber_proto.h @@ -108,6 +108,36 @@ public: } }; +class TUpgradeTask : public MZeroedObject +{ +protected: + ptrA szName, szInitData; + ThreadData *info; + int priority; + +public: + TUpgradeTask(ThreadData *pInfo, const char *pszMech) : + info(pInfo), + szName(mir_strdup(pszMech)) + {} + + virtual ~TUpgradeTask() {} + + __forceinline const char *getName() const { + return szName; + } + + __forceinline int getPriority() const { + return priority; + } + + void setInitData(const char *pszData) { + szInitData = mir_strdup(pszData); + } + + virtual bool perform(const TiXmlElement *src, TiXmlElement *dest) = 0; +}; + struct CJabberProto : public PROTO, public IJabberInterface { friend struct ThreadData; @@ -859,11 +889,11 @@ struct CJabberProto : public PROTO, public IJabberInterface void SearchDeleteFromRecent(const char *szAddr, bool deleteLastFromDB); void SearchAddToRecent(const char *szAddr, HWND hwndDialog = nullptr); - //---- jabber_secur.cpp -------------------------------------------------------------- + //---- jabber_auth.cpp --------------------------------------------------------------- - OBJLIST m_arSaslUpgrade; OBJLIST m_arAuthMechs; + OBJLIST m_arSaslUpgrade; bool OnProcessMechanism(const TiXmlElement *node, ThreadData *info); void OnProcessUpgrade(const TiXmlElement *node, ThreadData *info); diff --git a/protocols/JabberG/src/jabber_sasl2.cpp b/protocols/JabberG/src/jabber_sasl2.cpp index 5784bf8f3e..b63350acd9 100644 --- a/protocols/JabberG/src/jabber_sasl2.cpp +++ b/protocols/JabberG/src/jabber_sasl2.cpp @@ -17,41 +17,92 @@ along with this program. If not, see . #include "stdafx.h" +void Hi(const EVP_MD *hashMethod, uint8_t *res, char *passw, size_t passwLen, char *salt, size_t saltLen, int iterations); + +struct TScramTask : public TUpgradeTask +{ + const EVP_MD *hashMethod; + + TScramTask(ThreadData *info, const char *pszMech, const EVP_MD *pMethod, int iPriority) : + TUpgradeTask(info, pszMech), + hashMethod(pMethod) + { + priority = iPriority; + } + + ~TScramTask() {} + + bool perform(const TiXmlElement *src, TiXmlElement *dest) override + { + auto *salt = XmlGetChildByTag(src, "salt", "xmlns", "urn:xmpp:scram-upgrade:0"); + if (!salt || !mir_strlen(szInitData)) + return false; + + int iterations = salt->IntAttribute("iterations"); + auto *pszSalt = salt->GetText(); + if (!mir_strlen(pszSalt) || !iterations) + return false; + + size_t cbNonce, cbSalt; + ptrA szInit((char *)mir_base64_decode(szInitData, &cbNonce)); + ptrA szNonce((char*)mir_base64_decode(szInit.get() + 2, &cbNonce)); + ptrA szSalt((char *)mir_base64_decode(pszSalt, &cbSalt)); + ptrA cbd(mir_base64_encode("n,,", 3)), chl(mir_strdup("")), msg1(mir_strdup("")); + + int hashSize = EVP_MD_size(hashMethod); + + uint8_t saltedPassw[EVP_MAX_MD_SIZE]; + Hi(hashMethod, saltedPassw, info->conn.password, mir_strlen(info->conn.password), szSalt, cbSalt, iterations); + + uint8_t clientKey[EVP_MAX_MD_SIZE]; + unsigned int len; + HMAC(hashMethod, saltedPassw, hashSize, (uint8_t *)"Client Key", 10, clientKey, &len); + + uint8_t storedKey[EVP_MAX_MD_SIZE]; + { + EVP_MD_CTX *pctx = EVP_MD_CTX_new(); + EVP_DigestInit(pctx, hashMethod); + EVP_DigestUpdate(pctx, clientKey, hashSize); + EVP_DigestFinal(pctx, storedKey, &len); + EVP_MD_CTX_free(pctx); + } + + uint8_t clientSig[EVP_MAX_MD_SIZE]; + CMStringA authmsg(FORMAT, "%s,%s,c=%s,r=%s", msg1, chl.get(), cbd.get(), szNonce.get()); + HMAC(hashMethod, storedKey, hashSize, (uint8_t *)authmsg.c_str(), authmsg.GetLength(), clientSig, &len); + + uint8_t clientProof[EVP_MAX_MD_SIZE]; + for (int j = 0; j < hashSize; j++) + clientProof[j] = clientKey[j] ^ clientSig[j]; + + ptrA szEncoded(mir_base64_encode(clientProof, hashSize)); + auto *pHash = dest << XCHILD("hash", szEncoded); + pHash << XATTR("xmlns", "urn:xmpp:scram-upgrade:0"); + return true; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// SASL2: common tasks processing methods + void CJabberProto::OnProcessUpgrade(const TiXmlElement *n, ThreadData *info) { - /* - TJabberAuth *pAuth = nullptr; + TUpgradeTask *pTask; auto *szMechanism = n->GetText(); if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-1")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha1(), 500); - else if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-1-PLUS")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha1(), 601); - else if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-224")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha224(), 510); - else if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-224-PLUS")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha224(), 611); + pTask = new TScramTask(info, szMechanism, EVP_sha1(), 500); else if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-256")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha256(), 520); - else if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-256-PLUS")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha256(), 621); + pTask = new TScramTask(info, szMechanism, EVP_sha256(), 520); else if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-384")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha384(), 530); - else if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-384-PLUS")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha384(), 631); + pTask = new TScramTask(info, szMechanism, EVP_sha384(), 530); else if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-512")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha512(), 540); - else if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-512-PLUS")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha512(), 641); - - if (pAuth == nullptr) { + pTask = new TScramTask(info, szMechanism, EVP_sha512(), 540); + else { debugLogA("Unsupported mechanism for upgrade: %s, skipping", szMechanism); return; } - if (!pAuth->isValid()) - delete pAuth; - else - m_arSaslUpgrade.insert(pAuth);*/ + m_arSaslUpgrade.insert(pTask); } void CJabberProto::OnProcessContinue(const TiXmlElement *node, ThreadData *info) @@ -61,7 +112,7 @@ void CJabberProto::OnProcessContinue(const TiXmlElement *node, ThreadData *info) return; } - TJabberAuth *pTask = nullptr; + TUpgradeTask *pTask = nullptr; for (auto *task : TiXmlFilter(node->FirstChildElement("tasks"), "task")) for (auto &it : m_arSaslUpgrade) if (!mir_strcmp(it->getName(), task->GetText())) { @@ -75,9 +126,8 @@ void CJabberProto::OnProcessContinue(const TiXmlElement *node, ThreadData *info) return; } + pTask->setInitData(XmlGetChildText(node, "additional-data")); info->m_saslUpgrade = pTask; - if (auto *n = node->FirstChildElement("additional-data")) - info->saslInitData = mir_strdup(n->GetText()); XmlNode next("next"); next << XATTR("xmlns", JABBER_FEAT_SASL2) << XATTR("task", pTask->getName()); @@ -86,5 +136,13 @@ void CJabberProto::OnProcessContinue(const TiXmlElement *node, ThreadData *info) void CJabberProto::OnProcessTaskData(const TiXmlElement *node, ThreadData *info) { + if (!info->m_saslUpgrade) + return; + XmlNode reply("task-data"); + reply << XATTR("xmlns", JABBER_FEAT_SASL2); + if (info->m_saslUpgrade->perform(node, reply)) + info->send(reply); + else + info->send(""); // bye-bye } diff --git a/protocols/JabberG/src/jabber_thread.cpp b/protocols/JabberG/src/jabber_thread.cpp index d895c34ca0..77a702aec6 100644 --- a/protocols/JabberG/src/jabber_thread.cpp +++ b/protocols/JabberG/src/jabber_thread.cpp @@ -709,8 +709,8 @@ void CJabberProto::OnProcessFeatures(const TiXmlElement *node, ThreadData *info) // dunno why we need to handle that } } - else if (!mir_strcmp(c->Name(), "upgrade") && c->Attribute("xmlns", "urn:xmpp:sasl:upgrade:0")) - OnProcessUpgrade(c, info); + // else if (!mir_strcmp(c->Name(), "upgrade") && c->Attribute("xmlns", "urn:xmpp:sasl:upgrade:0")) + // OnProcessUpgrade(c, info); } } else if (!mir_strcmp(pszName, "session")) diff --git a/protocols/JabberG/src/stdafx.h b/protocols/JabberG/src/stdafx.h index 8bd9d976f9..fac757dfcd 100644 --- a/protocols/JabberG/src/stdafx.h +++ b/protocols/JabberG/src/stdafx.h @@ -383,8 +383,7 @@ struct ThreadData char* gssapiHostName; - class TJabberAuth *m_saslUpgrade; - ptrA saslInitData; + class TUpgradeTask *m_saslUpgrade; CJabberIqInfo *pPendingQuery; JabberCapsBits jabberServerCaps; -- cgit v1.2.3