unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* modify environments to make sandboxes
@ 2006-06-12 16:20 Mildred
  2006-06-12 16:47 ` Ludovic Courtès
  2006-06-28  4:13 ` Jon Wilson
  0 siblings, 2 replies; 7+ messages in thread
From: Mildred @ 2006-06-12 16:20 UTC (permalink / raw)


Hi,

I'm new to this mailing list and also new to the scheme language ...
I'm used to Lua but I try to search about functionnal programming, and
I found scheme. It looks like a good language but before using it in my
projects I would like to know if tere is an easy way to create
sandboxes.

In lua, it is easy, you create a table containing functions ... and the
table can be made environment for a function. So, you can easily create
secure sandboxes by loading lua code from file and changing the
environment of the loaded code.

I do not know how to do that in scheme. Apparetly the function
null-environment can return an environment and eval can evaluate some
code in an environment. But the question is how to define a variable in
an environment and also how to undefine a variable that you don't want
to appear.
I didn't found anything about modifying an environment. Is it
possible ? If not, why not ? and is it possible to create sandboxes ?
Thanks

Mildred

-- 
Mildred       <xmpp:mildred@jabber.fr> <http://mildred632.free.fr/>
Clef GPG :    <hkp://pgp.mit.edu> ou <http://mildred632.free.fr/gpg_key>
Fingerprint : 197C A7E6 645B 4299 6D37 684B 6F9D A8D6 [9A7D 2E2B]


_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: modify environments to make sandboxes
  2006-06-12 16:20 modify environments to make sandboxes Mildred
@ 2006-06-12 16:47 ` Ludovic Courtès
  2006-06-12 22:05   ` Neil Jerram
  2006-06-28  4:13 ` Jon Wilson
  1 sibling, 1 reply; 7+ messages in thread
From: Ludovic Courtès @ 2006-06-12 16:47 UTC (permalink / raw)
  Cc: guile-user

Hi,

Mildred <ml.mildred593@online.fr> writes:

> I do not know how to do that in scheme. Apparetly the function
> null-environment can return an environment and eval can evaluate some
> code in an environment. But the question is how to define a variable in
> an environment and also how to undefine a variable that you don't want
> to appear.

Code confinement is indeed an interesting feature.  Fortunately, Guile
offers various ways to do it (AFAIK, there's no standard way to do this
in R5RS Scheme).  :-)

Basically, in Guile, one can manipulate "modules".  Modules are
first-class representations of name spaces (i.e., environments with
bindings).  The second argument of `eval' is a module, so you can ask
Guile to evaluate a piece of code within the context of a particular
module.

The basic way to create a module is `make-module'.  However, this
returns a module with absolutely no defined binding (i.e., no variable
is bound within it, not even `define', `let', etc.).  If you were to
create a confined module to evaluate mathematical expressions, you could
start from there:

  (define (make-pure-math-module)
    (let ((m (make-module)))
      (module-define! m '+ +)
      (module-define! m '- -)
      (module-define! m '* *)
      (module-define! m '/ /)
      (module-define! m 'let let)
      m))

Now, thanks to this, you will be able to catch the following:

  (eval '(system "rm -rf /") (make-pure-math-module))

IOW, you can safely evaluate any expression, and you know that it will
fail if it's not a pure mathematical expression.


Now, in the general case, you want to create a module that contains
enough bindings to allow "friendly" code to evaluate, but you don't want
to list all those bindings one by one.  In this case, you can use
`make-root-module' which returns a new module that contains all the
default Guile bindings.  Then, you can selectively undefine or modify
various bindings within that module (using `module-remove!' and
`module-set!').  In particular, you'll want to make sure that no POSIX
syscall is available, and that `load' and `resolve-module' (which allows
one to load a Scheme file) are either undefined or restricted (e.g.,
such that only specific modules can be loaded from within the confined
module).

Finally, another approach is to start from an empty module (returned by
`make-module') and then use a set of `module-use!' calls to have it
import bindings from a few specific modules.

You can get more information about modules in `ice-9/boot-9.scm'.


Happy hacking!
Ludovic.


_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: modify environments to make sandboxes
  2006-06-12 16:47 ` Ludovic Courtès
@ 2006-06-12 22:05   ` Neil Jerram
  2006-06-22 12:34     ` Ludovic Courtès
  0 siblings, 1 reply; 7+ messages in thread
From: Neil Jerram @ 2006-06-12 22:05 UTC (permalink / raw)
  Cc: guile-user

ludovic.courtes@laas.fr (Ludovic Courtès) writes:

> Code confinement is indeed an interesting feature.  Fortunately, Guile
> offers various ways to do it (AFAIK, there's no standard way to do this
> in R5RS Scheme).  :-) [...]

That's a very nice explanation.  Is there a good place for it
somewhere in the manual?  (If not, perhaps we should start a "How can
I do XXX" section.)

Regards,
     Neil



_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: modify environments to make sandboxes
  2006-06-12 22:05   ` Neil Jerram
@ 2006-06-22 12:34     ` Ludovic Courtès
  2006-06-22 18:36       ` Alan Grover
  0 siblings, 1 reply; 7+ messages in thread
From: Ludovic Courtès @ 2006-06-22 12:34 UTC (permalink / raw)
  Cc: guile-user

Hi,

Neil Jerram <neil@ossau.uklinux.net> writes:

> ludovic.courtes@laas.fr (Ludovic Courtès) writes:
>
>> Code confinement is indeed an interesting feature.  Fortunately, Guile
>> offers various ways to do it (AFAIK, there's no standard way to do this
>> in R5RS Scheme).  :-) [...]
>
> That's a very nice explanation.  Is there a good place for it
> somewhere in the manual?  (If not, perhaps we should start a "How can
> I do XXX" section.)

(Sorry, I think I had skipped this message.)

We don't have "how to" sections in the manual, but for this specific
topic, perhaps we could add a subsection under "The Guile module
system"?

Note that control over a module's name space (as described in my post)
is not the only thing needed to safely evaluate untrusted code.  The
user would also need finer control over all the resources used by the
code at hand (in order to prevent DoS attacks), particularly memory
(heap and stack) and CPU.

Unfortunately, I don't think this can be realized using Guile, except
maybe by running the untrusted code in a separate process and relying on
the OS' resource accounting mechanisms (e.g., `setrlimit' --- but Guile
core doesn't provide bindings for it).  However, running untrusted code
in a separate process would preclude, practically, resource sharing with
the user's trusted code (e.g., an Xchat Scheme plug-in would be useless
as a separate process because it would be unable to access the data
structures of the "real" Xchat).

The "ideal" solution would imply things like:

  * changing the evaluator so that several evaluators with different
    `eval-options' can be instantiated (pretty much like the
    `guile-reader' approach); this way, untrusted code could be
    evaluated with an evaluator that has custom stack limits;

  * having, roughly, a `current-heap' fluid that would be referred to
    anytime heap is allocated (hmm...);

  * similarly, have CPU time slice capabilities that would be passed
    to `eval' either explicitly or via a fluid.

But, well, going back to the documentation issue at hand, perhaps we can
just mention that Guile lacks certain features to allow for really safe
execution of untrusted code.  ;-)

Thanks,
Ludovic.


_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: modify environments to make sandboxes
  2006-06-22 12:34     ` Ludovic Courtès
@ 2006-06-22 18:36       ` Alan Grover
  2006-06-23 14:22         ` Ludovic Courtès
  0 siblings, 1 reply; 7+ messages in thread
From: Alan Grover @ 2006-06-22 18:36 UTC (permalink / raw)


Ludovic Courtès wrote:
> Hi,
> 
> Neil Jerram <neil@ossau.uklinux.net> writes:
> 
> 
>>ludovic.courtes@laas.fr (Ludovic Courtès) writes:
>>
>>
>>>Code confinement is indeed an interesting feature.  Fortunately, Guile
>>>offers various ways to do it (AFAIK, there's no standard way to do this
>>>in R5RS Scheme).  :-) [...]

One approach is to write your own security-manager/white-list. You scan
the s-expr, check the symbols in the head of any (sub-)list, and fail if
they aren't approved. The comments of
http://schemecookbook.org/Cookbook/DynamicUntrustedEval gives an outline
of how to do this (minus parsing). You actually have to parse a bit: so
you can ignore argument-lists to lambda, the variables in "let", etc. An
example parser is in your guile distribution, scripts/lint, see the
detect-free-variables procedure (usually somewhere like
/usr/share/guile/1.6/scripts).  Of course, you don't have to parse if
you don't allow let/lambda and friends.

I think there is a R5RS way of doing code-confinement. The R5RS spec
says that the null-environment "is empty except for the (syntactic)
bindings for all syntactic keywords defined in this report that are
either required or both optional and supported by the implementation." I
take it to mean that you are guaranteed the required keywords, might
have the optional keywords, but won't have anything else.

So, you could do this:

(define (safe-eval s-expr)
    (let* (
            (lambda-expr-that-provides-some-procs
                ; extend null-environment with "+"
                `(lambda (+)
                    ,s-expr))
            (actual-lambda
                (eval
                    lambda-expr-that-provides-some-procs
                    (null-environment 5))))
    (actual-lambda +))) ; provide +
(safe-eval '(begin (define (x y) 1) (x 2)))
(safe-eval '1)
(safe-eval '(+ 1 2))
(safe-eval '(- 1 2)) ; fails, there is no "-" available

You can provide more procedures where I just provided "+". The trick has
two parts:

* Eval the untrusted expression in null-environment. A minimum of
procedures/etc. is available.
* "import" the other-procedures by providing them to a lambda, which
provides the lexical environment.

You could "black-list" keywords, maybe like this:
(define (safe-eval s-expr)
    (let* (
            (lambda-expr-that-provides-some-procs
                `(lambda (+ error)
                    ; re-bind "define" to something that won't work
                    (let ((define (lambda args (error "Not allowed"))))
                    ,s-expr)))
            (actual-lambda
                (eval
                    lambda-expr-that-provides-some-procs
                    (null-environment 5))))
    (actual-lambda + error)))
(safe-eval '1)
(safe-eval '(+ 1 2))
(safe-eval '(begin (define (x y) 1) (x 2)))

But, note that this doesn't quite fail the way you want: it will eval (x
y) and then call the local "define", which means you'll get an unbound
error on x. Well, at least you disabled "define".

FYI, here's the list of symbols in null-environment for guile 1.6:

    and begin case cond define define-syntax delay do if
    lambda let let* let-syntax letrec letrec-syntax or
    quasiquote quote set!

Beware, you have to provide a comprehensive black-list. Imagine if the
guile 1.6 null-environment didn't include every optional-keyword, but
1.8 did. Untrusted code could take advantage of the difference. You also
have to carefully consider the impact of allowing any keyword. For
example, "let" would allow the untrusted code to do an infinite loop
(via named-let).

I can't find the place where I originally found this method, but there
is another example at
http://www.cap-lore.com/CapTheory/Language/Scheme/SchemeFactory.html
and the preceding pages. You may want to read these pages to understand
some other issues around untrusted code.
And in thread:
http://ecos.sourceware.org/ml/guile/1999-02/msg00233.html


> Note that control over a module's name space (as described in my post)
> is not the only thing needed to safely evaluate untrusted code.  The
> user would also need finer control over all the resources used by the
> code at hand (in order to prevent DoS attacks), particularly memory
> (heap and stack) and CPU.
> 
> Unfortunately, I don't think this can be realized using Guile, except
> maybe by running the untrusted code in a separate process and relying on
> the OS' resource accounting mechanisms (e.g., `setrlimit' --- but Guile
> core doesn't provide bindings for it).  However, running untrusted code
> in a separate process would preclude, practically, resource sharing with
> the user's trusted code (e.g., an Xchat Scheme plug-in would be useless
> as a separate process because it would be unable to access the data
> structures of the "real" Xchat).
> 
> The "ideal" solution would imply things like:
> 
>   * changing the evaluator so that several evaluators with different
>     `eval-options' can be instantiated (pretty much like the
>     `guile-reader' approach); this way, untrusted code could be
>     evaluated with an evaluator that has custom stack limits;
> 
>   * having, roughly, a `current-heap' fluid that would be referred to
>     anytime heap is allocated (hmm...);
> 
>   * similarly, have CPU time slice capabilities that would be passed
>     to `eval' either explicitly or via a fluid.

You can also write a safe-interpreter in scheme, and thus in the same
process. You solve the above issues by charging the expression each time
it applies a procedure, each time it consumes heap (roughly at each
"cons"), etc.

This one is for chicken:
http://www.call-with-current-continuation.org/eggs/sandbox.scm
And, again, I can't locate a reference to the original example of this,
I believe it was R5RS though.



_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: modify environments to make sandboxes
  2006-06-22 18:36       ` Alan Grover
@ 2006-06-23 14:22         ` Ludovic Courtès
  0 siblings, 0 replies; 7+ messages in thread
From: Ludovic Courtès @ 2006-06-23 14:22 UTC (permalink / raw)
  Cc: guile-user

Hi,

Thanks for your comments and the nice links!

Alan Grover <awgrover@mail.msen.com> writes:

> One approach is to write your own security-manager/white-list. You scan
> the s-expr, check the symbols in the head of any (sub-)list, and fail if
> they aren't approved.

I don't really like this approach, it looks quite "ugly" and impractical
IMO, and it could only be made to work when one has access to the source
code.  Instead, evaluating untrusted code in an environment that only
provides access to the necessary bindings and resources (as I
suggested), thereby honoring the principle of least authority (POLA),
really looks nicer.

> I think there is a R5RS way of doing code-confinement. The R5RS spec
> says that the null-environment "is empty except for the (syntactic)
> bindings for all syntactic keywords defined in this report that are
> either required or both optional and supported by the implementation." I
> take it to mean that you are guaranteed the required keywords, might
> have the optional keywords, but won't have anything else.

Right.  Then you still lack control over the resources used by the
untrusted code.

> Beware, you have to provide a comprehensive black-list. Imagine if the
> guile 1.6 null-environment didn't include every optional-keyword, but
> 1.8 did. Untrusted code could take advantage of the difference. You also
> have to carefully consider the impact of allowing any keyword. For
> example, "let" would allow the untrusted code to do an infinite loop
> (via named-let).

Good point: "black listing" is obviously more risky than "white listing"
because you can always forget an important binding.

> I can't find the place where I originally found this method, but there
> is another example at
> http://www.cap-lore.com/CapTheory/Language/Scheme/SchemeFactory.html
> and the preceding pages. You may want to read these pages to understand
> some other issues around untrusted code.

This is a very nice reading.  It also refers to Jonathan Reed's ``A
Security Kernel Based on the Lambda-Calculus'' [0] which I'm going to
read as soon as I can!  :-)

>> The "ideal" solution would imply things like:
>> 
>>   * changing the evaluator so that several evaluators with different
>>     `eval-options' can be instantiated (pretty much like the
>>     `guile-reader' approach); this way, untrusted code could be
>>     evaluated with an evaluator that has custom stack limits;
>> 
>>   * having, roughly, a `current-heap' fluid that would be referred to
>>     anytime heap is allocated (hmm...);
>> 
>>   * similarly, have CPU time slice capabilities that would be passed
>>     to `eval' either explicitly or via a fluid.
>
> You can also write a safe-interpreter in scheme, and thus in the same
> process. You solve the above issues by charging the expression each time
> it applies a procedure, each time it consumes heap (roughly at each
> "cons"), etc.

The changes I was describing are the minimal changes that look necessary
to write such a "safe interpreter" (you don't need that if you run the
untrusted code as a separate process: you can rely on `setrlimit' and
friends).

As for control over heap allocation: unfortunately, rebinding `cons' to
a restricted version of `cons' (for instance, once that enforces some
quota) would not be sufficient in Guile.  This is because, for instance,
memory for SMOBs, vectors, etc., is not allocated via `cons'.
Controlling calls to `scm_cell ()' would be better, but still
insufficient.  And anyway, one could hardly control the heap used by
SMOBs (because there's more than one way SMOBs can allocate memory)...

(Of course, we are clearly not considering invocation of untrusted C
code by the untrusted Scheme code!)

> This one is for chicken:
> http://www.call-with-current-continuation.org/eggs/sandbox.scm
> And, again, I can't locate a reference to the original example of this,
> I believe it was R5RS though.

Hmm, I'm not sure I understand how it works.

Thanks,
Ludovic.

[0] http://mumble.net/~jar/pubs/secureos/


_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: modify environments to make sandboxes
  2006-06-12 16:20 modify environments to make sandboxes Mildred
  2006-06-12 16:47 ` Ludovic Courtès
@ 2006-06-28  4:13 ` Jon Wilson
  1 sibling, 0 replies; 7+ messages in thread
From: Jon Wilson @ 2006-06-28  4:13 UTC (permalink / raw)
  Cc: guile-user

Hi all,
How about having the ability to capture the current lexical environment and 
use it as an environment for eval?  Something like call/cc, but I guess 
call/env.  In essence, this would make environments useful as first class 
objects (perhaps).

(define my-env #f)

(let ((a 0) (b 1) (c 2))
   (call/env (lambda (x) (set! my-env x))))

(eval '(begin
         (display a) (newline)
         (display b) (newline)
         (display c) (newline)
         (display my-env) (newline))
       my-env)

 >> prints:
0
1
2
#<environment 0xDEADBEEF>


Perhaps an unlet would also be useful.  This would create a lexical 
environment in which several variables were unbound.

(unlet (eval read load)
  (call/env (lambda (x) (set! my-env x))))

(eval '(eval '(+ 1 2)) my-env)
=>
#error

Hmmm, perhaps this idea (unlet) couldn't work in any kind of a sensible, 
simple, lexical scoping system.  Maybe unlet could just set the variables 
given to it to #<unspecified> or #f or something.

So, which extant language feature have I overlooked that provides more or 
less exactly this? :)

Regards,
Jon

Mildred wrote:
> Hi,
> 
> I'm new to this mailing list and also new to the scheme language ...
> I'm used to Lua but I try to search about functionnal programming, and
> I found scheme. It looks like a good language but before using it in my
> projects I would like to know if tere is an easy way to create
> sandboxes.
> 
> In lua, it is easy, you create a table containing functions ... and the
> table can be made environment for a function. So, you can easily create
> secure sandboxes by loading lua code from file and changing the
> environment of the loaded code.
> 
> I do not know how to do that in scheme. Apparetly the function
> null-environment can return an environment and eval can evaluate some
> code in an environment. But the question is how to define a variable in
> an environment and also how to undefine a variable that you don't want
> to appear.
> I didn't found anything about modifying an environment. Is it
> possible ? If not, why not ? and is it possible to create sandboxes ?
> Thanks
> 
> Mildred
> 


_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2006-06-28  4:13 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-06-12 16:20 modify environments to make sandboxes Mildred
2006-06-12 16:47 ` Ludovic Courtès
2006-06-12 22:05   ` Neil Jerram
2006-06-22 12:34     ` Ludovic Courtès
2006-06-22 18:36       ` Alan Grover
2006-06-23 14:22         ` Ludovic Courtès
2006-06-28  4:13 ` Jon Wilson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).