s_security.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808
  1. /*
  2. Minetest
  3. Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include "cpp_api/s_security.h"
  17. #include "filesys.h"
  18. #include "porting.h"
  19. #include "server.h"
  20. #include "client/client.h"
  21. #include "settings.h"
  22. #include <cerrno>
  23. #include <string>
  24. #include <iostream>
  25. #define SECURE_API(lib, name) \
  26. lua_pushcfunction(L, sl_##lib##_##name); \
  27. lua_setfield(L, -2, #name);
  28. static inline void copy_safe(lua_State *L, const char *list[], unsigned len, int from=-2, int to=-1)
  29. {
  30. if (from < 0) from = lua_gettop(L) + from + 1;
  31. if (to < 0) to = lua_gettop(L) + to + 1;
  32. for (unsigned i = 0; i < (len / sizeof(list[0])); i++) {
  33. lua_getfield(L, from, list[i]);
  34. lua_setfield(L, to, list[i]);
  35. }
  36. }
  37. // Pushes the original version of a library function on the stack, from the old version
  38. static inline void push_original(lua_State *L, const char *lib, const char *func)
  39. {
  40. lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
  41. lua_getfield(L, -1, lib);
  42. lua_remove(L, -2); // Remove globals_backup
  43. lua_getfield(L, -1, func);
  44. lua_remove(L, -2); // Remove lib
  45. }
  46. void ScriptApiSecurity::initializeSecurity()
  47. {
  48. static const char *whitelist[] = {
  49. "assert",
  50. "core",
  51. "collectgarbage",
  52. "DIR_DELIM",
  53. "error",
  54. "getfenv",
  55. "getmetatable",
  56. "ipairs",
  57. "next",
  58. "pairs",
  59. "pcall",
  60. "print",
  61. "rawequal",
  62. "rawget",
  63. "rawset",
  64. "select",
  65. "setfenv",
  66. "setmetatable",
  67. "tonumber",
  68. "tostring",
  69. "type",
  70. "unpack",
  71. "_VERSION",
  72. "xpcall",
  73. // Completely safe libraries
  74. "coroutine",
  75. "string",
  76. "table",
  77. "math",
  78. };
  79. static const char *io_whitelist[] = {
  80. "close",
  81. "flush",
  82. "read",
  83. "type",
  84. "write",
  85. };
  86. static const char *os_whitelist[] = {
  87. "clock",
  88. "date",
  89. "difftime",
  90. "getenv",
  91. "setlocale",
  92. "time",
  93. "tmpname",
  94. };
  95. static const char *debug_whitelist[] = {
  96. "gethook",
  97. "traceback",
  98. "getinfo",
  99. "getmetatable",
  100. "setupvalue",
  101. "setmetatable",
  102. "upvalueid",
  103. "sethook",
  104. "debug",
  105. "setlocal",
  106. };
  107. static const char *package_whitelist[] = {
  108. "config",
  109. "cpath",
  110. "path",
  111. "searchpath",
  112. };
  113. #if USE_LUAJIT
  114. static const char *jit_whitelist[] = {
  115. "arch",
  116. "flush",
  117. "off",
  118. "on",
  119. "opt",
  120. "os",
  121. "status",
  122. "version",
  123. "version_num",
  124. };
  125. #endif
  126. m_secure = true;
  127. lua_State *L = getStack();
  128. // Backup globals to the registry
  129. lua_getglobal(L, "_G");
  130. lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
  131. // Replace the global environment with an empty one
  132. int thread = getThread(L);
  133. createEmptyEnv(L);
  134. setLuaEnv(L, thread);
  135. // Get old globals
  136. lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
  137. int old_globals = lua_gettop(L);
  138. // Copy safe base functions
  139. lua_getglobal(L, "_G");
  140. copy_safe(L, whitelist, sizeof(whitelist));
  141. // And replace unsafe ones
  142. SECURE_API(g, dofile);
  143. SECURE_API(g, load);
  144. SECURE_API(g, loadfile);
  145. SECURE_API(g, loadstring);
  146. SECURE_API(g, require);
  147. lua_pop(L, 1);
  148. // Copy safe IO functions
  149. lua_getfield(L, old_globals, "io");
  150. lua_newtable(L);
  151. copy_safe(L, io_whitelist, sizeof(io_whitelist));
  152. // And replace unsafe ones
  153. SECURE_API(io, open);
  154. SECURE_API(io, input);
  155. SECURE_API(io, output);
  156. SECURE_API(io, lines);
  157. lua_setglobal(L, "io");
  158. lua_pop(L, 1); // Pop old IO
  159. // Copy safe OS functions
  160. lua_getfield(L, old_globals, "os");
  161. lua_newtable(L);
  162. copy_safe(L, os_whitelist, sizeof(os_whitelist));
  163. // And replace unsafe ones
  164. SECURE_API(os, remove);
  165. SECURE_API(os, rename);
  166. lua_setglobal(L, "os");
  167. lua_pop(L, 1); // Pop old OS
  168. // Copy safe debug functions
  169. lua_getfield(L, old_globals, "debug");
  170. lua_newtable(L);
  171. copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
  172. lua_setglobal(L, "debug");
  173. lua_pop(L, 1); // Pop old debug
  174. // Copy safe package fields
  175. lua_getfield(L, old_globals, "package");
  176. lua_newtable(L);
  177. copy_safe(L, package_whitelist, sizeof(package_whitelist));
  178. lua_setglobal(L, "package");
  179. lua_pop(L, 1); // Pop old package
  180. #if USE_LUAJIT
  181. // Copy safe jit functions, if they exist
  182. lua_getfield(L, -1, "jit");
  183. if (!lua_isnil(L, -1)) {
  184. lua_newtable(L);
  185. copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
  186. lua_setglobal(L, "jit");
  187. }
  188. lua_pop(L, 1); // Pop old jit
  189. #endif
  190. lua_pop(L, 1); // Pop globals_backup
  191. }
  192. void ScriptApiSecurity::initializeSecurityClient()
  193. {
  194. static const char *whitelist[] = {
  195. "assert",
  196. "core",
  197. "collectgarbage",
  198. "DIR_DELIM",
  199. "error",
  200. "getfenv",
  201. "ipairs",
  202. "next",
  203. "pairs",
  204. "pcall",
  205. "print",
  206. "rawequal",
  207. "rawget",
  208. "rawset",
  209. "select",
  210. "setfenv",
  211. // getmetatable can be used to escape the sandbox
  212. "setmetatable",
  213. "tonumber",
  214. "tostring",
  215. "type",
  216. "unpack",
  217. "_VERSION",
  218. "xpcall",
  219. // Completely safe libraries
  220. "coroutine",
  221. "string",
  222. "table",
  223. "math",
  224. };
  225. static const char *os_whitelist[] = {
  226. "clock",
  227. "date",
  228. "difftime",
  229. "time"
  230. };
  231. static const char *debug_whitelist[] = {
  232. "getinfo",
  233. "traceback"
  234. };
  235. #if USE_LUAJIT
  236. static const char *jit_whitelist[] = {
  237. "arch",
  238. "flush",
  239. "off",
  240. "on",
  241. "opt",
  242. "os",
  243. "status",
  244. "version",
  245. "version_num",
  246. };
  247. #endif
  248. m_secure = true;
  249. lua_State *L = getStack();
  250. int thread = getThread(L);
  251. // create an empty environment
  252. createEmptyEnv(L);
  253. // Copy safe base functions
  254. lua_getglobal(L, "_G");
  255. lua_getfield(L, -2, "_G");
  256. copy_safe(L, whitelist, sizeof(whitelist));
  257. // And replace unsafe ones
  258. SECURE_API(g, dofile);
  259. SECURE_API(g, load);
  260. SECURE_API(g, loadfile);
  261. SECURE_API(g, loadstring);
  262. SECURE_API(g, require);
  263. lua_pop(L, 2);
  264. // Copy safe OS functions
  265. lua_getglobal(L, "os");
  266. lua_newtable(L);
  267. copy_safe(L, os_whitelist, sizeof(os_whitelist));
  268. lua_setfield(L, -3, "os");
  269. lua_pop(L, 1); // Pop old OS
  270. // Copy safe debug functions
  271. lua_getglobal(L, "debug");
  272. lua_newtable(L);
  273. copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
  274. lua_setfield(L, -3, "debug");
  275. lua_pop(L, 1); // Pop old debug
  276. #if USE_LUAJIT
  277. // Copy safe jit functions, if they exist
  278. lua_getglobal(L, "jit");
  279. lua_newtable(L);
  280. copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
  281. lua_setfield(L, -3, "jit");
  282. lua_pop(L, 1); // Pop old jit
  283. #endif
  284. // Set the environment to the one we created earlier
  285. setLuaEnv(L, thread);
  286. }
  287. int ScriptApiSecurity::getThread(lua_State *L)
  288. {
  289. #if LUA_VERSION_NUM <= 501
  290. int is_main = lua_pushthread(L); // Push the main thread
  291. FATAL_ERROR_IF(!is_main, "Security: ScriptApi's Lua state "
  292. "isn't the main Lua thread!");
  293. return lua_gettop(L);
  294. #endif
  295. return 0;
  296. }
  297. void ScriptApiSecurity::createEmptyEnv(lua_State *L)
  298. {
  299. lua_newtable(L); // Create new environment
  300. lua_pushvalue(L, -1);
  301. lua_setfield(L, -2, "_G"); // Create the _G loop
  302. }
  303. void ScriptApiSecurity::setLuaEnv(lua_State *L, int thread)
  304. {
  305. #if LUA_VERSION_NUM >= 502 // Lua >= 5.2
  306. // Set the global environment
  307. lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
  308. #else // Lua <= 5.1
  309. // Set the environment of the main thread
  310. FATAL_ERROR_IF(!lua_setfenv(L, thread), "Security: Unable to set "
  311. "environment of the main Lua thread!");
  312. lua_pop(L, 1); // Pop thread
  313. #endif
  314. }
  315. bool ScriptApiSecurity::isSecure(lua_State *L)
  316. {
  317. lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
  318. bool secure = !lua_isnil(L, -1);
  319. lua_pop(L, 1);
  320. return secure;
  321. }
  322. bool ScriptApiSecurity::safeLoadString(lua_State *L, const std::string &code, const char *chunk_name)
  323. {
  324. if (code.size() > 0 && code[0] == LUA_SIGNATURE[0]) {
  325. lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
  326. return false;
  327. }
  328. if (luaL_loadbuffer(L, code.data(), code.size(), chunk_name))
  329. return false;
  330. return true;
  331. }
  332. bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path, const char *display_name)
  333. {
  334. FILE *fp;
  335. char *chunk_name;
  336. if (!display_name)
  337. display_name = path;
  338. if (!path) {
  339. fp = stdin;
  340. chunk_name = const_cast<char *>("=stdin");
  341. } else {
  342. fp = fopen(path, "rb");
  343. if (!fp) {
  344. lua_pushfstring(L, "%s: %s", path, strerror(errno));
  345. return false;
  346. }
  347. size_t len = strlen(display_name) + 2;
  348. chunk_name = new char[len];
  349. snprintf(chunk_name, len, "@%s", display_name);
  350. }
  351. size_t start = 0;
  352. int c = std::getc(fp);
  353. if (c == '#') {
  354. // Skip the first line
  355. while ((c = std::getc(fp)) != EOF && c != '\n') {}
  356. if (c == '\n')
  357. std::getc(fp);
  358. start = std::ftell(fp);
  359. }
  360. // Read the file
  361. int ret = std::fseek(fp, 0, SEEK_END);
  362. if (ret) {
  363. lua_pushfstring(L, "%s: %s", path, strerror(errno));
  364. if (path) {
  365. std::fclose(fp);
  366. delete [] chunk_name;
  367. }
  368. return false;
  369. }
  370. size_t size = std::ftell(fp) - start;
  371. std::string code(size, '\0');
  372. ret = std::fseek(fp, start, SEEK_SET);
  373. if (ret) {
  374. lua_pushfstring(L, "%s: %s", path, strerror(errno));
  375. if (path) {
  376. std::fclose(fp);
  377. delete [] chunk_name;
  378. }
  379. return false;
  380. }
  381. size_t num_read = std::fread(&code[0], 1, size, fp);
  382. if (path)
  383. std::fclose(fp);
  384. if (num_read != size) {
  385. lua_pushliteral(L, "Error reading file to load.");
  386. if (path)
  387. delete [] chunk_name;
  388. return false;
  389. }
  390. bool result = safeLoadString(L, code, chunk_name);
  391. if (path)
  392. delete [] chunk_name;
  393. return result;
  394. }
  395. bool ScriptApiSecurity::checkPath(lua_State *L, const char *path,
  396. bool write_required, bool *write_allowed)
  397. {
  398. if (write_allowed)
  399. *write_allowed = false;
  400. std::string str; // Transient
  401. std::string abs_path = fs::AbsolutePath(path);
  402. if (!abs_path.empty()) {
  403. // Don't allow accessing the settings file
  404. str = fs::AbsolutePath(g_settings_path);
  405. if (str == abs_path) return false;
  406. }
  407. // If we couldn't find the absolute path (path doesn't exist) then
  408. // try removing the last components until it works (to allow
  409. // non-existent files/folders for mkdir).
  410. std::string cur_path = path;
  411. std::string removed;
  412. while (abs_path.empty() && !cur_path.empty()) {
  413. std::string component;
  414. cur_path = fs::RemoveLastPathComponent(cur_path, &component);
  415. if (component == "..") {
  416. // Parent components can't be allowed or we could allow something like
  417. // /home/user/minetest/worlds/foo/noexist/../../../../../../etc/passwd.
  418. // If we have previous non-relative elements in the path we might be
  419. // able to remove them so that things like worlds/foo/noexist/../auth.txt
  420. // could be allowed, but those paths will be interpreted as nonexistent
  421. // by the operating system anyways.
  422. return false;
  423. }
  424. removed.append(component).append(removed.empty() ? "" : DIR_DELIM + removed);
  425. abs_path = fs::AbsolutePath(cur_path);
  426. }
  427. if (abs_path.empty())
  428. return false;
  429. // Add the removed parts back so that you can't, eg, create a
  430. // directory in worldmods if worldmods doesn't exist.
  431. if (!removed.empty())
  432. abs_path += DIR_DELIM + removed;
  433. // Get server from registry
  434. lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
  435. ScriptApiBase *script;
  436. #if INDIRECT_SCRIPTAPI_RIDX
  437. script = (ScriptApiBase *) *(void**)(lua_touserdata(L, -1));
  438. #else
  439. script = (ScriptApiBase *) lua_touserdata(L, -1);
  440. #endif
  441. lua_pop(L, 1);
  442. const IGameDef *gamedef = script->getGameDef();
  443. if (!gamedef)
  444. return false;
  445. // Get mod name
  446. lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
  447. if (lua_isstring(L, -1)) {
  448. std::string mod_name = readParam<std::string>(L, -1);
  449. // Builtin can access anything
  450. if (mod_name == BUILTIN_MOD_NAME) {
  451. if (write_allowed) *write_allowed = true;
  452. return true;
  453. }
  454. // Allow paths in mod path
  455. // Don't bother if write access isn't important, since it will be handled later
  456. if (write_required || write_allowed != NULL) {
  457. const ModSpec *mod = gamedef->getModSpec(mod_name);
  458. if (mod) {
  459. str = fs::AbsolutePath(mod->path);
  460. if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
  461. if (write_allowed) *write_allowed = true;
  462. return true;
  463. }
  464. }
  465. }
  466. }
  467. lua_pop(L, 1); // Pop mod name
  468. // Allow read-only access to all mod directories
  469. if (!write_required) {
  470. const std::vector<ModSpec> &mods = gamedef->getMods();
  471. for (const ModSpec &mod : mods) {
  472. str = fs::AbsolutePath(mod.path);
  473. if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
  474. return true;
  475. }
  476. }
  477. }
  478. str = fs::AbsolutePath(gamedef->getWorldPath());
  479. if (!str.empty()) {
  480. // Don't allow access to other paths in the world mod/game path.
  481. // These have to be blocked so you can't override a trusted mod
  482. // by creating a mod with the same name in a world mod directory.
  483. // We add to the absolute path of the world instead of getting
  484. // the absolute paths directly because that won't work if they
  485. // don't exist.
  486. if (fs::PathStartsWith(abs_path, str + DIR_DELIM + "worldmods") ||
  487. fs::PathStartsWith(abs_path, str + DIR_DELIM + "game")) {
  488. return false;
  489. }
  490. // Allow all other paths in world path
  491. if (fs::PathStartsWith(abs_path, str)) {
  492. if (write_allowed) *write_allowed = true;
  493. return true;
  494. }
  495. }
  496. // Default to disallowing
  497. return false;
  498. }
  499. int ScriptApiSecurity::sl_g_dofile(lua_State *L)
  500. {
  501. int nret = sl_g_loadfile(L);
  502. if (nret != 1) {
  503. lua_error(L);
  504. // code after this function isn't executed
  505. }
  506. int top_precall = lua_gettop(L);
  507. lua_call(L, 0, LUA_MULTRET);
  508. // Return number of arguments returned by the function,
  509. // adjusting for the function being poped.
  510. return lua_gettop(L) - (top_precall - 1);
  511. }
  512. int ScriptApiSecurity::sl_g_load(lua_State *L)
  513. {
  514. size_t len;
  515. const char *buf;
  516. std::string code;
  517. const char *chunk_name = "=(load)";
  518. luaL_checktype(L, 1, LUA_TFUNCTION);
  519. if (!lua_isnone(L, 2)) {
  520. luaL_checktype(L, 2, LUA_TSTRING);
  521. chunk_name = lua_tostring(L, 2);
  522. }
  523. while (true) {
  524. lua_pushvalue(L, 1);
  525. lua_call(L, 0, 1);
  526. int t = lua_type(L, -1);
  527. if (t == LUA_TNIL) {
  528. break;
  529. }
  530. if (t != LUA_TSTRING) {
  531. lua_pushnil(L);
  532. lua_pushliteral(L, "Loader didn't return a string");
  533. return 2;
  534. }
  535. buf = lua_tolstring(L, -1, &len);
  536. code += std::string(buf, len);
  537. lua_pop(L, 1); // Pop return value
  538. }
  539. if (!safeLoadString(L, code, chunk_name)) {
  540. lua_pushnil(L);
  541. lua_insert(L, -2);
  542. return 2;
  543. }
  544. return 1;
  545. }
  546. int ScriptApiSecurity::sl_g_loadfile(lua_State *L)
  547. {
  548. #ifndef SERVER
  549. lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
  550. #if INDIRECT_SCRIPTAPI_RIDX
  551. ScriptApiBase *script = (ScriptApiBase *) *(void**)(lua_touserdata(L, -1));
  552. #else
  553. ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
  554. #endif
  555. lua_pop(L, 1);
  556. // Client implementation
  557. if (script->getType() == ScriptingType::Client) {
  558. std::string path = readParam<std::string>(L, 1);
  559. const std::string *contents = script->getClient()->getModFile(path);
  560. if (!contents) {
  561. std::string error_msg = "Coudln't find script called: " + path;
  562. lua_pushnil(L);
  563. lua_pushstring(L, error_msg.c_str());
  564. return 2;
  565. }
  566. std::string chunk_name = "@" + path;
  567. if (!safeLoadString(L, *contents, chunk_name.c_str())) {
  568. lua_pushnil(L);
  569. lua_insert(L, -2);
  570. return 2;
  571. }
  572. return 1;
  573. }
  574. #endif
  575. // Server implementation
  576. const char *path = NULL;
  577. if (lua_isstring(L, 1)) {
  578. path = lua_tostring(L, 1);
  579. CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
  580. }
  581. if (!safeLoadFile(L, path)) {
  582. lua_pushnil(L);
  583. lua_insert(L, -2);
  584. return 2;
  585. }
  586. return 1;
  587. }
  588. int ScriptApiSecurity::sl_g_loadstring(lua_State *L)
  589. {
  590. const char *chunk_name = "=(load)";
  591. luaL_checktype(L, 1, LUA_TSTRING);
  592. if (!lua_isnone(L, 2)) {
  593. luaL_checktype(L, 2, LUA_TSTRING);
  594. chunk_name = lua_tostring(L, 2);
  595. }
  596. size_t size;
  597. const char *code = lua_tolstring(L, 1, &size);
  598. std::string code_s(code, size);
  599. if (!safeLoadString(L, code_s, chunk_name)) {
  600. lua_pushnil(L);
  601. lua_insert(L, -2);
  602. return 2;
  603. }
  604. return 1;
  605. }
  606. int ScriptApiSecurity::sl_g_require(lua_State *L)
  607. {
  608. lua_pushliteral(L, "require() is disabled when mod security is on.");
  609. return lua_error(L);
  610. }
  611. int ScriptApiSecurity::sl_io_open(lua_State *L)
  612. {
  613. bool with_mode = lua_gettop(L) > 1;
  614. luaL_checktype(L, 1, LUA_TSTRING);
  615. const char *path = lua_tostring(L, 1);
  616. bool write_requested = false;
  617. if (with_mode) {
  618. luaL_checktype(L, 2, LUA_TSTRING);
  619. const char *mode = lua_tostring(L, 2);
  620. write_requested = strchr(mode, 'w') != NULL ||
  621. strchr(mode, '+') != NULL ||
  622. strchr(mode, 'a') != NULL;
  623. }
  624. CHECK_SECURE_PATH_INTERNAL(L, path, write_requested, NULL);
  625. push_original(L, "io", "open");
  626. lua_pushvalue(L, 1);
  627. if (with_mode) {
  628. lua_pushvalue(L, 2);
  629. }
  630. lua_call(L, with_mode ? 2 : 1, 2);
  631. return 2;
  632. }
  633. int ScriptApiSecurity::sl_io_input(lua_State *L)
  634. {
  635. if (lua_isstring(L, 1)) {
  636. const char *path = lua_tostring(L, 1);
  637. CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
  638. }
  639. push_original(L, "io", "input");
  640. lua_pushvalue(L, 1);
  641. lua_call(L, 1, 1);
  642. return 1;
  643. }
  644. int ScriptApiSecurity::sl_io_output(lua_State *L)
  645. {
  646. if (lua_isstring(L, 1)) {
  647. const char *path = lua_tostring(L, 1);
  648. CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);
  649. }
  650. push_original(L, "io", "output");
  651. lua_pushvalue(L, 1);
  652. lua_call(L, 1, 1);
  653. return 1;
  654. }
  655. int ScriptApiSecurity::sl_io_lines(lua_State *L)
  656. {
  657. if (lua_isstring(L, 1)) {
  658. const char *path = lua_tostring(L, 1);
  659. CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
  660. }
  661. int top_precall = lua_gettop(L);
  662. push_original(L, "io", "lines");
  663. lua_pushvalue(L, 1);
  664. lua_call(L, 1, LUA_MULTRET);
  665. // Return number of arguments returned by the function,
  666. // adjusting for the function being poped.
  667. return lua_gettop(L) - top_precall;
  668. }
  669. int ScriptApiSecurity::sl_os_rename(lua_State *L)
  670. {
  671. luaL_checktype(L, 1, LUA_TSTRING);
  672. const char *path1 = lua_tostring(L, 1);
  673. CHECK_SECURE_PATH_INTERNAL(L, path1, true, NULL);
  674. luaL_checktype(L, 2, LUA_TSTRING);
  675. const char *path2 = lua_tostring(L, 2);
  676. CHECK_SECURE_PATH_INTERNAL(L, path2, true, NULL);
  677. push_original(L, "os", "rename");
  678. lua_pushvalue(L, 1);
  679. lua_pushvalue(L, 2);
  680. lua_call(L, 2, 2);
  681. return 2;
  682. }
  683. int ScriptApiSecurity::sl_os_remove(lua_State *L)
  684. {
  685. luaL_checktype(L, 1, LUA_TSTRING);
  686. const char *path = lua_tostring(L, 1);
  687. CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);
  688. push_original(L, "os", "remove");
  689. lua_pushvalue(L, 1);
  690. lua_call(L, 1, 2);
  691. return 2;
  692. }