]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/lua_api/l_util.cpp
Check for valid base64 before decoding (#9904)
[dragonfireclient.git] / src / script / lua_api / l_util.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "lua_api/l_util.h"
21 #include "lua_api/l_internal.h"
22 #include "lua_api/l_settings.h"
23 #include "common/c_converter.h"
24 #include "common/c_content.h"
25 #include "cpp_api/s_async.h"
26 #include "serialization.h"
27 #include <json/json.h>
28 #include "cpp_api/s_security.h"
29 #include "porting.h"
30 #include "convert_json.h"
31 #include "debug.h"
32 #include "log.h"
33 #include "tool.h"
34 #include "filesys.h"
35 #include "settings.h"
36 #include "util/auth.h"
37 #include "util/base64.h"
38 #include "config.h"
39 #include "version.h"
40 #include "util/hex.h"
41 #include "util/sha1.h"
42 #include <algorithm>
43
44
45 // log([level,] text)
46 // Writes a line to the logger.
47 // The one-argument version logs to LL_NONE.
48 // The two-argument version accepts a log level.
49 // Either the special case "deprecated" for deprecation notices, or any specified in
50 // Logger::stringToLevel(name).
51 int ModApiUtil::l_log(lua_State *L)
52 {
53         NO_MAP_LOCK_REQUIRED;
54         std::string text;
55         LogLevel level = LL_NONE;
56         if (lua_isnone(L, 2)) {
57                 text = luaL_checkstring(L, 1);
58         } else {
59                 std::string name = luaL_checkstring(L, 1);
60                 text = luaL_checkstring(L, 2);
61                 if (name == "deprecated") {
62                         log_deprecated(L, text, 2);
63                         return 0;
64                 }
65                 level = Logger::stringToLevel(name);
66                 if (level == LL_MAX) {
67                         warningstream << "Tried to log at unknown level '" << name
68                                 << "'.  Defaulting to \"none\"." << std::endl;
69                         level = LL_NONE;
70                 }
71         }
72         g_logger.log(level, text);
73         return 0;
74 }
75
76 // get_us_time()
77 int ModApiUtil::l_get_us_time(lua_State *L)
78 {
79         NO_MAP_LOCK_REQUIRED;
80         lua_pushnumber(L, porting::getTimeUs());
81         return 1;
82 }
83
84 // parse_json(str[, nullvalue])
85 int ModApiUtil::l_parse_json(lua_State *L)
86 {
87         NO_MAP_LOCK_REQUIRED;
88
89         const char *jsonstr = luaL_checkstring(L, 1);
90
91         // Use passed nullvalue or default to nil
92         int nullindex = 2;
93         if (lua_isnone(L, nullindex)) {
94                 lua_pushnil(L);
95                 nullindex = lua_gettop(L);
96         }
97
98         Json::Value root;
99
100         {
101                 std::istringstream stream(jsonstr);
102
103                 Json::CharReaderBuilder builder;
104                 builder.settings_["collectComments"] = false;
105                 std::string errs;
106
107                 if (!Json::parseFromStream(builder, stream, &root, &errs)) {
108                         errorstream << "Failed to parse json data " << errs << std::endl;
109                         size_t jlen = strlen(jsonstr);
110                         if (jlen > 100) {
111                                 errorstream << "Data (" << jlen
112                                         << " bytes) printed to warningstream." << std::endl;
113                                 warningstream << "data: \"" << jsonstr << "\"" << std::endl;
114                         } else {
115                                 errorstream << "data: \"" << jsonstr << "\"" << std::endl;
116                         }
117                         lua_pushnil(L);
118                         return 1;
119                 }
120         }
121
122         if (!push_json_value(L, root, nullindex)) {
123                 errorstream << "Failed to parse json data, "
124                         << "depth exceeds lua stack limit" << std::endl;
125                 errorstream << "data: \"" << jsonstr << "\"" << std::endl;
126                 lua_pushnil(L);
127         }
128         return 1;
129 }
130
131 // write_json(data[, styled]) -> string or nil and error message
132 int ModApiUtil::l_write_json(lua_State *L)
133 {
134         NO_MAP_LOCK_REQUIRED;
135
136         bool styled = false;
137         if (!lua_isnone(L, 2)) {
138                 styled = readParam<bool>(L, 2);
139                 lua_pop(L, 1);
140         }
141
142         Json::Value root;
143         try {
144                 read_json_value(L, root, 1);
145         } catch (SerializationError &e) {
146                 lua_pushnil(L);
147                 lua_pushstring(L, e.what());
148                 return 2;
149         }
150
151         std::string out;
152         if (styled) {
153                 out = root.toStyledString();
154         } else {
155                 out = fastWriteJson(root);
156         }
157         lua_pushlstring(L, out.c_str(), out.size());
158         return 1;
159 }
160
161 // get_dig_params(groups, tool_capabilities)
162 int ModApiUtil::l_get_dig_params(lua_State *L)
163 {
164         NO_MAP_LOCK_REQUIRED;
165         ItemGroupList groups;
166         read_groups(L, 1, groups);
167         ToolCapabilities tp = read_tool_capabilities(L, 2);
168         push_dig_params(L, getDigParams(groups, &tp));
169         return 1;
170 }
171
172 // get_hit_params(groups, tool_capabilities[, time_from_last_punch])
173 int ModApiUtil::l_get_hit_params(lua_State *L)
174 {
175         NO_MAP_LOCK_REQUIRED;
176         std::unordered_map<std::string, int> groups;
177         read_groups(L, 1, groups);
178         ToolCapabilities tp = read_tool_capabilities(L, 2);
179         if(lua_isnoneornil(L, 3))
180                 push_hit_params(L, getHitParams(groups, &tp));
181         else
182                 push_hit_params(L, getHitParams(groups, &tp, readParam<float>(L, 3)));
183         return 1;
184 }
185
186 // check_password_entry(name, entry, password)
187 int ModApiUtil::l_check_password_entry(lua_State *L)
188 {
189         NO_MAP_LOCK_REQUIRED;
190         std::string name = luaL_checkstring(L, 1);
191         std::string entry = luaL_checkstring(L, 2);
192         std::string password = luaL_checkstring(L, 3);
193
194         if (base64_is_valid(entry)) {
195                 std::string hash = translate_password(name, password);
196                 lua_pushboolean(L, hash == entry);
197                 return 1;
198         }
199
200         std::string salt;
201         std::string verifier;
202
203         if (!decode_srp_verifier_and_salt(entry, &verifier, &salt)) {
204                 // invalid format
205                 warningstream << "Invalid password format for " << name << std::endl;
206                 lua_pushboolean(L, false);
207                 return 1;
208         }
209         std::string gen_verifier = generate_srp_verifier(name, password, salt);
210
211         lua_pushboolean(L, gen_verifier == verifier);
212         return 1;
213 }
214
215 // get_password_hash(name, raw_password)
216 int ModApiUtil::l_get_password_hash(lua_State *L)
217 {
218         NO_MAP_LOCK_REQUIRED;
219         std::string name = luaL_checkstring(L, 1);
220         std::string raw_password = luaL_checkstring(L, 2);
221         std::string hash = translate_password(name, raw_password);
222         lua_pushstring(L, hash.c_str());
223         return 1;
224 }
225
226 // is_yes(arg)
227 int ModApiUtil::l_is_yes(lua_State *L)
228 {
229         NO_MAP_LOCK_REQUIRED;
230
231         lua_getglobal(L, "tostring"); // function to be called
232         lua_pushvalue(L, 1); // 1st argument
233         lua_call(L, 1, 1); // execute function
234         std::string str = readParam<std::string>(L, -1); // get result
235         lua_pop(L, 1);
236
237         bool yes = is_yes(str);
238         lua_pushboolean(L, yes);
239         return 1;
240 }
241
242 // is_nan(arg)
243 int ModApiUtil::l_is_nan(lua_State *L)
244 {
245         NO_MAP_LOCK_REQUIRED;
246
247         lua_pushboolean(L, isNaN(L, 1));
248         return 1;
249 }
250
251 // get_builtin_path()
252 int ModApiUtil::l_get_builtin_path(lua_State *L)
253 {
254         NO_MAP_LOCK_REQUIRED;
255
256         std::string path = porting::path_share + DIR_DELIM + "builtin" + DIR_DELIM;
257         lua_pushstring(L, path.c_str());
258
259         return 1;
260 }
261
262 // compress(data, method, level)
263 int ModApiUtil::l_compress(lua_State *L)
264 {
265         NO_MAP_LOCK_REQUIRED;
266
267         size_t size;
268         const char *data = luaL_checklstring(L, 1, &size);
269
270         int level = -1;
271         if (!lua_isnone(L, 3) && !lua_isnil(L, 3))
272                 level = readParam<float>(L, 3);
273
274         std::ostringstream os;
275         compressZlib(std::string(data, size), os, level);
276
277         std::string out = os.str();
278
279         lua_pushlstring(L, out.data(), out.size());
280         return 1;
281 }
282
283 // decompress(data, method)
284 int ModApiUtil::l_decompress(lua_State *L)
285 {
286         NO_MAP_LOCK_REQUIRED;
287
288         size_t size;
289         const char *data = luaL_checklstring(L, 1, &size);
290
291         std::istringstream is(std::string(data, size));
292         std::ostringstream os;
293         decompressZlib(is, os);
294
295         std::string out = os.str();
296
297         lua_pushlstring(L, out.data(), out.size());
298         return 1;
299 }
300
301 // encode_base64(string)
302 int ModApiUtil::l_encode_base64(lua_State *L)
303 {
304         NO_MAP_LOCK_REQUIRED;
305
306         size_t size;
307         const char *data = luaL_checklstring(L, 1, &size);
308
309         std::string out = base64_encode((const unsigned char *)(data), size);
310
311         lua_pushlstring(L, out.data(), out.size());
312         return 1;
313 }
314
315 // decode_base64(string)
316 int ModApiUtil::l_decode_base64(lua_State *L)
317 {
318         NO_MAP_LOCK_REQUIRED;
319
320         size_t size;
321         const char *d = luaL_checklstring(L, 1, &size);
322         const std::string data = std::string(d, size);
323
324         if (!base64_is_valid(data))
325                 return 0;
326
327         std::string out = base64_decode(data);
328
329         lua_pushlstring(L, out.data(), out.size());
330         return 1;
331 }
332
333 // mkdir(path)
334 int ModApiUtil::l_mkdir(lua_State *L)
335 {
336         NO_MAP_LOCK_REQUIRED;
337         const char *path = luaL_checkstring(L, 1);
338         CHECK_SECURE_PATH(L, path, true);
339         lua_pushboolean(L, fs::CreateAllDirs(path));
340         return 1;
341 }
342
343 // get_dir_list(path, is_dir)
344 int ModApiUtil::l_get_dir_list(lua_State *L)
345 {
346         NO_MAP_LOCK_REQUIRED;
347         const char *path = luaL_checkstring(L, 1);
348         bool list_all = !lua_isboolean(L, 2); // if its not a boolean list all
349         bool list_dirs = readParam<bool>(L, 2); // true: list dirs, false: list files
350
351         CHECK_SECURE_PATH(L, path, false);
352
353         std::vector<fs::DirListNode> list = fs::GetDirListing(path);
354
355         int index = 0;
356         lua_newtable(L);
357
358         for (const fs::DirListNode &dln : list) {
359                 if (list_all || list_dirs == dln.dir) {
360                         lua_pushstring(L, dln.name.c_str());
361                         lua_rawseti(L, -2, ++index);
362                 }
363         }
364
365         return 1;
366 }
367
368 // safe_file_write(path, content)
369 int ModApiUtil::l_safe_file_write(lua_State *L)
370 {
371         NO_MAP_LOCK_REQUIRED;
372         const char *path = luaL_checkstring(L, 1);
373         size_t size;
374         const char *content = luaL_checklstring(L, 2, &size);
375
376         CHECK_SECURE_PATH(L, path, true);
377
378         bool ret = fs::safeWriteToFile(path, std::string(content, size));
379         lua_pushboolean(L, ret);
380
381         return 1;
382 }
383
384 // request_insecure_environment()
385 int ModApiUtil::l_request_insecure_environment(lua_State *L)
386 {
387         NO_MAP_LOCK_REQUIRED;
388
389         // Just return _G if security is disabled
390         if (!ScriptApiSecurity::isSecure(L)) {
391                 lua_getglobal(L, "_G");
392                 return 1;
393         }
394
395         // We have to make sure that this function is being called directly by
396         // a mod, otherwise a malicious mod could override this function and
397         // steal its return value.
398         lua_Debug info;
399         // Make sure there's only one item below this function on the stack...
400         if (lua_getstack(L, 2, &info)) {
401                 return 0;
402         }
403         FATAL_ERROR_IF(!lua_getstack(L, 1, &info), "lua_getstack() failed");
404         FATAL_ERROR_IF(!lua_getinfo(L, "S", &info), "lua_getinfo() failed");
405         // ...and that that item is the main file scope.
406         if (strcmp(info.what, "main") != 0) {
407                 return 0;
408         }
409
410         // Get mod name
411         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
412         if (!lua_isstring(L, -1)) {
413                 return 0;
414         }
415
416         // Check secure.trusted_mods
417         std::string mod_name = readParam<std::string>(L, -1);
418         std::string trusted_mods = g_settings->get("secure.trusted_mods");
419         trusted_mods.erase(std::remove_if(trusted_mods.begin(),
420                         trusted_mods.end(), static_cast<int(*)(int)>(&std::isspace)),
421                         trusted_mods.end());
422         std::vector<std::string> mod_list = str_split(trusted_mods, ',');
423         if (std::find(mod_list.begin(), mod_list.end(), mod_name) ==
424                         mod_list.end()) {
425                 return 0;
426         }
427
428         // Push insecure environment
429         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
430         return 1;
431 }
432
433 // get_version()
434 int ModApiUtil::l_get_version(lua_State *L)
435 {
436         lua_createtable(L, 0, 3);
437         int table = lua_gettop(L);
438
439         lua_pushstring(L, PROJECT_NAME_C);
440         lua_setfield(L, table, "project");
441
442         lua_pushstring(L, g_version_string);
443         lua_setfield(L, table, "string");
444
445         if (strcmp(g_version_string, g_version_hash) != 0) {
446                 lua_pushstring(L, g_version_hash);
447                 lua_setfield(L, table, "hash");
448         }
449
450         return 1;
451 }
452
453 int ModApiUtil::l_sha1(lua_State *L)
454 {
455         NO_MAP_LOCK_REQUIRED;
456         size_t size;
457         const char *data = luaL_checklstring(L, 1, &size);
458         bool hex = !lua_isboolean(L, 2) || !readParam<bool>(L, 2);
459
460         // Compute actual checksum of data
461         std::string data_sha1;
462         {
463                 SHA1 ctx;
464                 ctx.addBytes(data, size);
465                 unsigned char *data_tmpdigest = ctx.getDigest();
466                 data_sha1.assign((char*) data_tmpdigest, 20);
467                 free(data_tmpdigest);
468         }
469
470         if (hex) {
471                 std::string sha1_hex = hex_encode(data_sha1);
472                 lua_pushstring(L, sha1_hex.c_str());
473         } else {
474                 lua_pushlstring(L, data_sha1.data(), data_sha1.size());
475         }
476
477         return 1;
478 }
479
480 void ModApiUtil::Initialize(lua_State *L, int top)
481 {
482         API_FCT(log);
483
484         API_FCT(get_us_time);
485
486         API_FCT(parse_json);
487         API_FCT(write_json);
488
489         API_FCT(get_dig_params);
490         API_FCT(get_hit_params);
491
492         API_FCT(check_password_entry);
493         API_FCT(get_password_hash);
494
495         API_FCT(is_yes);
496         API_FCT(is_nan);
497
498         API_FCT(get_builtin_path);
499
500         API_FCT(compress);
501         API_FCT(decompress);
502
503         API_FCT(mkdir);
504         API_FCT(get_dir_list);
505         API_FCT(safe_file_write);
506
507         API_FCT(request_insecure_environment);
508
509         API_FCT(encode_base64);
510         API_FCT(decode_base64);
511
512         API_FCT(get_version);
513         API_FCT(sha1);
514
515         LuaSettings::create(L, g_settings, g_settings_path);
516         lua_setfield(L, top, "settings");
517 }
518
519 void ModApiUtil::InitializeClient(lua_State *L, int top)
520 {
521         API_FCT(log);
522
523         API_FCT(get_us_time);
524
525         API_FCT(parse_json);
526         API_FCT(write_json);
527
528         API_FCT(is_yes);
529         API_FCT(is_nan);
530
531         API_FCT(compress);
532         API_FCT(decompress);
533
534         API_FCT(encode_base64);
535         API_FCT(decode_base64);
536
537         API_FCT(get_version);
538         API_FCT(sha1);
539 }
540
541 void ModApiUtil::InitializeAsync(lua_State *L, int top)
542 {
543         API_FCT(log);
544
545         API_FCT(get_us_time);
546
547         API_FCT(parse_json);
548         API_FCT(write_json);
549
550         API_FCT(is_yes);
551
552         API_FCT(get_builtin_path);
553
554         API_FCT(compress);
555         API_FCT(decompress);
556
557         API_FCT(mkdir);
558         API_FCT(get_dir_list);
559
560         API_FCT(encode_base64);
561         API_FCT(decode_base64);
562
563         API_FCT(get_version);
564         API_FCT(sha1);
565
566         LuaSettings::create(L, g_settings, g_settings_path);
567         lua_setfield(L, top, "settings");
568 }
569