From: Mike G.
Subject: Lisp security / safe mode
Date: 
Message-ID: <1194284399.096841.40340@q3g2000prf.googlegroups.com>
Hi all,

I was wondering: what is the preferred way to achieve "safe mode"?
That is, to allow running untrusted code? I want something that blocks
run-program, sockets, and probably bunches of other stuff that I
haven't thought about.

Is there an implementation (or better, a portable package) that can do
this? I can think of ways to do this, but it is certainly very
complex, or at least, there are a great number of devils inside the
many details.

-M

From: Alessio
Subject: Re: Lisp security / safe mode
Date: 
Message-ID: <1194287246.319017.222400@d55g2000hsg.googlegroups.com>
Hi Mike. I think it depends on what you mean by "running code".
If the untrusted code is input by the user in the form of s-
expressions (perhaps by typing them in, or by loading .lisp files,
or...), you can, at read time, control that such code contains only
"trusted" symbols (e.g. symbols exported in a :safe-cl package). Note
that you should forbid executable objects inside such code (I'm
thinking about #.(lambda () ...) and *read-eval* in general), and
maybe, depending on what you want to do, provide safe versions of
load, eval, etc.
If instead the untrusted code is already compiled from unknown source,
and then loaded at runtime, I don't think you can do much about it
(Lisp gurus mught know better of course...). Imagine someone loading
in the same image (e.g. by using the implementation's initialization
file) your safe system plus a bunch of untrusted fasls...

cheers
Alessio Stalla

On 5 Nov, 18:39, "Mike G." <···············@gmail.com> wrote:
> Hi all,
>
> I was wondering: what is the preferred way to achieve "safe mode"?
> That is, to allow running untrusted code? I want something that blocks
> run-program, sockets, and probably bunches of other stuff that I
> haven't thought about.
>
> Is there an implementation (or better, a portable package) that can do
> this? I can think of ways to do this, but it is certainly very
> complex, or at least, there are a great number of devils inside the
> many details.
>
> -M
From: Albert Krewinkel
Subject: Re: Lisp security / safe mode
Date: 
Message-ID: <fgnrq5$edj$1@registered.motzarella.org>
On Mon, 05 Nov 2007 09:39:59 -0800, Mike G. wrote:
> I was wondering: what is the preferred way to achieve "safe mode"?
> That is, to allow running untrusted code? I want something that blocks
> run-program, sockets, and probably bunches of other stuff that I
> haven't thought about.
> 
> Is there an implementation (or better, a portable package) that can do
> this? I can think of ways to do this, but it is certainly very
> complex, or at least, there are a great number of devils inside the
> many details.
> 
> -M

You could try to run each lisp instance in it's own chroot
enviroment. That should give you quite good control about what users
can do, but I never really tried to set something like this up.
From: Mike G.
Subject: Re: Lisp security / safe mode
Date: 
Message-ID: <1194370964.969632.9780@y27g2000pre.googlegroups.com>
On Nov 5, 2:44 pm, Albert Krewinkel <·······@KEIN-SPAMnatwiss.uni-
luebeck.de> wrote:
> On Mon, 05 Nov 2007 09:39:59 -0800, Mike G. wrote:
> > I was wondering: what is the preferred way to achieve "safe mode"?
> > That is, to allow running untrusted code? I want something that blocks
> > run-program, sockets, and probably bunches of other stuff that I
> > haven't thought about.
>
> > Is there an implementation (or better, a portable package) that can do
> > this? I can think of ways to do this, but it is certainly very
> > complex, or at least, there are a great number of devils inside the
> > many details.
>
> > -M
>
> You could try to run each lisp instance in it's own chroot
> enviroment. That should give you quite good control about what users
> can do, but I never really tried to set something like this up.

A chroot environment is nice for isolation from the rest of the
system,
sure - but it won't help me prevent the untrusted code from opening
sockets in Lisp.
From: John Thingstad
Subject: Re: Lisp security / safe mode
Date: 
Message-ID: <op.t1ec5wqqut4oq5@pandora.alfanett.no>
P� Tue, 06 Nov 2007 18:42:44 +0100, skrev Mike G.  
<···············@gmail.com>:

>
> A chroot environment is nice for isolation from the rest of the
> system,
> sure - but it won't help me prevent the untrusted code from opening
> sockets in Lisp.
>

But a firewall can.. Get one that blocks outgoing connection unless you OK  
them.
http://www.spirit.com/Network/net0602.html

-- 
Sendt med Operas revolusjonerende e-postprogram: http://www.opera.com/mail/
From: Kamen TOMOV
Subject: Re: Lisp security / safe mode
Date: 
Message-ID: <ubqa8z3rw.fsf@cybuild.com>
On Mon, Nov 05 2007, Mike G. wrote:

> Hi all,
>
> I was wondering: what is the preferred way to achieve "safe mode"?
> That is, to allow running untrusted code? I want something that
> blocks run-program, sockets, and probably bunches of other stuff
> that I haven't thought about.

It depends on what you aim to achieve. If the purpose is running
untrusted code on the client's machines you can consider porting your
code to run on CLR or JVM. Of course you have the option of writing of
a CL browser plug-in and implementing your own sandbox model
there. However, the latter looks like a task for the capabilities of a
corporation so I'd bet on CLR/JVM or even Flash solution.

Anyway, in the other case you would like to be running untrusted code
on a server machine (which generally should mean that you have full
control on it). The Java sandbox model proved to be unscalable and bug
prone so people advice against using it.

The preferred way of achieving security when running untrusted code on
a server machine is confining the behavior of the resulting (Operating
System) process. However, generally Operating Systems do not provide
enough facilities for that purpose so some of them had been enhanced
for similar purposes. The extension is called Mandatory Access
Control.

http://en.wikipedia.org/wiki/Mandatory_access_control

Sorry for the not so lispish answer, but that would be only until we
have a Lisp OS :-)

-- 
Kamen
From: Mike G.
Subject: Re: Lisp security / safe mode
Date: 
Message-ID: <1194371866.448635.58550@i13g2000prf.googlegroups.com>
On Nov 5, 6:11 pm, Kamen TOMOV <·····@cybuild.com> wrote:
> On Mon, Nov 05 2007, Mike G. wrote:
> > Hi all,
>
> > I was wondering: what is the preferred way to achieve "safe mode"?
> > That is, to allow running untrusted code? I want something that
> > blocks run-program, sockets, and probably bunches of other stuff
> > that I haven't thought about.
>
> It depends on what you aim to achieve. If the purpose is running
> untrusted code on the client's machines you can consider porting your
> code to run on CLR or JVM. Of course you have the option of writing of
> a CL browser plug-in and implementing your own sandbox model
> there. However, the latter looks like a task for the capabilities of a
> corporation so I'd bet on CLR/JVM or even Flash solution.
>
> Anyway, in the other case you would like to be running untrusted code
> on a server machine (which generally should mean that you have full
> control on it). The Java sandbox model proved to be unscalable and bug
> prone so people advice against using it.
>
> The preferred way of achieving security when running untrusted code on
> a server machine is confining the behavior of the resulting (Operating
> System) process. However, generally Operating Systems do not provide
> enough facilities for that purpose so some of them had been enhanced
> for similar purposes. The extension is called Mandatory Access
> Control.
>
> http://en.wikipedia.org/wiki/Mandatory_access_control
>
> Sorry for the not so lispish answer, but that would be only until we
> have a Lisp OS :-)
>
> --
> Kamen

Something like might work, but seems to be a nightmare. For instance,
I don't want untrusted code brought in from the user to be able to
open TCP sockets, nor to run local executables. If they are chrooted,
I guess it isn't _so_ bad if they can run /bin/sh from Lisp -- but I
don't even want to give that.

SeLinux controls seem to help at first - but I need _my_ code running
in SBCL to be able to open TCP, I just want to restrict code from the
user.  I don't think I can do this with SeLinux, I think the access
controls need to be in Lisp.

I was thinking of something which intercepts the Lisp functions calls
and performs verification there. I know I can intercept - I don't
know how I'm going to achieve true isolation though. I have more to
think about, I guess.

I was hoping someone thought all this out for me, though :)

Thanks for the responses -- any other ideas or comments would be
welcome.
I need to get this right :)

-M
From: Kamen TOMOV
Subject: Re: Lisp security / safe mode
Date: 
Message-ID: <u8x5b5fy9.fsf@cybuild.com>
On Tue, Nov 06 2007, Mike G. wrote:

> Something like might work, but seems to be a nightmare. 

I'd agree if you mean that one needs to have quite different skillset
to deal with MAC. Apart from that - it's just another issue that needs
to be dealt with.

> For instance, I don't want untrusted code brought in from the user
> to be able to open TCP sockets, nor to run local executables. If
> they are chrooted, I guess it isn't _so_ bad if they can run /bin/sh
> from Lisp -- but I don't even want to give that.
>
> SeLinux controls seem to help at first - but I need _my_ code
> running in SBCL to be able to open TCP, I just want to restrict code
> from the user.  I don't think I can do this with SeLinux, I think
> the access controls need to be in Lisp.

If running restricted and unrestricted code in the same OS process is
a nailed as design requirement, then indeed, you need to look for
solution elsewhere.

> I was thinking of something which intercepts the Lisp functions
> calls and performs verification there. I know I can intercept - I
> don't know how I'm going to achieve true isolation though. 

That's like begging for trouble.

-- 
Kamen
From: Cameron Desautels
Subject: Re: Lisp security / safe mode
Date: 
Message-ID: <1194572465.528109.22210@i38g2000prf.googlegroups.com>
On Nov 5, 5:11 pm, Kamen TOMOV <·····@cybuild.com> wrote:
> It depends on what you aim to achieve.

I've wondered about Mike's question as well (or my own variant of
it).  Suppose we want to create something akin to "try ruby! (in your
browser)" (http://tryruby.hobix.com/) but for Common Lisp.  Naturally
we can't have the user deleting files off our webserver or opening
sockets and sending spam.  Is there an easy way to achieve this?

Can we add the functions we want to be accessible into a new package
and conceptually chroot the user into that package?  Even if we IN-
PACKAGE the user into our safe package (s)he could just COMMON-LISP:IN-
PACKAGE back out.  Any thoughts on how hard it would be to modify the
reader to not allow access to symbols in other packages?  Etc.

--
Cameron Desautels <······@gmail.com>
http://keyboardphilosopher.com
From: Barry Margolin
Subject: Re: Lisp security / safe mode
Date: 
Message-ID: <barmar-7766AA.21571108112007@comcast.dca.giganews.com>
In article <·······················@i38g2000prf.googlegroups.com>,
 Cameron Desautels <······@gmail.com> wrote:

> On Nov 5, 5:11 pm, Kamen TOMOV <·····@cybuild.com> wrote:
> > It depends on what you aim to achieve.
> 
> I've wondered about Mike's question as well (or my own variant of
> it).  Suppose we want to create something akin to "try ruby! (in your
> browser)" (http://tryruby.hobix.com/) but for Common Lisp.  Naturally
> we can't have the user deleting files off our webserver or opening
> sockets and sending spam.  Is there an easy way to achieve this?
> 
> Can we add the functions we want to be accessible into a new package
> and conceptually chroot the user into that package?  Even if we IN-
> PACKAGE the user into our safe package (s)he could just COMMON-LISP:IN-
> PACKAGE back out.  Any thoughts on how hard it would be to modify the
> reader to not allow access to symbols in other packages?  Etc.

Maybe you could change the readtable syntax of #\: to something that 
signals an error.

You'll also need to leave either INTERN and FIND-SYMBOL or FUNCALL and 
APPLY out of your package, so that they can't do (funcall (find-symbol 
"DELETE-FILE" "COMMON-LISP") ...).

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
From: Richard M Kreuter
Subject: Re: Lisp security / safe mode
Date: 
Message-ID: <87ode3b85h.fsf@progn.net>
Barry Margolin <······@alum.mit.edu> writes:
> In article <·······················@i38g2000prf.googlegroups.com>,
>  Cameron Desautels <······@gmail.com> wrote:
>
>> Any thoughts on how hard it would be to modify the reader to not
>> allow access to symbols in other packages?  Etc.
>
> Maybe you could change the readtable syntax of #\: to something that 
> signals an error.

I don't think there's an easy way of doing this that will parse a
subset of CL syntax that includes keywords. If #\: is a terminating
macro character that signals an error, then

(defparameter *rt* (copy-readtable nil))
(setf *readtable* *rt*)
(set-macro-character #\: (lambda (s c) (error "Saw a #\\:")) nil)

(with-input-from-string (s "CL:CAR")
  (loop for thing = (read s nil)
        while thing
        do (prin1 thing) (terpri)))
CL
Error: saw a #\:

(with-input-from-string (s ":CAR")
  (loop for thing = (read s nil)
        while thing
        do (prin1 thing) (terpri)))
Error: saw a #\:

And if #\: is a non-terminating macro character, then

(set-macro-character #\: (lambda (s c) (error "Saw a #\\:")) t)

(with-input-from-string (s "CL:CAR")
  (loop for thing = (read s nil)
        while thing
        do (prin1 thing) (terpri)))
CL:CAR
=> NIL

(with-input-from-string (s ":CAR")
  (loop for thing = (read s nil)
        while thing
        do (prin1 thing) (terpri)))
Error: saw a #\:

So if the read macro unconditionally signals an error, then you'll
always get an error if a token begins with #\:.  But the reader
doesn't supply enough information to read macro functions to let us
write a macro function that conditionally errors: a terminating macro
doesn't have access to the text preceding the macro character (which
we'd need to determine whether the colon occurred in the middle of a
token), and a non-terminating macro doesn't ever get called in the
middle of a token.

If you're willing to parse input in a way that's not strictly a subset
of CL syntax but that ensures that your reader only returns symbols in
KEYWORD or some specific package, you can do so with a terminating
read macro:

(defpackage "SAFE-PACKAGE" (:import-from "CL" "CAR"))
(defparameter *a-standard-readtable* (copy-readtable nil))
(defparameter *rt* (copy-readtable nil))
(setf *readtable* *rt*)
(set-macro-character
  #\:
  (lambda (s c)
    (unread-char c s)
    (with-standard-io-syntax
      (let ((*package* (find-package "SAFE-PACKAGE"))
            (*readtable* *a-standard-readtable*)
            (*read-eval* nil))
        (read s))))
  nil)

This way, source that employs only keywords and unqualified tokens
will parse as CL does, though source containing what would be package
qualified symbols in CL will parse into s-exprs that won't do the
right thing in general, but that can't escape to other packages:

(car '(1 2 3))
=> 1
(car '(:foo 2 3))
=> :FOO
(cl:car '(:foo 2 3))
; in: LAMBDA NIL
;     (CL :CAR '(:FOO 2 3))         <--- This is what the reader returned.
; 
; caught STYLE-WARNING:
;   undefined function: CL

; 
; caught STYLE-WARNING:
;   This function is undefined:
;     CL
; 
; compilation unit finished
;   caught 2 STYLE-WARNING conditions
The function CL is undefined.
   [Condition of type UNDEFINED-FUNCTION]

Unfortunately, when this reader returns (CL :CAR ...), it's not
possible to determine whether the input was "(CL:CAR ...)" or 
"(CL :CAR ...)", so the strongest thing you can say when specifying a
syntax implemented this way is that it's an error for source to
contain a token containing a non-colon constituent followed by a
colon.  This is admittedly fairly cheesy, but is clearly trivial
to do, and might suffice for some problems.

--
RmK
From: Thomas A. Russ
Subject: Re: Lisp security / safe mode
Date: 
Message-ID: <ymid4ujwc19.fsf@blackcat.isi.edu>
Cameron Desautels <······@gmail.com> writes:

> Can we add the functions we want to be accessible into a new package
> and conceptually chroot the user into that package?  Even if we IN-
> PACKAGE the user into our safe package (s)he could just COMMON-LISP:IN-
> PACKAGE back out.
> Any thoughts on how hard it would be to modify the
> reader to not allow access to symbols in other packages?  Etc.

Well, access to any symbol means you don't have to even change packages,
so your question about the reader is quite important.  I think that the
real answer, though, doesn't involve the reader at all.

Instead, what one will need to do is implement a specialized
interpreter.  So instead of running Lisp code directly, you would have
your own sandboxed interpreter running the code.  That way you have full
control over what get executed.  And a pared-down Common Lisp
interpreter in Common Lisp shouldn't be that hard to come up with.
Meta-circular intepreters are pretty common.  You would have to decide
what to include and what to leave out, though.  That part is probably
the most difficult part.


-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Dimiter "malkia" Stanev
Subject: Re: Lisp security / safe mode
Date: 
Message-ID: <5pk9sqFrm52qU1@mid.individual.net>
> http://tryruby.hobix.com/

Lovely!!!! Nice tutorial, it would make me try Ruby, if nothing else for 
fun :)

But there's always telnet://prompt.franz.com

Just type: telnet prompt.franz.com
From: Thomas F. Burdick
Subject: Re: Lisp security / safe mode
Date: 
Message-ID: <1194373266.964560.171130@50g2000hsm.googlegroups.com>
On Nov 5, 6:39 pm, "Mike G." <···············@gmail.com> wrote:
> Hi all,
>
> I was wondering: what is the preferred way to achieve "safe mode"?
> That is, to allow running untrusted code? I want something that blocks
> run-program, sockets, and probably bunches of other stuff that I
> haven't thought about.
>
> Is there an implementation (or better, a portable package) that can do
> this? I can think of ways to do this, but it is certainly very
> complex, or at least, there are a great number of devils inside the
> many details.

Without more information (what is the nature of the code, where does
it come from, why are you running it, etc) it's hard to give a good
answer.  The reason being that, as usual, there are a krillion ways to
do this in CL, of which about (- n 1) are wrong.

That said, it may be that you want to write a little interpreter or
(toy Lisp->Lisp) compiler.  I'm talking about the kind you'll find
outlined in PAIP, L.I.S.P., etc.  This gives you complete control of
what the dialect can do, and of course 99% of the heavy lifting is
done for you by the underlying implementation.