1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
|
if not modules then modules = { } end modules ['luat-iop'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
-- this paranoid stuff in web2c ... we cannot hook checks into the
-- input functions because one can always change the callback but
-- we can feed back specific patterns and paths into the next
-- mechanism
-- os.execute os.exec os.spawn io.fopen
-- os.remove lfs.chdir lfs.mkdir
-- io.open zip.open epdf.open mlib.new
-- cache
local topattern, find = string.topattern, string.find
local report_limiter = logs.reporter("system","limiter")
-- the basic methods
local function match(ruleset,name)
local n = #ruleset
if n > 0 then
for i=1,n do
local r = ruleset[i]
if find(name,r[1]) then
return r[2]
end
end
return false
else
-- nothing defined (or any)
return true
end
end
local function protect(ruleset,proc)
return function(name,...)
if name == "" then
-- report_limiter("no access permitted: <no name>") -- can happen in mplib code
return nil, "no name given"
elseif match(ruleset,name) then
return proc(name,...)
else
report_limiter("no access permitted: %s",name)
return nil, name .. ": no access permitted"
end
end
end
function io.limiter(preset)
preset = preset or { }
local ruleset = { }
for i=1,#preset do
local p = preset[i]
local what, spec = p[1] or "", p[2] or ""
if spec == "" then
-- skip 'm
elseif what == "tree" then
resolvers.dowithpath(spec, function(r)
local spec = resolvers.resolve(r) or ""
if spec ~= "" then
ruleset[#ruleset+1] = { topattern(spec,true), true }
end
end)
elseif what == "permit" then
ruleset[#ruleset+1] = { topattern(spec,true), true }
elseif what == "forbid" then
ruleset[#ruleset+1] = { topattern(spec,true), false }
end
end
if #ruleset > 0 then
return {
match = function(name) return match (ruleset,name) end,
protect = function(proc) return protect(ruleset,proc) end,
}
else
return {
match = function(name) return true end,
protect = proc,
}
end
end
-- a few handlers
io.i_limiters = { }
io.o_limiters = { }
function io.i_limiter(v)
local i = io.i_limiters[v]
if i then
local i_limiter = io.limiter(i)
function io.i_limiter()
return i_limiter
end
return i_limiter
end
end
function io.o_limiter(v)
local o = io.o_limiters[v]
if o then
local o_limiter = io.limiter(o)
function io.o_limiter()
return o_limiter
end
return o_limiter
end
end
-- the real thing (somewhat fuzzy as we need to know what gets done)
local i_opener, i_limited = io.open, false
local o_opener, o_limited = io.open, false
local function i_register(v)
if not i_limited then
local i_limiter = io.i_limiter(v)
if i_limiter then
local protect = i_limiter.protect
i_opener = protect(i_opener)
i_limited = true
report_limiter("input mode: %s",v)
end
end
end
local function o_register(v)
if not o_limited then
local o_limiter = io.o_limiter(v)
if o_limiter then
local protect = o_limiter.protect
o_opener = protect(o_opener)
o_limited = true
report_limiter("output mode: %s",v)
end
end
end
function io.open(name,method)
if method and find(method,"[wa]") then
return o_opener(name,method)
else
return i_opener(name,method)
end
end
directives.register("system.inputmode", i_register)
directives.register("system.outputmode", o_register)
local i_limited = false
local o_limited = false
local function i_register(v)
if not i_limited then
local i_limiter = io.i_limiter(v)
if i_limiter then
local protect = i_limiter.protect
lfs.chdir = protect(lfs.chdir) -- needs checking
i_limited = true
end
end
end
local function o_register(v)
if not o_limited then
local o_limiter = io.o_limiter(v)
if o_limiter then
local protect = o_limiter.protect
os.remove = protect(os.remove) -- rather okay
lfs.chdir = protect(lfs.chdir) -- needs checking
lfs.mkdir = protect(lfs.mkdir) -- needs checking
o_limited = true
end
end
end
directives.register("system.inputmode", i_register)
directives.register("system.outputmode", o_register)
-- the definitions
local limiters = resolvers.variable("limiters")
if limiters then
io.i_limiters = limiters.input or { }
io.o_limiters = limiters.output or { }
end
|