From e1f73541ce8bb231fb2deb90e56dd7a89b1ff1fc Mon Sep 17 00:00:00 2001 From: George Hazan Date: Mon, 2 Dec 2024 19:50:18 +0300 Subject: code reorganizing & cleaning --- protocols/JabberG/jabber.vcxproj | 2 +- protocols/JabberG/jabber.vcxproj.filters | 6 +- protocols/JabberG/src/jabber_auth.cpp | 443 +++++++++++++++++++++++++++++++ protocols/JabberG/src/jabber_proto.cpp | 9 +- protocols/JabberG/src/jabber_proto.h | 44 ++- protocols/JabberG/src/jabber_sasl2.cpp | 90 +++++++ protocols/JabberG/src/jabber_secur.cpp | 442 ------------------------------ protocols/JabberG/src/jabber_secur.h | 123 --------- protocols/JabberG/src/jabber_thread.cpp | 41 +-- protocols/JabberG/src/stdafx.h | 5 +- 10 files changed, 615 insertions(+), 590 deletions(-) create mode 100644 protocols/JabberG/src/jabber_auth.cpp create mode 100644 protocols/JabberG/src/jabber_sasl2.cpp delete mode 100644 protocols/JabberG/src/jabber_secur.cpp delete mode 100644 protocols/JabberG/src/jabber_secur.h (limited to 'protocols') diff --git a/protocols/JabberG/jabber.vcxproj b/protocols/JabberG/jabber.vcxproj index ea5a4e6cd3..ff150b77fd 100644 --- a/protocols/JabberG/jabber.vcxproj +++ b/protocols/JabberG/jabber.vcxproj @@ -68,6 +68,7 @@ + @@ -100,7 +101,6 @@ - diff --git a/protocols/JabberG/jabber.vcxproj.filters b/protocols/JabberG/jabber.vcxproj.filters index 4bed895b71..e6f3fe9fe8 100644 --- a/protocols/JabberG/jabber.vcxproj.filters +++ b/protocols/JabberG/jabber.vcxproj.filters @@ -164,6 +164,9 @@ Source Files + + Source Files + @@ -211,9 +214,6 @@ Header Files - - Header Files - Header Files diff --git a/protocols/JabberG/src/jabber_auth.cpp b/protocols/JabberG/src/jabber_auth.cpp new file mode 100644 index 0000000000..239586d58f --- /dev/null +++ b/protocols/JabberG/src/jabber_auth.cpp @@ -0,0 +1,443 @@ +/* + +Jabber Protocol Plugin for Miranda NG + +Copyright (c) 2002-04 Santithorn Bunchua +Copyright (c) 2005-12 George Hazan +Copyright (C) 2012-24 Miranda NG team + +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. + +*/ + +#include "stdafx.h" + +class TPlainAuth : public TJabberAuth +{ + typedef TJabberAuth CSuper; + + bool bOld; + +public: + TPlainAuth(ThreadData *info, bool old) : + TJabberAuth(info, "PLAIN"), + bOld(old) + { + priority = (old) ? 100 : 101; + } + + char* getInitialRequest() override + { + CMStringA buf; + if (bOld) + buf.Format("%s@%s%c%s%c%s", info->conn.username, info->conn.server, 0, info->conn.username, 0, info->conn.password); + else + buf.Format("%c%s%c%s", 0, info->conn.username, 0, info->conn.password); + + return mir_base64_encode(buf, buf.GetLength()); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// ntlm auth - LanServer based authorization + +class TNtlmAuth : public TJabberAuth +{ + typedef TJabberAuth CSuper; + + HANDLE hProvider = 0; + ptrA szInitRequest; + +public: + TNtlmAuth(ThreadData *info, const char *mechanism) : + TJabberAuth(info, mechanism) + { + bIsValid = false; + + const wchar_t *szProvider; + if (!mir_strcmp(mechanism, "GSS-SPNEGO")) + szProvider = L"Negotiate", priority = 703; + else if (!mir_strcmp(mechanism, "GSSAPI")) + szProvider = L"Kerberos", priority = 702; + else if (!mir_strcmp(mechanism, "NTLM")) + szProvider = L"NTLM", priority = 701; + else + return; + + wchar_t szSpn[1024]; szSpn[0] = 0; + if (!mir_strcmp(mechanism, "GSSAPI")) + if (!getSpn(szSpn, _countof(szSpn))) + return; + + if ((hProvider = Netlib_InitSecurityProvider(szProvider, szSpn)) == nullptr) + return; + + // This generates login method advertisement packet + if (info->conn.password[0] != 0) + szInitRequest = Netlib_NtlmCreateResponse(hProvider, "", Utf2T(info->conn.username), Utf2T(info->conn.password), complete); + else + szInitRequest = Netlib_NtlmCreateResponse(hProvider, "", nullptr, nullptr, complete); + if (szInitRequest == nullptr) + return; + + bIsValid = true; + } + + ~TNtlmAuth() + { + if (hProvider != nullptr) + Netlib_DestroySecurityProvider(hProvider); + } + + char *getInitialRequest() override + { + return szInitRequest.detach(); + } + + char *getChallenge(const char *challenge) override + { + if (!hProvider) + return nullptr; + + const char *text((!mir_strcmp(challenge, "=")) ? "" : challenge); + if (info->conn.password[0] != 0) + return Netlib_NtlmCreateResponse(hProvider, text, Utf2T(info->conn.username), Utf2T(info->conn.password), complete); + + return Netlib_NtlmCreateResponse(hProvider, text, nullptr, nullptr, complete); + } + + bool getSpn(wchar_t *szSpn, size_t dwSpnLen) + { + wchar_t szFullUserName[128] = L""; + ULONG szFullUserNameLen = _countof(szFullUserName); + if (!GetUserNameEx(NameDnsDomain, szFullUserName, &szFullUserNameLen)) { + szFullUserName[0] = 0; + szFullUserNameLen = _countof(szFullUserName); + GetUserNameEx(NameSamCompatible, szFullUserName, &szFullUserNameLen); + } + + wchar_t *name = wcsrchr(szFullUserName, '\\'); + if (name) *name = 0; + else return false; + + if (info->gssapiHostName && info->gssapiHostName[0]) { + wchar_t *szFullUserNameU = wcsupr(mir_wstrdup(szFullUserName)); + mir_snwprintf(szSpn, dwSpnLen, L"xmpp/%s/%s@%s", info->gssapiHostName, szFullUserName, szFullUserNameU); + mir_free(szFullUserNameU); + } + else { + const char *connectHost = info->conn.manualHost[0] ? info->conn.manualHost : info->conn.server; + + unsigned long ip = inet_addr(connectHost); + PHOSTENT host = (ip == INADDR_NONE) ? nullptr : gethostbyaddr((char *)&ip, 4, AF_INET); + if (host && host->h_name) + connectHost = host->h_name; + + wchar_t *connectHostT = mir_a2u(connectHost); + mir_snwprintf(szSpn, dwSpnLen, L"xmpp/%s@%s", connectHostT, wcsupr(szFullUserName)); + mir_free(connectHostT); + } + + Netlib_Logf(nullptr, "SPN: %S", szSpn); + return true; + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// md5 auth - digest-based authorization + +class TMD5Auth : public TJabberAuth +{ + typedef TJabberAuth CSuper; + + int iCallCount = 0; + +public: + TMD5Auth(ThreadData *info) : + TJabberAuth(info, "DIGEST-MD5") + { + priority = 301; + } + + char *getChallenge(const char *challenge) override + { + if (iCallCount > 0) + return nullptr; + + iCallCount++; + + size_t resultLen; + ptrA text((char *)mir_base64_decode(challenge, &resultLen)); + + TStringPairs pairs(text); + const char *realm = pairs["realm"], *nonce = pairs["nonce"]; + + char cnonce[40], tmpBuf[40]; + uint32_t digest[4], hash1[4], hash2[4]; + mir_md5_state_t ctx; + + Utils_GetRandom(digest, sizeof(digest)); + mir_snprintf(cnonce, "%08x%08x%08x%08x", htonl(digest[0]), htonl(digest[1]), htonl(digest[2]), htonl(digest[3])); + + ptrA serv(mir_utf8encode(info->conn.server)); + + mir_md5_init(&ctx); + mir_md5_append(&ctx, (uint8_t *)info->conn.username, (int)mir_strlen(info->conn.username)); + mir_md5_append(&ctx, (uint8_t *)":", 1); + mir_md5_append(&ctx, (uint8_t *)realm, (int)mir_strlen(realm)); + mir_md5_append(&ctx, (uint8_t *)":", 1); + mir_md5_append(&ctx, (uint8_t *)info->conn.password, (int)mir_strlen(info->conn.password)); + mir_md5_finish(&ctx, (uint8_t *)hash1); + + mir_md5_init(&ctx); + mir_md5_append(&ctx, (uint8_t *)hash1, 16); + mir_md5_append(&ctx, (uint8_t *)":", 1); + mir_md5_append(&ctx, (uint8_t *)nonce, (int)mir_strlen(nonce)); + mir_md5_append(&ctx, (uint8_t *)":", 1); + mir_md5_append(&ctx, (uint8_t *)cnonce, (int)mir_strlen(cnonce)); + mir_md5_finish(&ctx, (uint8_t *)hash1); + + mir_md5_init(&ctx); + mir_md5_append(&ctx, (uint8_t *)"AUTHENTICATE:xmpp/", 18); + mir_md5_append(&ctx, (uint8_t *)(char *)serv, (int)mir_strlen(serv)); + mir_md5_finish(&ctx, (uint8_t *)hash2); + + mir_md5_init(&ctx); + mir_snprintf(tmpBuf, "%08x%08x%08x%08x", htonl(hash1[0]), htonl(hash1[1]), htonl(hash1[2]), htonl(hash1[3])); + mir_md5_append(&ctx, (uint8_t *)tmpBuf, (int)mir_strlen(tmpBuf)); + mir_md5_append(&ctx, (uint8_t *)":", 1); + mir_md5_append(&ctx, (uint8_t *)nonce, (int)mir_strlen(nonce)); + mir_snprintf(tmpBuf, ":%08d:", iCallCount); + mir_md5_append(&ctx, (uint8_t *)tmpBuf, (int)mir_strlen(tmpBuf)); + mir_md5_append(&ctx, (uint8_t *)cnonce, (int)mir_strlen(cnonce)); + mir_md5_append(&ctx, (uint8_t *)":auth:", 6); + mir_snprintf(tmpBuf, "%08x%08x%08x%08x", htonl(hash2[0]), htonl(hash2[1]), htonl(hash2[2]), htonl(hash2[3])); + mir_md5_append(&ctx, (uint8_t *)tmpBuf, (int)mir_strlen(tmpBuf)); + mir_md5_finish(&ctx, (uint8_t *)digest); + + char *buf = (char *)alloca(8000); + int cbLen = mir_snprintf(buf, 8000, + "username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\",nc=%08d," + "qop=auth,digest-uri=\"xmpp/%s\",charset=utf-8,response=%08x%08x%08x%08x", + info->conn.username, realm, nonce, cnonce, iCallCount, serv.get(), + htonl(digest[0]), htonl(digest[1]), htonl(digest[2]), htonl(digest[3])); + + return mir_base64_encode(buf, cbLen); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// SCRAM-SHA-1 authorization + +class TScramAuth : public TJabberAuth +{ + typedef TJabberAuth CSuper; + + char *bindFlag, *cnonce = 0, *msg1 = 0, *serverSignature = 0; + MBinBuffer bindData; + const EVP_MD *hashMethod; + +public: + TScramAuth(ThreadData *info, const char *pszMech, const EVP_MD *pMethod, int iPriority) : + TJabberAuth(info, pszMech), + hashMethod(pMethod) + { + priority = iPriority; + } + + ~TScramAuth() + { + mir_free(cnonce); + mir_free(msg1); + mir_free(serverSignature); + } + + char* getInitialRequest() override + { + unsigned char nonce[24]; + Utils_GetRandom(nonce, sizeof(nonce)); + cnonce = mir_base64_encode(nonce, sizeof(nonce)); + + bindFlag = "n,,"; + if ((priority % 10) == 1) { + if (info->proto->m_bTlsExporter) { + int cbLen, tlsVer = true; + void *pData = Netlib_GetTlsUnique(info->s, cbLen, tlsVer); + if (pData == nullptr) + return nullptr; + + bindFlag = (tlsVer == 13) ? "p=tls-exporter,," : "p=tls-unique,,"; + bindData.append(pData, cbLen); + } + } + + CMStringA buf(FORMAT, "n=%s,r=%s", info->conn.username, cnonce); + msg1 = mir_strdup(buf); + + buf.Insert(0, bindFlag); + return mir_base64_encode(buf, buf.GetLength()); + } + + char* getChallenge(const char *challenge) override + { + size_t chlLen, saltLen = 0; + ptrA snonce, salt; + int ind = -1; + + ptrA chl((char *)mir_base64_decode(challenge, &chlLen)), cbd; + if (bindData.isEmpty()) + cbd = mir_base64_encode(bindFlag, mir_strlen(bindFlag)); + else { + bindData.appendBefore((void *)bindFlag, mir_strlen(bindFlag)); + cbd = mir_base64_encode(bindData.data(), bindData.length()); + } + + for (char *p = strtok(NEWSTR_ALLOCA(chl), ","); p != nullptr; p = strtok(nullptr, ",")) { + if (*p == 'r' && p[1] == '=') { // snonce + if (strncmp(cnonce, p + 2, mir_strlen(cnonce))) + return nullptr; + snonce = mir_strdup(p + 2); + } + 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); + } + + if (snonce == nullptr || salt == nullptr || ind == -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); + + 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(), snonce.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]; + + /* Calculate the server signature */ + uint8_t serverKey[EVP_MAX_MD_SIZE]; + HMAC(hashMethod, saltedPassw, hashSize, (uint8_t *)"Server Key", 10, serverKey, &len); + + uint8_t srvSig[EVP_MAX_MD_SIZE]; + HMAC(hashMethod, serverKey, hashSize, (uint8_t *)authmsg.c_str(), authmsg.GetLength(), srvSig, &len); + serverSignature = mir_base64_encode(srvSig, hashSize); + + ptrA encproof(mir_base64_encode(clientProof, hashSize)); + CMStringA buf(FORMAT, "c=%s,r=%s,p=%s", cbd.get(), snonce.get(), encproof.get()); + return mir_base64_encode(buf, buf.GetLength()); + } + + bool validateLogin(const char *challenge) override + { + size_t chlLen; + 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]; + } + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// module entry point + +bool CJabberProto::OnProcessMechanism(const TiXmlElement *n, ThreadData *info) +{ + if (!mir_strcmp(n->Name(), "mechanism")) { + TJabberAuth *pAuth = nullptr; + auto *szMechanism = n->GetText(); + if (!mir_strcmp(szMechanism, "PLAIN")) { + m_arAuthMechs.insert(new TPlainAuth(info, false)); + pAuth = new TPlainAuth(info, true); + } + else if (!mir_strcmp(szMechanism, "DIGEST-MD5")) + pAuth = new TMD5Auth(info); + else if (!mir_strcmp(szMechanism, "SCRAM-SHA-1")) + pAuth = new TScramAuth(info, szMechanism, EVP_sha1(), 500); + else if (!mir_strcmp(szMechanism, "SCRAM-SHA-1-PLUS")) + pAuth = new TScramAuth(info, szMechanism, EVP_sha1(), 601); + else if (!mir_strcmp(szMechanism, "SCRAM-SHA-224")) + pAuth = new TScramAuth(info, szMechanism, EVP_sha224(), 510); + else if (!mir_strcmp(szMechanism, "SCRAM-SHA-224-PLUS")) + pAuth = new TScramAuth(info, szMechanism, EVP_sha224(), 611); + else if (!mir_strcmp(szMechanism, "SCRAM-SHA-256")) + pAuth = new TScramAuth(info, szMechanism, EVP_sha256(), 520); + else if (!mir_strcmp(szMechanism, "SCRAM-SHA-256-PLUS")) + pAuth = new TScramAuth(info, szMechanism, EVP_sha256(), 621); + else if (!mir_strcmp(szMechanism, "SCRAM-SHA-384")) + pAuth = new TScramAuth(info, szMechanism, EVP_sha384(), 530); + else if (!mir_strcmp(szMechanism, "SCRAM-SHA-384-PLUS")) + pAuth = new TScramAuth(info, szMechanism, EVP_sha384(), 631); + else if (!mir_strcmp(szMechanism, "SCRAM-SHA-512")) + pAuth = new TScramAuth(info, szMechanism, EVP_sha512(), 540); + else if (!mir_strcmp(szMechanism, "SCRAM-SHA-512-PLUS")) + pAuth = new TScramAuth(info, szMechanism, EVP_sha512(), 641); + else if (!mir_strcmp(szMechanism, "NTLM") || !mir_strcmp(szMechanism, "GSS-SPNEGO") || !mir_strcmp(szMechanism, "GSSAPI")) + pAuth = new TNtlmAuth(info, szMechanism); + else { + debugLogA("Unsupported auth mechanism: %s, skipping", szMechanism); + return true; + } + + if (!pAuth->isValid()) + delete pAuth; + else + m_arAuthMechs.insert(pAuth); + return true; + } + + if (!mir_strcmp(n->Name(), "hostname")) { + const char *mech = XmlGetAttr(n, "mechanism"); + if (mech && mir_strcmpi(mech, "GSSAPI") == 0) + info->gssapiHostName = mir_strdup(n->GetText()); + return true; + } + + return false; +} diff --git a/protocols/JabberG/src/jabber_proto.cpp b/protocols/JabberG/src/jabber_proto.cpp index fbb772ac56..0ec19dfc69 100644 --- a/protocols/JabberG/src/jabber_proto.cpp +++ b/protocols/JabberG/src/jabber_proto.cpp @@ -27,7 +27,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "jabber_iq.h" #include "jabber_caps.h" #include "jabber_disco.h" -#include "jabber_secur.h" #pragma warning(disable:4355) @@ -53,12 +52,18 @@ static int compareListItems(const JABBER_LIST_ITEM *p1, const JABBER_LIST_ITEM * return mir_strcmpi(szp1, szp2); } +static int compareAuth(const TJabberAuth *p1, const TJabberAuth *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, &TJabberAuth::compare), + m_arAuthMechs(1, compareAuth), + m_arSaslUpgrade(1, compareAuth), 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 83d13b8797..8ec6d498be 100644 --- a/protocols/JabberG/src/jabber_proto.h +++ b/protocols/JabberG/src/jabber_proto.h @@ -74,7 +74,39 @@ struct CChatMark CMStringA szId, szFrom; }; +// basic class - provides interface for various Jabber auth +class TJabberAuth : public MZeroedObject +{ +protected: bool bIsValid = true; + ptrA szName; + unsigned complete; + int priority; + ThreadData *info; +public: + TJabberAuth(ThreadData *pInfo, const char *pszMech) : + info(pInfo), + szName(mir_strdup(pszMech)) + {} + + virtual ~TJabberAuth() {} + + virtual char* getInitialRequest() { return nullptr; } + virtual char* getChallenge(const char*) { return nullptr; } + virtual bool validateLogin(const char*) { return true; } + + __forceinline int getPriority() const { + return priority; + } + + __forceinline const char *getName() const { + return szName; + } + + __forceinline bool isValid() const { + return bIsValid; + } +}; struct CJabberProto : public PROTO, public IJabberInterface { @@ -827,6 +859,14 @@ struct CJabberProto : public PROTO, public IJabberInterface void SearchDeleteFromRecent(const char *szAddr, bool deleteLastFromDB); void SearchAddToRecent(const char *szAddr, HWND hwndDialog = nullptr); + //---- jabber_secur.cpp -------------------------------------------------------------- + + OBJLIST m_arSaslUpgrade; + OBJLIST m_arAuthMechs; + + bool OnProcessMechanism(const TiXmlElement *node, ThreadData *info); + void OnProcessUpgrade(const TiXmlElement *node, ThreadData *info); + //---- jabber_svc.c ------------------------------------------------------------------ void CheckMenuItems(); @@ -856,7 +896,6 @@ struct CJabberProto : public PROTO, public IJabberInterface ptrA m_szGroupDelimiter; ptrW m_savedPassword; - OBJLIST m_arAuthMechs; bool m_hasSession, m_hasAuth, m_hasSasl2; void __cdecl ServerThread(JABBER_CONN_DATA *info); @@ -868,6 +907,8 @@ struct CJabberProto : public PROTO, public IJabberInterface void OnProcessError(const TiXmlElement *node, ThreadData *info); void OnProcessSuccess(const TiXmlElement *node, ThreadData *info); void OnProcessChallenge(const TiXmlElement *node, ThreadData *info); + void OnProcessContinue(const TiXmlElement *node, ThreadData *info); + void OnProcessTaskData(const TiXmlElement *node, ThreadData *info); void OnProcessProceed(const TiXmlElement *node, ThreadData *info); void OnProcessCompressed(const TiXmlElement *node, ThreadData *info); //message processing helpers @@ -884,7 +925,6 @@ struct CJabberProto : public PROTO, public IJabberInterface void PerformRegistration(ThreadData *info); void PerformIqAuth(ThreadData *info); void PerformAuthentication(ThreadData *info); - bool OnProcessMechanism(const TiXmlElement *node, ThreadData *info); void OnProcessFeatures(const TiXmlElement *node, ThreadData *info); void xmlStreamInitialize(char *which); diff --git a/protocols/JabberG/src/jabber_sasl2.cpp b/protocols/JabberG/src/jabber_sasl2.cpp new file mode 100644 index 0000000000..5784bf8f3e --- /dev/null +++ b/protocols/JabberG/src/jabber_sasl2.cpp @@ -0,0 +1,90 @@ +/* +Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org) + +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 version 2 +of the License. + +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, see . +*/ + +#include "stdafx.h" + +void CJabberProto::OnProcessUpgrade(const TiXmlElement *n, ThreadData *info) +{ + /* + TJabberAuth *pAuth = nullptr; + 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); + 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); + 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); + 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) { + debugLogA("Unsupported mechanism for upgrade: %s, skipping", szMechanism); + return; + } + + if (!pAuth->isValid()) + delete pAuth; + else + m_arSaslUpgrade.insert(pAuth);*/ +} + +void CJabberProto::OnProcessContinue(const TiXmlElement *node, ThreadData *info) +{ + if (!node->Attribute("xmlns", JABBER_FEAT_SASL2)) { + debugLogA("Missing xmlns for continue, ignoring"); + return; + } + + TJabberAuth *pTask = nullptr; + for (auto *task : TiXmlFilter(node->FirstChildElement("tasks"), "task")) + for (auto &it : m_arSaslUpgrade) + if (!mir_strcmp(it->getName(), task->GetText())) { + pTask = it; + break; + } + + if (!pTask) { + debugLogA("Unsupported task type, ignoring"); + info->send(""); // bye-bye + return; + } + + 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()); + info->send(next); +} + +void CJabberProto::OnProcessTaskData(const TiXmlElement *node, ThreadData *info) +{ + +} diff --git a/protocols/JabberG/src/jabber_secur.cpp b/protocols/JabberG/src/jabber_secur.cpp deleted file mode 100644 index 2b52830960..0000000000 --- a/protocols/JabberG/src/jabber_secur.cpp +++ /dev/null @@ -1,442 +0,0 @@ -/* - -Jabber Protocol Plugin for Miranda NG - -Copyright (c) 2002-04 Santithorn Bunchua -Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-24 Miranda NG team - -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. - -*/ - -#include "stdafx.h" -#include "jabber_secur.h" - -bool CJabberProto::OnProcessMechanism(const TiXmlElement *n, ThreadData *info) -{ - if (!mir_strcmp(n->Name(), "mechanism")) { - TJabberAuth *pAuth = nullptr; - const char *szMechanism = n->GetText(); - if (!mir_strcmp(szMechanism, "PLAIN")) { - m_arAuthMechs.insert(new TPlainAuth(info, false)); - pAuth = new TPlainAuth(info, true); - } - else if (!mir_strcmp(szMechanism, "DIGEST-MD5")) - pAuth = new TMD5Auth(info); - else if (!mir_strcmp(szMechanism, "SCRAM-SHA-1")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha1(), 500); - else if (!mir_strcmp(szMechanism, "SCRAM-SHA-1-PLUS")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha1(), 601); - else if (!mir_strcmp(szMechanism, "SCRAM-SHA-224")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha224(), 510); - else if (!mir_strcmp(szMechanism, "SCRAM-SHA-224-PLUS")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha224(), 611); - else if (!mir_strcmp(szMechanism, "SCRAM-SHA-256")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha256(), 520); - else if (!mir_strcmp(szMechanism, "SCRAM-SHA-256-PLUS")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha256(), 621); - else if (!mir_strcmp(szMechanism, "SCRAM-SHA-384")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha384(), 530); - else if (!mir_strcmp(szMechanism, "SCRAM-SHA-384-PLUS")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha384(), 631); - else if (!mir_strcmp(szMechanism, "SCRAM-SHA-512")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha512(), 540); - else if (!mir_strcmp(szMechanism, "SCRAM-SHA-512-PLUS")) - pAuth = new TScramAuth(info, szMechanism, EVP_sha512(), 641); - else if (!mir_strcmp(szMechanism, "NTLM") || !mir_strcmp(szMechanism, "GSS-SPNEGO") || !mir_strcmp(szMechanism, "GSSAPI")) - pAuth = new TNtlmAuth(info, szMechanism); - else { - debugLogA("Unsupported auth mechanism: %s, skipping", szMechanism); - return true; - } - - if (!pAuth->isValid()) - delete pAuth; - else - m_arAuthMechs.insert(pAuth); - return true; - } - - if (!mir_strcmp(n->Name(), "hostname")) { - const char *mech = XmlGetAttr(n, "mechanism"); - if (mech && mir_strcmpi(mech, "GSSAPI") == 0) - info->gssapiHostName = mir_strdup(n->GetText()); - return true; - } - - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// ntlm auth - LanServer based authorization - -TNtlmAuth::TNtlmAuth(ThreadData *info, const char *mechanism) : - TJabberAuth(info, mechanism) -{ - bIsValid = false; - - const wchar_t *szProvider; - if (!mir_strcmp(mechanism, "GSS-SPNEGO")) - szProvider = L"Negotiate", priority = 703; - else if (!mir_strcmp(mechanism, "GSSAPI")) - szProvider = L"Kerberos", priority = 702; - else if (!mir_strcmp(mechanism, "NTLM")) - szProvider = L"NTLM", priority = 701; - else - return; - - wchar_t szSpn[1024]; szSpn[0] = 0; - if (!mir_strcmp(mechanism, "GSSAPI")) - if (!getSpn(szSpn, _countof(szSpn))) - return; - - if ((hProvider = Netlib_InitSecurityProvider(szProvider, szSpn)) == nullptr) - return; - - // This generates login method advertisement packet - if (info->conn.password[0] != 0) - szInitRequest = Netlib_NtlmCreateResponse(hProvider, "", Utf2T(info->conn.username), Utf2T(info->conn.password), complete); - else - szInitRequest = Netlib_NtlmCreateResponse(hProvider, "", nullptr, nullptr, complete); - if (szInitRequest == nullptr) - return; - - bIsValid = true; -} - -TNtlmAuth::~TNtlmAuth() -{ - if (hProvider != nullptr) - Netlib_DestroySecurityProvider(hProvider); -} - -bool TNtlmAuth::getSpn(wchar_t* szSpn, size_t dwSpnLen) -{ - wchar_t szFullUserName[128] = L""; - ULONG szFullUserNameLen = _countof(szFullUserName); - if (!GetUserNameEx(NameDnsDomain, szFullUserName, &szFullUserNameLen)) { - szFullUserName[0] = 0; - szFullUserNameLen = _countof(szFullUserName); - GetUserNameEx(NameSamCompatible, szFullUserName, &szFullUserNameLen); - } - - wchar_t *name = wcsrchr(szFullUserName, '\\'); - if (name) *name = 0; - else return false; - - if (info->gssapiHostName && info->gssapiHostName[0]) { - wchar_t *szFullUserNameU = wcsupr(mir_wstrdup(szFullUserName)); - mir_snwprintf(szSpn, dwSpnLen, L"xmpp/%s/%s@%s", info->gssapiHostName, szFullUserName, szFullUserNameU); - mir_free(szFullUserNameU); - } - else { - const char* connectHost = info->conn.manualHost[0] ? info->conn.manualHost : info->conn.server; - - unsigned long ip = inet_addr(connectHost); - PHOSTENT host = (ip == INADDR_NONE) ? nullptr : gethostbyaddr((char*)&ip, 4, AF_INET); - if (host && host->h_name) - connectHost = host->h_name; - - wchar_t *connectHostT = mir_a2u(connectHost); - mir_snwprintf(szSpn, dwSpnLen, L"xmpp/%s@%s", connectHostT, wcsupr(szFullUserName)); - mir_free(connectHostT); - } - - Netlib_Logf(nullptr, "SPN: %S", szSpn); - return true; -} - -char* TNtlmAuth::getInitialRequest() -{ - return szInitRequest.detach(); -} - -char* TNtlmAuth::getChallenge(const char *challenge) -{ - if (!hProvider) - return nullptr; - - const char *text((!mir_strcmp(challenge, "=")) ? "" : challenge); - if (info->conn.password[0] != 0) - return Netlib_NtlmCreateResponse(hProvider, text, Utf2T(info->conn.username), Utf2T(info->conn.password), complete); - - return Netlib_NtlmCreateResponse(hProvider, text, nullptr, nullptr, complete); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// md5 auth - digest-based authorization - -TMD5Auth::TMD5Auth(ThreadData *info) : - TJabberAuth(info, "DIGEST-MD5"), - iCallCount(0) -{ - priority = 301; -} - -TMD5Auth::~TMD5Auth() -{ -} - -char* TMD5Auth::getChallenge(const char *challenge) -{ - if (iCallCount > 0) - return nullptr; - - iCallCount++; - - size_t resultLen; - ptrA text((char*)mir_base64_decode(challenge, &resultLen)); - - TStringPairs pairs(text); - const char *realm = pairs["realm"], *nonce = pairs["nonce"]; - - char cnonce[40], tmpBuf[40]; - uint32_t digest[4], hash1[4], hash2[4]; - mir_md5_state_t ctx; - - Utils_GetRandom(digest, sizeof(digest)); - mir_snprintf(cnonce, "%08x%08x%08x%08x", htonl(digest[0]), htonl(digest[1]), htonl(digest[2]), htonl(digest[3])); - - ptrA serv(mir_utf8encode(info->conn.server)); - - mir_md5_init(&ctx); - mir_md5_append(&ctx, (uint8_t*)info->conn.username, (int)mir_strlen(info->conn.username)); - mir_md5_append(&ctx, (uint8_t*)":", 1); - mir_md5_append(&ctx, (uint8_t*)realm, (int)mir_strlen(realm)); - mir_md5_append(&ctx, (uint8_t*)":", 1); - mir_md5_append(&ctx, (uint8_t*)info->conn.password, (int)mir_strlen(info->conn.password)); - mir_md5_finish(&ctx, (uint8_t*)hash1); - - mir_md5_init(&ctx); - mir_md5_append(&ctx, (uint8_t*)hash1, 16); - mir_md5_append(&ctx, (uint8_t*)":", 1); - mir_md5_append(&ctx, (uint8_t*)nonce, (int)mir_strlen(nonce)); - mir_md5_append(&ctx, (uint8_t*)":", 1); - mir_md5_append(&ctx, (uint8_t*)cnonce, (int)mir_strlen(cnonce)); - mir_md5_finish(&ctx, (uint8_t*)hash1); - - mir_md5_init(&ctx); - mir_md5_append(&ctx, (uint8_t*)"AUTHENTICATE:xmpp/", 18); - mir_md5_append(&ctx, (uint8_t*)(char*)serv, (int)mir_strlen(serv)); - mir_md5_finish(&ctx, (uint8_t*)hash2); - - mir_md5_init(&ctx); - mir_snprintf(tmpBuf, "%08x%08x%08x%08x", htonl(hash1[0]), htonl(hash1[1]), htonl(hash1[2]), htonl(hash1[3])); - mir_md5_append(&ctx, (uint8_t*)tmpBuf, (int)mir_strlen(tmpBuf)); - mir_md5_append(&ctx, (uint8_t*)":", 1); - mir_md5_append(&ctx, (uint8_t*)nonce, (int)mir_strlen(nonce)); - mir_snprintf(tmpBuf, ":%08d:", iCallCount); - mir_md5_append(&ctx, (uint8_t*)tmpBuf, (int)mir_strlen(tmpBuf)); - mir_md5_append(&ctx, (uint8_t*)cnonce, (int)mir_strlen(cnonce)); - mir_md5_append(&ctx, (uint8_t*)":auth:", 6); - mir_snprintf(tmpBuf, "%08x%08x%08x%08x", htonl(hash2[0]), htonl(hash2[1]), htonl(hash2[2]), htonl(hash2[3])); - mir_md5_append(&ctx, (uint8_t*)tmpBuf, (int)mir_strlen(tmpBuf)); - mir_md5_finish(&ctx, (uint8_t*)digest); - - char *buf = (char*)alloca(8000); - int cbLen = mir_snprintf(buf, 8000, - "username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\",nc=%08d," - "qop=auth,digest-uri=\"xmpp/%s\",charset=utf-8,response=%08x%08x%08x%08x", - info->conn.username, realm, nonce, cnonce, iCallCount, serv.get(), - htonl(digest[0]), htonl(digest[1]), htonl(digest[2]), htonl(digest[3])); - - return mir_base64_encode(buf, cbLen); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// SCRAM-SHA-1 authorization - -TScramAuth::TScramAuth(ThreadData *info, const char *pszMech, const EVP_MD *pMethod, int iPriority) : - TJabberAuth(info, pszMech), - hashMethod(pMethod) -{ - priority = iPriority; -} - -TScramAuth::~TScramAuth() -{ - mir_free(cnonce); - mir_free(msg1); - mir_free(serverSignature); -} - -void TScramAuth::Hi(uint8_t *res, char *passw, size_t passwLen, char *salt, size_t saltLen, int ind) -{ - 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 < ind; 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]; - } -} - -char* TScramAuth::getInitialRequest() -{ - unsigned char nonce[24]; - Utils_GetRandom(nonce, sizeof(nonce)); - cnonce = mir_base64_encode(nonce, sizeof(nonce)); - - bindFlag = "n,,"; - if ((priority % 10) == 1) { - if (info->proto->m_bTlsExporter) { - int cbLen, tlsVer = true; - void *pData = Netlib_GetTlsUnique(info->s, cbLen, tlsVer); - if (pData == nullptr) - return nullptr; - - bindFlag = (tlsVer == 13) ? "p=tls-exporter,," : "p=tls-unique,,"; - bindData.append(pData, cbLen); - } - } - - CMStringA buf(FORMAT, "n=%s,r=%s", info->conn.username, cnonce); - msg1 = mir_strdup(buf); - - buf.Insert(0, bindFlag); - return mir_base64_encode(buf, buf.GetLength()); -} - -char* TScramAuth::getChallenge(const char *challenge) -{ - size_t chlLen, saltLen = 0; - ptrA snonce, salt; - int ind = -1; - - ptrA chl((char *)mir_base64_decode(challenge, &chlLen)), cbd; - if (bindData.isEmpty()) - cbd = mir_base64_encode(bindFlag, mir_strlen(bindFlag)); - else { - bindData.appendBefore((void*)bindFlag, mir_strlen(bindFlag)); - cbd = mir_base64_encode(bindData.data(), bindData.length()); - } - - for (char *p = strtok(NEWSTR_ALLOCA(chl), ","); p != nullptr; p = strtok(nullptr, ",")) { - if (*p == 'r' && p[1] == '=') { // snonce - if (strncmp(cnonce, p + 2, mir_strlen(cnonce))) - return nullptr; - snonce = mir_strdup(p + 2); - } - 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); - } - - if (snonce == nullptr || salt == nullptr || ind == -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); - - 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(), snonce.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]; - - /* Calculate the server signature */ - uint8_t serverKey[EVP_MAX_MD_SIZE]; - HMAC(hashMethod, saltedPassw, hashSize, (uint8_t*)"Server Key", 10, serverKey, &len); - - uint8_t srvSig[EVP_MAX_MD_SIZE]; - HMAC(hashMethod, serverKey, hashSize, (uint8_t*)authmsg.c_str(), authmsg.GetLength(), srvSig, &len); - serverSignature = mir_base64_encode(srvSig, hashSize); - - ptrA encproof(mir_base64_encode(clientProof, hashSize)); - CMStringA buf(FORMAT, "c=%s,r=%s,p=%s", cbd.get(), snonce.get(), encproof.get()); - return mir_base64_encode(buf, buf.GetLength()); -} - -bool TScramAuth::validateLogin(const char *challenge) -{ - size_t chlLen; - ptrA chl((char*)mir_base64_decode(challenge, &chlLen)); - return chl && strncmp((char*)chl + 2, serverSignature, chlLen - 2) == 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// plain auth - the most simple one - -TPlainAuth::TPlainAuth(ThreadData *info, bool old) : - TJabberAuth(info, "PLAIN"), - bOld(old) -{ - priority = (old) ? 100 : 101; -} - -char* TPlainAuth::getInitialRequest() -{ - CMStringA buf; - if (bOld) - buf.Format("%s@%s%c%s%c%s", info->conn.username, info->conn.server, 0, info->conn.username, 0, info->conn.password); - else - buf.Format("%c%s%c%s", 0, info->conn.username, 0, info->conn.password); - - return mir_base64_encode(buf, buf.GetLength()); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// basic type - -TJabberAuth::TJabberAuth(ThreadData *pInfo, const char *pszMech) : - info(pInfo), - szName(mir_strdup(pszMech)) -{ -} - -TJabberAuth::~TJabberAuth() -{ -} - -char* TJabberAuth::getInitialRequest() -{ - return nullptr; -} - -char* TJabberAuth::getChallenge(const char*) -{ - return nullptr; -} - -bool TJabberAuth::validateLogin(const char*) -{ - return true; -} diff --git a/protocols/JabberG/src/jabber_secur.h b/protocols/JabberG/src/jabber_secur.h deleted file mode 100644 index 0a9a8f843c..0000000000 --- a/protocols/JabberG/src/jabber_secur.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - -Jabber Protocol Plugin for Miranda NG - -Copyright (c) 2002-04 Santithorn Bunchua -Copyright (c) 2005-12 George Hazan -Copyright (C) 2012-24 Miranda NG team - -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. - -*/ - -#pragma once - -#include "stdafx.h" - -// basic class - provides interface for various Jabber auth - -class TJabberAuth : public MZeroedObject -{ -protected: bool bIsValid = true; - ptrA szName; - unsigned complete; - int priority; - ThreadData *info; -public: - TJabberAuth(ThreadData *pInfo, const char *pszMech); - virtual ~TJabberAuth(); - - virtual char* getInitialRequest(); - virtual char* getChallenge(const char *challenge); - virtual bool validateLogin(const char *challenge); - - static int compare(const TJabberAuth *p1, const TJabberAuth *p2) - { return p2->priority - p1->priority; // reverse sorting order - } - - inline const char* getName() const - { return szName; - } - - inline bool isValid() const - { return bIsValid; - } -}; - -// plain auth - the most simple one - -class TPlainAuth : public TJabberAuth -{ - typedef TJabberAuth CSuper; - - bool bOld; - -public: - TPlainAuth(ThreadData*, bool); - - char* getInitialRequest() override; -}; - -// md5 auth - digest-based authorization - -class TMD5Auth : public TJabberAuth -{ - typedef TJabberAuth CSuper; - - int iCallCount; -public: - TMD5Auth(ThreadData*); - ~TMD5Auth(); - - char* getChallenge(const char *challenge) override; -}; - -class TScramAuth : public TJabberAuth -{ - typedef TJabberAuth CSuper; - - char *bindFlag, *cnonce = 0, *msg1 = 0, *serverSignature = 0; - MBinBuffer bindData; - const EVP_MD *hashMethod; - -public: - TScramAuth(ThreadData *pInfo, const char *pszMech, const EVP_MD *pMethod, int priority); - ~TScramAuth(); - - char* getInitialRequest() override; - char* getChallenge(const char *challenge) override; - bool validateLogin(const char *challenge) override; - - void Hi(uint8_t* res , char* passw, size_t passwLen, char* salt, size_t saltLen, int ind); -}; - -// ntlm auth - LanServer based authorization - -class TNtlmAuth : public TJabberAuth -{ - typedef TJabberAuth CSuper; - - HANDLE hProvider; - ptrA szInitRequest; - -public: - TNtlmAuth(ThreadData*, const char* mechanism); - ~TNtlmAuth(); - - char* getInitialRequest() override; - char* getChallenge(const char *challenge) override; - - bool getSpn(wchar_t* szSpn, size_t dwSpnLen); -}; diff --git a/protocols/JabberG/src/jabber_thread.cpp b/protocols/JabberG/src/jabber_thread.cpp index b43afa84c0..d895c34ca0 100644 --- a/protocols/JabberG/src/jabber_thread.cpp +++ b/protocols/JabberG/src/jabber_thread.cpp @@ -29,7 +29,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "jabber_list.h" #include "jabber_iq.h" -#include "jabber_secur.h" #include "jabber_caps.h" #include "jabber_privacy.h" #include "jabber_rc.h" @@ -639,6 +638,8 @@ void CJabberProto::PerformAuthentication(ThreadData *info) if (m_hasSasl2) { XmlNode node("authenticate"); + for (auto &it : m_arSaslUpgrade) + node << XCHILD("upgrade", it->getName()) << XATTR("xmlns", "urn:xmpp:sasl:upgrade:0"); node << XATTR("xmlns", JABBER_FEAT_SASL2) << XATTR("mechanism", auth.getName()) << XCHILD("initial-response", auth.getInitialRequest()); info->send(node); } @@ -693,6 +694,7 @@ void CJabberProto::OnProcessFeatures(const TiXmlElement *node, ThreadData *info) else if (!mir_strcmp(pszName, "authentication") && !mir_strcmp(xmlns, JABBER_FEAT_SASL2) && m_bEnableSasl2) { m_hasSasl2 = areMechanismsDefined = true; m_arAuthMechs.destroy(); + m_arSaslUpgrade.destroy(); replaceStr(info->gssapiHostName, nullptr); for (auto *c : TiXmlEnum(n)) { @@ -707,6 +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(pszName, "session")) @@ -867,40 +871,45 @@ void CJabberProto::OnProcessProtocol(const TiXmlElement *node, ThreadData *info) if (m_StrmMgmt.HandleIncommingNode(node)) return; - if (!mir_strcmp(node->Name(), "proceed")) + auto *pszName = node->Name(); + if (!mir_strcmp(pszName, "proceed")) OnProcessProceed(node, info); - else if (!mir_strcmp(node->Name(), "compressed")) + else if (!mir_strcmp(pszName, "compressed")) OnProcessCompressed(node, info); - else if (!mir_strcmp(node->Name(), "stream:features")) + else if (!mir_strcmp(pszName, "stream:features")) OnProcessFeatures(node, info); - else if (!mir_strcmp(node->Name(), "stream:stream")) + else if (!mir_strcmp(pszName, "stream:stream")) OnProcessStreamOpening(node, info); - else if (!mir_strcmp(node->Name(), "success")) + else if (!mir_strcmp(pszName, "success")) OnProcessSuccess(node, info); - else if (!mir_strcmp(node->Name(), "failure")) + else if (!mir_strcmp(pszName, "failure")) OnProcessFailure(node, info); - else if (!mir_strcmp(node->Name(), "stream:error")) + else if (!mir_strcmp(pszName, "continue")) + OnProcessContinue(node, info); + else if (!mir_strcmp(pszName, "task-data")) + OnProcessTaskData(node, info); + else if (!mir_strcmp(pszName, "stream:error")) OnProcessError(node, info); - else if (!mir_strcmp(node->Name(), "challenge")) + else if (!mir_strcmp(pszName, "challenge")) OnProcessChallenge(node, info); else if (!info->bIsReg) { - if (!mir_strcmp(node->Name(), "message")) + if (!mir_strcmp(pszName, "message")) OnProcessMessage(node, info); - else if (!mir_strcmp(node->Name(), "presence")) + else if (!mir_strcmp(pszName, "presence")) OnProcessPresence(node, info); - else if (!mir_strcmp(node->Name(), "iq")) + else if (!mir_strcmp(pszName, "iq")) OnProcessIq(node); - else if (!mir_strcmp(node->Name(), "failed")) + else if (!mir_strcmp(pszName, "failed")) OnProcessFailed(node, info); - else if (!mir_strcmp(node->Name(), "enabled")) + else if (!mir_strcmp(pszName, "enabled")) OnProcessEnabled(node, info); - else if (m_bEnableStreamMgmt && !mir_strcmp(node->Name(), "resumed")) + else if (m_bEnableStreamMgmt && !mir_strcmp(pszName, "resumed")) m_StrmMgmt.OnProcessResumed(node, info); else debugLogA("Invalid top-level tag (only and allowed)"); } else { - if (!mir_strcmp(node->Name(), "iq")) + if (!mir_strcmp(pszName, "iq")) OnProcessRegIq(node, info); else debugLogA("Invalid top-level tag (only allowed)"); diff --git a/protocols/JabberG/src/stdafx.h b/protocols/JabberG/src/stdafx.h index e3d575442d..8bd9d976f9 100644 --- a/protocols/JabberG/src/stdafx.h +++ b/protocols/JabberG/src/stdafx.h @@ -381,7 +381,10 @@ struct ThreadData char fullJID[JABBER_MAX_JID_LEN]; ptrA tszNewPassword; - char *gssapiHostName; + char* gssapiHostName; + + class TJabberAuth *m_saslUpgrade; + ptrA saslInitData; CJabberIqInfo *pPendingQuery; JabberCapsBits jabberServerCaps; -- cgit v1.2.3