[plt-scheme] Stack inspection security

From: David Van Horn (dvanhorn at cs.uvm.edu)
Date: Tue Oct 14 02:34:03 EDT 2003

I've been thinking about a security model for MzScheme that would allow mobile
or partially trusted modules to be run under a given security policy enforced
using a stack inspection mechanism.  Here are some thoughts and questions on
such a model.  I'd appreciate any feedback.

MzScheme has continuation-mark primitives which can perform stack inspection
by annotating and examining continuation frames, but in order to really act as
a security mechanism we need a notion of code principals.  A principal is the
entity on whose behalf a piece of code executes.  This may be constituted by a
URL of the code's origin and attached digital signatures, as in the case of
Java[1].  A policy would specify which privileges are allowed and disallowed
for given principals and loaded at start up.  The user (or site administrator)
would draft a policy based on the extent to which these principals can be
trusted to perform sensitive operations.  A default policy might specify that
all (lib ...) code is fully trusted and all non-local code is completely
untrusted.

We could redefine #%app to mark the current continuation frame with the
current principal using with-continuation-mark.

   (check-privilege privilege thunk) -> (thunk) or (raise security-exn)

Check-privilege inspects the current-continuation-marks from most to least
recent annotations.  If the principal marked on the frame lacks the privilege
in question, an exception is raised.  If the frame is marked as privileged,
(thunk) is returned.  Otherwise the principal must have the privilege in
question and the remaining frames are inspected in order to make a decision.
If all frames have been inspected we can return (thunk) or fail (design decision).

   (do-privileged thunk) -> (thunk)

Do-privileged would cause the security context of continuation-marks to be
ignored, and security decisions would be based on privileges granted to the
principal of the current code only, that is, it marks the current frame as
privileged and evaluates thunk.  Do-privileged only enables privileges the
current principal is already granted; code cannot acquire permissions beyond
what it has been granted in the security policy.

An example:

   (module abracadabra mzscheme
     (provide change-password read-password-file write-password-file)
     (define pwd-file "/usr/password")

     ;; Prompt for current password and new password (x2).
     (define (change-password)
       (let ((current-pwd (do-privileged read-password-file)))
         ...
         (do-privileged (lamnda () (write-password-file new-pwd)))
         ...))

     (define (write-password-file v)
       (with-output-to-file pwd-file (lambda () (write v))))

     (define (read-password-file)
       (with-input-from-file pwd-file read)))

Consider the above example of library code that we wish to provide to
applications to change some password file.  Assume our policy grants
abracadabra permission to read and write the password file.  We might want to
allow a partially trusted module to call the functions provided by abracadabra
and yet disallow reading or writing any file in the file system.  If the code
called read- or write-password-file directly, an exception would occur since
the mobile code lacks the requisite privilege (this assumes
with-input-from-file and with-output-to-file call check-privilege).  On the
other hand, this module could call change-password and since the read/write is
performed under the privileges of abracadabra, the operation would succeed.

Code that we trusted to perform read/write operations on the password file
could of course make calls to read- and write-password-file directly.

Policies could be specified by a MzScheme policy language and indicated on the
command line when invoking mzscheme.

Eg.

  (module my-policy security-policy
    (grant (string->url "http://www.cs.uvm.edu/") (signature "*")
      (file-permission '(read write) "/tmp/*")))

The above policy allows code originating from UVM, with or without a signature
to read and write to files in /tmp.

This model would allow more fine-grained access control policies than the
current security-guard mechanism and arbitrary privileges could be constructed
and required to appear in the continuation-marks by code performing security
sensitive operations.  Also, code of various levels of trust can interact in a
disciplined manner via stack inspection enforcement of the security policy.
For example two non-local modules with various degrees of privilege can call
each other and the right security decision will be made based on calling contexts.

So my questions are:
- Is there already a suitable notion of code principal in MzScheme?
- Are code signing mechanisms going to be employed in the PLaneT system?
- Is a security stack inspection mechanism going to be employed in the PLaneT
system?
- Thoughts on such a model?

Thanks.

-d

[1] Actually in Java it is more complicated.  Policies are specified in terms
of protection domains, which are constituted by the code source (the url of
the code's origin and signatures), a principal (the user running the code),
and the class loader.  I would call all this information the "principal", but
Java uses the term "protection domain", and uses the term "principal" to refer
to the user running a piece of code, which is only one piece of information
used in security decisions.



Posted on the users mailing list.