Compare commits

...

2 Commits

Author SHA1 Message Date
Izaya 4c462a5d4e add access control support to MTFS 2020-10-17 18:50:30 +11:00
Izaya 2363890151 added basic ACLs for the RPC library 2020-10-17 18:49:00 +11:00
4 changed files with 57 additions and 5 deletions

View File

@ -5,13 +5,28 @@ local rpc = require "rpc"
local tA, tO = shell.parse(...) local tA, tO = shell.parse(...)
if #tA < 1 then if #tA < 1 then
print("Usage: exportfs <directory> [-d] [--rw] [--name=<name>]") print("Usage: exportfs <directory> [-d] [--rw] [--name=<name>] [--allow=hostname[,hostname,...]] [--deny=hostname[,hostname,...]]")
return return
end end
local allow, deny = {}, {}
for host in (tO.allow or ""):gmatch("[^,]+") do
allow[#allow+1] = host
end
for host in (tO.deny or ""):gmatch("[^,]+") do
deny[#deny+1] = host
end
local px = fsproxy.new(tA[1], not tO.rw) local px = fsproxy.new(tA[1], not tO.rw)
local name = tO.name or tA[1] local name = tO.name or tA[1]
for l,m in pairs(px) do for l,m in pairs(px) do
m = not tO.d and m or nil m = not tO.d and m or nil
rpc.register("fs_"..name.."_"..l,m) rpc.register("fs_"..name.."_"..l,m)
for k,v in pairs(allow) do
rpc.allow("fs_"..name.."_"..l,v)
end
for k,v in pairs(deny) do
rpc.deny("fs_"..name.."_"..l,v)
end
end end
print(string.format("%s (%s)", name, (tO.rw and "rw") or "ro")) print(string.format("%s (%s)", name, (tO.rw and "rw") or "ro"))

View File

@ -57,7 +57,10 @@ if px.dirstat then -- use single call for file info
return gce(path, 4) or olm(path) return gce(path, 4) or olm(path)
end end
end end
local iro = px.isReadOnly() local iro,e = px.isReadOnly()
if not iro then
error(e)
end
function px.isReadOnly() function px.isReadOnly()
return iro return iro
end end

View File

@ -6,6 +6,7 @@ local rpcf = {}
local rpcrunning = false local rpcrunning = false
local rpc = {} local rpc = {}
rpc.port = 111 rpc.port = 111
function rpc.call(hostname,fn,...) function rpc.call(hostname,fn,...)
if hostname == "localhost" then if hostname == "localhost" then
return rpcf[fn](...) return rpcf[fn](...)
@ -39,19 +40,37 @@ function rpc.proxy(hostname,filter)
return rt return rt
end end
local function setacl(self, fname, host)
self[fname] = self[fname] or {}
self[fname][host] = true
end
rpc.allow = setmetatable({},{__call=setacl})
rpc.deny = setmetatable({},{__call=setacl})
local function isPermitted(host,fn)
if rpc.allow[fn] then
return rpc.allow[fn][host] or false
end
if rpc.deny[fn] and rpc.deny[fn][host] then
return false
end
return true
end
function rpc.register(name,fn) function rpc.register(name,fn)
if not rpcrunning then if not rpcrunning then
event.listen("net_msg",function(_, from, port, data) event.listen("net_msg",function(_, from, port, data)
if port == rpc.port then if port == rpc.port then
local rpcrq = serial.unserialize(data) local rpcrq = serial.unserialize(data)
local rpcn, rpcid = table.remove(rpcrq,1), table.remove(rpcrq,1) local rpcn, rpcid = table.remove(rpcrq,1), table.remove(rpcrq,1)
if rpcf[rpcn] then if rpcf[rpcn] and isPermitted(from,rpcn) then
local rt = {pcall(rpcf[rpcn],table.unpack(rpcrq))} local rt = {pcall(rpcf[rpcn],table.unpack(rpcrq))}
if rt[1] == true then if rt[1] == true then
table.remove(rt,1) table.remove(rt,1)
end end
minitel.send(from,port,serial.serialize({rpcid,table.unpack(rt)})) minitel.send(from,port,serial.serialize({rpcid,table.unpack(rt)}))
else else
minitel.send(from,port,serial.serialize({rpcid,false,"function unavailable"}))
end end
end end
end) end)
@ -67,5 +86,4 @@ function rpc.register(name,fn)
rpcf[name] = fn rpcf[name] = fn
end end
return rpc return rpc

View File

@ -1,5 +1,5 @@
# RPC # RPC
Minitel Remote Procedure Call Library Minitel Remote Procedure Call Library for OpenOS
## API ## API
In all instances, if *hostname* is replaced with *localhost*, an attempt will be made to call the registered procedure on the local machine. In all instances, if *hostname* is replaced with *localhost*, an attempt will be made to call the registered procedure on the local machine.
@ -13,7 +13,23 @@ Return a table containing the functions on *hostname* matching *filter*, which i
### rpc.register(*name*, *function*) ### rpc.register(*name*, *function*)
Registers *function* as the RPC call for *name* on the current host. Registers *function* as the RPC call for *name* on the current host.
### rpc.allow(*fname*, *hostname*)
Adds *hostname* to the list of remote hosts allowed to execute the function *fname*.
## Access control
Access control is implemented by way of an "allow" list and a "deny" list. Any function lacking both lists will default to the function being available to all hosts.
Any entries in the 'allow' list for a function will disable access for any but those in the allow list.
Entries in the 'deny' list will result in the hosts in said list being denied, but other hosts are allowed. This will not override the 'allow' list.
## Variables ## Variables
### rpc.port = 111 ### rpc.port = 111
Port to use for RPC calls and registration. Port to use for RPC calls and registration.
### rpc.allow = {}
Table containing the allow lists of exported functions. Contents subject to change.
### rpc.deny = {}
Table containing the deny lists of exported functions. Contents subject to change.