]> git.lizzy.rs Git - minetest.git/blob - src/script/cpp_api/s_security.cpp
Removed debug.upvaluejoin to prevent leak of insecure environment
[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                 "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         // Backup globals to the registry
144         lua_getglobal(L, "_G");
145         lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
146
147         // Replace the global environment with an empty one
148         int thread = getThread(L);
149         createEmptyEnv(L);
150         setLuaEnv(L, thread);
151
152         // Get old globals
153         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
154         int old_globals = lua_gettop(L);
155
156
157         // Copy safe base functions
158         lua_getglobal(L, "_G");
159         copy_safe(L, whitelist, sizeof(whitelist));
160
161         // And replace unsafe ones
162         SECURE_API(g, dofile);
163         SECURE_API(g, load);
164         SECURE_API(g, loadfile);
165         SECURE_API(g, loadstring);
166         SECURE_API(g, require);
167         lua_pop(L, 1);
168
169
170         // Copy safe IO functions
171         lua_getfield(L, old_globals, "io");
172         lua_newtable(L);
173         copy_safe(L, io_whitelist, sizeof(io_whitelist));
174
175         // And replace unsafe ones
176         SECURE_API(io, open);
177         SECURE_API(io, input);
178         SECURE_API(io, output);
179         SECURE_API(io, lines);
180
181         lua_setglobal(L, "io");
182         lua_pop(L, 1);  // Pop old IO
183
184
185         // Copy safe OS functions
186         lua_getfield(L, old_globals, "os");
187         lua_newtable(L);
188         copy_safe(L, os_whitelist, sizeof(os_whitelist));
189
190         // And replace unsafe ones
191         SECURE_API(os, remove);
192         SECURE_API(os, rename);
193
194         lua_setglobal(L, "os");
195         lua_pop(L, 1);  // Pop old OS
196
197
198         // Copy safe debug functions
199         lua_getfield(L, old_globals, "debug");
200         lua_newtable(L);
201         copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
202         lua_setglobal(L, "debug");
203         lua_pop(L, 1);  // Pop old debug
204
205
206         // Copy safe package fields
207         lua_getfield(L, old_globals, "package");
208         lua_newtable(L);
209         copy_safe(L, package_whitelist, sizeof(package_whitelist));
210         lua_setglobal(L, "package");
211         lua_pop(L, 1);  // Pop old package
212
213 #if USE_LUAJIT
214         // Copy safe jit functions, if they exist
215         lua_getfield(L, -1, "jit");
216         if (!lua_isnil(L, -1)) {
217                 lua_newtable(L);
218                 copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
219                 lua_setglobal(L, "jit");
220         }
221         lua_pop(L, 1);  // Pop old jit
222 #endif
223
224         lua_pop(L, 1); // Pop globals_backup
225 }
226
227 void ScriptApiSecurity::initializeSecurityClient()
228 {
229         static const char *whitelist[] = {
230                 "assert",
231                 "core",
232                 "collectgarbage",
233                 "DIR_DELIM",
234                 "error",
235                 "getfenv",
236                 "ipairs",
237                 "next",
238                 "pairs",
239                 "pcall",
240                 "print",
241                 "rawequal",
242                 "rawget",
243                 "rawset",
244                 "select",
245                 "setfenv",
246                 "setmetatable",
247                 "tonumber",
248                 "tostring",
249                 "type",
250                 "unpack",
251                 "_VERSION",
252                 "xpcall",
253                 // Completely safe libraries
254                 "coroutine",
255                 "string",
256                 "table",
257                 "math",
258         };
259         static const char *os_whitelist[] = {
260                 "clock",
261                 "date",
262                 "difftime",
263                 "time"
264         };
265         static const char *debug_whitelist[] = {
266                 "getinfo",
267         };
268
269 #if USE_LUAJIT
270         static const char *jit_whitelist[] = {
271                 "arch",
272                 "flush",
273                 "off",
274                 "on",
275                 "opt",
276                 "os",
277                 "status",
278                 "version",
279                 "version_num",
280         };
281 #endif
282
283         m_secure = true;
284
285         lua_State *L = getStack();
286         int thread = getThread(L);
287
288         // create an empty environment
289         createEmptyEnv(L);
290
291         // Copy safe base functions
292         lua_getglobal(L, "_G");
293         lua_getfield(L, -2, "_G");
294         copy_safe(L, whitelist, sizeof(whitelist));
295
296         // And replace unsafe ones
297         SECURE_API(g, dofile);
298         SECURE_API(g, load);
299         SECURE_API(g, loadfile);
300         SECURE_API(g, loadstring);
301         SECURE_API(g, require);
302         lua_pop(L, 2);
303
304
305
306         // Copy safe OS functions
307         lua_getglobal(L, "os");
308         lua_newtable(L);
309         copy_safe(L, os_whitelist, sizeof(os_whitelist));
310         lua_setfield(L, -3, "os");
311         lua_pop(L, 1);  // Pop old OS
312
313
314         // Copy safe debug functions
315         lua_getglobal(L, "debug");
316         lua_newtable(L);
317         copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
318         lua_setfield(L, -3, "debug");
319         lua_pop(L, 1);  // Pop old debug
320
321 #if USE_LUAJIT
322         // Copy safe jit functions, if they exist
323         lua_getglobal(L, "jit");
324         lua_newtable(L);
325         copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
326         lua_setfield(L, -3, "jit");
327         lua_pop(L, 1);  // Pop old jit
328 #endif
329
330         // Set the environment to the one we created earlier
331         setLuaEnv(L, thread);
332 }
333
334 int ScriptApiSecurity::getThread(lua_State *L)
335 {
336 #if LUA_VERSION_NUM <= 501
337         int is_main = lua_pushthread(L);  // Push the main thread
338         FATAL_ERROR_IF(!is_main, "Security: ScriptApi's Lua state "
339                 "isn't the main Lua thread!");
340         return lua_gettop(L);
341 #endif
342         return 0;
343 }
344
345 void ScriptApiSecurity::createEmptyEnv(lua_State *L)
346 {
347         lua_newtable(L);  // Create new environment
348         lua_pushvalue(L, -1);
349         lua_setfield(L, -2, "_G");  // Create the _G loop
350 }
351
352 void ScriptApiSecurity::setLuaEnv(lua_State *L, int thread)
353 {
354 #if LUA_VERSION_NUM >= 502  // Lua >= 5.2
355         // Set the global environment
356         lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
357 #else  // Lua <= 5.1
358         // Set the environment of the main thread
359         FATAL_ERROR_IF(!lua_setfenv(L, thread), "Security: Unable to set "
360                 "environment of the main Lua thread!");
361         lua_pop(L, 1);  // Pop thread
362 #endif
363 }
364
365 bool ScriptApiSecurity::isSecure(lua_State *L)
366 {
367         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
368         bool secure = !lua_isnil(L, -1);
369         lua_pop(L, 1);
370         return secure;
371 }
372
373
374 #define CHECK_FILE_ERR(ret, fp) \
375         if (ret) { \
376                 lua_pushfstring(L, "%s: %s", path, strerror(errno)); \
377                 if (fp) std::fclose(fp); \
378                 return false; \
379         }
380
381
382 bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path, const char *display_name)
383 {
384         FILE *fp;
385         char *chunk_name;
386         if (!display_name)
387                 display_name = path;
388         if (!path) {
389                 fp = stdin;
390                 chunk_name = const_cast<char *>("=stdin");
391         } else {
392                 fp = fopen(path, "rb");
393                 if (!fp) {
394                         lua_pushfstring(L, "%s: %s", path, strerror(errno));
395                         return false;
396                 }
397                 chunk_name = new char[strlen(display_name) + 2];
398                 chunk_name[0] = '@';
399                 chunk_name[1] = '\0';
400                 strcat(chunk_name, display_name);
401         }
402
403         size_t start = 0;
404         int c = std::getc(fp);
405         if (c == '#') {
406                 // Skip the first line
407                 while ((c = std::getc(fp)) != EOF && c != '\n');
408                 if (c == '\n') c = std::getc(fp);
409                 start = std::ftell(fp);
410         }
411
412         if (c == LUA_SIGNATURE[0]) {
413                 lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
414                 std::fclose(fp);
415                 if (path) {
416                         delete [] chunk_name;
417                 }
418                 return false;
419         }
420
421         // Read the file
422         int ret = std::fseek(fp, 0, SEEK_END);
423         if (ret) {
424                 lua_pushfstring(L, "%s: %s", path, strerror(errno));
425                 std::fclose(fp);
426                 if (path) {
427                         delete [] chunk_name;
428                 }
429                 return false;
430         }
431
432         size_t size = std::ftell(fp) - start;
433         char *code = new char[size];
434         ret = std::fseek(fp, start, SEEK_SET);
435         if (ret) {
436                 lua_pushfstring(L, "%s: %s", path, strerror(errno));
437                 std::fclose(fp);
438                 delete [] code;
439                 if (path) {
440                         delete [] chunk_name;
441                 }
442                 return false;
443         }
444
445         size_t num_read = std::fread(code, 1, size, fp);
446         if (path) {
447                 std::fclose(fp);
448         }
449         if (num_read != size) {
450                 lua_pushliteral(L, "Error reading file to load.");
451                 delete [] code;
452                 if (path) {
453                         delete [] chunk_name;
454                 }
455                 return false;
456         }
457
458         if (luaL_loadbuffer(L, code, size, chunk_name)) {
459                 delete [] code;
460                 return false;
461         }
462
463         delete [] code;
464
465         if (path) {
466                 delete [] chunk_name;
467         }
468         return true;
469 }
470
471
472 bool ScriptApiSecurity::checkPath(lua_State *L, const char *path,
473                 bool write_required, bool *write_allowed)
474 {
475         if (write_allowed)
476                 *write_allowed = false;
477
478         std::string str;  // Transient
479
480         std::string abs_path = fs::AbsolutePath(path);
481
482         if (!abs_path.empty()) {
483                 // Don't allow accessing the settings file
484                 str = fs::AbsolutePath(g_settings_path);
485                 if (str == abs_path) return false;
486         }
487
488         // If we couldn't find the absolute path (path doesn't exist) then
489         // try removing the last components until it works (to allow
490         // non-existent files/folders for mkdir).
491         std::string cur_path = path;
492         std::string removed;
493         while (abs_path.empty() && !cur_path.empty()) {
494                 std::string component;
495                 cur_path = fs::RemoveLastPathComponent(cur_path, &component);
496                 if (component == "..") {
497                         // Parent components can't be allowed or we could allow something like
498                         // /home/user/minetest/worlds/foo/noexist/../../../../../../etc/passwd.
499                         // If we have previous non-relative elements in the path we might be
500                         // able to remove them so that things like worlds/foo/noexist/../auth.txt
501                         // could be allowed, but those paths will be interpreted as nonexistent
502                         // by the operating system anyways.
503                         return false;
504                 }
505                 removed.append(component).append(removed.empty() ? "" : DIR_DELIM + removed);
506                 abs_path = fs::AbsolutePath(cur_path);
507         }
508         if (abs_path.empty())
509                 return false;
510         // Add the removed parts back so that you can't, eg, create a
511         // directory in worldmods if worldmods doesn't exist.
512         if (!removed.empty())
513                 abs_path += DIR_DELIM + removed;
514
515         // Get server from registry
516         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
517         ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
518         lua_pop(L, 1);
519         const IGameDef *gamedef = script->getGameDef();
520         if (!gamedef)
521                 return false;
522
523         // Get mod name
524         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
525         if (lua_isstring(L, -1)) {
526                 std::string mod_name = readParam<std::string>(L, -1);
527
528                 // Builtin can access anything
529                 if (mod_name == BUILTIN_MOD_NAME) {
530                         if (write_allowed) *write_allowed = true;
531                         return true;
532                 }
533
534                 // Allow paths in mod path
535                 // Don't bother if write access isn't important, since it will be handled later
536                 if (write_required || write_allowed != NULL) {
537                         const ModSpec *mod = gamedef->getModSpec(mod_name);
538                         if (mod) {
539                                 str = fs::AbsolutePath(mod->path);
540                                 if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
541                                         if (write_allowed) *write_allowed = true;
542                                         return true;
543                                 }
544                         }
545                 }
546         }
547         lua_pop(L, 1);  // Pop mod name
548
549         // Allow read-only access to all mod directories
550         if (!write_required) {
551                 const std::vector<ModSpec> &mods = gamedef->getMods();
552                 for (const ModSpec &mod : mods) {
553                         str = fs::AbsolutePath(mod.path);
554                         if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
555                                 return true;
556                         }
557                 }
558         }
559
560         str = fs::AbsolutePath(gamedef->getWorldPath());
561         if (!str.empty()) {
562                 // Don't allow access to other paths in the world mod/game path.
563                 // These have to be blocked so you can't override a trusted mod
564                 // by creating a mod with the same name in a world mod directory.
565                 // We add to the absolute path of the world instead of getting
566                 // the absolute paths directly because that won't work if they
567                 // don't exist.
568                 if (fs::PathStartsWith(abs_path, str + DIR_DELIM + "worldmods") ||
569                                 fs::PathStartsWith(abs_path, str + DIR_DELIM + "game")) {
570                         return false;
571                 }
572                 // Allow all other paths in world path
573                 if (fs::PathStartsWith(abs_path, str)) {
574                         if (write_allowed) *write_allowed = true;
575                         return true;
576                 }
577         }
578
579         // Default to disallowing
580         return false;
581 }
582
583
584 int ScriptApiSecurity::sl_g_dofile(lua_State *L)
585 {
586         int nret = sl_g_loadfile(L);
587         if (nret != 1) {
588                 lua_error(L);
589                 // code after this function isn't executed
590         }
591         int top_precall = lua_gettop(L);
592         lua_call(L, 0, LUA_MULTRET);
593         // Return number of arguments returned by the function,
594         // adjusting for the function being poped.
595         return lua_gettop(L) - (top_precall - 1);
596 }
597
598
599 int ScriptApiSecurity::sl_g_load(lua_State *L)
600 {
601         size_t len;
602         const char *buf;
603         std::string code;
604         const char *chunk_name = "=(load)";
605
606         luaL_checktype(L, 1, LUA_TFUNCTION);
607         if (!lua_isnone(L, 2)) {
608                 luaL_checktype(L, 2, LUA_TSTRING);
609                 chunk_name = lua_tostring(L, 2);
610         }
611
612         while (true) {
613                 lua_pushvalue(L, 1);
614                 lua_call(L, 0, 1);
615                 int t = lua_type(L, -1);
616                 if (t == LUA_TNIL) {
617                         break;
618                 }
619
620                 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 = readParam<std::string>(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