1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
/*
Copyright (C) 2012-25 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 <http://www.gnu.org/licenses/>.
*/
#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));
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);
ptrA szEncoded(mir_base64_encode(saltedPassw, 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)
{
TUpgradeTask *pTask;
auto *szMechanism = n->GetText();
if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-1"))
pTask = new TScramTask(info, szMechanism, EVP_sha1(), 500);
else if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-256"))
pTask = new TScramTask(info, szMechanism, EVP_sha256(), 520);
else if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-384"))
pTask = new TScramTask(info, szMechanism, EVP_sha384(), 530);
// uncomment those lines when ejabberd will support SHA-512 normally
// else if (!mir_strcmp(szMechanism, "UPGR-SCRAM-SHA-512"))
// pTask = new TScramTask(info, szMechanism, EVP_sha512(), 540);
else {
debugLogA("Unsupported mechanism for upgrade: %s, skipping", szMechanism);
return;
}
m_arSaslUpgrade.insert(pTask);
}
void CJabberProto::OnProcessContinue(const TiXmlElement *node, ThreadData *info)
{
if (!node->Attribute("xmlns", JABBER_FEAT_SASL2)) {
debugLogA("Missing xmlns for continue, ignoring");
return;
}
TUpgradeTask *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("</stream:stream>"); // bye-bye
return;
}
info->m_saslUpgrade = pTask;
if (auto *pszInitData = XmlGetChildText(node, "additional-data"))
pTask->setInitData(pszInitData);
XmlNode next("next");
next << XATTR("xmlns", JABBER_FEAT_SASL2) << XATTR("task", pTask->getName());
info->send(next);
}
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("</stream:stream>"); // bye-bye
}
|