about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2022-03-07 10:35:23 -0800
committerKartik K. Agaram <vc@akkartik.com>2022-03-07 10:55:18 -0800
commit38063812b6e931740f9d9d7b4bc95429dcfb3aa6 (patch)
tree3d71c8c5dba878c99551aebbba760a4c4e0deab7
parent7a315e3d9f1c668b54d57b472b612c7f6d738ede (diff)
downloadteliva-38063812b6e931740f9d9d7b4bc95429dcfb3aa6.tar.gz
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.
-rw-r--r--src/liolib.c14
-rw-r--r--src/teliva.c11
-rw-r--r--src/teliva.h2
-rw-r--r--zet.tlv37
4 files changed, 64 insertions, 0 deletions
diff --git a/src/liolib.c b/src/liolib.c
index 45d40a9..a1a5b35 100644
--- a/src/liolib.c
+++ b/src/liolib.c
@@ -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;
diff --git a/src/teliva.c b/src/teliva.c
index 71576b5..db59428 100644
--- a/src/teliva.c
+++ b/src/teliva.c
@@ -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");
diff --git a/src/teliva.h b/src/teliva.h
index db2138d..e322aa3 100644
--- a/src/teliva.h
+++ b/src/teliva.h
@@ -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 */
diff --git a/zet.tlv b/zet.tlv
index e9e08cc..1963796 100644
--- a/zet.tlv
+++ b/zet.tlv
@@ -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