From 7e939a1efe055c425465de20489b4760b5e12374 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Mon, 2 Dec 2024 13:24:58 +0300 Subject: fixes #3792 (XEP-0388: Extensible SASL Profile support) --- protocols/JabberG/src/jabber_caps.h | 4 +- protocols/JabberG/src/jabber_iqid.cpp | 2 +- protocols/JabberG/src/jabber_omemo.cpp | 2 +- protocols/JabberG/src/jabber_opt.cpp | 1 + protocols/JabberG/src/jabber_proto.cpp | 1 + protocols/JabberG/src/jabber_proto.h | 4 +- protocols/JabberG/src/jabber_secur.cpp | 55 +++++++++++++++++++ protocols/JabberG/src/jabber_strm_mgmt.cpp | 24 ++++---- protocols/JabberG/src/jabber_thread.cpp | 88 +++++++++++------------------- 9 files changed, 111 insertions(+), 70 deletions(-) (limited to 'protocols') diff --git a/protocols/JabberG/src/jabber_caps.h b/protocols/JabberG/src/jabber_caps.h index eaea80b919..c6981be3a7 100644 --- a/protocols/JabberG/src/jabber_caps.h +++ b/protocols/JabberG/src/jabber_caps.h @@ -198,15 +198,17 @@ typedef unsigned __int64 JabberCapsBits; #define JABBER_FEAT_ARCHIVE "urn:xmpp:archive" #define JABBER_FEAT_BIND "urn:ietf:params:xml:ns:xmpp-bind" #define JABBER_FEAT_CAPTCHA "urn:xmpp:captcha" +#define JABBER_FEAT_CHANNEL_BINDING "urn:xmpp:sasl-cb:0" #define JABBER_FEAT_CSI "urn:xmpp:csi:0" #define JABBER_FEAT_JUD "jabber:iq:search" #define JABBER_FEAT_IDLE "urn:xmpp:idle:1" +#define JABBER_FEAT_SASL2 "urn:xmpp:sasl:2" #define JABBER_FEAT_SERVER_AVATAR "storage:client:avatar" #define JABBER_FEAT_SID "urn:xmpp:sid:0" +#define JABBER_FEAT_SM "urn:xmpp:sm:3" #define JABBER_FEAT_SOFTWARE_INFO "urn:xmpp:dataforms:softwareinfo" #define JABBER_FEAT_UPLOAD "urn:xmpp:http:upload" #define JABBER_FEAT_UPLOAD0 "urn:xmpp:http:upload:0" -#define JABBER_FEAT_CHANNEL_BINDING "urn:xmpp:sasl-cb:0" #define JABBER_FEAT_RSM "http://jabber.org/protocol/rsm" #define JABBER_FEAT_PUBSUB_EVENT "http://jabber.org/protocol/pubsub#event" diff --git a/protocols/JabberG/src/jabber_iqid.cpp b/protocols/JabberG/src/jabber_iqid.cpp index bbd7a0dd60..2839a73e30 100644 --- a/protocols/JabberG/src/jabber_iqid.cpp +++ b/protocols/JabberG/src/jabber_iqid.cpp @@ -389,7 +389,7 @@ void CJabberProto::OnIqResultBind(const TiXmlElement *iqNode, CJabberIqInfo *pIn } } - if (m_isSessionAvailable) { + if (m_hasSession) { m_ThreadInfo->send( XmlNodeIq(AddIQ(&CJabberProto::OnIqResultSession, JABBER_IQ_TYPE_SET)) << XCHILDNS("session", "urn:ietf:params:xml:ns:xmpp-session")); diff --git a/protocols/JabberG/src/jabber_omemo.cpp b/protocols/JabberG/src/jabber_omemo.cpp index 7a01603a1e..39b6b7d8dd 100644 --- a/protocols/JabberG/src/jabber_omemo.cpp +++ b/protocols/JabberG/src/jabber_omemo.cpp @@ -1733,7 +1733,7 @@ void CJabberProto::OmemoOnIqResultGetBundle(const TiXmlElement *iqNode, CJabberI return; //failed to build signal(omemo) session } - OmemoCheckSession((MCONTACT)IqInfo->GetUserData(), false); + OmemoCheckSession((UINT_PTR)IqInfo->GetUserData(), false); } int CJabberProto::OmemoEncryptMessage(XmlNode &msg, const char *msg_text, MCONTACT hContact) diff --git a/protocols/JabberG/src/jabber_opt.cpp b/protocols/JabberG/src/jabber_opt.cpp index f85836701f..5254d9ea10 100644 --- a/protocols/JabberG/src/jabber_opt.cpp +++ b/protocols/JabberG/src/jabber_opt.cpp @@ -741,6 +741,7 @@ public: pOptions->AddOption(LPGENW("Server options"), LPGENW("Use Stream Management (XEP-0198)"), m_proto->m_bEnableStreamMgmt); pOptions->AddOption(LPGENW("Server options"), LPGENW("Disable SASL authentication (for old servers)"), m_proto->m_bDisable3920auth); + pOptions->AddOption(LPGENW("Server options"), LPGENW("Enable SASL2 authentication, if present"), m_proto->m_bEnableSasl2); pOptions->AddOption(LPGENW("Server options"), LPGENW("Enable stream compression"), m_proto->m_bEnableZlib); pOptions->AddOption(LPGENW("Other"), LPGENW("Enable remote controlling (from another resource of same JID only)"), m_proto->m_bEnableRemoteControl); diff --git a/protocols/JabberG/src/jabber_proto.cpp b/protocols/JabberG/src/jabber_proto.cpp index c4f02c7774..fbb772ac56 100644 --- a/protocols/JabberG/src/jabber_proto.cpp +++ b/protocols/JabberG/src/jabber_proto.cpp @@ -98,6 +98,7 @@ CJabberProto::CJabberProto(const char *aProtoName, const wchar_t *aUserName) : m_bEnableMam(this, "EnableMam", true), m_bEnableMsgArchive(this, "EnableMsgArchive", false), m_bEnableRemoteControl(this, "EnableRemoteControl", false), + m_bEnableSasl2(this, "EnableSasl2", true), m_bEnableStreamMgmt(this, "UseStreamMgmt", false), m_bEnableUserActivity(this, "EnableUserActivity", true), m_bEnableUserMood(this, "EnableUserMood", true), diff --git a/protocols/JabberG/src/jabber_proto.h b/protocols/JabberG/src/jabber_proto.h index f1e0f97f44..407e5c91ca 100644 --- a/protocols/JabberG/src/jabber_proto.h +++ b/protocols/JabberG/src/jabber_proto.h @@ -203,6 +203,7 @@ struct CJabberProto : public PROTO, public IJabberInterface CMOption m_bEnableMsgArchive; CMOption m_bEnableMam; CMOption m_bEnableRemoteControl; + CMOption m_bEnableSasl2; CMOption m_bEnableStreamMgmt; CMOption m_bEnableUserActivity; CMOption m_bEnableUserMood; @@ -816,7 +817,7 @@ struct CJabberProto : public PROTO, public IJabberInterface ptrW m_savedPassword; OBJLIST m_arAuthMechs; - bool m_isSessionAvailable, m_isAuthAvailable; + bool m_hasSession, m_hasAuth, m_hasSasl2; void __cdecl ServerThread(JABBER_CONN_DATA *info); bool ServerThreadStub(ThreadData &info); @@ -842,6 +843,7 @@ 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_secur.cpp b/protocols/JabberG/src/jabber_secur.cpp index 104c225504..2b52830960 100644 --- a/protocols/JabberG/src/jabber_secur.cpp +++ b/protocols/JabberG/src/jabber_secur.cpp @@ -25,6 +25,61 @@ 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 diff --git a/protocols/JabberG/src/jabber_strm_mgmt.cpp b/protocols/JabberG/src/jabber_strm_mgmt.cpp index 48074cdb38..c92ba4ea98 100644 --- a/protocols/JabberG/src/jabber_strm_mgmt.cpp +++ b/protocols/JabberG/src/jabber_strm_mgmt.cpp @@ -52,7 +52,7 @@ void strm_mgmt::OnProcessEnabled(const TiXmlElement *node, ThreadData * /*info*/ void strm_mgmt::OnProcessResumed(const TiXmlElement *node, ThreadData * /*info*/) { - if (mir_strcmp(XmlGetAttr(node, "xmlns"), "urn:xmpp:sm:3")) + if (mir_strcmp(XmlGetAttr(node, "xmlns"), JABBER_FEAT_SM)) return; auto *var = XmlGetAttr(node, "previd"); @@ -74,7 +74,7 @@ void strm_mgmt::OnProcessResumed(const TiXmlElement *node, ThreadData * /*info*/ void strm_mgmt::OnProcessSMa(const TiXmlElement *node) { - if (mir_strcmp(XmlGetAttr(node, "xmlns"), "urn:xmpp:sm:3")) + if (mir_strcmp(XmlGetAttr(node, "xmlns"), JABBER_FEAT_SM)) return; if (!m_bRequestPending) @@ -118,13 +118,13 @@ void strm_mgmt::ProcessCache(uint32_t nSrvHCount, bool resuming) void strm_mgmt::OnProcessSMr(const TiXmlElement *node) { - if (!mir_strcmp(XmlGetAttr(node, "xmlns"), "urn:xmpp:sm:3")) + if (!mir_strcmp(XmlGetAttr(node, "xmlns"), JABBER_FEAT_SM)) SendAck(); } void strm_mgmt::OnProcessFailed(const TiXmlElement *node, ThreadData *info) //used failed instead of failure, notes: https://xmpp.org/extensions/xep-0198.html#errors { - if (mir_strcmp(XmlGetAttr(node, "xmlns"), "urn:xmpp:sm:3")) + if (mir_strcmp(XmlGetAttr(node, "xmlns"), JABBER_FEAT_SM)) return; proto->debugLogA("strm_mgmt: error: Failed to resume session %s", m_sResumeId.c_str()); @@ -147,11 +147,15 @@ void strm_mgmt::OnProcessFailed(const TiXmlElement *node, ThreadData *info) //us void strm_mgmt::CheckStreamFeatures(const TiXmlElement *node) { + // this may be necessary to reset counters if session resume id is not set if (!IsResumeIdPresent()) - ResetState(); //this may be necessary to reset counters if session resume id is not set - if (mir_strcmp(node->Name(), "sm") || !XmlGetAttr(node, "xmlns") || mir_strcmp(XmlGetAttr(node, "xmlns"), "urn:xmpp:sm:3")) //we work only with version 3 or higher of sm + ResetState(); + + // we work only with version 3 or higher of sm + if (mir_strcmp(node->Name(), "sm") || !XmlGetAttr(node, "xmlns") || mir_strcmp(XmlGetAttr(node, "xmlns"), JABBER_FEAT_SM)) return; - if (!(proto->m_bJabberOnline)) + + if (!proto->m_bJabberOnline) m_bPendingEnable = true; else EnableStrmMgmt(); @@ -224,14 +228,14 @@ void strm_mgmt::EnableStrmMgmt() if (m_sResumeId.empty()) { XmlNode enable_sm("enable"); - XmlAddAttr(enable_sm, "xmlns", "urn:xmpp:sm:3"); + XmlAddAttr(enable_sm, "xmlns", JABBER_FEAT_SM); XmlAddAttr(enable_sm, "resume", "true"); // enable resumption (most useful part of this xep) proto->m_ThreadInfo->send(enable_sm); m_nLocalSCount = 0; } else { // resume session XmlNode enable_sm("resume"); - enable_sm << XATTR("xmlns", "urn:xmpp:sm:3") << XATTRI("h", m_nLocalHCount) << XATTR("previd", m_sResumeId.c_str()); + enable_sm << XATTR("xmlns", JABBER_FEAT_SM) << XATTRI("h", m_nLocalHCount) << XATTR("previd", m_sResumeId.c_str()); proto->m_ThreadInfo->send(enable_sm); } } @@ -243,7 +247,7 @@ void strm_mgmt::SendAck() proto->debugLogA("strm_mgmt: info: sending ack: locally received node count %d", m_nLocalHCount); XmlNode ack_node("a"); - ack_node << XATTR("xmlns", "urn:xmpp:sm:3") << XATTRI("h", m_nLocalHCount); + ack_node << XATTR("xmlns", JABBER_FEAT_SM) << XATTRI("h", m_nLocalHCount); proto->m_ThreadInfo->send_no_strm_mgmt(ack_node); } diff --git a/protocols/JabberG/src/jabber_thread.cpp b/protocols/JabberG/src/jabber_thread.cpp index b1d5eb28cf..b5b13ce4ec 100644 --- a/protocols/JabberG/src/jabber_thread.cpp +++ b/protocols/JabberG/src/jabber_thread.cpp @@ -225,8 +225,11 @@ void CJabberProto::xmlStreamInitializeNow(ThreadData *info) if (m_tszSelectedLang) n << XATTR("xml:lang", m_tszSelectedLang); - if (!m_bDisable3920auth) + if (!m_bDisable3920auth) { n << XATTR("version", "1.0"); + if (m_bEnableSasl2) + n << XATTR("from", CMStringA(FORMAT, "%s@%s", info->conn.username, info->conn.server)); + } tinyxml2::XMLPrinter printer(0, true); n.Print(&printer); @@ -615,8 +618,8 @@ void CJabberProto::PerformAuthentication(ThreadData *info) // no known mechanisms if (m_arAuthMechs.getCount() == 0) { // if iq_auth is available, use it - if (m_isAuthAvailable) { - m_isAuthAvailable = false; + if (m_hasAuth) { + m_hasAuth = false; PerformIqAuth(info); return; } @@ -667,72 +670,45 @@ void CJabberProto::OnProcessFeatures(const TiXmlElement *node, ThreadData *info) } } + auto *xmlns = n->Attribute("xmlns"); if (!mir_strcmp(pszName, "mechanisms")) { + areMechanismsDefined = true; m_arAuthMechs.destroy(); replaceStr(info->gssapiHostName, nullptr); - areMechanismsDefined = true; + for (auto *c : TiXmlEnum(n)) + OnProcessMechanism(c, info); + } + else if (!mir_strcmp(pszName, "register")) + isRegisterAvailable = true; + else if (!mir_strcmp(pszName, "auth")) + m_hasAuth = true; + else if (!mir_strcmp(pszName, "authentication") && !mir_strcmp(xmlns, JABBER_FEAT_SASL2) && m_bEnableSasl2) { + m_hasSasl2 = areMechanismsDefined = true; + m_arAuthMechs.destroy(); + replaceStr(info->gssapiHostName, nullptr); for (auto *c : TiXmlEnum(n)) { - if (!mir_strcmp(c->Name(), "mechanism")) { - TJabberAuth *pAuth = nullptr; - const char *szMechanism = c->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); - continue; - } + if (OnProcessMechanism(c, info)) + continue; - if (!pAuth->isValid()) - delete pAuth; - else - m_arAuthMechs.insert(pAuth); - } - else if (!mir_strcmp(c->Name(), "hostname")) { - const char *mech = XmlGetAttr(c, "mechanism"); - if (mech && mir_strcmpi(mech, "GSSAPI") == 0) - info->gssapiHostName = mir_strdup(c->GetText()); + if (!mir_strcmp(c->Name(), "inline")) { + if (auto *sm = XmlGetChildByTag(c, "sm", "xmlns", JABBER_FEAT_SM)) + m_StrmMgmt.CheckStreamFeatures(sm); + + if (auto *bind = XmlGetChildByTag(c, "sm", "xmlns", "urn:xmpp:bind:0")) { + // dunno why we need to handle that + } } } } - else if (!mir_strcmp(pszName, "register")) - isRegisterAvailable = true; - else if (!mir_strcmp(pszName, "auth")) - m_isAuthAvailable = true; else if (!mir_strcmp(pszName, "session")) - m_isSessionAvailable = true; + m_hasSession = true; else if (m_bEnableStreamMgmt && !mir_strcmp(pszName, "sm")) m_StrmMgmt.CheckStreamFeatures(n); - else if (!mir_strcmp(pszName, "csi") && n->Attribute("xmlns", JABBER_FEAT_CSI)) + else if (!mir_strcmp(pszName, "csi") && !mir_strcmp(xmlns, JABBER_FEAT_CSI)) m_bCisAvailable = true; - else if (!mir_strcmp(pszName, "c") && !mir_strcmp(n->Attribute("xmlns"), JABBER_FEAT_ENTITY_CAPS)) { + else if (!mir_strcmp(pszName, "c") && !mir_strcmp(xmlns, JABBER_FEAT_ENTITY_CAPS)) { auto *szNode = n->Attribute("node"), *szHash = n->Attribute("ver"); auto *pCaps = g_clientCapsManager.GetPartialCaps(szNode, szHash); if (pCaps == nullptr) { @@ -741,7 +717,7 @@ void CJabberProto::OnProcessFeatures(const TiXmlElement *node, ThreadData *info) } else info->jabberServerCaps |= pCaps->GetCaps(); } - else if (!mir_strcmp(pszName, "sasl-channel-binding") && !mir_strcmp(n->Attribute("xmlns"), JABBER_FEAT_CHANNEL_BINDING)) { + else if (!mir_strcmp(pszName, "sasl-channel-binding") && !mir_strcmp(xmlns, JABBER_FEAT_CHANNEL_BINDING)) { for (auto *it : TiXmlFilter(n, "channel-binding")) { if (auto *pszType = it->Attribute("type")) { if (!mir_strcmp(pszType, "tls-exporter")) -- cgit v1.2.3