]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/cpp_api/s_security.cpp
Modernize source code: last part (#6285)
[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.append(component).append(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 (const ModSpec &mod : mods) {
555                         str = fs::AbsolutePath(mod.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                 }
621
622                 if (t != LUA_TSTRING) {
623                         lua_pushnil(L);
624                         lua_pushliteral(L, "Loader didn't return a string");
625                         return 2;
626                 }
627                 buf = lua_tolstring(L, -1, &len);
628                 code += std::string(buf, len);
629                 lua_pop(L, 1); // Pop return value
630         }
631         if (code[0] == LUA_SIGNATURE[0]) {
632                 lua_pushnil(L);
633                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
634                 return 2;
635         }
636         if (luaL_loadbuffer(L, code.data(), code.size(), chunk_name)) {
637                 lua_pushnil(L);
638                 lua_insert(L, lua_gettop(L) - 1);
639                 return 2;
640         }
641         return 1;
642 }
643
644
645 int ScriptApiSecurity::sl_g_loadfile(lua_State *L)
646 {
647 #ifndef SERVER
648         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
649         ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
650         lua_pop(L, 1);
651
652         if (script->getType() == ScriptingType::Client) {
653                 std:: string display_path = lua_tostring(L, 1);
654                 const std::string *path = script->getClient()->getModFile(display_path);
655                 if (!path) {
656                         std::string error_msg = "Coudln't find script called:" + display_path;
657                         lua_pushnil(L);
658                         lua_pushstring(L, error_msg.c_str());
659                         return 2;
660                 }
661                 if (!safeLoadFile(L, path->c_str(), display_path.c_str())) {
662                         lua_pushnil(L);
663                         lua_insert(L, -2);
664                         return 2;
665                 }
666                 return 1;
667         }
668 #endif
669         const char *path = NULL;
670         if (lua_isstring(L, 1)) {
671                 path = lua_tostring(L, 1);
672                 CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
673         }
674
675         if (!safeLoadFile(L, path)) {
676                 lua_pushnil(L);
677                 lua_insert(L, -2);
678                 return 2;
679         }
680
681         return 1;
682 }
683
684
685 int ScriptApiSecurity::sl_g_loadstring(lua_State *L)
686 {
687         const char *chunk_name = "=(load)";
688
689         luaL_checktype(L, 1, LUA_TSTRING);
690         if (!lua_isnone(L, 2)) {
691                 luaL_checktype(L, 2, LUA_TSTRING);
692                 chunk_name = lua_tostring(L, 2);
693         }
694
695         size_t size;
696         const char *code = lua_tolstring(L, 1, &size);
697
698         if (size > 0 && code[0] == LUA_SIGNATURE[0]) {
699                 lua_pushnil(L);
700                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
701                 return 2;
702         }
703         if (luaL_loadbuffer(L, code, size, chunk_name)) {
704                 lua_pushnil(L);
705                 lua_insert(L, lua_gettop(L) - 1);
706                 return 2;
707         }
708         return 1;
709 }
710
711
712 int ScriptApiSecurity::sl_g_require(lua_State *L)
713 {
714         lua_pushliteral(L, "require() is disabled when mod security is on.");
715         return lua_error(L);
716 }
717
718
719 int ScriptApiSecurity::sl_io_open(lua_State *L)
720 {
721         bool with_mode = lua_gettop(L) > 1;
722
723         luaL_checktype(L, 1, LUA_TSTRING);
724         const char *path = lua_tostring(L, 1);
725
726         bool write_requested = false;
727         if (with_mode) {
728                 luaL_checktype(L, 2, LUA_TSTRING);
729                 const char *mode = lua_tostring(L, 2);
730                 write_requested = strchr(mode, 'w') != NULL ||
731                         strchr(mode, '+') != NULL ||
732                         strchr(mode, 'a') != NULL;
733         }
734         CHECK_SECURE_PATH_INTERNAL(L, path, write_requested, NULL);
735
736         push_original(L, "io", "open");
737         lua_pushvalue(L, 1);
738         if (with_mode) {
739                 lua_pushvalue(L, 2);
740         }
741
742         lua_call(L, with_mode ? 2 : 1, 2);
743         return 2;
744 }
745
746
747 int ScriptApiSecurity::sl_io_input(lua_State *L)
748 {
749         if (lua_isstring(L, 1)) {
750                 const char *path = lua_tostring(L, 1);
751                 CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
752         }
753
754         push_original(L, "io", "input");
755         lua_pushvalue(L, 1);
756         lua_call(L, 1, 1);
757         return 1;
758 }
759
760
761 int ScriptApiSecurity::sl_io_output(lua_State *L)
762 {
763         if (lua_isstring(L, 1)) {
764                 const char *path = lua_tostring(L, 1);
765                 CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);
766         }
767
768         push_original(L, "io", "output");
769         lua_pushvalue(L, 1);
770         lua_call(L, 1, 1);
771         return 1;
772 }
773
774
775 int ScriptApiSecurity::sl_io_lines(lua_State *L)
776 {
777         if (lua_isstring(L, 1)) {
778                 const char *path = lua_tostring(L, 1);
779                 CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
780         }
781
782         int top_precall = lua_gettop(L);
783         push_original(L, "io", "lines");
784         lua_pushvalue(L, 1);
785         lua_call(L, 1, LUA_MULTRET);
786         // Return number of arguments returned by the function,
787         // adjusting for the function being poped.
788         return lua_gettop(L) - top_precall;
789 }
790
791
792 int ScriptApiSecurity::sl_os_rename(lua_State *L)
793 {
794         luaL_checktype(L, 1, LUA_TSTRING);
795         const char *path1 = lua_tostring(L, 1);
796         CHECK_SECURE_PATH_INTERNAL(L, path1, true, NULL);
797
798         luaL_checktype(L, 2, LUA_TSTRING);
799         const char *path2 = lua_tostring(L, 2);
800         CHECK_SECURE_PATH_INTERNAL(L, path2, true, NULL);
801
802         push_original(L, "os", "rename");
803         lua_pushvalue(L, 1);
804         lua_pushvalue(L, 2);
805         lua_call(L, 2, 2);
806         return 2;
807 }
808
809
810 int ScriptApiSecurity::sl_os_remove(lua_State *L)
811 {
812         luaL_checktype(L, 1, LUA_TSTRING);
813         const char *path = lua_tostring(L, 1);
814         CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);
815
816         push_original(L, "os", "remove");
817         lua_pushvalue(L, 1);
818         lua_call(L, 1, 2);
819         return 2;
820 }
821