byuth1 ([info]byuth1) wrote in [info]lisp,
@ 2006-05-05 11:21:00
Previous Entry  Add to memories!  Next Entry
retranslation from ru_scheme community: SE Scheme
Selected notes from [info]ru_scheme community
"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))
> +
4


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:
call-with-input-file call-with-output-file
with-input-from-file with-output-to-file
open-input-file  open-output-file
close-input-port close-output-port
load eval


The procedure below allows us to evaluate expressions in a safe R5RS environment:
(define (protect-untrusted exp)
  (define lst '(call-with-input-file call-with-output-file
                 with-input-from-file with-output-to-file
                 open-input-file  open-output-file
                 close-input-port close-output-port
                 load eval))
  `(let ,(map (lambda(x) `(,x #f)) lst) ,exp))

(define (untrusted-eval exp)
        (eval (protect-untrusted exp)
                                 untrusted-env))


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)
  (let ((gate
         (eval `(begin (define ,proc
                         (call-with-current-continuation (lambda(x) x)))
                       ,proc)
               untrusted-env)))
    (or (eq? gate real-proc)
        (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 [info]voins and [info]_avm_



(Post a new comment)


[info]heptadecagram
2006-05-05 12:15 pm UTC (link)

Link to the original, спасибо?

(Reply to this) (Thread)


[info]voins
2006-05-05 12:39 pm UTC (link)
http://community.livejournal.com/ru_scheme/8185.html

(Reply to this) (Parent)


[info]darius
2006-05-06 07:24 am UTC (link)
There are more primitives that need taming: all the I/O procedures that let you omit a default port argument, plus the current-input-port and current-output-port procedures. scheme-report-environment needs to be tamed lest it give access to the capabilities you tried to suppress; but eval on the other hand is safe as long as it requires an explicit environment argument (it's the environment that conveys authority, not the eval procedure -- the untrusted user could write eval themselves in the tamed language subset). I may have forgotten other R5RS capabilities, since I'm more used to R4RS.

I'd recommend taming call/cc too, unless the sandbox is freshly created for each use or else is 'deep frozen' (set up to not be able to retain state).

Also potentially dangerous is any unspecified behavior or results of Scheme constructs, though that may not be a problem in practice.

The above code doesn't define untrusted-env, so maybe I'm missing something.

Why is bind-to-untrusted not just (eval `(define ,proc ',real-proc) untrusted-env)? Because R5RS doesn't define how to handle a quoted procedure value?

It'd be nice to work metacircularly, so a sandboxed agent can easily sandbox another; this isn't supported here because you both use and suppress eval.

You might find my http://www.accesscom.com/~darius/software/consp.html interesting.

(Reply to this) (Thread)


[info]byuth1
2006-05-06 10:02 am UTC (link)
Thank you for your comment. It's very interesting for me.


>I'd recommend taming call/cc too, unless the sandbox is freshly created for each use or else is 'deep frozen' (set up to not be able to retain state).

I still not understand why I need to hide call/cc.


>The above code doesn't define untrusted-env, so maybe I'm missing something.

Untrusted environment is implementation specific ;)
For example guile provides (current-module) for it, other can use (interactive-environment) etc.

>Why is bind-to-untrusted not just (eval `(define ,proc ',real-proc) untrusted-env)?
>Because R5RS doesn't define how to handle a quoted procedure value?

real-proc doesn't exist in untrusted enviroment, therefore, it will not work.

I agree with you about eval, it will be interesting to have sandboxes inside sandboxes.

I will see your project, it's very interesting for me.

(Reply to this) (Parent) (Thread)


[info]darius
2006-05-07 07:14 am UTC (link)
About call/cc, my concern is that the untrusted code could stash away a continuation and return multiple times unexpectedly, and such tricks. It seems hard to plan for these things. On the other hand, some more limited kind of continuation feature would be useful.

real-proc doesn't exist in untrusted enviroment, therefore, it will not work.

Oh, I think you're saying real-proc is the name of a procedure, not the procedure object itself. Is that right?

(Reply to this) (Parent) (Thread)


[info]byuth1
2006-05-10 09:05 am UTC (link)
real-proc is the procedure object in my sample.

(Reply to this) (Parent) (Thread)


[info]darius
2006-05-12 06:30 am UTC (link)
OK, I still don't understand why it "will not work" in the sandbox. I didn't have any trouble passing external procedures to confined code in my own hacking around...

(Reply to this) (Parent) (Thread)


[info]byuth1
2006-05-15 09:04 am UTC (link)
OOPS .. you are right ;)
I can do it.

(Reply to this) (Parent)


[info]byuth1
2006-05-15 09:15 am UTC (link)
gambit-C allow it
guile allow it,
scheme48 allow only a definition of variables, not procedures ;)

(Reply to this) (Parent)


[info]byuth1
2006-05-15 09:29 am UTC (link)
looks like it is not a standard way.

(Reply to this) (Parent)


Log in now.
(Create account, or use OpenID)