securitycommon-lisp

Possible ways to restrict CL API, disable a double colon access, permission access manager in CL


I need to hide dungerous function(s), for example, cl:open for thirdparty code downloaded from internet.

So I can do like this:

(package :dangerous (:use :cl))
(import 'cl:open :dangerous)
(import 'cl:export :dangerous)
...

(unexport 'open :cl)
(unexport 'export :cl)
...

; instead of cl:export which hided implement own export fn and then import it to untrusted package

(load "untrusted-lib.lisp")

As I understand the untrusted code cant call cl:open at all, except using cl::open, right? Is possible to call cl:open by another way?

At first sight CL has potencial to restrict access to API if a double colon access will disabled. Theoretically it becomes possible to create a security manager (permission access manager) Is possible to disable "::" calls for SBCL and another CL compilers?


Solution

  • The code you give is both very nonconforming (you modify the CL package) and would not solve the problem if it was conforming.

    Here is an easy way of defeating it:

    (setf (fdefinition 'my-open)
          (do-symbols (s :cl)
            (when (string= (symbol-name s) "OPEN")
              (return (fdefinition s)))))
    

    Here's another (this is better, but I thought of it later):

    (setf (fdefinition 'my-open)
         (fdefinition (find-symbol "OPEN" :cl)))
    

    There will be more.

    So something like this is really a hopeless approach to solving the problem, even if was legal Common Lisp, which it is not.

    CL is not a language designed with this kind of restriction in mind, so a good solution is somewhere between difficult and impossible.

    Perhaps an approach might be as follows.

    1. Work out what a safe subset of the language is, in terms of symbols exported from the CL package and other features. It is almost certainly better to work your way up from a very small subset rather than work out what you need to exclude from the language, because there are snares everywhere (for instance you must disallow anything which manipulates the package system, or anything which allows string-to-symbol lookup, as well as many other things).
    2. Design a package which reexports only the names from CL which are in this safe subset.
    3. Design a safe reader. This should at least prevent read-time evaluation, and probably other things. Bear in mind that CL:READ probably was not designed with safety in mind.
    4. Have a user package which uses only the safe package above. You may want to either remove all the internal symbols of this package frequently or simply delete & recreate the package frequently.
    5. Write a function which, once a form has been read, will walk over it checking that all the symbols in it are either in your good list or are internal to the user package. Make sure that this walker both terminates in all cases (beware of cycles), and walks into all the structures it needs to.
    6. Now your process is pretty much:
      1. read the a form with your safe reader with packages set correctly;
      2. check this form contains no nasties with your walker;
      3. evaluate the form.
    7. ... Profit.

    You may well also want to have a 'sanitize' stage: stash away the package state of the system before each evaluation (this is laborious but possible) and then, after it, check nothing you did not expect has changed. You will probably also want some kind of timeout mechanism you can wrap around evaluations.

    I do not claim this is sufficient. I do not claim that this is even completely possible in CL. But something like this may help, if it is.

    I happen to know that conduit packages was originally written as part of a system which tried to do some of this. So it's a good way to define a package which reexports only parts of, for instance CL:

    (define-conduit-package :just-conses
      (:extends/including :cl
       #:cons #:car #:cdr #:nil ...))
    

    In general a better approach is almost certainly to sandbox the system using a platform-level sandbox: people, including at least some who understand security, have worked hard on these things to make them actually secure, and they get tested against malicious people all the time.