]> git.lizzy.rs Git - minetest.git/blob - src/script/cpp_api/s_security.cpp
[CSM] Improve security for client-sided mods (#5100)
[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_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_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                 "getenv",
103                 "setlocale",
104                 "time",
105                 "tmpname",
106         };
107         static const char *debug_whitelist[] = {
108                 "gethook",
109                 "traceback",
110                 "getinfo",
111                 "getmetatable",
112                 "setupvalue",
113                 "setmetatable",
114                 "upvalueid",
115                 "upvaluejoin",
116                 "sethook",
117                 "debug",
118                 "setlocal",
119         };
120         static const char *package_whitelist[] = {
121                 "config",
122                 "cpath",
123                 "path",
124                 "searchpath",
125         };
126         static const char *jit_whitelist[] = {
127                 "arch",
128                 "flush",
129                 "off",
130                 "on",
131                 "opt",
132                 "os",
133                 "status",
134                 "version",
135                 "version_num",
136         };
137
138         m_secure = true;
139
140         lua_State *L = getStack();
141
142
143         int old_globals = backupGlobals(L);
144
145
146         // Copy safe base functions
147         lua_getglobal(L, "_G");
148         copy_safe(L, whitelist, sizeof(whitelist));
149
150         // And replace unsafe ones
151         SECURE_API(g, dofile);
152         SECURE_API(g, load);
153         SECURE_API(g, loadfile);
154         SECURE_API(g, loadstring);
155         SECURE_API(g, require);
156         lua_pop(L, 1);
157
158
159         // Copy safe IO functions
160         lua_getfield(L, old_globals, "io");
161         lua_newtable(L);
162         copy_safe(L, io_whitelist, sizeof(io_whitelist));
163
164         // And replace unsafe ones
165         SECURE_API(io, open);
166         SECURE_API(io, input);
167         SECURE_API(io, output);
168         SECURE_API(io, lines);
169
170         lua_setglobal(L, "io");
171         lua_pop(L, 1);  // Pop old IO
172
173
174         // Copy safe OS functions
175         lua_getfield(L, old_globals, "os");
176         lua_newtable(L);
177         copy_safe(L, os_whitelist, sizeof(os_whitelist));
178
179         // And replace unsafe ones
180         SECURE_API(os, remove);
181         SECURE_API(os, rename);
182
183         lua_setglobal(L, "os");
184         lua_pop(L, 1);  // Pop old OS
185
186
187         // Copy safe debug functions
188         lua_getfield(L, old_globals, "debug");
189         lua_newtable(L);
190         copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
191         lua_setglobal(L, "debug");
192         lua_pop(L, 1);  // Pop old debug
193
194
195         // Copy safe package fields
196         lua_getfield(L, old_globals, "package");
197         lua_newtable(L);
198         copy_safe(L, package_whitelist, sizeof(package_whitelist));
199         lua_setglobal(L, "package");
200         lua_pop(L, 1);  // Pop old package
201
202 #if USE_LUAJIT
203         // Copy safe jit functions, if they exist
204         lua_getfield(L, -1, "jit");
205         if (!lua_isnil(L, -1)) {
206                 lua_newtable(L);
207                 copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
208                 lua_setglobal(L, "jit");
209         }
210         lua_pop(L, 1);  // Pop old jit
211 #endif
212
213         lua_pop(L, 1); // Pop globals_backup
214 }
215
216 void ScriptApiSecurity::initializeSecurityClient()
217 {
218         static const char *whitelist[] = {
219                 "assert",
220                 "core",
221                 "collectgarbage",
222                 "DIR_DELIM",
223                 "error",
224                 "getfenv",
225                 "ipairs",
226                 "next",
227                 "pairs",
228                 "pcall",
229                 "print",
230                 "rawequal",
231                 "rawget",
232                 "rawset",
233                 "select",
234                 "setfenv",
235                 "setmetatable",
236                 "tonumber",
237                 "tostring",
238                 "type",
239                 "unpack",
240                 "_VERSION",
241                 "xpcall",
242                 // Completely safe libraries
243                 "coroutine",
244                 "string",
245                 "table",
246                 "math",
247         };
248         static const char *io_whitelist[] = {
249                 "close",
250                 "flush",
251                 "read",
252                 "type",
253                 "write",
254         };
255         static const char *os_whitelist[] = {
256                 "clock",
257                 "date",
258                 "difftime",     
259                 "time",
260                 "setlocale",
261         };
262         static const char *debug_whitelist[] = {
263                 "getinfo",
264         };
265
266         static const char *jit_whitelist[] = {
267                 "arch",
268                 "flush",
269                 "off",
270                 "on",
271                 "opt",
272                 "os",
273                 "status",
274                 "version",
275                 "version_num",
276         };
277
278         m_secure = true;
279
280         lua_State *L = getStack();
281
282
283         int old_globals = backupGlobals(L);
284
285
286         // Copy safe base functions
287         lua_getglobal(L, "_G");
288         copy_safe(L, whitelist, sizeof(whitelist));
289
290         // And replace unsafe ones
291         SECURE_API(g, dofile);
292         SECURE_API(g, loadstring);
293         SECURE_API(g, require);
294         lua_pop(L, 1);
295
296
297         // Copy safe IO functions
298         lua_getfield(L, old_globals, "io");
299         lua_newtable(L);
300         copy_safe(L, io_whitelist, sizeof(io_whitelist));
301
302         // And replace unsafe ones
303         SECURE_API(io, open);
304         SECURE_API(io, input);
305         SECURE_API(io, output);
306         SECURE_API(io, lines);
307
308         lua_setglobal(L, "io");
309         lua_pop(L, 1);  // Pop old IO
310
311
312         // Copy safe OS functions
313         lua_getfield(L, old_globals, "os");
314         lua_newtable(L);
315         copy_safe(L, os_whitelist, sizeof(os_whitelist));
316         lua_setglobal(L, "os");
317         lua_pop(L, 1);  // Pop old OS
318
319
320         // Copy safe debug functions
321         lua_getfield(L, old_globals, "debug");
322         lua_newtable(L);
323         copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
324         lua_setglobal(L, "debug");
325         lua_pop(L, 1);  // Pop old debug
326
327         // Remove all of package
328         lua_newtable(L);
329         lua_setglobal(L, "package");
330
331 #if USE_LUAJIT
332         // Copy safe jit functions, if they exist
333         lua_getfield(L, -1, "jit");
334         if (!lua_isnil(L, -1)) {
335                 lua_newtable(L);
336                 copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
337                 lua_setglobal(L, "jit");
338         }
339         lua_pop(L, 1);  // Pop old jit
340 #endif
341
342         lua_pop(L, 1); // Pop globals_backup
343 }
344
345 int ScriptApiSecurity::backupGlobals(lua_State *L)
346 {
347         // Backup globals to the registry
348         lua_getglobal(L, "_G");
349         lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
350
351         // Replace the global environment with an empty one
352 #if LUA_VERSION_NUM <= 501
353         int is_main = lua_pushthread(L);  // Push the main thread
354         FATAL_ERROR_IF(!is_main, "Security: ScriptApi's Lua state "
355                 "isn't the main Lua thread!");
356 #endif
357         lua_newtable(L);  // Create new environment
358         lua_pushvalue(L, -1);
359         lua_setfield(L, -2, "_G");  // Set _G of new environment
360 #if LUA_VERSION_NUM >= 502  // Lua >= 5.2
361         // Set the global environment
362         lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
363 #else  // Lua <= 5.1
364         // Set the environment of the main thread
365         FATAL_ERROR_IF(!lua_setfenv(L, -2), "Security: Unable to set "
366                 "environment of the main Lua thread!");
367         lua_pop(L, 1);  // Pop thread
368 #endif
369
370         // Get old globals
371         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
372         return lua_gettop(L);
373 }
374
375 bool ScriptApiSecurity::isSecure(lua_State *L)
376 {
377         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
378         bool secure = !lua_isnil(L, -1);
379         lua_pop(L, 1);
380         return secure;
381 }
382
383
384 #define CHECK_FILE_ERR(ret, fp) \
385         if (ret) { \
386                 lua_pushfstring(L, "%s: %s", path, strerror(errno)); \
387                 if (fp) std::fclose(fp); \
388                 return false; \
389         }
390
391
392 bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path)
393 {
394         FILE *fp;
395         char *chunk_name;
396         if (path == NULL) {
397                 fp = stdin;
398                 chunk_name = const_cast<char *>("=stdin");
399         } else {
400                 fp = fopen(path, "rb");
401                 if (!fp) {
402                         lua_pushfstring(L, "%s: %s", path, strerror(errno));
403                         return false;
404                 }
405                 chunk_name = new char[strlen(path) + 2];
406                 chunk_name[0] = '@';
407                 chunk_name[1] = '\0';
408                 strcat(chunk_name, path);
409         }
410
411         size_t start = 0;
412         int c = std::getc(fp);
413         if (c == '#') {
414                 // Skip the first line
415                 while ((c = std::getc(fp)) != EOF && c != '\n');
416                 if (c == '\n') c = std::getc(fp);
417                 start = std::ftell(fp);
418         }
419
420         if (c == LUA_SIGNATURE[0]) {
421                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
422                 std::fclose(fp);
423                 if (path) {
424                         delete [] chunk_name;
425                 }
426                 return false;
427         }
428
429         // Read the file
430         int ret = std::fseek(fp, 0, SEEK_END);
431         CHECK_FILE_ERR(ret, fp);
432
433         size_t size = std::ftell(fp) - start;
434         char *code = new char[size];
435         ret = std::fseek(fp, start, SEEK_SET);
436         if (ret) {
437                 lua_pushfstring(L, "%s: %s", path, strerror(errno));
438                 std::fclose(fp);
439                 delete [] code;
440                 if (path) {
441                         delete [] chunk_name;
442                 }
443                 return false;
444         }
445
446         size_t num_read = std::fread(code, 1, size, fp);
447         if (path) {
448                 std::fclose(fp);
449         }
450         if (num_read != size) {
451                 lua_pushliteral(L, "Error reading file to load.");
452                 delete [] code;
453                 if (path) {
454                         delete [] chunk_name;
455                 }
456                 return false;
457         }
458
459         if (luaL_loadbuffer(L, code, size, chunk_name)) {
460                 delete [] code;
461                 return false;
462         }
463
464         delete [] code;
465
466         if (path) {
467                 delete [] chunk_name;
468         }
469         return true;
470 }
471
472
473 bool ScriptApiSecurity::checkPath(lua_State *L, const char *path,
474                 bool write_required, bool *write_allowed)
475 {
476         if (write_allowed)
477                 *write_allowed = false;
478
479         std::string str;  // Transient
480
481         std::string abs_path = fs::AbsolutePath(path);
482
483         if (!abs_path.empty()) {
484                 // Don't allow accessing the settings file
485                 str = fs::AbsolutePath(g_settings_path);
486                 if (str == abs_path) return false;
487         }
488
489         // If we couldn't find the absolute path (path doesn't exist) then
490         // try removing the last components until it works (to allow
491         // non-existent files/folders for mkdir).
492         std::string cur_path = path;
493         std::string removed;
494         while (abs_path.empty() && !cur_path.empty()) {
495                 std::string component;
496                 cur_path = fs::RemoveLastPathComponent(cur_path, &component);
497                 if (component == "..") {
498                         // Parent components can't be allowed or we could allow something like
499                         // /home/user/minetest/worlds/foo/noexist/../../../../../../etc/passwd.
500                         // If we have previous non-relative elements in the path we might be
501                         // able to remove them so that things like worlds/foo/noexist/../auth.txt
502                         // could be allowed, but those paths will be interpreted as nonexistent
503                         // by the operating system anyways.
504                         return false;
505                 }
506                 removed = component + (removed.empty() ? "" : DIR_DELIM + removed);
507                 abs_path = fs::AbsolutePath(cur_path);
508         }
509         if (abs_path.empty())
510                 return false;
511         // Add the removed parts back so that you can't, eg, create a
512         // directory in worldmods if worldmods doesn't exist.
513         if (!removed.empty())
514                 abs_path += DIR_DELIM + removed;
515
516         // Get server from registry
517         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
518         ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
519         lua_pop(L, 1);
520         const IGameDef *gamedef = script->getGameDef();
521         if (!gamedef)
522                 return false;
523
524         // Get mod name
525         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
526         if (lua_isstring(L, -1)) {
527                 std::string mod_name = lua_tostring(L, -1);
528
529                 // Builtin can access anything
530                 if (mod_name == BUILTIN_MOD_NAME) {
531                         if (write_allowed) *write_allowed = true;
532                         return true;
533                 }
534
535                 // Allow paths in mod path
536                 // Don't bother if write access isn't important, since it will be handled later
537                 if (write_required || write_allowed != NULL) {
538                         const ModSpec *mod = gamedef->getModSpec(mod_name);
539                         if (mod) {
540                                 str = fs::AbsolutePath(mod->path);
541                                 if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
542                                         if (write_allowed) *write_allowed = true;
543                                         return true;
544                                 }
545                         }
546                 }
547         }
548         lua_pop(L, 1);  // Pop mod name
549
550         // Allow read-only access to all mod directories
551         if (!write_required) {
552                 const std::vector<ModSpec> mods = gamedef->getMods();
553                 for (size_t i = 0; i < mods.size(); ++i) {
554                         str = fs::AbsolutePath(mods[i].path);
555                         if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
556                                 return true;
557                         }
558                 }
559         }
560
561         str = fs::AbsolutePath(gamedef->getWorldPath());
562         if (!str.empty()) {
563                 // Don't allow access to other paths in the world mod/game path.
564                 // These have to be blocked so you can't override a trusted mod
565                 // by creating a mod with the same name in a world mod directory.
566                 // We add to the absolute path of the world instead of getting
567                 // the absolute paths directly because that won't work if they
568                 // don't exist.
569                 if (fs::PathStartsWith(abs_path, str + DIR_DELIM + "worldmods") ||
570                                 fs::PathStartsWith(abs_path, str + DIR_DELIM + "game")) {
571                         return false;
572                 }
573                 // Allow all other paths in world path
574                 if (fs::PathStartsWith(abs_path, str)) {
575                         if (write_allowed) *write_allowed = true;
576                         return true;
577                 }
578         }
579
580         // Default to disallowing
581         return false;
582 }
583
584
585 int ScriptApiSecurity::sl_g_dofile(lua_State *L)
586 {
587         int nret = sl_g_loadfile(L);
588         if (nret != 1) {
589                 lua_error(L);
590                 // code after this function isn't executed
591         }
592         int top_precall = lua_gettop(L);
593         lua_call(L, 0, LUA_MULTRET);
594         // Return number of arguments returned by the function,
595         // adjusting for the function being poped.
596         return lua_gettop(L) - (top_precall - 1);
597 }
598
599
600 int ScriptApiSecurity::sl_g_load(lua_State *L)
601 {
602         size_t len;
603         const char *buf;
604         std::string code;
605         const char *chunk_name = "=(load)";
606
607         luaL_checktype(L, 1, LUA_TFUNCTION);
608         if (!lua_isnone(L, 2)) {
609                 luaL_checktype(L, 2, LUA_TSTRING);
610                 chunk_name = lua_tostring(L, 2);
611         }
612
613         while (true) {
614                 lua_pushvalue(L, 1);
615                 lua_call(L, 0, 1);
616                 int t = lua_type(L, -1);
617                 if (t == LUA_TNIL) {
618                         break;
619                 } else if (t != LUA_TSTRING) {
620                         lua_pushnil(L);
621                         lua_pushliteral(L, "Loader didn't return a string");
622                         return 2;
623                 }
624                 buf = lua_tolstring(L, -1, &len);
625                 code += std::string(buf, len);
626                 lua_pop(L, 1); // Pop return value
627         }
628         if (code[0] == LUA_SIGNATURE[0]) {
629                 lua_pushnil(L);
630                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
631                 return 2;
632         }
633         if (luaL_loadbuffer(L, code.data(), code.size(), chunk_name)) {
634                 lua_pushnil(L);
635                 lua_insert(L, lua_gettop(L) - 1);
636                 return 2;
637         }
638         return 1;
639 }
640
641
642 int ScriptApiSecurity::sl_g_loadfile(lua_State *L)
643 {
644         const char *path = NULL;
645
646         if (lua_isstring(L, 1)) {
647                 path = lua_tostring(L, 1);
648                 CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
649         }
650
651         if (!safeLoadFile(L, path)) {
652                 lua_pushnil(L);
653                 lua_insert(L, -2);
654                 return 2;
655         }
656
657         return 1;
658 }
659
660
661 int ScriptApiSecurity::sl_g_loadstring(lua_State *L)
662 {
663         const char *chunk_name = "=(load)";
664
665         luaL_checktype(L, 1, LUA_TSTRING);
666         if (!lua_isnone(L, 2)) {
667                 luaL_checktype(L, 2, LUA_TSTRING);
668                 chunk_name = lua_tostring(L, 2);
669         }
670
671         size_t size;
672         const char *code = lua_tolstring(L, 1, &size);
673
674         if (size > 0 && code[0] == LUA_SIGNATURE[0]) {
675                 lua_pushnil(L);
676                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
677                 return 2;
678         }
679         if (luaL_loadbuffer(L, code, size, chunk_name)) {
680                 lua_pushnil(L);
681                 lua_insert(L, lua_gettop(L) - 1);
682                 return 2;
683         }
684         return 1;
685 }
686
687
688 int ScriptApiSecurity::sl_g_require(lua_State *L)
689 {
690         lua_pushliteral(L, "require() is disabled when mod security is on.");
691         return lua_error(L);
692 }
693
694
695 int ScriptApiSecurity::sl_io_open(lua_State *L)
696 {
697         bool with_mode = lua_gettop(L) > 1;
698
699         luaL_checktype(L, 1, LUA_TSTRING);
700         const char *path = lua_tostring(L, 1);
701
702         bool write_requested = false;
703         if (with_mode) {
704                 luaL_checktype(L, 2, LUA_TSTRING);
705                 const char *mode = lua_tostring(L, 2);
706                 write_requested = strchr(mode, 'w') != NULL ||
707                         strchr(mode, '+') != NULL ||
708                         strchr(mode, 'a') != NULL;
709         }
710         CHECK_SECURE_PATH_INTERNAL(L, path, write_requested, NULL);
711
712         push_original(L, "io", "open");
713         lua_pushvalue(L, 1);
714         if (with_mode) {
715                 lua_pushvalue(L, 2);
716         }
717
718         lua_call(L, with_mode ? 2 : 1, 2);
719         return 2;
720 }
721
722
723 int ScriptApiSecurity::sl_io_input(lua_State *L)
724 {
725         if (lua_isstring(L, 1)) {
726                 const char *path = lua_tostring(L, 1);
727                 CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
728         }
729
730         push_original(L, "io", "input");
731         lua_pushvalue(L, 1);
732         lua_call(L, 1, 1);
733         return 1;
734 }
735
736
737 int ScriptApiSecurity::sl_io_output(lua_State *L)
738 {
739         if (lua_isstring(L, 1)) {
740                 const char *path = lua_tostring(L, 1);
741                 CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);
742         }
743
744         push_original(L, "io", "output");
745         lua_pushvalue(L, 1);
746         lua_call(L, 1, 1);
747         return 1;
748 }
749
750
751 int ScriptApiSecurity::sl_io_lines(lua_State *L)
752 {
753         if (lua_isstring(L, 1)) {
754                 const char *path = lua_tostring(L, 1);
755                 CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
756         }
757
758         int top_precall = lua_gettop(L);
759         push_original(L, "io", "lines");
760         lua_pushvalue(L, 1);
761         lua_call(L, 1, LUA_MULTRET);
762         // Return number of arguments returned by the function,
763         // adjusting for the function being poped.
764         return lua_gettop(L) - top_precall;
765 }
766
767
768 int ScriptApiSecurity::sl_os_rename(lua_State *L)
769 {
770         luaL_checktype(L, 1, LUA_TSTRING);
771         const char *path1 = lua_tostring(L, 1);
772         CHECK_SECURE_PATH_INTERNAL(L, path1, true, NULL);
773
774         luaL_checktype(L, 2, LUA_TSTRING);
775         const char *path2 = lua_tostring(L, 2);
776         CHECK_SECURE_PATH_INTERNAL(L, path2, true, NULL);
777
778         push_original(L, "os", "rename");
779         lua_pushvalue(L, 1);
780         lua_pushvalue(L, 2);
781         lua_call(L, 2, 2);
782         return 2;
783 }
784
785
786 int ScriptApiSecurity::sl_os_remove(lua_State *L)
787 {
788         luaL_checktype(L, 1, LUA_TSTRING);
789         const char *path = lua_tostring(L, 1);
790         CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);
791
792         push_original(L, "os", "remove");
793         lua_pushvalue(L, 1);
794         lua_call(L, 1, 2);
795         return 2;
796 }
797