]> git.lizzy.rs Git - minetest.git/blob - src/script/cpp_api/s_security.cpp
Move client-specific files to 'src/client' (#7902)
[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 "client/client.h"
26 #include "settings.h"
27
28 #include <cerrno>
29 #include <string>
30 #include <iostream>
31
32
33 #define SECURE_API(lib, name) \
34         lua_pushcfunction(L, sl_##lib##_##name); \
35         lua_setfield(L, -2, #name);
36
37
38 static inline void copy_safe(lua_State *L, const char *list[], unsigned len, int from=-2, int to=-1)
39 {
40         if (from < 0) from = lua_gettop(L) + from + 1;
41         if (to   < 0) to   = lua_gettop(L) + to   + 1;
42         for (unsigned i = 0; i < (len / sizeof(list[0])); i++) {
43                 lua_getfield(L, from, list[i]);
44                 lua_setfield(L, to,   list[i]);
45         }
46 }
47
48 // Pushes the original version of a library function on the stack, from the old version
49 static inline void push_original(lua_State *L, const char *lib, const char *func)
50 {
51         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
52         lua_getfield(L, -1, lib);
53         lua_remove(L, -2);  // Remove globals_backup
54         lua_getfield(L, -1, func);
55         lua_remove(L, -2);  // Remove lib
56 }
57
58
59 void ScriptApiSecurity::initializeSecurity()
60 {
61         static const char *whitelist[] = {
62                 "assert",
63                 "core",
64                 "collectgarbage",
65                 "DIR_DELIM",
66                 "error",
67                 "getfenv",
68                 "getmetatable",
69                 "ipairs",
70                 "next",
71                 "pairs",
72                 "pcall",
73                 "print",
74                 "rawequal",
75                 "rawget",
76                 "rawset",
77                 "select",
78                 "setfenv",
79                 "setmetatable",
80                 "tonumber",
81                 "tostring",
82                 "type",
83                 "unpack",
84                 "_VERSION",
85                 "xpcall",
86                 // Completely safe libraries
87                 "coroutine",
88                 "string",
89                 "table",
90                 "math",
91         };
92         static const char *io_whitelist[] = {
93                 "close",
94                 "flush",
95                 "read",
96                 "type",
97                 "write",
98         };
99         static const char *os_whitelist[] = {
100                 "clock",
101                 "date",
102                 "difftime",
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                 "setlocal",
120         };
121         static const char *package_whitelist[] = {
122                 "config",
123                 "cpath",
124                 "path",
125                 "searchpath",
126         };
127 #if USE_LUAJIT
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 #endif
140         m_secure = true;
141
142         lua_State *L = getStack();
143
144         // Backup globals to the registry
145         lua_getglobal(L, "_G");
146         lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
147
148         // Replace the global environment with an empty one
149         int thread = getThread(L);
150         createEmptyEnv(L);
151         setLuaEnv(L, thread);
152
153         // Get old globals
154         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
155         int old_globals = lua_gettop(L);
156
157
158         // Copy safe base functions
159         lua_getglobal(L, "_G");
160         copy_safe(L, whitelist, sizeof(whitelist));
161
162         // And replace unsafe ones
163         SECURE_API(g, dofile);
164         SECURE_API(g, load);
165         SECURE_API(g, loadfile);
166         SECURE_API(g, loadstring);
167         SECURE_API(g, require);
168         lua_pop(L, 1);
169
170
171         // Copy safe IO functions
172         lua_getfield(L, old_globals, "io");
173         lua_newtable(L);
174         copy_safe(L, io_whitelist, sizeof(io_whitelist));
175
176         // And replace unsafe ones
177         SECURE_API(io, open);
178         SECURE_API(io, input);
179         SECURE_API(io, output);
180         SECURE_API(io, lines);
181
182         lua_setglobal(L, "io");
183         lua_pop(L, 1);  // Pop old IO
184
185
186         // Copy safe OS functions
187         lua_getfield(L, old_globals, "os");
188         lua_newtable(L);
189         copy_safe(L, os_whitelist, sizeof(os_whitelist));
190
191         // And replace unsafe ones
192         SECURE_API(os, remove);
193         SECURE_API(os, rename);
194
195         lua_setglobal(L, "os");
196         lua_pop(L, 1);  // Pop old OS
197
198
199         // Copy safe debug functions
200         lua_getfield(L, old_globals, "debug");
201         lua_newtable(L);
202         copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
203         lua_setglobal(L, "debug");
204         lua_pop(L, 1);  // Pop old debug
205
206
207         // Copy safe package fields
208         lua_getfield(L, old_globals, "package");
209         lua_newtable(L);
210         copy_safe(L, package_whitelist, sizeof(package_whitelist));
211         lua_setglobal(L, "package");
212         lua_pop(L, 1);  // Pop old package
213
214 #if USE_LUAJIT
215         // Copy safe jit functions, if they exist
216         lua_getfield(L, -1, "jit");
217         if (!lua_isnil(L, -1)) {
218                 lua_newtable(L);
219                 copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
220                 lua_setglobal(L, "jit");
221         }
222         lua_pop(L, 1);  // Pop old jit
223 #endif
224
225         lua_pop(L, 1); // Pop globals_backup
226 }
227
228 void ScriptApiSecurity::initializeSecurityClient()
229 {
230         static const char *whitelist[] = {
231                 "assert",
232                 "core",
233                 "collectgarbage",
234                 "DIR_DELIM",
235                 "error",
236                 "getfenv",
237                 "ipairs",
238                 "next",
239                 "pairs",
240                 "pcall",
241                 "print",
242                 "rawequal",
243                 "rawget",
244                 "rawset",
245                 "select",
246                 "setfenv",
247                 "setmetatable",
248                 "tonumber",
249                 "tostring",
250                 "type",
251                 "unpack",
252                 "_VERSION",
253                 "xpcall",
254                 // Completely safe libraries
255                 "coroutine",
256                 "string",
257                 "table",
258                 "math",
259         };
260         static const char *os_whitelist[] = {
261                 "clock",
262                 "date",
263                 "difftime",
264                 "time"
265         };
266         static const char *debug_whitelist[] = {
267                 "getinfo",
268         };
269
270 #if USE_LUAJIT
271         static const char *jit_whitelist[] = {
272                 "arch",
273                 "flush",
274                 "off",
275                 "on",
276                 "opt",
277                 "os",
278                 "status",
279                 "version",
280                 "version_num",
281         };
282 #endif
283
284         m_secure = true;
285
286         lua_State *L = getStack();
287         int thread = getThread(L);
288
289         // create an empty environment
290         createEmptyEnv(L);
291
292         // Copy safe base functions
293         lua_getglobal(L, "_G");
294         lua_getfield(L, -2, "_G");
295         copy_safe(L, whitelist, sizeof(whitelist));
296
297         // And replace unsafe ones
298         SECURE_API(g, dofile);
299         SECURE_API(g, load);
300         SECURE_API(g, loadfile);
301         SECURE_API(g, loadstring);
302         SECURE_API(g, require);
303         lua_pop(L, 2);
304
305
306
307         // Copy safe OS functions
308         lua_getglobal(L, "os");
309         lua_newtable(L);
310         copy_safe(L, os_whitelist, sizeof(os_whitelist));
311         lua_setfield(L, -3, "os");
312         lua_pop(L, 1);  // Pop old OS
313
314
315         // Copy safe debug functions
316         lua_getglobal(L, "debug");
317         lua_newtable(L);
318         copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
319         lua_setfield(L, -3, "debug");
320         lua_pop(L, 1);  // Pop old debug
321
322 #if USE_LUAJIT
323         // Copy safe jit functions, if they exist
324         lua_getglobal(L, "jit");
325         lua_newtable(L);
326         copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
327         lua_setfield(L, -3, "jit");
328         lua_pop(L, 1);  // Pop old jit
329 #endif
330
331         // Set the environment to the one we created earlier
332         setLuaEnv(L, thread);
333 }
334
335 int ScriptApiSecurity::getThread(lua_State *L)
336 {
337 #if LUA_VERSION_NUM <= 501
338         int is_main = lua_pushthread(L);  // Push the main thread
339         FATAL_ERROR_IF(!is_main, "Security: ScriptApi's Lua state "
340                 "isn't the main Lua thread!");
341         return lua_gettop(L);
342 #endif
343         return 0;
344 }
345
346 void ScriptApiSecurity::createEmptyEnv(lua_State *L)
347 {
348         lua_newtable(L);  // Create new environment
349         lua_pushvalue(L, -1);
350         lua_setfield(L, -2, "_G");  // Create the _G loop
351 }
352
353 void ScriptApiSecurity::setLuaEnv(lua_State *L, int thread)
354 {
355 #if LUA_VERSION_NUM >= 502  // Lua >= 5.2
356         // Set the global environment
357         lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
358 #else  // Lua <= 5.1
359         // Set the environment of the main thread
360         FATAL_ERROR_IF(!lua_setfenv(L, thread), "Security: Unable to set "
361                 "environment of the main Lua thread!");
362         lua_pop(L, 1);  // Pop thread
363 #endif
364 }
365
366 bool ScriptApiSecurity::isSecure(lua_State *L)
367 {
368         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
369         bool secure = !lua_isnil(L, -1);
370         lua_pop(L, 1);
371         return secure;
372 }
373
374
375 #define CHECK_FILE_ERR(ret, fp) \
376         if (ret) { \
377                 lua_pushfstring(L, "%s: %s", path, strerror(errno)); \
378                 if (fp) std::fclose(fp); \
379                 return false; \
380         }
381
382
383 bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path, const char *display_name)
384 {
385         FILE *fp;
386         char *chunk_name;
387         if (!display_name)
388                 display_name = path;
389         if (!path) {
390                 fp = stdin;
391                 chunk_name = const_cast<char *>("=stdin");
392         } else {
393                 fp = fopen(path, "rb");
394                 if (!fp) {
395                         lua_pushfstring(L, "%s: %s", path, strerror(errno));
396                         return false;
397                 }
398                 chunk_name = new char[strlen(display_name) + 2];
399                 chunk_name[0] = '@';
400                 chunk_name[1] = '\0';
401                 strcat(chunk_name, display_name);
402         }
403
404         size_t start = 0;
405         int c = std::getc(fp);
406         if (c == '#') {
407                 // Skip the first line
408                 while ((c = std::getc(fp)) != EOF && c != '\n');
409                 if (c == '\n') c = std::getc(fp);
410                 start = std::ftell(fp);
411         }
412
413         if (c == LUA_SIGNATURE[0]) {
414                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
415                 std::fclose(fp);
416                 if (path) {
417                         delete [] chunk_name;
418                 }
419                 return false;
420         }
421
422         // Read the file
423         int ret = std::fseek(fp, 0, SEEK_END);
424         if (ret) {
425                 lua_pushfstring(L, "%s: %s", path, strerror(errno));
426                 std::fclose(fp);
427                 if (path) {
428                         delete [] chunk_name;
429                 }
430                 return false;
431         }
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.append(component).append(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 = readParam<std::string>(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 (const ModSpec &mod : mods) {
554                         str = fs::AbsolutePath(mod.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                 }
620
621                 if (t != LUA_TSTRING) {
622                         lua_pushnil(L);
623                         lua_pushliteral(L, "Loader didn't return a string");
624                         return 2;
625                 }
626                 buf = lua_tolstring(L, -1, &len);
627                 code += std::string(buf, len);
628                 lua_pop(L, 1); // Pop return value
629         }
630         if (code[0] == LUA_SIGNATURE[0]) {
631                 lua_pushnil(L);
632                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
633                 return 2;
634         }
635         if (luaL_loadbuffer(L, code.data(), code.size(), chunk_name)) {
636                 lua_pushnil(L);
637                 lua_insert(L, lua_gettop(L) - 1);
638                 return 2;
639         }
640         return 1;
641 }
642
643
644 int ScriptApiSecurity::sl_g_loadfile(lua_State *L)
645 {
646 #ifndef SERVER
647         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
648         ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
649         lua_pop(L, 1);
650
651         if (script->getType() == ScriptingType::Client) {
652                 std::string display_path = readParam<std::string>(L, 1);
653                 const std::string *path = script->getClient()->getModFile(display_path);
654                 if (!path) {
655                         std::string error_msg = "Coudln't find script called:" + display_path;
656                         lua_pushnil(L);
657                         lua_pushstring(L, error_msg.c_str());
658                         return 2;
659                 }
660                 if (!safeLoadFile(L, path->c_str(), display_path.c_str())) {
661                         lua_pushnil(L);
662                         lua_insert(L, -2);
663                         return 2;
664                 }
665                 return 1;
666         }
667 #endif
668         const char *path = NULL;
669         if (lua_isstring(L, 1)) {
670                 path = lua_tostring(L, 1);
671                 CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
672         }
673
674         if (!safeLoadFile(L, path)) {
675                 lua_pushnil(L);
676                 lua_insert(L, -2);
677                 return 2;
678         }
679
680         return 1;
681 }
682
683
684 int ScriptApiSecurity::sl_g_loadstring(lua_State *L)
685 {
686         const char *chunk_name = "=(load)";
687
688         luaL_checktype(L, 1, LUA_TSTRING);
689         if (!lua_isnone(L, 2)) {
690                 luaL_checktype(L, 2, LUA_TSTRING);
691                 chunk_name = lua_tostring(L, 2);
692         }
693
694         size_t size;
695         const char *code = lua_tolstring(L, 1, &size);
696
697         if (size > 0 && code[0] == LUA_SIGNATURE[0]) {
698                 lua_pushnil(L);
699                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
700                 return 2;
701         }
702         if (luaL_loadbuffer(L, code, size, chunk_name)) {
703                 lua_pushnil(L);
704                 lua_insert(L, lua_gettop(L) - 1);
705                 return 2;
706         }
707         return 1;
708 }
709
710
711 int ScriptApiSecurity::sl_g_require(lua_State *L)
712 {
713         lua_pushliteral(L, "require() is disabled when mod security is on.");
714         return lua_error(L);
715 }
716
717
718 int ScriptApiSecurity::sl_io_open(lua_State *L)
719 {
720         bool with_mode = lua_gettop(L) > 1;
721
722         luaL_checktype(L, 1, LUA_TSTRING);
723         const char *path = lua_tostring(L, 1);
724
725         bool write_requested = false;
726         if (with_mode) {
727                 luaL_checktype(L, 2, LUA_TSTRING);
728                 const char *mode = lua_tostring(L, 2);
729                 write_requested = strchr(mode, 'w') != NULL ||
730                         strchr(mode, '+') != NULL ||
731                         strchr(mode, 'a') != NULL;
732         }
733         CHECK_SECURE_PATH_INTERNAL(L, path, write_requested, NULL);
734
735         push_original(L, "io", "open");
736         lua_pushvalue(L, 1);
737         if (with_mode) {
738                 lua_pushvalue(L, 2);
739         }
740
741         lua_call(L, with_mode ? 2 : 1, 2);
742         return 2;
743 }
744
745
746 int ScriptApiSecurity::sl_io_input(lua_State *L)
747 {
748         if (lua_isstring(L, 1)) {
749                 const char *path = lua_tostring(L, 1);
750                 CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
751         }
752
753         push_original(L, "io", "input");
754         lua_pushvalue(L, 1);
755         lua_call(L, 1, 1);
756         return 1;
757 }
758
759
760 int ScriptApiSecurity::sl_io_output(lua_State *L)
761 {
762         if (lua_isstring(L, 1)) {
763                 const char *path = lua_tostring(L, 1);
764                 CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);
765         }
766
767         push_original(L, "io", "output");
768         lua_pushvalue(L, 1);
769         lua_call(L, 1, 1);
770         return 1;
771 }
772
773
774 int ScriptApiSecurity::sl_io_lines(lua_State *L)
775 {
776         if (lua_isstring(L, 1)) {
777                 const char *path = lua_tostring(L, 1);
778                 CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
779         }
780
781         int top_precall = lua_gettop(L);
782         push_original(L, "io", "lines");
783         lua_pushvalue(L, 1);
784         lua_call(L, 1, LUA_MULTRET);
785         // Return number of arguments returned by the function,
786         // adjusting for the function being poped.
787         return lua_gettop(L) - top_precall;
788 }
789
790
791 int ScriptApiSecurity::sl_os_rename(lua_State *L)
792 {
793         luaL_checktype(L, 1, LUA_TSTRING);
794         const char *path1 = lua_tostring(L, 1);
795         CHECK_SECURE_PATH_INTERNAL(L, path1, true, NULL);
796
797         luaL_checktype(L, 2, LUA_TSTRING);
798         const char *path2 = lua_tostring(L, 2);
799         CHECK_SECURE_PATH_INTERNAL(L, path2, true, NULL);
800
801         push_original(L, "os", "rename");
802         lua_pushvalue(L, 1);
803         lua_pushvalue(L, 2);
804         lua_call(L, 2, 2);
805         return 2;
806 }
807
808
809 int ScriptApiSecurity::sl_os_remove(lua_State *L)
810 {
811         luaL_checktype(L, 1, LUA_TSTRING);
812         const char *path = lua_tostring(L, 1);
813         CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);
814
815         push_original(L, "os", "remove");
816         lua_pushvalue(L, 1);
817         lua_call(L, 1, 2);
818         return 2;
819 }
820