Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "JSInterface_Lobby.h"
21 :
22 : #include "gui/GUIManager.h"
23 : #include "lib/utf8.h"
24 : #include "lobby/IXmppClient.h"
25 : #include "network/NetServer.h"
26 : #include "ps/CLogger.h"
27 : #include "ps/CStr.h"
28 : #include "ps/Util.h"
29 : #include "scriptinterface/FunctionWrapper.h"
30 :
31 : #include "third_party/encryption/pkcs5_pbkdf2.h"
32 :
33 : #include <string>
34 :
35 : namespace JSI_Lobby
36 : {
37 0 : bool HasXmppClient()
38 : {
39 0 : return g_XmppClient;
40 : }
41 :
42 0 : void SetRankedGame(bool isRanked)
43 : {
44 0 : g_rankedGame = isRanked;
45 0 : }
46 :
47 : #if CONFIG2_LOBBY
48 :
49 0 : void StartXmppClient(const ScriptRequest& rq, const std::wstring& username, const std::wstring& password, const std::wstring& room, const std::wstring& nick, int historyRequestSize)
50 : {
51 0 : if (g_XmppClient)
52 : {
53 0 : ScriptException::Raise(rq, "Cannot call StartXmppClient with an already initialized XmppClient!");
54 0 : return;
55 : }
56 :
57 0 : g_XmppClient =
58 0 : IXmppClient::create(
59 0 : g_GUI->GetScriptInterface().get(),
60 0 : utf8_from_wstring(username),
61 0 : utf8_from_wstring(password),
62 0 : utf8_from_wstring(room),
63 0 : utf8_from_wstring(nick),
64 : historyRequestSize);
65 :
66 0 : g_rankedGame = true;
67 : }
68 :
69 0 : void StartRegisterXmppClient(const ScriptRequest& rq, const std::wstring& username, const std::wstring& password)
70 : {
71 0 : if (g_XmppClient)
72 : {
73 0 : ScriptException::Raise(rq, "Cannot call StartRegisterXmppClient with an already initialized XmppClient!");
74 0 : return;
75 : }
76 :
77 0 : g_XmppClient =
78 0 : IXmppClient::create(
79 0 : g_GUI->GetScriptInterface().get(),
80 0 : utf8_from_wstring(username),
81 0 : utf8_from_wstring(password),
82 0 : std::string(),
83 0 : std::string(),
84 : 0,
85 : true);
86 : }
87 :
88 0 : void StopXmppClient(const ScriptRequest& rq)
89 : {
90 0 : if (!g_XmppClient)
91 : {
92 0 : ScriptException::Raise(rq, "Cannot call StopXmppClient without an initialized XmppClient!");
93 0 : return;
94 : }
95 :
96 0 : SAFE_DELETE(g_XmppClient);
97 0 : g_rankedGame = false;
98 : }
99 :
100 : ////////////////////////////////////////////////
101 : ////////////////////////////////////////////////
102 :
103 0 : IXmppClient* XmppGetter(const ScriptRequest&, JS::CallArgs&)
104 : {
105 0 : if (!g_XmppClient)
106 : {
107 0 : LOGERROR("Cannot use XMPPClient functions without an initialized XmppClient!");
108 0 : return nullptr;
109 : }
110 0 : return g_XmppClient;
111 : }
112 :
113 0 : void SendRegisterGame(const ScriptInterface& scriptInterface, JS::HandleValue data)
114 : {
115 0 : if (!g_XmppClient)
116 : {
117 0 : ScriptRequest rq(scriptInterface);
118 0 : ScriptException::Raise(rq, "Cannot call SendRegisterGame without an initialized XmppClient!");
119 0 : return;
120 : }
121 :
122 : // Prevent JS mods to register matches in the lobby that were started with lobby authentication disabled
123 0 : if (!g_NetServer || !g_NetServer->UseLobbyAuth())
124 : {
125 0 : LOGERROR("Registering games in the lobby requires lobby authentication to be enabled!");
126 0 : return;
127 : }
128 :
129 0 : g_XmppClient->SendIqRegisterGame(scriptInterface, data);
130 : }
131 :
132 : // Unlike other functions, this one just returns Undefined if XmppClient isn't initialised.
133 0 : JS::Value GuiPollNewMessages(const ScriptInterface& scriptInterface)
134 : {
135 0 : if (!g_XmppClient)
136 0 : return JS::UndefinedValue();
137 :
138 0 : return g_XmppClient->GuiPollNewMessages(scriptInterface);
139 : }
140 :
141 :
142 : // Non-public secure PBKDF2 hash function with salting and 1,337 iterations
143 : //
144 : // TODO: We should use libsodium's crypto_pwhash instead of this. The first reason is that
145 : // libsodium doesn't propose a bare PBKDF2 hash in its API and it's too bad to rely on custom
146 : // code when we have a fully-fledged library available; the second reason is that Argon2 (the
147 : // default algorithm for crypto_pwhash) is better than what we use (and it's the default one
148 : // in the lib for a reason).
149 : // However changing the hashing method should be planned carefully, by trying to login with a
150 : // password hashed the old way, and, if successful, updating the password in the database using
151 : // the new hashing method. Dropping the old hashing code can only be done either by giving users
152 : // a way to reset their password, or by keeping track of successful password updates and dropping
153 : // old unused accounts after some time.
154 0 : std::string EncryptPassword(const std::string& password, const std::string& username)
155 : {
156 0 : ENSURE(sodium_init() >= 0);
157 :
158 0 : const int DIGESTSIZE = crypto_hash_sha256_BYTES;
159 0 : const int ITERATIONS = 1337;
160 :
161 : cassert(DIGESTSIZE == 32);
162 :
163 : static const unsigned char salt_base[DIGESTSIZE] = {
164 : 244, 243, 249, 244, 32, 33, 34, 35, 10, 11, 12, 13, 14, 15, 16, 17,
165 : 18, 19, 20, 32, 33, 244, 224, 127, 129, 130, 140, 153, 133, 123, 234, 123 };
166 :
167 : // initialize the salt buffer
168 0 : unsigned char salt_buffer[DIGESTSIZE] = {0};
169 : crypto_hash_sha256_state state;
170 0 : crypto_hash_sha256_init(&state);
171 :
172 0 : crypto_hash_sha256_update(&state, salt_base, sizeof(salt_base));
173 0 : crypto_hash_sha256_update(&state, (unsigned char*)username.c_str(), username.length());
174 :
175 0 : crypto_hash_sha256_final(&state, salt_buffer);
176 :
177 : // PBKDF2 to create the buffer
178 : unsigned char encrypted[DIGESTSIZE];
179 0 : pbkdf2(encrypted, (unsigned char*)password.c_str(), password.length(), salt_buffer, DIGESTSIZE, ITERATIONS);
180 :
181 0 : return CStr(Hexify(encrypted, DIGESTSIZE)).UpperCase();
182 : }
183 :
184 : #endif
185 :
186 12 : void RegisterScriptFunctions(const ScriptRequest& rq)
187 : {
188 : // Lobby functions
189 12 : ScriptFunction::Register<&HasXmppClient>(rq, "HasXmppClient");
190 12 : ScriptFunction::Register<&SetRankedGame>(rq, "SetRankedGame");
191 : #if CONFIG2_LOBBY // Allow the lobby to be disabled
192 12 : ScriptFunction::Register<&StartXmppClient>(rq, "StartXmppClient");
193 12 : ScriptFunction::Register<&StartRegisterXmppClient>(rq, "StartRegisterXmppClient");
194 12 : ScriptFunction::Register<&StopXmppClient>(rq, "StopXmppClient");
195 :
196 : #define REGISTER_XMPP(func, name) \
197 : ScriptFunction::Register<&IXmppClient::func, &XmppGetter>(rq, name)
198 :
199 12 : REGISTER_XMPP(connect, "ConnectXmppClient");
200 12 : REGISTER_XMPP(disconnect, "DisconnectXmppClient");
201 12 : REGISTER_XMPP(isConnected, "IsXmppClientConnected");
202 12 : REGISTER_XMPP(SendIqGetBoardList, "SendGetBoardList");
203 12 : REGISTER_XMPP(SendIqGetProfile, "SendGetProfile");
204 12 : REGISTER_XMPP(SendIqGameReport, "SendGameReport");
205 12 : ScriptFunction::Register<&SendRegisterGame>(rq, "SendRegisterGame");
206 12 : REGISTER_XMPP(SendIqUnregisterGame, "SendUnregisterGame");
207 12 : REGISTER_XMPP(SendIqChangeStateGame, "SendChangeStateGame");
208 12 : REGISTER_XMPP(GUIGetPlayerList, "GetPlayerList");
209 12 : REGISTER_XMPP(GUIGetGameList, "GetGameList");
210 12 : REGISTER_XMPP(GUIGetBoardList, "GetBoardList");
211 12 : REGISTER_XMPP(GUIGetProfile, "GetProfile");
212 :
213 12 : ScriptFunction::Register<&GuiPollNewMessages>(rq, "LobbyGuiPollNewMessages");
214 12 : REGISTER_XMPP(GuiPollHistoricMessages, "LobbyGuiPollHistoricMessages");
215 12 : REGISTER_XMPP(GuiPollHasPlayerListUpdate, "LobbyGuiPollHasPlayerListUpdate");
216 12 : REGISTER_XMPP(SendMUCMessage, "LobbySendMessage");
217 12 : REGISTER_XMPP(SetPresence, "LobbySetPlayerPresence");
218 12 : REGISTER_XMPP(SetNick, "LobbySetNick");
219 12 : REGISTER_XMPP(GetNick, "LobbyGetNick");
220 12 : REGISTER_XMPP(GetJID, "LobbyGetJID");
221 12 : REGISTER_XMPP(kick, "LobbyKick");
222 12 : REGISTER_XMPP(ban, "LobbyBan");
223 12 : REGISTER_XMPP(GetPresence, "LobbyGetPlayerPresence");
224 12 : REGISTER_XMPP(GetRole, "LobbyGetPlayerRole");
225 12 : REGISTER_XMPP(GetRating, "LobbyGetPlayerRating");
226 12 : REGISTER_XMPP(GetSubject, "LobbyGetRoomSubject");
227 : #undef REGISTER_XMPP
228 :
229 12 : ScriptFunction::Register<&EncryptPassword>(rq, "EncryptPassword");
230 : #endif // CONFIG2_LOBBY
231 12 : }
232 3 : }
|