zet.tlv: switch file writes to new API

The interface for apps looks much nicer now, see 'main' in zet.tlv.
However there are some open issues:

- It can still be confusing to the computer owner that an app tries to
  write to some temporary file that isn't mentioned anywhere.

- File renames can fail if /tmp is on a different volume.

- What happens if an app overrides start_writing()? The computer owner
  may think they've audited the caller of start_writing and give it
  blanket file permissions. Teliva tunnels through start_writing when
  computing the caller. If the app can control what start_writing does,
  the app could be performing arbitrary malicious file operations.

  Right now things actually seem perfectly secure. Overriding
  start_writing has no effect. Our approach for loading .tlv files (in
  reverse chronological order, preventing older versions from overriding
  newer ones) has the accidentally _great_ property that Teliva apps can
  never override system definitions.

  So we have a new reason to put standard libraries in a .lua file: if
  we need to prevent apps from overriding it.

  This feels like something that needs an automated test, both to make
  sure I'm running the right experiment and to ensure I don't
  accidentally cause a regression in the future. I can totally imagine a
  future rewrite that tried a different approach than
  reverse-chronological.
This commit is contained in:
Kartik K. Agaram 2022-03-07 10:35:23 -08:00
parent 7a315e3d9f
commit 38063812b6
4 changed files with 64 additions and 0 deletions

View File

@ -122,6 +122,11 @@ static int io_tostring (lua_State *L) {
}
static int is_equal(const char *a, const char *b) {
return strcmp(a, b) == 0;
}
static char iolib_errbuf[1024] = {0};
static int io_open (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
@ -134,6 +139,15 @@ static int io_open (lua_State *L) {
const char *caller = get_caller(L);
if (file_operation_permitted(caller, filename, mode))
*pf = fopen(filename, mode);
else if (is_equal(caller, "start_writing") || is_equal(caller, "start_reading")) {
caller = get_caller_of_caller(L);
if (file_operation_permitted(caller, filename, mode))
*pf = fopen(filename, mode);
else {
snprintf(iolib_errbuf, 1024, "app tried to open file '%s' from caller '%s'; adjust its permissions (ctrl-p) if that is expected", filename, caller);
Previous_message = iolib_errbuf;
}
}
else {
snprintf(iolib_errbuf, 1024, "app tried to open file '%s' from caller '%s'; adjust its permissions (ctrl-p) if that is expected", filename, caller);
Previous_message = iolib_errbuf;

View File

@ -337,6 +337,17 @@ char* get_caller(lua_State* L) {
return result;
}
char* get_caller_of_caller(lua_State* L) {
static char result[1024] = {0};
lua_Debug ar;
lua_getstack(L, 2, &ar);
lua_getinfo(L, "n", &ar);
memset(result, '\0', 1024);
if (ar.name)
strncpy(result, ar.name, 1020);
return result;
}
void save_caller_as(lua_State* L, const char* name, const char* caller_name) {
// push table of caller tables
luaL_newmetatable(L, "__teliva_caller");

View File

@ -168,6 +168,8 @@ extern void assign_call_graph_depth_to_name(lua_State* L, int depth, const char*
extern char* get_caller(lua_State* L);
extern void save_caller(lua_State* L, const char* name, int call_graph_depth);
extern void draw_callers_of_current_definition(lua_State* L);
extern char* get_caller_of_caller(lua_State* L);
extern void append_to_audit_log(lua_State* L, const char* buffer);
/* Standard UI elements */

37
zet.tlv
View File

@ -3593,3 +3593,40 @@
>function read_zettels(infile)
> zettels = jsonf.decode(infile)
>end
- __teliva_timestamp:
>Mon Mar 7 10:31:27 2022
main:
>function main()
> init_colors()
> curses.curs_set(0) -- hide cursor except when editing
>
> -- load any saved zettels
> local infile = start_reading(nil, 'zet')
> if infile then
> read_zettels(infile)
> end
> current_zettel_id = zettels.root -- cursor
> view_settings.first_zettel = zettels.root -- start rendering here
>
> while true do
> render(Window)
> update(Window)
>
> -- save zettels
> local outfile = start_writing(nil, 'zet')
> if outfile then
> write_zettels(outfile)
> end
> -- TODO: what if io.open failed for a non-sandboxing related reason?!
> -- We could silently fail to save.
> end
>end
- __teliva_timestamp:
>Mon Mar 7 10:32:08 2022
__teliva_note:
>switch to new file API for writing
write_zettels:
>function write_zettels(outfile)
> outfile:send(json.encode(zettels))
> outfile:close()
>end