mirror of
https://github.com/luanti-org/luanti.git
synced 2025-08-01 17:38:41 +00:00
add API and security doc
This commit is contained in:
parent
58e5d0b4ff
commit
ff1cb8f9df
2 changed files with 288 additions and 0 deletions
212
doc/sscsm_api.md
Normal file
212
doc/sscsm_api.md
Normal file
|
@ -0,0 +1,212 @@
|
|||
# Server-sent client-side modding (SSCSM) API reference
|
||||
|
||||
**Warning:** SSCSM is very experimental. The API will break. Always start your
|
||||
mod with a version check (using `core.get_version()`).
|
||||
|
||||
In SSCSM, the server sends scripts to the client, which it executes
|
||||
client-side (in a sandbox, see also `sscsm_security.md`).
|
||||
As modder, you can add these scripts to your server-side mod, and tell the engine
|
||||
to send them.
|
||||
|
||||
Please refer to `lua_api.md` for server-side modding.
|
||||
(And refer to `client_lua_api.md` for client-provided client-side modding (CPCSM).)
|
||||
|
||||
|
||||
|
||||
## Loading mods
|
||||
|
||||
### Paths
|
||||
|
||||
SSCSM uses a virtual file system (just a dictionary of virtual paths (strings))
|
||||
to file contents (strings).
|
||||
|
||||
Each mod's files have paths of the form `modname:foo/bla.lua`.
|
||||
Please don't rely on this, use `core.get_modpath()` instead.
|
||||
|
||||
The virtual file paths within a mod are meant to mimic the filepaths on the
|
||||
server, for example `<modpath>/common/foo.lua` gets sent as `modname:common/foo.lua`.
|
||||
|
||||
The engine loads `modname:init.lua` for all mods, in server mod dependency order.
|
||||
|
||||
There is client and server builtin (modnames are `*client_builtin*` and
|
||||
`*server_builtin*`). The server builtin is sent from the server, like any other
|
||||
SSCSM, and the client builtin is located on the client.
|
||||
|
||||
|
||||
### Mod sending API
|
||||
|
||||
Currently, you can not add any mods. There's only a small hardcoded preview script
|
||||
in C++ which is loaded when you set `enable_sscsm` to `singleplayer`.
|
||||
|
||||
|
||||
|
||||
## API
|
||||
|
||||
Unless noted otherwise, these work the same as in the server modding API.
|
||||
|
||||
### Global callbacks
|
||||
|
||||
* `core.register_globalstep(function(dtime))`
|
||||
|
||||
|
||||
### SSCSM-specific API
|
||||
|
||||
* `core.get_node_or_nil(pos)`
|
||||
* `core.get_content_id(name)`
|
||||
* `core.get_name_from_content_id(id)`
|
||||
|
||||
|
||||
### Util API
|
||||
|
||||
* `core.log([level,] text)`
|
||||
* `core.get_us_time()`
|
||||
* Limited in precision.
|
||||
* `core.parse_json(str[, nullvalue])`
|
||||
* `core.write_json(data[, styled])`
|
||||
* `core.is_yes(arg)`
|
||||
* `core.compress(data, method, ...)`
|
||||
* `core.decompress(data, method, ...)`
|
||||
* `core.encode_base64(string)`
|
||||
* `core.decode_base64(string)`
|
||||
* `core.get_version()`
|
||||
* `core.sha1(string, raw)`
|
||||
* `core.sha256(string, raw)`
|
||||
* `core.colorspec_to_colorstring(colorspec)`
|
||||
* `core.colorspec_to_bytes(colorspec)`
|
||||
* `core.colorspec_to_table(colorspec)`
|
||||
* `core.time_to_day_night_ratio(time_of_day)`
|
||||
* `core.get_last_run_mod()`
|
||||
* `core.set_last_run_mod(modname)`
|
||||
* `core.urlencode(value)`
|
||||
|
||||
|
||||
### Other
|
||||
|
||||
* `core.get_current_modname()`
|
||||
* `core.get_modpath(modname)`
|
||||
|
||||
|
||||
### Builtin helpers
|
||||
|
||||
* `math.*` additions
|
||||
|
||||
* `vector.*`
|
||||
|
||||
* `core.global_exists(name)`
|
||||
|
||||
* `core.serialize(value)`
|
||||
* `core.deserialize(str, safe)`
|
||||
|
||||
* `dump2(obj, name, dumped)`
|
||||
* `dump(obj, dumped)`
|
||||
* `string.*` additions
|
||||
* `table.*` additions
|
||||
* `core.formspec_escape(text)`
|
||||
* `core.hypertext_escape(text)`
|
||||
* `core.wrap_text(str, limit, as_table)`
|
||||
* `core.explode_table_event(evt)`
|
||||
* `core.explode_textlist_event(evt)`
|
||||
* `core.explode_scrollbar_event(evt)`
|
||||
* `core.rgba(r, g, b, a)`
|
||||
* `core.pos_to_string(pos, decimal_places)`
|
||||
* `core.string_to_pos(value)`
|
||||
* `core.string_to_area(value, relative_to)`
|
||||
* `core.get_color_escape_sequence(color)`
|
||||
* `core.get_background_escape_sequence(color)`
|
||||
* `core.colorize(color, message)`
|
||||
* `core.strip_foreground_colors(str)`
|
||||
* `core.strip_background_colors(str)`
|
||||
* `core.strip_colors(str)`
|
||||
* `core.translate(textdomain, str, ...)`
|
||||
* `core.translate_n(textdomain, str, str_plural, n, ...)`
|
||||
* `core.get_translator(textdomain)`
|
||||
* `core.pointed_thing_to_face_pos(placer, pointed_thing)`
|
||||
* `core.string_to_privs(str, delim)`
|
||||
* `core.privs_to_string(privs, delim)`
|
||||
* `core.is_nan(number)`
|
||||
* `core.parse_relative_number(arg, relative_to)`
|
||||
* `core.parse_coordinates(x, y, z, relative_to)`
|
||||
|
||||
* `core.inventorycube(img1, img2, img3)`
|
||||
* `core.dir_to_facedir(dir, is6d)`
|
||||
* `core.facedir_to_dir(facedir)`
|
||||
* `core.dir_to_fourdir(dir)`
|
||||
* `core.fourdir_to_dir(fourdir)`
|
||||
* `core.dir_to_wallmounted(dir)`
|
||||
* `core.wallmounted_to_dir(wallmounted)`
|
||||
* `core.dir_to_yaw(dir)`
|
||||
* `core.yaw_to_dir(yaw)`
|
||||
* `core.is_colored_paramtype(ptype)`
|
||||
* `core.strip_param2_color(param2, paramtype2)`
|
||||
|
||||
* `core.after(time, func, ...)`
|
||||
|
||||
|
||||
### Lua standard library
|
||||
|
||||
* `assert`
|
||||
* `collectgarbage`
|
||||
* `error`
|
||||
* `getfenv`
|
||||
* `ipairs`
|
||||
* `next`
|
||||
* `pairs`
|
||||
* `pcall`
|
||||
* `rawequal`
|
||||
* `rawget`
|
||||
* `rawset`
|
||||
* `select`
|
||||
* `setfenv`
|
||||
* `getmetatable`
|
||||
* `setmetatable`
|
||||
* `tonumber`
|
||||
* `tostring`
|
||||
* `type`
|
||||
* `unpack`
|
||||
* `_VERSION`
|
||||
* `xpcall`
|
||||
* `dofile`
|
||||
* Overwritten.
|
||||
* `load`
|
||||
* Overwritten.
|
||||
* `loadfile`
|
||||
* Overwritten.
|
||||
* `loadstring`
|
||||
* Overwritten.
|
||||
* `coroutine.*`
|
||||
* `table.*`
|
||||
* `math.*`
|
||||
* `string.*`
|
||||
* except `string.dump`
|
||||
* `os.difftime`
|
||||
* `os.time`
|
||||
* `os.clock`
|
||||
* Reduced precision.
|
||||
* `debug.traceback`
|
||||
|
||||
|
||||
### LuaJIT `jit` library
|
||||
|
||||
* `jit.arch`
|
||||
* `jit.flush`
|
||||
* `jit.off`
|
||||
* `jit.on`
|
||||
* `jit.opt`
|
||||
* `jit.os`
|
||||
* `jit.status`
|
||||
* `jit.version`
|
||||
* `jit.version_num`
|
||||
|
||||
|
||||
### Bit library
|
||||
|
||||
* `bit.*`
|
||||
|
||||
|
||||
### API only for client builtin
|
||||
|
||||
* `core.get_builtin_path()`
|
||||
* Returns path, depending on which builtin currently loads, or `nil`.
|
||||
* `debug.getinfo(...)`
|
||||
* `INIT`
|
||||
* Is `"sscsm"`.
|
76
doc/sscsm_security.md
Normal file
76
doc/sscsm_security.md
Normal file
|
@ -0,0 +1,76 @@
|
|||
# SSCSM security
|
||||
|
||||
|
||||
## Threat model
|
||||
|
||||
* SSCSM scripts come from the server (potential malicious actor). We are the client.
|
||||
* Authenticity of server is not given (our networking is not secure). So we have
|
||||
to expect anyone who can send us UDP packets to the appropriate IP address to be
|
||||
able to act on behalf of the server.
|
||||
* The server may not tamper with, or get access to information of, anything besides
|
||||
the stuff explicitly made accessible via the modding API (i.e. gameplay relevant
|
||||
stuff, like map, node definitions, ...).
|
||||
In particular, this excludes for (non-exhaustive) example files, file paths,
|
||||
and settings.
|
||||
* DOS is not an issue (as it is already easily possible to DOS a client).
|
||||
* We already have an API via network packets (see `networkprotocol.h`).
|
||||
This acts as upper bound: Every SSCSM API function could instead be a network
|
||||
packet endpoint. There are no efforts to make SSCSM more secure than this.
|
||||
|
||||
|
||||
## Non-binary `enable_sscsm` setting
|
||||
|
||||
The `enable_sscsm` setting does not just allow en-/disabling SSCSM, it also allows
|
||||
limiting on what sort of servers to enable SSCSM. Options are `nowhere`, `singleplayer`,
|
||||
`localhost` (or singleplayer), `lan` (or lower), and everywhere.
|
||||
On options `localhost` and lower, we know that (anyone who acts on the behalf of)
|
||||
the server runs on the same machine, and the risk of it being malicious is pretty
|
||||
much zero.
|
||||
|
||||
Until sufficient security measures are in place, users are disallowed to set this
|
||||
setting to anything higher than `localhost`.
|
||||
|
||||
|
||||
## Lua sandbox
|
||||
|
||||
* We execute only Lua scripts, in a Lua sandbox.
|
||||
* See also `initializeSecuritySSCSM()`.
|
||||
* We do not trust the Lua implementation to not have bugs. => Additional process
|
||||
isolation layer as fallback.
|
||||
|
||||
|
||||
## Process isolation
|
||||
|
||||
* Not yet implemented.
|
||||
* Separate SSCSM process.
|
||||
* Sandboxing:
|
||||
* Linux: Uses SECCOMP.
|
||||
* ... (FIXME: write down stuff when you implement)
|
||||
|
||||
|
||||
## Limit where we call into SSCSM
|
||||
|
||||
* Even if the Lua sandbox and/or the process isolation are bug-free, the main
|
||||
process client code can still be vulnerable. Consider this example:
|
||||
* Client has an inventorylist A.
|
||||
* User moves an item.
|
||||
* SSCSM gets called (callback when item is moved).
|
||||
* SSCSM can do anything now. It decides to delete A, then returns.
|
||||
* Client still has reference to A on stack, tries to access it.
|
||||
* => Use-after-free.
|
||||
* To avoid these sort of issues, we only give control-flow to SSCSM in few special
|
||||
places.
|
||||
In particular, this includes packet handlers, and the client's `step()` function.
|
||||
* In these places, the client already does not assume anything about the current
|
||||
state (e.g. that an inventory exists).
|
||||
* This makes sure that SSCSM API calls can also just happen in these places.
|
||||
In packet handlers, the server can already cause arbitrary network API "calls"
|
||||
to happen. Hence, new SSCSM API calls here do not lead to new vulnerabilities
|
||||
that a network API would not cause as well.
|
||||
|
||||
|
||||
## No precise clocks
|
||||
|
||||
To mitigate time-based side-channel attacks, all available clock API functions
|
||||
(`os.clock()` and `core.get_us_time()`) only have a precision of
|
||||
`SSCSM_CLOCK_RESOLUTION_US` (20) us.
|
Loading…
Add table
Add a link
Reference in a new issue