]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/cpp_api/s_security.cpp
Expose getPointedThing to Lua
[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 "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                 "setlocale",
266         };
267         static const char *debug_whitelist[] = {
268                 "getinfo",
269         };
270
271 #if USE_LUAJIT
272         static const char *jit_whitelist[] = {
273                 "arch",
274                 "flush",
275                 "off",
276                 "on",
277                 "opt",
278                 "os",
279                 "status",
280                 "version",
281                 "version_num",
282         };
283 #endif
284
285         m_secure = true;
286
287         lua_State *L = getStack();
288         int thread = getThread(L);
289
290         // create an empty environment
291         createEmptyEnv(L);
292
293         // Copy safe base functions
294         lua_getglobal(L, "_G");
295         lua_getfield(L, -2, "_G");
296         copy_safe(L, whitelist, sizeof(whitelist));
297
298         // And replace unsafe ones
299         SECURE_API(g, dofile);
300         SECURE_API(g, load);
301         SECURE_API(g, loadfile);
302         SECURE_API(g, loadstring);
303         SECURE_API(g, require);
304         lua_pop(L, 2);
305
306
307
308         // Copy safe OS functions
309         lua_getglobal(L, "os");
310         lua_newtable(L);
311         copy_safe(L, os_whitelist, sizeof(os_whitelist));
312         lua_setfield(L, -3, "os");
313         lua_pop(L, 1);  // Pop old OS
314
315
316         // Copy safe debug functions
317         lua_getglobal(L, "debug");
318         lua_newtable(L);
319         copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
320         lua_setfield(L, -3, "debug");
321         lua_pop(L, 1);  // Pop old debug
322
323 #if USE_LUAJIT
324         // Copy safe jit functions, if they exist
325         lua_getglobal(L, "jit");
326         lua_newtable(L);
327         copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
328         lua_setfield(L, -3, "jit");
329         lua_pop(L, 1);  // Pop old jit
330 #endif
331
332         // Set the environment to the one we created earlier
333         setLuaEnv(L, thread);
334 }
335
336 int ScriptApiSecurity::getThread(lua_State *L)
337 {
338 #if LUA_VERSION_NUM <= 501
339         int is_main = lua_pushthread(L);  // Push the main thread
340         FATAL_ERROR_IF(!is_main, "Security: ScriptApi's Lua state "
341                 "isn't the main Lua thread!");
342         return lua_gettop(L);
343 #endif
344         return 0;
345 }
346
347 void ScriptApiSecurity::createEmptyEnv(lua_State *L)
348 {
349         lua_newtable(L);  // Create new environment
350         lua_pushvalue(L, -1);
351         lua_setfield(L, -2, "_G");  // Create the _G loop
352 }
353
354 void ScriptApiSecurity::setLuaEnv(lua_State *L, int thread)
355 {
356 #if LUA_VERSION_NUM >= 502  // Lua >= 5.2
357         // Set the global environment
358         lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
359 #else  // Lua <= 5.1
360         // Set the environment of the main thread
361         FATAL_ERROR_IF(!lua_setfenv(L, thread), "Security: Unable to set "
362                 "environment of the main Lua thread!");
363         lua_pop(L, 1);  // Pop thread
364 #endif
365 }
366
367 bool ScriptApiSecurity::isSecure(lua_State *L)
368 {
369         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
370         bool secure = !lua_isnil(L, -1);
371         lua_pop(L, 1);
372         return secure;
373 }
374
375
376 #define CHECK_FILE_ERR(ret, fp) \
377         if (ret) { \
378                 lua_pushfstring(L, "%s: %s", path, strerror(errno)); \
379                 if (fp) std::fclose(fp); \
380                 return false; \
381         }
382
383
384 bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path, const char *display_name)
385 {
386         FILE *fp;
387         char *chunk_name;
388         if (!display_name)
389                 display_name = path;
390         if (!path) {
391                 fp = stdin;
392                 chunk_name = const_cast<char *>("=stdin");
393         } else {
394                 fp = fopen(path, "rb");
395                 if (!fp) {
396                         lua_pushfstring(L, "%s: %s", path, strerror(errno));
397                         return false;
398                 }
399                 chunk_name = new char[strlen(display_name) + 2];
400                 chunk_name[0] = '@';
401                 chunk_name[1] = '\0';
402                 strcat(chunk_name, display_name);
403         }
404
405         size_t start = 0;
406         int c = std::getc(fp);
407         if (c == '#') {
408                 // Skip the first line
409                 while ((c = std::getc(fp)) != EOF && c != '\n');
410                 if (c == '\n') c = std::getc(fp);
411                 start = std::ftell(fp);
412         }
413
414         if (c == LUA_SIGNATURE[0]) {
415                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
416                 std::fclose(fp);
417                 if (path) {
418                         delete [] chunk_name;
419                 }
420                 return false;
421         }
422
423         // Read the file
424         int ret = std::fseek(fp, 0, SEEK_END);
425         if (ret) {
426                 lua_pushfstring(L, "%s: %s", path, strerror(errno));
427                 std::fclose(fp);
428                 if (path) {
429                         delete [] chunk_name;
430                 }
431                 return false;
432         }
433
434         size_t size = std::ftell(fp) - start;
435         char *code = new char[size];
436         ret = std::fseek(fp, start, SEEK_SET);
437         if (ret) {
438                 lua_pushfstring(L, "%s: %s", path, strerror(errno));
439                 std::fclose(fp);
440                 delete [] code;
441                 if (path) {
442                         delete [] chunk_name;
443                 }
444                 return false;
445         }
446
447         size_t num_read = std::fread(code, 1, size, fp);
448         if (path) {
449                 std::fclose(fp);
450         }
451         if (num_read != size) {
452                 lua_pushliteral(L, "Error reading file to load.");
453                 delete [] code;
454                 if (path) {
455                         delete [] chunk_name;
456                 }
457                 return false;
458         }
459
460         if (luaL_loadbuffer(L, code, size, chunk_name)) {
461                 delete [] code;
462                 return false;
463         }
464
465         delete [] code;
466
467         if (path) {
468                 delete [] chunk_name;
469         }
470         return true;
471 }
472
473
474 bool ScriptApiSecurity::checkPath(lua_State *L, const char *path,
475                 bool write_required, bool *write_allowed)
476 {
477         if (write_allowed)
478                 *write_allowed = false;
479
480         std::string str;  // Transient
481
482         std::string abs_path = fs::AbsolutePath(path);
483
484         if (!abs_path.empty()) {
485                 // Don't allow accessing the settings file
486                 str = fs::AbsolutePath(g_settings_path);
487                 if (str == abs_path) return false;
488         }
489
490         // If we couldn't find the absolute path (path doesn't exist) then
491         // try removing the last components until it works (to allow
492         // non-existent files/folders for mkdir).
493         std::string cur_path = path;
494         std::string removed;
495         while (abs_path.empty() && !cur_path.empty()) {
496                 std::string component;
497                 cur_path = fs::RemoveLastPathComponent(cur_path, &component);
498                 if (component == "..") {
499                         // Parent components can't be allowed or we could allow something like
500                         // /home/user/minetest/worlds/foo/noexist/../../../../../../etc/passwd.
501                         // If we have previous non-relative elements in the path we might be
502                         // able to remove them so that things like worlds/foo/noexist/../auth.txt
503                         // could be allowed, but those paths will be interpreted as nonexistent
504                         // by the operating system anyways.
505                         return false;
506                 }
507                 removed = component + (removed.empty() ? "" : DIR_DELIM + removed);
508                 abs_path = fs::AbsolutePath(cur_path);
509         }
510         if (abs_path.empty())
511                 return false;
512         // Add the removed parts back so that you can't, eg, create a
513         // directory in worldmods if worldmods doesn't exist.
514         if (!removed.empty())
515                 abs_path += DIR_DELIM + removed;
516
517         // Get server from registry
518         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
519         ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
520         lua_pop(L, 1);
521         const IGameDef *gamedef = script->getGameDef();
522         if (!gamedef)
523                 return false;
524
525         // Get mod name
526         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
527         if (lua_isstring(L, -1)) {
528                 std::string mod_name = lua_tostring(L, -1);
529
530                 // Builtin can access anything
531                 if (mod_name == BUILTIN_MOD_NAME) {
532                         if (write_allowed) *write_allowed = true;
533                         return true;
534                 }
535
536                 // Allow paths in mod path
537                 // Don't bother if write access isn't important, since it will be handled later
538                 if (write_required || write_allowed != NULL) {
539                         const ModSpec *mod = gamedef->getModSpec(mod_name);
540                         if (mod) {
541                                 str = fs::AbsolutePath(mod->path);
542                                 if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
543                                         if (write_allowed) *write_allowed = true;
544                                         return true;
545                                 }
546                         }
547                 }
548         }
549         lua_pop(L, 1);  // Pop mod name
550
551         // Allow read-only access to all mod directories
552         if (!write_required) {
553                 const std::vector<ModSpec> mods = gamedef->getMods();
554                 for (size_t i = 0; i < mods.size(); ++i) {
555                         str = fs::AbsolutePath(mods[i].path);
556                         if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
557                                 return true;
558                         }
559                 }
560         }
561
562         str = fs::AbsolutePath(gamedef->getWorldPath());
563         if (!str.empty()) {
564                 // Don't allow access to other paths in the world mod/game path.
565                 // These have to be blocked so you can't override a trusted mod
566                 // by creating a mod with the same name in a world mod directory.
567                 // We add to the absolute path of the world instead of getting
568                 // the absolute paths directly because that won't work if they
569                 // don't exist.
570                 if (fs::PathStartsWith(abs_path, str + DIR_DELIM + "worldmods") ||
571                                 fs::PathStartsWith(abs_path, str + DIR_DELIM + "game")) {
572                         return false;
573                 }
574                 // Allow all other paths in world path
575                 if (fs::PathStartsWith(abs_path, str)) {
576                         if (write_allowed) *write_allowed = true;
577                         return true;
578                 }
579         }
580
581         // Default to disallowing
582         return false;
583 }
584
585
586 int ScriptApiSecurity::sl_g_dofile(lua_State *L)
587 {
588         int nret = sl_g_loadfile(L);
589         if (nret != 1) {
590                 lua_error(L);
591                 // code after this function isn't executed
592         }
593         int top_precall = lua_gettop(L);
594         lua_call(L, 0, LUA_MULTRET);
595         // Return number of arguments returned by the function,
596         // adjusting for the function being poped.
597         return lua_gettop(L) - (top_precall - 1);
598 }
599
600
601 int ScriptApiSecurity::sl_g_load(lua_State *L)
602 {
603         size_t len;
604         const char *buf;
605         std::string code;
606         const char *chunk_name = "=(load)";
607
608         luaL_checktype(L, 1, LUA_TFUNCTION);
609         if (!lua_isnone(L, 2)) {
610                 luaL_checktype(L, 2, LUA_TSTRING);
611                 chunk_name = lua_tostring(L, 2);
612         }
613
614         while (true) {
615                 lua_pushvalue(L, 1);
616                 lua_call(L, 0, 1);
617                 int t = lua_type(L, -1);
618                 if (t == LUA_TNIL) {
619                         break;
620                 } else if (t != LUA_TSTRING) {
621                         lua_pushnil(L);
622                         lua_pushliteral(L, "Loader didn't return a string");
623                         return 2;
624                 }
625                 buf = lua_tolstring(L, -1, &len);
626                 code += std::string(buf, len);
627                 lua_pop(L, 1); // Pop return value
628         }
629         if (code[0] == LUA_SIGNATURE[0]) {
630                 lua_pushnil(L);
631                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
632                 return 2;
633         }
634         if (luaL_loadbuffer(L, code.data(), code.size(), chunk_name)) {
635                 lua_pushnil(L);
636                 lua_insert(L, lua_gettop(L) - 1);
637                 return 2;
638         }
639         return 1;
640 }
641
642
643 int ScriptApiSecurity::sl_g_loadfile(lua_State *L)
644 {
645 #ifndef SERVER
646         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
647         ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
648         lua_pop(L, 1);
649
650         if (script->getType() == ScriptingType::Client) {
651                 std:: string display_path = lua_tostring(L, 1);
652                 const std::string *path = script->getClient()->getModFile(display_path);
653                 if (!path) {
654                         std::string error_msg = "Coudln't find script called:" + display_path;
655                         lua_pushnil(L);
656                         lua_pushstring(L, error_msg.c_str());
657                         return 2;
658                 }
659                 if (!safeLoadFile(L, path->c_str(), display_path.c_str())) {
660                         lua_pushnil(L);
661                         lua_insert(L, -2);
662                         return 2;
663                 }
664                 return 1;
665         }
666 #endif
667         const char *path = NULL;
668         if (lua_isstring(L, 1)) {
669                 path = lua_tostring(L, 1);
670                 CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
671         }
672
673         if (!safeLoadFile(L, path)) {
674                 lua_pushnil(L);
675                 lua_insert(L, -2);
676                 return 2;
677         }
678
679         return 1;
680 }
681
682
683 int ScriptApiSecurity::sl_g_loadstring(lua_State *L)
684 {
685         const char *chunk_name = "=(load)";
686
687         luaL_checktype(L, 1, LUA_TSTRING);
688         if (!lua_isnone(L, 2)) {
689                 luaL_checktype(L, 2, LUA_TSTRING);
690                 chunk_name = lua_tostring(L, 2);
691         }
692
693         size_t size;
694         const char *code = lua_tolstring(L, 1, &size);
695
696         if (size > 0 && code[0] == LUA_SIGNATURE[0]) {
697                 lua_pushnil(L);
698                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
699                 return 2;
700         }
701         if (luaL_loadbuffer(L, code, size, chunk_name)) {
702                 lua_pushnil(L);
703                 lua_insert(L, lua_gettop(L) - 1);
704                 return 2;
705         }
706         return 1;
707 }
708
709
710 int ScriptApiSecurity::sl_g_require(lua_State *L)
711 {
712         lua_pushliteral(L, "require() is disabled when mod security is on.");
713         return lua_error(L);
714 }
715
716
717 int ScriptApiSecurity::sl_io_open(lua_State *L)
718 {
719         bool with_mode = lua_gettop(L) > 1;
720
721         luaL_checktype(L, 1, LUA_TSTRING);
722         const char *path = lua_tostring(L, 1);
723
724         bool write_requested = false;
725         if (with_mode) {
726                 luaL_checktype(L, 2, LUA_TSTRING);
727                 const char *mode = lua_tostring(L, 2);
728                 write_requested = strchr(mode, 'w') != NULL ||
729                         strchr(mode, '+') != NULL ||
730                         strchr(mode, 'a') != NULL;
731         }
732         CHECK_SECURE_PATH_INTERNAL(L, path, write_requested, NULL);
733
734         push_original(L, "io", "open");
735         lua_pushvalue(L, 1);
736         if (with_mode) {
737                 lua_pushvalue(L, 2);
738         }
739
740         lua_call(L, with_mode ? 2 : 1, 2);
741         return 2;
742 }
743
744
745 int ScriptApiSecurity::sl_io_input(lua_State *L)
746 {
747         if (lua_isstring(L, 1)) {
748                 const char *path = lua_tostring(L, 1);
749                 CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
750         }
751
752         push_original(L, "io", "input");
753         lua_pushvalue(L, 1);
754         lua_call(L, 1, 1);
755         return 1;
756 }
757
758
759 int ScriptApiSecurity::sl_io_output(lua_State *L)
760 {
761         if (lua_isstring(L, 1)) {
762                 const char *path = lua_tostring(L, 1);
763                 CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);
764         }
765
766         push_original(L, "io", "output");
767         lua_pushvalue(L, 1);
768         lua_call(L, 1, 1);
769         return 1;
770 }
771
772
773 int ScriptApiSecurity::sl_io_lines(lua_State *L)
774 {
775         if (lua_isstring(L, 1)) {
776                 const char *path = lua_tostring(L, 1);
777                 CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
778         }
779
780         int top_precall = lua_gettop(L);
781         push_original(L, "io", "lines");
782         lua_pushvalue(L, 1);
783         lua_call(L, 1, LUA_MULTRET);
784         // Return number of arguments returned by the function,
785         // adjusting for the function being poped.
786         return lua_gettop(L) - top_precall;
787 }
788
789
790 int ScriptApiSecurity::sl_os_rename(lua_State *L)
791 {
792         luaL_checktype(L, 1, LUA_TSTRING);
793         const char *path1 = lua_tostring(L, 1);
794         CHECK_SECURE_PATH_INTERNAL(L, path1, true, NULL);
795
796         luaL_checktype(L, 2, LUA_TSTRING);
797         const char *path2 = lua_tostring(L, 2);
798         CHECK_SECURE_PATH_INTERNAL(L, path2, true, NULL);
799
800         push_original(L, "os", "rename");
801         lua_pushvalue(L, 1);
802         lua_pushvalue(L, 2);
803         lua_call(L, 2, 2);
804         return 2;
805 }
806
807
808 int ScriptApiSecurity::sl_os_remove(lua_State *L)
809 {
810         luaL_checktype(L, 1, LUA_TSTRING);
811         const char *path = lua_tostring(L, 1);
812         CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);
813
814         push_original(L, "os", "remove");
815         lua_pushvalue(L, 1);
816         lua_call(L, 1, 2);
817         return 2;
818 }
819