retranslation from ru_scheme community: SE Scheme
Selected notes from ru_scheme
"How to create Security Enhanced Scheme?"
How to create Security Enhanced Scheme?
It's possible to create "sandboxes" using standard (R5RS) scheme enviroment.
Standard provides to us a very interesting tool, an environment, possible sets
of bindings. There are three default enviroments:
1. interaction-enviroment contains implementation-defined bindings, typically a superset of those listed in the standard.
2. scheme-report-environment is empty except for all bindings defined in R5RS.
3. null-environment is empty except for the (syntactic) bindings for all syntactic keywords defined in standard.
An environment is not a "sandbox", but an interface. Every interface can include other interfaces as a subsets; therefore, a simple re-definition applied to one enviroment can affect the contents of other environments.
I've seen two scheme implementations, where following example works:
> (eval '(define + 4) (scheme-report-environment 5))
However, standard guarantees that some procedures are inaccessible during evaluation process.
So we can get the R5RS enviroment and wrap our expressions into "let" with the dangerous procedures turned off.
The only insecure procedures in the standard environment are:
The procedure below allows us to evaluate expressions in a safe
(define (protect-untrusted exp)
(define lst '(call-with-input-file call-with-output-file
`(let ,(map (lambda(x) `(,x #f)) lst) ,exp))
(define (untrusted-eval exp)
(eval (protect-untrusted exp)
How to allow untrusted procedures to perform dangerous operations in a controlled way?
We have to create a gate from the trusted (our current) environment to the untrusted (safe R5RS) one.
Eval can only evaluate expressions (list of lists and constants) and return a result.
But we need to bind variables from one enviroment to other.
Call-with-current-continuation can help us.
We can capture a continuation in the untrusted enviroment, return it to the trusted (current) one and go back with the variable we want to bind.
(define (bind-to-untrusted proc real-proc)
(eval `(begin (define ,proc
(call-with-current-continuation (lambda(x) x)))
(or (eq? gate real-proc)
We use "or", because we will return to this point after the continuation finishes.
That's all. So we can have the following security architecture:
1. Two enviroments: trusted (current enviroment of our program) and untrusted (clean and safe R5RS)
2. Untrusted procedures are evaluated in the untrusted environment as closures, so they can not see each other.
3. All untrusted procedures can communicate via some "gate" with the security monitor to get privileged procedures they need.
4. The monitor lives in the trusted enviroment and processes requests according to some security policies.
TRUSTED | UNTRUSTED
------- | -----------
MONITOR <-- [GATE]
\/ | [ PROCESS1 ]
POLICY | [ PROCESS2 ]
| [ PROCESS3 ]
There are no restrictions for security policies:
1. You can simply accept or reject requests from untrusted procedures.
2. You can return wrappers for requested operations with additional checks,
e.g. for filenames, for current time etc.
So we can create Security Enhanced Scheme without using any implementation dependent features
in five minutes ;)Translated from Russian with big help of voins and _avm_