]> git.lizzy.rs Git - minetest.git/blob - src/script/cpp_api/s_security.cpp
dofile error reporting for syntax errors
[minetest.git] / src / script / cpp_api / s_security.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 "cpp_api/s_security.h"
21
22 #include "filesys.h"
23 #include "porting.h"
24 #include "server.h"
25 #include "settings.h"
26
27 #include <cerrno>
28 #include <string>
29 #include <iostream>
30
31
32 #define SECURE_API(lib, name) \
33         lua_pushcfunction(L, sl_##lib##_##name); \
34         lua_setfield(L, -2, #name);
35
36
37 static inline void copy_safe(lua_State *L, const char *list[], unsigned len, int from=-2, int to=-1)
38 {
39         if (from < 0) from = lua_gettop(L) + from + 1;
40         if (to   < 0) to   = lua_gettop(L) + to   + 1;
41         for (unsigned i = 0; i < (len / sizeof(list[0])); i++) {
42                 lua_getfield(L, from, list[i]);
43                 lua_setfield(L, to,   list[i]);
44         }
45 }
46
47 // Pushes the original version of a library function on the stack, from the old version
48 static inline void push_original(lua_State *L, const char *lib, const char *func)
49 {
50         lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup");
51         lua_getfield(L, -1, lib);
52         lua_remove(L, -2);  // Remove globals_backup
53         lua_getfield(L, -1, func);
54         lua_remove(L, -2);  // Remove lib
55 }
56
57
58 void ScriptApiSecurity::initializeSecurity()
59 {
60         static const char *whitelist[] = {
61                 "assert",
62                 "core",
63                 "collectgarbage",
64                 "DIR_DELIM",
65                 "error",
66                 "getfenv",
67                 "getmetatable",
68                 "ipairs",
69                 "next",
70                 "pairs",
71                 "pcall",
72                 "print",
73                 "rawequal",
74                 "rawget",
75                 "rawset",
76                 "select",
77                 "setfenv",
78                 "setmetatable",
79                 "tonumber",
80                 "tostring",
81                 "type",
82                 "unpack",
83                 "_VERSION",
84                 "xpcall",
85                 // Completely safe libraries
86                 "coroutine",
87                 "string",
88                 "table",
89                 "math",
90         };
91         static const char *io_whitelist[] = {
92                 "close",
93                 "flush",
94                 "read",
95                 "type",
96                 "write",
97         };
98         static const char *os_whitelist[] = {
99                 "clock",
100                 "date",
101                 "difftime",
102                 "exit",
103                 "getenv",
104                 "setlocale",
105                 "time",
106                 "tmpname",
107         };
108         static const char *debug_whitelist[] = {
109                 "gethook",
110                 "traceback",
111                 "getinfo",
112                 "getmetatable",
113                 "setupvalue",
114                 "setmetatable",
115                 "upvalueid",
116                 "upvaluejoin",
117                 "sethook",
118                 "debug",
119                 "getupvalue",
120                 "setlocal",
121         };
122         static const char *package_whitelist[] = {
123                 "config",
124                 "cpath",
125                 "path",
126                 "searchpath",
127         };
128         static const char *jit_whitelist[] = {
129                 "arch",
130                 "flush",
131                 "off",
132                 "on",
133                 "opt",
134                 "os",
135                 "status",
136                 "version",
137                 "version_num",
138         };
139
140         m_secure = true;
141
142         lua_State *L = getStack();
143
144         // Backup globals to the registry
145         lua_getglobal(L, "_G");
146         lua_setfield(L, LUA_REGISTRYINDEX, "globals_backup");
147
148         // Replace the global environment with an empty one
149 #if LUA_VERSION_NUM <= 501
150         int is_main = lua_pushthread(L);  // Push the main thread
151         FATAL_ERROR_IF(!is_main, "Security: ScriptApi's Lua state "
152                         "isn't the main Lua thread!");
153 #endif
154         lua_newtable(L);  // Create new environment
155         lua_pushvalue(L, -1);
156         lua_setfield(L, -2, "_G");  // Set _G of new environment
157 #if LUA_VERSION_NUM >= 502  // Lua >= 5.2
158         // Set the global environment
159         lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
160 #else  // Lua <= 5.1
161         // Set the environment of the main thread
162         FATAL_ERROR_IF(!lua_setfenv(L, -2), "Security: Unable to set "
163                         "environment of the main Lua thread!");
164         lua_pop(L, 1);  // Pop thread
165 #endif
166
167         // Get old globals
168         lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup");
169         int old_globals = lua_gettop(L);
170
171
172         // Copy safe base functions
173         lua_getglobal(L, "_G");
174         copy_safe(L, whitelist, sizeof(whitelist));
175
176         // And replace unsafe ones
177         SECURE_API(g, dofile);
178         SECURE_API(g, load);
179         SECURE_API(g, loadfile);
180         SECURE_API(g, loadstring);
181         SECURE_API(g, require);
182         lua_pop(L, 1);
183
184
185         // Copy safe IO functions
186         lua_getfield(L, old_globals, "io");
187         lua_newtable(L);
188         copy_safe(L, io_whitelist, sizeof(io_whitelist));
189
190         // And replace unsafe ones
191         SECURE_API(io, open);
192         SECURE_API(io, input);
193         SECURE_API(io, output);
194         SECURE_API(io, lines);
195
196         lua_setglobal(L, "io");
197         lua_pop(L, 1);  // Pop old IO
198
199
200         // Copy safe OS functions
201         lua_getfield(L, old_globals, "os");
202         lua_newtable(L);
203         copy_safe(L, os_whitelist, sizeof(os_whitelist));
204
205         // And replace unsafe ones
206         SECURE_API(os, remove);
207         SECURE_API(os, rename);
208
209         lua_setglobal(L, "os");
210         lua_pop(L, 1);  // Pop old OS
211
212
213         // Copy safe debug functions
214         lua_getfield(L, old_globals, "debug");
215         lua_newtable(L);
216         copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
217         lua_setglobal(L, "debug");
218         lua_pop(L, 1);  // Pop old debug
219
220
221         // Copy safe package fields
222         lua_getfield(L, old_globals, "package");
223         lua_newtable(L);
224         copy_safe(L, package_whitelist, sizeof(package_whitelist));
225         lua_setglobal(L, "package");
226         lua_pop(L, 1);  // Pop old package
227
228
229         // Copy safe jit functions, if they exist
230         lua_getfield(L, -1, "jit");
231         if (!lua_isnil(L, -1)) {
232                 lua_newtable(L);
233                 copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
234                 lua_setglobal(L, "jit");
235         }
236         lua_pop(L, 1);  // Pop old jit
237
238         lua_pop(L, 1); // Pop globals_backup
239 }
240
241
242 bool ScriptApiSecurity::isSecure(lua_State *L)
243 {
244         lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup");
245         bool secure = !lua_isnil(L, -1);
246         lua_pop(L, 1);
247         return secure;
248 }
249
250
251 #define CHECK_FILE_ERR(ret, fp) \
252         if (ret) { \
253                 if (fp) std::fclose(fp); \
254                 lua_pushfstring(L, "%s: %s", path, strerror(errno)); \
255                 return false; \
256         }
257
258
259 bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path)
260 {
261         FILE *fp;
262         char *chunk_name;
263         if (path == NULL) {
264                 fp = stdin;
265                 chunk_name = const_cast<char *>("=stdin");
266         } else {
267                 fp = fopen(path, "rb");
268                 if (!fp) {
269                         lua_pushfstring(L, "%s: %s", path, strerror(errno));
270                         return false;
271                 }
272                 chunk_name = new char[strlen(path) + 2];
273                 chunk_name[0] = '@';
274                 chunk_name[1] = '\0';
275                 strcat(chunk_name, path);
276         }
277
278         size_t start = 0;
279         int c = std::getc(fp);
280         if (c == '#') {
281                 // Skip the first line
282                 while ((c = std::getc(fp)) != EOF && c != '\n');
283                 if (c == '\n') c = std::getc(fp);
284                 start = std::ftell(fp);
285         }
286
287         if (c == LUA_SIGNATURE[0]) {
288                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
289                 return false;
290         }
291
292         // Read the file
293         int ret = std::fseek(fp, 0, SEEK_END);
294         CHECK_FILE_ERR(ret, fp);
295         if (ret) {
296                 std::fclose(fp);
297                 lua_pushfstring(L, "%s: %s", path, strerror(errno));
298                 return false;
299         }
300         size_t size = std::ftell(fp) - start;
301         char *code = new char[size];
302         ret = std::fseek(fp, start, SEEK_SET);
303         CHECK_FILE_ERR(ret, fp);
304         if (ret) {
305                 std::fclose(fp);
306                 lua_pushfstring(L, "%s: %s", path, strerror(errno));
307                 return false;
308         }
309         size_t num_read = std::fread(code, 1, size, fp);
310         if (path) {
311                 std::fclose(fp);
312         }
313         if (num_read != size) {
314                 lua_pushliteral(L, "Error reading file to load.");
315                 return false;
316         }
317
318         if (luaL_loadbuffer(L, code, size, chunk_name)) {
319                 return false;
320         }
321
322         if (path) {
323                 delete [] chunk_name;
324         }
325         return true;
326 }
327
328
329 bool ScriptApiSecurity::checkPath(lua_State *L, const char *path)
330 {
331         std::string str;  // Transient
332
333         std::string norel_path = fs::RemoveRelativePathComponents(path);
334         std::string abs_path = fs::AbsolutePath(norel_path);
335
336         if (!abs_path.empty()) {
337                 // Don't allow accessing the settings file
338                 str = fs::AbsolutePath(g_settings_path);
339                 if (str == abs_path) return false;
340         }
341
342         // If we couldn't find the absolute path (path doesn't exist) then
343         // try removing the last components until it works (to allow
344         // non-existent files/folders for mkdir).
345         std::string cur_path = norel_path;
346         std::string removed;
347         while (abs_path.empty() && !cur_path.empty()) {
348                 std::string tmp_rmed;
349                 cur_path = fs::RemoveLastPathComponent(cur_path, &tmp_rmed);
350                 removed = tmp_rmed + (removed.empty() ? "" : DIR_DELIM + removed);
351                 abs_path = fs::AbsolutePath(cur_path);
352         }
353         if (abs_path.empty()) return false;
354         // Add the removed parts back so that you can't, eg, create a
355         // directory in worldmods if worldmods doesn't exist.
356         if (!removed.empty()) abs_path += DIR_DELIM + removed;
357
358         // Get server from registry
359         lua_getfield(L, LUA_REGISTRYINDEX, "scriptapi");
360         ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
361         lua_pop(L, 1);
362         const Server *server = script->getServer();
363
364         if (!server) return false;
365
366         // Get mod name
367         lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
368         if (lua_isstring(L, -1)) {
369                 std::string mod_name = lua_tostring(L, -1);
370
371                 // Builtin can access anything
372                 if (mod_name == BUILTIN_MOD_NAME) {
373                         return true;
374                 }
375
376                 // Allow paths in mod path
377                 const ModSpec *mod = server->getModSpec(mod_name);
378                 if (mod) {
379                         str = fs::AbsolutePath(mod->path);
380                         if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
381                                 return true;
382                         }
383                 }
384         }
385         lua_pop(L, 1);  // Pop mod name
386
387         str = fs::AbsolutePath(server->getWorldPath());
388         if (str.empty()) return false;
389         // Don't allow access to world mods.  We add to the absolute path
390         // of the world instead of getting the absolute paths directly
391         // because that won't work if they don't exist.
392         if (fs::PathStartsWith(abs_path, str + DIR_DELIM + "worldmods") ||
393                         fs::PathStartsWith(abs_path, str + DIR_DELIM + "game")) {
394                 return false;
395         }
396         // Allow all other paths in world path
397         if (fs::PathStartsWith(abs_path, str)) {
398                 return true;
399         }
400
401         // Default to disallowing
402         return false;
403 }
404
405
406 int ScriptApiSecurity::sl_g_dofile(lua_State *L)
407 {
408         int nret = sl_g_loadfile(L);
409         if (nret != 1) {
410                 lua_error(L);
411                 // code after this function isn't executed
412         }
413         int top_precall = lua_gettop(L);
414         lua_call(L, 0, LUA_MULTRET);
415         // Return number of arguments returned by the function,
416         // adjusting for the function being poped.
417         return lua_gettop(L) - (top_precall - 1);
418 }
419
420
421 int ScriptApiSecurity::sl_g_load(lua_State *L)
422 {
423         size_t len;
424         const char *buf;
425         std::string code;
426         const char *chunk_name = "=(load)";
427
428         luaL_checktype(L, 1, LUA_TFUNCTION);
429         if (!lua_isnone(L, 2)) {
430                 luaL_checktype(L, 2, LUA_TSTRING);
431                 chunk_name = lua_tostring(L, 2);
432         }
433
434         while (true) {
435                 lua_pushvalue(L, 1);
436                 lua_call(L, 0, 1);
437                 int t = lua_type(L, -1);
438                 if (t == LUA_TNIL) {
439                         break;
440                 } else if (t != LUA_TSTRING) {
441                         lua_pushnil(L);
442                         lua_pushliteral(L, "Loader didn't return a string");
443                         return 2;
444                 }
445                 buf = lua_tolstring(L, -1, &len);
446                 code += std::string(buf, len);
447                 lua_pop(L, 1); // Pop return value
448         }
449         if (code[0] == LUA_SIGNATURE[0]) {
450                 lua_pushnil(L);
451                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
452                 return 2;
453         }
454         if (luaL_loadbuffer(L, code.data(), code.size(), chunk_name)) {
455                 lua_pushnil(L);
456                 lua_insert(L, lua_gettop(L) - 1);
457                 return 2;
458         }
459         return 1;
460 }
461
462
463 int ScriptApiSecurity::sl_g_loadfile(lua_State *L)
464 {
465         const char *path = NULL;
466
467         if (lua_isstring(L, 1)) {
468                 path = lua_tostring(L, 1);
469                 CHECK_SECURE_PATH(L, path);
470         }
471
472         if (!safeLoadFile(L, path)) {
473                 lua_pushnil(L);
474                 lua_insert(L, -2);
475                 return 2;
476         }
477
478         return 1;
479 }
480
481
482 int ScriptApiSecurity::sl_g_loadstring(lua_State *L)
483 {
484         const char *chunk_name = "=(load)";
485
486         luaL_checktype(L, 1, LUA_TSTRING);
487         if (!lua_isnone(L, 2)) {
488                 luaL_checktype(L, 2, LUA_TSTRING);
489                 chunk_name = lua_tostring(L, 2);
490         }
491
492         size_t size;
493         const char *code = lua_tolstring(L, 1, &size);
494
495         if (size > 0 && code[0] == LUA_SIGNATURE[0]) {
496                 lua_pushnil(L);
497                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
498                 return 2;
499         }
500         if (luaL_loadbuffer(L, code, size, chunk_name)) {
501                 lua_pushnil(L);
502                 lua_insert(L, lua_gettop(L) - 1);
503                 return 2;
504         }
505         return 1;
506 }
507
508
509 int ScriptApiSecurity::sl_g_require(lua_State *L)
510 {
511         lua_pushliteral(L, "require() is disabled when mod security is on.");
512         return lua_error(L);
513 }
514
515
516 int ScriptApiSecurity::sl_io_open(lua_State *L)
517 {
518         luaL_checktype(L, 1, LUA_TSTRING);
519         const char *path = lua_tostring(L, 1);
520         CHECK_SECURE_PATH(L, path);
521
522         push_original(L, "io", "open");
523         lua_pushvalue(L, 1);
524         lua_pushvalue(L, 2);
525         lua_call(L, 2, 2);
526         return 2;
527 }
528
529
530 int ScriptApiSecurity::sl_io_input(lua_State *L)
531 {
532         if (lua_isstring(L, 1)) {
533                 const char *path = lua_tostring(L, 1);
534                 CHECK_SECURE_PATH(L, path);
535         }
536
537         push_original(L, "io", "input");
538         lua_pushvalue(L, 1);
539         lua_call(L, 1, 1);
540         return 1;
541 }
542
543
544 int ScriptApiSecurity::sl_io_output(lua_State *L)
545 {
546         if (lua_isstring(L, 1)) {
547                 const char *path = lua_tostring(L, 1);
548                 CHECK_SECURE_PATH(L, path);
549         }
550
551         push_original(L, "io", "output");
552         lua_pushvalue(L, 1);
553         lua_call(L, 1, 1);
554         return 1;
555 }
556
557
558 int ScriptApiSecurity::sl_io_lines(lua_State *L)
559 {
560         if (lua_isstring(L, 1)) {
561                 const char *path = lua_tostring(L, 1);
562                 CHECK_SECURE_PATH(L, path);
563         }
564
565         push_original(L, "io", "lines");
566         lua_pushvalue(L, 1);
567         int top_precall = lua_gettop(L);
568         lua_call(L, 1, LUA_MULTRET);
569         // Return number of arguments returned by the function,
570         // adjusting for the function being poped.
571         return lua_gettop(L) - (top_precall - 1);
572 }
573
574
575 int ScriptApiSecurity::sl_os_rename(lua_State *L)
576 {
577         luaL_checktype(L, 1, LUA_TSTRING);
578         const char *path1 = lua_tostring(L, 1);
579         CHECK_SECURE_PATH(L, path1);
580
581         luaL_checktype(L, 2, LUA_TSTRING);
582         const char *path2 = lua_tostring(L, 2);
583         CHECK_SECURE_PATH(L, path2);
584
585         push_original(L, "os", "rename");
586         lua_pushvalue(L, 1);
587         lua_pushvalue(L, 2);
588         lua_call(L, 2, 2);
589         return 2;
590 }
591
592
593 int ScriptApiSecurity::sl_os_remove(lua_State *L)
594 {
595         luaL_checktype(L, 1, LUA_TSTRING);
596         const char *path = lua_tostring(L, 1);
597         CHECK_SECURE_PATH(L, path);
598
599         push_original(L, "os", "remove");
600         lua_pushvalue(L, 1);
601         lua_call(L, 1, 2);
602         return 2;
603 }
604