]> git.lizzy.rs Git - minetest.git/blob - src/script/cpp_api/s_security.cpp
Fix Windows build, clean up included headers
[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                 return nret;
411         }
412         int top_precall = lua_gettop(L);
413         lua_call(L, 0, LUA_MULTRET);
414         // Return number of arguments returned by the function,
415         // adjusting for the function being poped.
416         return lua_gettop(L) - (top_precall - 1);
417 }
418
419
420 int ScriptApiSecurity::sl_g_load(lua_State *L)
421 {
422         size_t len;
423         const char *buf;
424         std::string code;
425         const char *chunk_name = "=(load)";
426
427         luaL_checktype(L, 1, LUA_TFUNCTION);
428         if (!lua_isnone(L, 2)) {
429                 luaL_checktype(L, 2, LUA_TSTRING);
430                 chunk_name = lua_tostring(L, 2);
431         }
432
433         while (true) {
434                 lua_pushvalue(L, 1);
435                 lua_call(L, 0, 1);
436                 int t = lua_type(L, -1);
437                 if (t == LUA_TNIL) {
438                         break;
439                 } else if (t != LUA_TSTRING) {
440                         lua_pushnil(L);
441                         lua_pushliteral(L, "Loader didn't return a string");
442                         return 2;
443                 }
444                 buf = lua_tolstring(L, -1, &len);
445                 code += std::string(buf, len);
446                 lua_pop(L, 1); // Pop return value
447         }
448         if (code[0] == LUA_SIGNATURE[0]) {
449                 lua_pushnil(L);
450                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
451                 return 2;
452         }
453         if (luaL_loadbuffer(L, code.data(), code.size(), chunk_name)) {
454                 lua_pushnil(L);
455                 lua_insert(L, lua_gettop(L) - 1);
456                 return 2;
457         }
458         return 1;
459 }
460
461
462 int ScriptApiSecurity::sl_g_loadfile(lua_State *L)
463 {
464         const char *path = NULL;
465
466         if (lua_isstring(L, 1)) {
467                 path = lua_tostring(L, 1);
468                 CHECK_SECURE_PATH(L, path);
469         }
470
471         if (!safeLoadFile(L, path)) {
472                 lua_pushnil(L);
473                 lua_insert(L, -2);
474                 return 2;
475         }
476
477         return 1;
478 }
479
480
481 int ScriptApiSecurity::sl_g_loadstring(lua_State *L)
482 {
483         const char *chunk_name = "=(load)";
484
485         luaL_checktype(L, 1, LUA_TSTRING);
486         if (!lua_isnone(L, 2)) {
487                 luaL_checktype(L, 2, LUA_TSTRING);
488                 chunk_name = lua_tostring(L, 2);
489         }
490
491         size_t size;
492         const char *code = lua_tolstring(L, 1, &size);
493
494         if (size > 0 && code[0] == LUA_SIGNATURE[0]) {
495                 lua_pushnil(L);
496                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
497                 return 2;
498         }
499         if (luaL_loadbuffer(L, code, size, chunk_name)) {
500                 lua_pushnil(L);
501                 lua_insert(L, lua_gettop(L) - 1);
502                 return 2;
503         }
504         return 1;
505 }
506
507
508 int ScriptApiSecurity::sl_g_require(lua_State *L)
509 {
510         lua_pushliteral(L, "require() is disabled when mod security is on.");
511         return lua_error(L);
512 }
513
514
515 int ScriptApiSecurity::sl_io_open(lua_State *L)
516 {
517         luaL_checktype(L, 1, LUA_TSTRING);
518         const char *path = lua_tostring(L, 1);
519         CHECK_SECURE_PATH(L, path);
520
521         push_original(L, "io", "open");
522         lua_pushvalue(L, 1);
523         lua_pushvalue(L, 2);
524         lua_call(L, 2, 2);
525         return 2;
526 }
527
528
529 int ScriptApiSecurity::sl_io_input(lua_State *L)
530 {
531         if (lua_isstring(L, 1)) {
532                 const char *path = lua_tostring(L, 1);
533                 CHECK_SECURE_PATH(L, path);
534         }
535
536         push_original(L, "io", "input");
537         lua_pushvalue(L, 1);
538         lua_call(L, 1, 1);
539         return 1;
540 }
541
542
543 int ScriptApiSecurity::sl_io_output(lua_State *L)
544 {
545         if (lua_isstring(L, 1)) {
546                 const char *path = lua_tostring(L, 1);
547                 CHECK_SECURE_PATH(L, path);
548         }
549
550         push_original(L, "io", "output");
551         lua_pushvalue(L, 1);
552         lua_call(L, 1, 1);
553         return 1;
554 }
555
556
557 int ScriptApiSecurity::sl_io_lines(lua_State *L)
558 {
559         if (lua_isstring(L, 1)) {
560                 const char *path = lua_tostring(L, 1);
561                 CHECK_SECURE_PATH(L, path);
562         }
563
564         push_original(L, "io", "lines");
565         lua_pushvalue(L, 1);
566         int top_precall = lua_gettop(L);
567         lua_call(L, 1, LUA_MULTRET);
568         // Return number of arguments returned by the function,
569         // adjusting for the function being poped.
570         return lua_gettop(L) - (top_precall - 1);
571 }
572
573
574 int ScriptApiSecurity::sl_os_rename(lua_State *L)
575 {
576         luaL_checktype(L, 1, LUA_TSTRING);
577         const char *path1 = lua_tostring(L, 1);
578         CHECK_SECURE_PATH(L, path1);
579
580         luaL_checktype(L, 2, LUA_TSTRING);
581         const char *path2 = lua_tostring(L, 2);
582         CHECK_SECURE_PATH(L, path2);
583
584         push_original(L, "os", "rename");
585         lua_pushvalue(L, 1);
586         lua_pushvalue(L, 2);
587         lua_call(L, 2, 2);
588         return 2;
589 }
590
591
592 int ScriptApiSecurity::sl_os_remove(lua_State *L)
593 {
594         luaL_checktype(L, 1, LUA_TSTRING);
595         const char *path = lua_tostring(L, 1);
596         CHECK_SECURE_PATH(L, path);
597
598         push_original(L, "os", "remove");
599         lua_pushvalue(L, 1);
600         lua_call(L, 1, 2);
601         return 2;
602 }
603