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