Security
MzScheme offers several mechanisms for managing security:
Custodians (section 9.2) manage resource allocation.
Security guards (section 9.1) control access to the filesystem and network.
Inspectors (section 4.5) control access to the content of otherwise opaque structures and modules (see section 9.4).
Namespaces (section 8) control access to Scheme bindings.
Thread groups (section 9.3) control CPU allocation.
All security mechanisms rely on thread-specific parameters (see section 7.9).
9.1 Security Guards
A security guard provides a set of access-checking
procedures to be called when a thread initiates access of a file,
directory, or network connection through a primitive procedure. For
example, when a thread calls
, the thread's
current security guard is consulted to check whether the thread is
allowed read access to the file. If access is granted, the thread
receives a port that it may use indefinitely, regardless of changes
to the security guard (although the port's custodian could shut down
the port; see section 9.2).open-input-file
A thread's current security guard is determined by the
current-security-guard
parameter (see
section 7.9.1.8). Every security guard has a
parent, and a parent's access procedures are called whenever a
child's access procedures are called. Thus, a thread cannot increase
its own access arbitrarily by installing a new guard. The initial
security guard enforces no access restrictions other than those
enforced by the host platform.
(make-security-guard
parent-security-guard file-proc network-proc
)
creates a new security guard whose parent is
parent-security-guard
.
The file-proc
procedure must accept three arguments:
a symbol for the primitive procedure that triggered the access check, which is useful for raising an exception to deny access.
a path (see section 11.3.1), or
#f
to check access for pathless queries, such as(
,current-directory
)(
, andfilesystem-root-list
)(
. A path provided tofind-system-path
symbol)file-proc
is not processed before checking access; it may be a relative path, for example.an immutable list containing one or more of the following symbols:
'read
-- read a file or directory'write
-- modify or create a file or directory-
'delete
-- delete a file or directory'exists
-- determine whether a file or directory exists, or that a path string is well-formed
The
'exists
symbol is never combined with other symbols in the last argument tofile-proc
, but any other combination is possible. When the second argument tofile-proc
is#f
, the last argument always contains only'exists
.
The network-proc
procedure must accept four arguments:
a symbol for the primitive operation that triggered the access check, which is useful for raising an exception to deny access.
an immutable string representing the target hostname for a client connection or the accepting hostname for a listening server;
#f
for a listening server or UDP socket that accepts connections at all of the host's address; or#f
an unbound UDP socket.an exact integer between
1
and65535
(inclusive) representing the port number, or#f
for an unbound UDP socket. In the case of a client connection, the port number is the target port on the server. For a listening server, the port number is the local port number.a symbol, either
'client
or'server
, indicating whether the check is for the creation of a client connection or a listening server. The opening of an unbound UDP socket is identified as a'client
connection; explicitly binding the socket is identified as a'server
action.
The return value of file-proc
or network-proc
is
ignored. To deny access, the procedure must raise an exception or
otherwise escape from the context of the primitive call. If the
procedure returns, the parent's corresponding procedure is called on
the same inputs, and so on up the chain of security guards.
The file-proc
and network-proc
procedures are invoked in
the thread that called the access-checked primitive. Breaks may or
may not be enabled (see section 6.6). Full continuation
jumps are blocked going into or out of the file-proc
or
network-proc
call (see section 6.3).
(security-guard?
v
)
returns #t
if v
is a
security guard value, #f
otherwise.
9.2 Custodians
A custodian manages a collection of threads, file-stream
ports, TCP ports, TCP listeners, UDP sockets, and byte
converters.21
Whenever a thread, file-stream port, TCP port, TCP listener, or UDP
socket is created, it is placed under the management of the current
custodian (as determined by the current-custodian
parameter; see section 7.9.1.8).
The main operation on a custodian is to shut down its managed values
via
. In other words,
custodian-shutdown-all
generalizes custodian-shutdown-all
to
forcibly and immediately close a set of ports, TCP connections, etc.,
as well as terminate (or suspend) a set of threads. For example, a web
server might use a custodian to manage all of the resources of a
particular session so that the session can be cleanly terminated if
it exceeds its allowed lifetime.kill-thread
A custodian that has been shut down cannot manage new objects. If the
current custodian is shut down before a procedure is called to create
a managed resource (e.g., open-input-port
, thread
),
the exn:fail:contract
exception is raised.
A thread can have multiple managing custodians, and a suspended thread
created with thread/suspend-to-kill
can have zero
custodians. Extra custodians become associated with a thread through
thread-resume
(see section 7.1). When a thread has
multiple custodians, it is not necessarily killed by a
, but shut-down custodians are removed
from the thread's managing set, and the thread is killed when its
managing set becomes empty.custodian-shutdown-all
The values managed by a custodian are only weakly held by the custodian. As a result, a will (see section 13.3) can be executed for a value that is managed by a custodian. In addition, a custodian only weakly references its subordinate custodians; if a subordinate custodian is unreferenced but has its own subordinates, then the custodian may be collected, at which point its subordinates become immediately subordinate to the collected custodian's superordinate custodian.
(make-custodian
[custodian
])
creates a new custodian that is
subordinate to custodian
. When custodian
is directed (via
) to shut down all of its managed
values, the new subordinate custodian is automatically directed to
shut down its managed values as well. The default value for
custodian-shutdown-all
custodian
is the current custodian.
(custodian-shutdown-all
custodian
)
closes all open ports and
closes all active TCP listeners and UDP sockets that are managed by
custodian
. It also removes custodian
(and its
subordinates) as managers of all threads; when a thread has no
managers, it is killed.22 If the
current thread is to be killed, all other shut-down actions take
place before killing the thread.
(custodian?
v
)
returns #t
if v
is a custodian value,
#f
otherwise.
(custodian-managed-list
custodian super-custodian
)
returns a list
of immediately managed objects and subordinate custodians for
custodian
, where custodian
is itself subordinate to
super-custodian
(directly or indirectly). If custodian
is
not strictly subordinate to super-custodian
, the
exn:fail:contract
exception is raised.
(custodian-require-memory
need-k custodian
)
registers a require
check if MzScheme is compiled with support for memory accounting,
otherwise the exn:fail:unsupported
exception is raised. If a check is
registered, and if MzScheme later reaches a state after garbage
collection (see section 13.4) where need-k
bytes
are not available to the current custodian, custodian
is shut
down.
(custodian-limit-memory
limit-custodian limit-k stop-custodian
)
registers a limit check if MzScheme is compiled with support for
memory accounting (a.k.a. ``3m''), otherwise
the exn:fail:unsupported
exception is raised. If a check is registered, and if
MzScheme later reaches a state after garbage collection
(see section 13.4) where limit-custodian
owns
more than limit-k
bytes, then stop-custodian
is shut
down.
9.3 Thread Groups
A thread group is a collection of threads and other thread
groups that have equal claim to the CPU. By nesting thread groups and
by creating certain threads within certain groups, a programmer can
control the amount of CPU allocated to a set of threads. Every thread
belongs to a thread group, which is determined by the
current-thread-group
parameter (see
section 7.9.1.8) when the thread is created. Thread
groups and custodians (see section 9.2) are independent.
The root thread group receives all of the CPU that the operating system gives MzScheme. Every thread or nested group in a particular thread group receives equal allocation of the CPU (a portion of the group's access), although a thread may relinquish part of its allocation by sleeping or synchronizing with other processes.
(make-thread-group
[thread-group
])
creates a new thread group
that belongs to thread-group
. The default value for
thread-group
is the current thread group, as determined by the
current-thread-group
parameter.
(thread-group?
v
)
returns #t
if v
is a thread group
value, #f
otherwise.
9.4 Inspectors and Modules
Just as inspectors control access to structure fields (see
section 4.5), inspectors control access to module bindings
(see section 5). The default inspector for modules is
determined by the current-code-inspector
parameter, instead
of the current-inspector
parameter.
When a module
declaration is evaluated, the value of the
current-code-inspector
parameter is associated with the
module declaration. When the module is invoked via require
or dynamic-require
, a sub-inspector of the module's
declaration-time inspector is created, and this sub-inspector
is associated with the module invocation. Any inspector that controls
the sub-inspector (i.e., the declaration-time inspector and its
superior) controls the module invocation.
Control over a module invocation enables
the use of
module->namespace
on the module;access to the module's protected identifiers, i.e. those identifiers exported from the module with
protect
; andaccess to the module's protected and unexported variables within compiled code from
read
(see section 14.3).
If the value of current-code-inspector
never changes, then no
control is lost for any module invocation, since the module's
invocation is associated with a sub-inspector of
current-code-inspector
.
The inspector for a module invocation is specific to a particular
module registry, in case a module is attached to a new registry via
namespace-attach-module
. The invocation inspector in a
particular registry can be changed via
namespace-unprotect-module
(but changing the inspector
requires control over the old one).
Control over a module declaration (as opposed to a mere invocation) enables the reconstruction of syntax objects that contain references to the module's unexported identifiers. Otherwise, the compiler and macro expander prevent any reference to an unexported identifier, unless the reference appears within an expression that was generated by the module's macros (or, more precisely, a macro from a module whose declaration inspector controls the invocation of the identifier's module). See section 12.6.3 for further information.