summaryrefslogtreecommitdiff
path: root/protocols/JabberG/src/jabber_sasl2.cpp
blob: 3b27a66719c3e87dbd5b7a265fe9474d3799fddf (plain)
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
}