may consume a resource or invoke an operation on an object. Since a real .... Introduction of viruses and Trojan horses: An agent or the kernel is tricked into ...
Secure Mobile Code: The JavaSeal experiment Jan Vitek
Ciar an Bryce University of Geneva
Abstract
Mobile agents are programs that move between sites during execution to bene t from the services and information present at each site. To gain wide acceptance, strong security guarantees must be given: sites must be protected from malicious agents and agents must be protected from each other. Software based protection is widely viewed as the most ecient way of enforcing agent security. In the rst part of the paper, we review programming language support for security. This review also helps to highlight weaknesses in the Java security model. In the second part of the paper, we make good on the lessons learned in the review to design a security architecture for the JavaSeal agent platform.
1 Introduction Wide area networks such as the Internet hold the promise of a brave new wired world of global computing. The hope is to have distributed applications that scale to the size of the Internet and seamlessly provide access to massive amounts of information and value added services. But programming these new applications is proving more dicult than anticipated. The blame lies in part with the prevalent client-server paradigm which acts as straight jacket, forcing components to interact through coarse-grained network interfaces. Mobile agent programming is a promising alternative to client-server computing. Mobile agents control where computation happens by moving programs as well as data. Agents are well suited to collaborative applications [33] and data mining where the application's data space is huge. In such applications, moving an agent to the server reduces network connectivity, eliminates latency and overcomes limitations imposed by rewalls. Network management [37] and electronic commerce [27] are other promising application domains for the technology. Agents shift the locus of interaction from the network to the machines, and co-located components can interact through ne-grained interfaces. Contrary to conventional wisdom, such interactions can be more secure than interaction over the network. Consider an accounting package used for lling out income tax forms. The user's trust in the software is clearly related to the package's physical location. A program on a remote server requiring nancial data to be sent on the network is outside of the user's sphere of in uence. This is not so for a package installed on the local machine; the user can control the software's interaction with his data. As an extreme measure, he may unplug the computer from the network while the package is running to prevent information leaks. Of course, execution of foreign code raises security concerns which cannot be 1
solved by simply unplugging the network cord. The program's actions must be controlled to guarantee that it does not damage the system or other programs. Programming language based protection mechanisms provide security support for the execution of mobile agents. Placing security squarely in the language has several advantages. Security is de ned in terms of the entities manipulated by the programmer, and is given a formal meaning. The same techniques used for ensuring correct program behavior can also ensure secure program behavior. At the same time, program optimization technology can be used to reduce the overhead of security. Although considerable attention has been devoted to security in languages recently, many accounts are biased towards particular implementations [13, 36]. Moreover, in the eld of mobile agent programming security remains a dicult problem with few comprehensive solutions. The contribution of this paper is twofold: We describe and classify programming language protection techniques. We propose a security architecture for Java based mobile agents, and describe its implementation in the JavaSeal system. The introduction of this new security architecture was motivated by problems highlighted during our analysis of existing mechanisms, and aims to correct inadequacies that exist in several existing security architectures, including that of Java.
Road map This paper is structured as follows. Section 2 de nes what we mean by \security" and gives the security requirements of an agent system. Section 3 reviews programming language techniques for security. This section lists solutions and highlights security problems that languages are prone to. Special attention is paid to the current Java security architecture. Section 4 introduces the JavaSeal mobile agent system. Section 5 introduces the security model of JavaSeal which is designed to tackle problems highlighted in Section 2. Section 6 describes related work.
2 Background: Security and Agents We start this section with some security de nitions, and then look at how these de nitions apply in the mobile agent context.
Security
In any computer system, principals are the entities of a system whose actions must be controlled. Principals consume resources and invoke operations on objects. It is the role of the system's security policy to determine if a principal may consume a resource or invoke an operation on an object. Since a real system possesses a large number of objects and operations, for eciency and management of security, not all operations executed by principals are veri ed. Instead, the abstraction of a protection domain [10] is employed. A protection domain is a context in which a principal executes. It contains objects \owned" by that principal and for which the security policy does not need to be checked. 2
Only operations that cross domains need be mediated by the security policy. Figure 1 illustrates these concepts. The term Reference Monitor is used for that component of a system that veri es the legality of each operation by consulting the security policy [11]. To make a secure system, its reference monitor must satisfy two properties. (1) Total mediation : it intercepts each operation by a protection domain on an object or resource, and consults the security policy to ascertain if this operation is legal and can continue; (2) Encapsulation : it is protected from tampering. PRINCIPAL 1
PRINCIPAL 2
PROTECTION DOMAIN 1
PROTECTION DOMAIN 2
Object 1 Object 1 Object 1 Object 1
op1
Object 2
check REFERENCE MONITOR
Figure 1: A system's security architecture. The reference monitor intercepts each access to an object from a remote domain, and queries the security policy.
The rst role of a security policy is to specify the access rights that a protection domain possesses for an object operation and for a resource. A second role is to de ne the dynamics of access rights: how the security policy can be programmed to exchange rights between domains, to assign rights to new domains, and to revoke rights from domains. In real systems, a reference monitor and its security policy are typically implemented using access control lists or capabilities [25]. Unix takes the ACL approach for instance: each access to a le is mediated by its ACL, and the ACL contains the policy speci ed by the le owner. Amoeba [35] uses capabilities: here an object name includes the access rights for the object that the domain containing the name possesses for the object. One of the reasons that security is hard to achieve is that controlling operations executes is rarely enough to ensure the con nement of information. A real system contains a variety of channels over which protection domains can exchange information [22]. Legitimate channels are mechanisms included in a system precisely as a means for domains to communicate. Storage channels are elements of the environment that can be read or written by several domains, and which might not be detected by the reference monitor. An example is a run-time variable to which all programs (protection domains) have access. The last category is that of covert channels which are means of communication that were not designed for this purpose. For instance, a domain could signal a secret value by setting its disk access per second ratio to this value. Covert channels are hard to block, and many security architectures are satis ed if the bandwidth 3
of covert communication is suciently low (regulatory bodies suggest a threshold of 100 bits/second [7, 11]) Of course, if the secret is a PIN or bank account number, even low-bandwidth communication is unacceptable.
Agent Security Requirements
Spurred by the success of the Internet, a number of projects are experimenting with new approaches for constructing distributed systems. Mobile agents are software components comprising code (agent behavior) and data (agent state). Agents carry out distributed computations by moving around the network. At each site, an agent may use that site's resources and interact with other agents there. The programming style emphasizes a clear separation between local and remote interaction. Local interaction takes place amongst co-located agents; on the other hand, remote interaction is obtained by moving programs [15, 44, 37, 5]. Agent systems contain an agent execution platform, or agent kernel, whose role includes the provision of agent protection mechanisms. By agent programming language, we mean the language in which the agents and kernel are programmed. With its emphasis on security and portability, Java is an obvious choice, and a widely adopted language for mobile agent systems. [2, 1, 3, 23]. Figure 2 illustrates a typical agent system. Agent Program
Agent Program code data
code
data
Resource Agent execution platform ClassLoader API
Network API
RMI Serialization API
Java Virtual Machine Host (OS)
Figure 2: Mobile agent architecture. The mobile agent paradigm is an interesting case study for the security community. An agent is a program executing in an environment that it does not fully trust, and which interacts with co-located agents which it also does not fully trust. This mistrust is mutual. The reason for this mistrust is the susceptibility of an agent and the kernel to the following kinds of attack:
Unauthorized disclosure: Secret data stored within an agent, the kernel, or the enclosing environment of the kernel is leaked to an unauthorized agent. 4
Unauthorized modi cations: The code or data of an agent is subvertly
modi ed, thus damaging the agent. When such attacks succeed on the kernel, then the whole platform is paralyzed. Denial of service: An agent reduces the availability of some shared system resource by consuming an inordinate amount. CPU, memory and network bandwidth are classic targets for these attacks. Introduction of viruses and Trojan horses: An agent or the kernel is tricked into executing malicious code. This code can cause disclosure, modi cation and denial of service attacks.
To avoid these attacks, an agent platform must be able to eectively implement security policies. In an agent context, the \objects" of the security model are Java objects and classes; the operations are methods. Security requires that the platform contain an Agent Reference Monitor, de ned as follows.
De nition 1 (Agent Reference Monitor) mediates the following operations, and veri es their legality with respect to the security policy in place: 1. Creation/destruction of an object instance by a protection domain. 2. Transmission of objects between protection domains. 3. Invocation of a method on an object or class by a protection domain. 4. Creation of class by a protection domain. Example Agent Application We motivate the discussion by the Hyper-
system [27], implemented on top of our JavaSeal platform. Hypersupports the commercial distribution and protection of short-lived digital documents such as newspaper articles. Instead of treating digital documents as data stored in a format suitable for display, HyperNews digital documents are programs that must be executed to reveal their contents. This content is not restricted to data, but can also contain instructions which, e.g., verify the document's integrity, decrypt sensitive parts or seek feedback from the reader. The core HyperNews architecture consist of a JavaSeal agent kernel loaded on each user's machine. To get articles from some news provider, a user will register and wait for the provider to send a NewsProxy agent to his site. The proxy is a mobile agent that provides an interface to that provider's articles. Articles migrate to the user's site via courier agents; these are agents with behavior for payment. Security is required at several levels in HyperNews. Firstly, the agent kernel itself must be protected from attacks by agents. Further requirements restrict the actions of proxies and enforce information con nement. For instance, proxies are not allowed to migrate, they may not use the network, and cannot interact with other proxies. Also, a proxy cannot interact with article agents of other providers. It is interesting to note the dierent \layers" of security. The basic policy deals with proxies and courier agents. A proxy agent has a policy to protect itself and its articles. Finally, an article includes a policy to protect its content. Each of these layers is speci ed by a dierent part of the system policy, independently of the others. News
News
5
3 Protection in Object Oriented Languages Protection is a term used for all mechanisms that control an access by a program to a computer system component, i.e., the mechanisms that implement the reference monitor. Traditionally, protection was the realm of operating systems and mostly relied on hardware enforced address space boundaries. In this approach, objects are divided between address spaces, with no guarantees of protection within a domain. In the last few years there has been an increasing number of attempts at providing protection in software [16, 6, 26] as the cost of crossing protection domains is signi cantly lower. Programming language based protection goes a step further and uses language semantics, such as type systems, and language implementation technology, such as compilers and run-time systems to enforce security. Programming language based protection mechanisms have signi cant advantages. The language semantics provides the basis with which one can formally reason about the security of an application. Moreover, the cost of cross-domain calls can be reduced by optimizing away checks at compile time [28, 43]. This section discusses security for object oriented languages. We begin in Section 3.1 with a related topic, that of language safety. Section 3.2 looks at how protection domains can be implemented in programs. Section 3.3 looks at a reference monitor is integrated into a program, that is, how operations between protection domains are mediated.
3.1 Language safety
A \safe" program is a program written in a language whose semantics and runtime environment guarantee the absence of a class of errors. Typical techniques used to ensure safety include: Code integrity: Veri cation that the code is \well-formed", containing only instructions de ned by the language, e.g., bytecode veri cation [45]. Type correctness: Well-typed programs give well-typed results [42]. For instance, a pointer can never be interpreted as an integer in a well-typed program. Typing may be static (performed before program execution), dynamic (performed during execution) or a mixture of both as in Java. Automatic memory management: Object allocation and deallocation is under run-time control, thus preventing a program from deleting an object still in use in some part of the system. Memory protection: Checking for out of bound array accesses and method invocation (stack over ow). Safety is costly on account of the checks made at runtime. Moreover, safety is jeopardized by native code. This is platform-speci c code that implements detailed language features such as synchronization. This last point remains a problem even if approaches such as proof carrying code appear promising [29]. Nevertheless, safety mechanisms are the basic tools that we dispose of for implementing security at the language level.
6
3.2 Protection domains
A protection domain regroups objects \owned" by a principal, meaning that access to these objects from within the domain need not be mediated by the reference monitor. The main requirement for implementing protection domains is that they remain isolated from each other. There are three alternatives for implementing a protection domain in a language.
3.2.1 Object domains
Fine grained protection is achieved by decreeing that each object instance de nes its own protection domain. Domain crossings occur when an object calls a method of another object. Object-level protection has been proposed by Hailpern and Ossher [17] in the context of databases, but is too ne grained to be practical as it implies managing large amounts of security information and frequent access control checks.
3.2.2 Class domains
All objects of the same class belong to the same domain. Invocation of a method implemented by another class corresponds to a domain crossing. Less intuitively, a base class may belong to a dierent domain to its derived classes thus giving rise to \vertical" domain crossings. Class domains take vertical crossings into account. The interface between a class and its subclasses must be just as clear cut as the interface between a class and its client classes since base classes and descendant classes are also in a client-server relationship [34]. The drawbacks of class-based domains is that their granularity is still too ne to check all cross domain operations. A conservative estimate of cross-domain operations is around 30; 000 ops/second [43] on a representative set of Java benchmarks. Furthermore, class domains do not facilitate resource accounting: though one can control what code is using memory and CPU resources, one cannot control who is using this code.
3.2.3 Namespace domains
In the most general sense, a namespace is a mapping from identi ers to entities, e.g., classes and objects. Invocation of a method on an object in another namespace is a domain crossing. Vertical domain crossings may still occur if a base class exists in another domain. Namespace level protection domains are coarser and thus potentially entail less domain crossings and more manageable security policies. Further, they are a more logical representation for a principal in a system: we want to control the whole program and data of a principal, and not just individual parts. Resource accounting on the basis of namespaces is also easier: principals are charged for the resources that their objects and classes use. The main disadvantage of namespaces is that they do not correspond to a single language construct. Even though packages and modules de ne spaces of classes and procedures, these are compile-time notions and the boundaries disappear when the program is compiled. In Telescript [36], each object is tagged with protection domain information as an extra instance variable, thus namespaces are de ned dynamically by the set of object with the same tag. Leroy and 7
Rouaix have formalized the security properties that can be obtained by systematic namespace management [24]. In essence, they show how to isolate namespaces with abstract types, and prove that isolation is maintained throughout computation. A related approach is used in operating systems [32, 4] to protect the system kernel from code dynamically linked into it. Other approaches to namespace management simply rely on hiding certain dangerous functions [31].
3.3 Access Control
By \access control", we mean the mediation of communications between domains by a reference monitor. The reference monitor and the security policy checks that it eects can be static or dynamic. In the rst case, the policy is veri ed at program compile or link-time. In the second case, the reference monitor exists at run-time and intercepts communications between domains.
3.3.1 Static access controls
Checking mechanically the access rights of principals is more ecient than dynamic checks since run-time performance is not degraded. There are two basic language techniques for controlling access to objects: Access modi ers: Access modi ers such as private, public and protected declarations provide four classes of restrictions for the scope of attributes: no restriction, package scope, subclass scope, class scope. Type abstraction: Abstract data types protect the implementation of a type by hiding the type de nition, e.g., Modula-3 opaque types [30], and subtyping.
Access modi ers do not take protection domains into account. They only
statically specify what groups of classes may use the features of the class. These groups are coarse grained: public (all other classes), protected (classes in the same package), private (code of the same class) and private protected (access from code of class or derived classes). Thus it is not possible to specify that only certain \trusted" classes have access to a eld, or to dynamically de ne the members of this \trusted" set. Access modi ers are also used to specify \vertical" protection boundaries: a derived class can be refused visibility over an attribute by declaring the attribute private. Nevertheless, because of their static and compile-time nature, access modi ers are not useful for controlling access between namespaces. Another common modi er is nal; its role is to restrict subtyping. This is useful to prevent Trojan horse attacks. For instance, if a class expects class C as argument, an attacker simply needs to de ne a derived class of C that rede nes C 's methods to contain malicious code.
Type abstraction is more useful for access control mediation. Consider a
Printable interface that contains only a print method. Type abstraction can enforce the access control that a domain only use the print method for an object of type Printable. Even if the object has more methods, the receiving domain does not see the other methods [20]. However, this control can be bypassed
8
in dynamically typed languages like Java, where it is possible to downcast Printable to the object's concrete type. One can add additional checks that guarantee that the protection domain being handed a reference of type Printable does not possess the concrete type of the object, or that no downcast to the type is present in the code. Such checks can be achieved by static analysis: in Java, this is simply veri ed by inspection of the class les loaded. Ironically, re ection in Java complicates this task as program may query objects about their type and thus increase its working set of types dynamically.
Critique Access modi ers and types were not conceived as security mechanisms, but rather as software engineering mechanisms. Their goal is to ease the development of reusable and correct code. By forcing them in a dual role, there is a risk of getting programs where either good design or security is compromised. Further, their static nature does not sit well with namespaces; in HyperNews for instance: a proxy agent has access to an article based on its identity, and not on its classes. 3.3.2 Dynamic access control: Program Logic
It is possible to implement checks at run-time by explicitly programming the reference monitor into the code of the application. This program logic is a set of calls to the security policy. In Java for instance, a le system object queries the security policy (manager) on receiving a request from a client in the following way: File open(String name) { /* explicitly programmed reference monitor */ if (checkFileAccess()) { ... proceed ... } else { ... deny ... } }
In Java, the checkFileAccess method inspects the call stack to verify if the caller has appropriate access rights [43]. Stack inspection is useful since the call may have been transitively made; for instance, a user calls a le directory object which in turns calls the le system object. Use of the call stack for security policies raises an important issue. The walk-through of the call stack cannot be done at the language level for security reasons at least | one cannot allow a program to peek and poke the stack of another domain's thread. To address this issue, the implementation requires primitives oered by the language environment. A typical approach is to de ne several primitives that view the state of the call stack and that can be used by an object to de ne its security policy. This was the approach taken by Telescript which gave access to the calling object owner (principal), the original issuer of the call (the sponsor) and the caller. An example policy that can be programmed is: 9
public void mySafeOp( ) { if (friends.contain(sponsor) && classesItrust.contain(caller)) { realOp(); } else { throw AccessViolation }
Of course, the call stack veri cation approach relies on the fact that the same thread is used in a nested call. Sometimes it is important that a new thread be created within a called domain to execute the desired operation [18, 14]: this prevents a domain from halting a thread in a remote domain and leaving that domain in an inconsistent state.
Critique By interspersing access checks in the code of the program it becomes
dicult to reuse code in another context. The security check made may not be required and only degrade performance. Symmetrically, some system provided classes may, from the point of view of an application, require security checks, yet as they are in compiled, those checks can not be added. Further, the onus is on the server (called object) to invoke the reference monitor | this is not sucient for mandatory security policies where the use of a speci ed security policy must be enforced irrespective of the policies that the domains wish to use [11].
3.3.3 Dynamic access control: Checked Primitives
A second form of run-time access control is checked primitives. A checked primitive is an instruction whose invocation automatically causes a trap into the reference monitor, analogous to an IPC in an operating system, where a send on a socket traps into the OS where the transportation work is done. Reference monitor mediation is achieved by associating a checked primitive with each cross-domain operation. An advantage of this approach is that it is suitable for the implementation of mandatory security policies. Examples of checked primitives in programming languages include assignment (where the type of the variable is checked against the object) and object creation (where the run-time veri es that enough memory is available for the object). The use of checked primitives implies that two sets of communication mechanisms are in place: one for communication with objects within the same protection domain, one for communication with objects outside. Although this duplication can be hidden, e.g., RMI with stubs, the semantics of the two sets of operations are usually subtly dierent.
3.4 Putting it all together
The Java development kit 1.2 (JDK) integrates many of these techniques in its security architecture. The main concept of protection domain is the class, and system security is based on static veri cation and program logic (implement via the security manager). Access visibilities are used to prevent access to sensitive elds of the JVM. Examples include threads (where the priority attribute cannot be directly changed), subtyping control of key classes (for instance, java.lang.Compiler is nal) and preventing the rede nition of essential 10
methods (for instance, the de neClass() method of java.lang.ClassLoader that links a class le into the JVM is nal). The program logic based reference monitor (SecurityManager) is inserted in the code of system classes before sensitive operations. Security checks are performed by checking that the instruction has been issued by an authorized protection domain (by checking the classes on the stack). As mentioned in relation to class-based protection domains, this model does not allow for resource accounting as it is not clear who should be charged for resources like memory or disk. The primary goal of this security architecture is to protect the virtual machine from the programs running on top of it. In addition to protecting the JVM from applets, applet programs running on the JVM must be separated from one another (at least applets loaded from dierent origins). For this purpose a form of namespace protection domain is adopted. Each applet has its own class loader, and each loader has its own instance of classes. Java typing ensures that a cast of an object reference to an object in another namespace is trapped as a type cast error. Assignment becomes an inter-domain checked primitive enforced through typing. This ensures that applets do not acquire references to objects belonging to other applets [15]. For added security, applets are not allowed to communicate. Figure 3 depicts this architecture. APPLET n
APPLET 1
SECURITY MANAGER
CLASS LOADER
JAVA VIRTUAL MACHINE
Figure 3: JDK Applet security architecture. The overlap of JVM and applets represents the shared set of JDK core classes.
Breaching security One can view the JDK security architecture as containing a system domain for JDK core classes and a separate domain for each applet running over this core. Security breaches can easily occur for two reasons: Domains overlap leaving storage channels. The core JDK classes are shared by all domains. The problem is that class and object sharing can be exploited as storage channels. As a case in point, an embarrassing bug was discovered in the digital signature infrastructure released by SUN. The problem was that an applet could obtain a reference on the system's internal list of signatures, and so could freely add or remove entries from that list. Failure of Checked Primitives. The JDK model uses checked primitives to implement namespace isolation: an object can only reference an object of 11
a class loaded by the same loader, or by the system loader1. As said, this is a form of checked primitive since an explicit cast to an object of a remote load/name space is signaled as a type violation. However, dynamic typing can lead to a bypassing of this rule. Consider a class C which is loaded by two loaders; we denote the resulting class instances C1 and C2 . Suppose that each class implements the Java interface I and that this interface is loaded by the system loader. If a shared class exists that stores objects of type I , since both domains may reference objects of interface I ; then an object o of one domain may be assigned to a variable of type I , which may be accessed via a reference in the other domain. Under these circumstances, security breaches inevitably occur and proving that an application built over the JVM is secure is bound to be dicult. On a more fundamental level, the problem with JDK is that the shared kernel interface (comprised of the JDK core classes) is too big to reason with, and there are no checked primitives to mediate access to the kernel's protection domain.
4 The JavaSeal experiment is micro-kernel for agent systems [38, 39, 40] which has been implemented at the University of Geneva. The implementation and agent languages are both Java, the system was originally implemented for JDK 1.1.5 and later ported to JDK 1.2. The current implementation comprises 75 classes, and 10'000 lines of sparsely commented Java code. JavaSeal oers the basic support required to develop mobile agent application [3, 23], but with a strong emphasis on security. This orientation permeates the design of JavaSeal. Before discussing the security motivations that shaped the system, we give a brief overview of the main features of JavaSeal. JavaSeal is layered on top of the JDK and virtual machine. A small kernel implements the core functionalities and provides an API for agents. Agents are written in Java, but with a number of restrictions stated below. The JavaSeal API contains the class that are visible to agent programmer. The main new abstraction is the Seal which represents mobile program composed of objects and classes, along with a number of dedicated threads. Seals can be nested hierarchically, thus a seal can have a number of subseals executing within it. This nesting was inspired by the work of Lepreau et al. on recursive virtual machines [12]. Seal has two derived classes, RootSeal and Agent, the former stands for the base of the seal hierarchy and is the interface to the outside world (i.e., the rest of the virtual machine and the operating system), the latter is the base class for all user de ned mobile agents. Seal communicate by synchronous message exchange over named channels. These channels are instance of the Channel class and they transfer Capsules. Class Strand is a restricted equivalent of Thread, the name has been changed to avoid confusion. Finally, the SealLoader class implements seal loading and veri cation. Some selected interfaces are shown in Figure 4. More detailed descriptions of the key classes are given in the following paragraphs.
JavaSeal
1 This explains how the core JDK classes can be shared. These classes are loaded with the system loader. Since all domains need to have access to these classes, the typing rule is \bent"to accommodate this.
12
public abstract class Seal implements Runnable, Serializable f public static Seal currentSeal() public static void dispose(Name subseal) public static void rename(Name subseal, Name subseal) public static Name subseal(int) public void run(); ... g
public nal class Channel f private Channel(); public static Capsule recv(Name channel, Name seal); public static void send(Name channel, Name seal, Capsule caps); g
public nal class Portal f private Portal(); public static int status(Name channel, Name seal); public static int open(Name channel, Name seal); public static int close(Name channel, Name seal); g
public class Capsule implements Serializablef public Capsule(Object obj); public Object open(); g
public class Strand f private Strand(); public static Strand create(Runnable target); public static Strand currentStrand(); public void start(); public void stop(); ...
g
Figure 4: JavaSeal classes used to program agent applications. Seal is the parent class of all agents. They provide method for manipulating subseals. Named Channel are used to communicate across seal boundaries. Portals take care of access control. (both classes are purely static with no instances) Capsules transfer objects sent over channels. Strands implement a restricted interface over Java Threads.
13
Seals A seal is composed of a set of classes, objects and threads. The classes of a seal are loaded by a dedicated SealLoader instance and are not shared with any other seal. All objects that are reachable from a seal instance are considered to belong to the seal. All communication with other seals must be done through channels. Every seal has a run method which is called to start execution. Channels and Portals Named channels allow seals to communicate by synchronous message passing. To exchange a message, two seals must be ready to communicate on the same channel. Portals control acces to channels. The channel class has methods send and recv that transfer an object of type Capsule from the sender to the receiver. The Portal is associated with a seal and channel, it has an open method to allow communication on the channel and a close method to revoke authorization.
Capsules Capsules transfer data across channels. A capsule is a deep copy of
a group of objects. The copy is done in kernel mode and ensures that the capsule does not share references with the sender. Capsules are currently implemented with Java serialization. Opening a capsule releases its contents in the local environment. A capsule is opened successfully only if the SealLoader of the receiver is able to resolve all the classes required by the objects in the capsule. The SealCap class specializes the capsule class for moving seals, it contains the serialized state of a seal along with a class archive. The class archive contains a copy of all classes used by the seal.2
Strands A strand is a wrapper around a thread object. Strands dier from threads in two respects. A strand is bound to its seal and can never leave it. Second, a strand's methods have been modi ed to involve only information about other strands running within the current seal. Thus, when requesting information about the number of strands, only those that belong to the seal will be listed. In the implementation, there is a mapping between threads and strands. An initial strand is explicitly created when a seal is started and executes the run method of that seal. To handle parallelism, strands should be started automatically. For convenience, daemon strands should be started to service external calls. Mobility Movement of seals in JavaSeal is not transparent. Since we did
not modify the JVM, there is no way to capture thread state.3 For this reason, threads (strands) in a JavaSeal agent are killed, and only the data reachable (in the garbage collection sense) from a seal instance is moved. The mobility model of JavaSeal is based on the seal's run method which is executed when a seal is created, and also when a seal is re-started after a move, basically the same approach is used in all Java agent systems [3, 23]. To move an active seal, one must rst close all portals, and stop all strands currently within the seal. The seal at this point is passive and can be stored in a capsule. After movement, the capsule is opened, and the seal's run method is invoked. The
2 In the future, we plan to use 128 bit ngerprints to identify class les and transfer class les only if necessary. 3 Without direct JVM support, there is no way to capture thread state in a multi-threaded system since locking and thread queue management is implemented with native code.
14
run will typically check whether the seal is a newborn or whether it is being re-started, load new subseals or selectively restart the serialized subseals.
4.0.1 Building secure applications in JavaSeal
From the point of view of building secure applications, the essential characteristic of seals is that they can be nested; a seal can contain other seals logically executing within their scope. Thus a JavaSeal platform is structured as a hierarchy of seals running under a single RootSeal. As we will see, this structure is essential to the design of security policies. It is also important for mobility as seals are moved along with all of their subseals. In the HyperNews application, Figure 5, a number of trusted service seals (e.g., network interface and a le system manager) run at the top level. Mobile agents execute within a dedicated seal referred to as a playground. A playground is user-programmed seal which controls the behavior of its subseals. root seal
net seal
file system manager seal
hypernews playground
proxy agent
article agent
article agent
Figure 5: The HyperNews seal hierarchy. Each seal is allowed to communicate only with its parent. Agents are restricted by the security policy of the playground seal. Moving a seal implies moving all of its subseals.
Security depends on another feature of the JavaSeal design: a hierarchical message passing model. In the system, a seal may only communicate with its direct parent. All other patterns of interaction must programmed. Thus, if two arbitrary seals must exchange a message, this message will travel up the hierarchy to the common parent of the seals and will then be propagated down to the destination seal. This is used for security: in the above scenario, the playground implements security by interposition [21] as all communication between mobile agents and other parts of the agent kernel must pass through it.
5 The JavaSeal security architecture The JavaSeal security architecture is designed to implement the agent reference monitor (ARM) of De nition 1. There are three goals for the implementation: 15
Con nement : Agents execute in protection domains that must be con ned. All communications between domains must be mediated. In particular, storage and covert channels in the kernel must be detected and removed. Flexible policy enforcement : As mentioned in the case of HyperNews, an agent application needs to be able to enforce a variety of security policies. Multiple policies can be in place in the system at any time. Resource usage control : Control agents' usage of resources such as CPU, memory. Figure 6 illustrates the security architecture. A seal corresponds to a protection domain. The JavaSeal kernel executes in its own privileged protection domain. Seals interact with the kernel through the JavaSeal Kernel Interface (JKI). One of the roles of the kernel is to implement the Message Passing Interface (MPI) for seals based on the channel and portal abstractions presented in Section 4. Since seals are, by de nition, restricted to communicate only with their parent, it is possible to interpose security policies in the parent that control all requests of subseals and check their legality. Dierent policies can be placed at dierent levels of the hierarchy. Resources are allocated and accounted for each seal. The security architecture strongly depends on the safety mechanisms oered by the Java environment, e.g., byte-code veri cation and typing. In particular, classes containing calls to native code are rejected by the seal loader.
SEAL 1
MPI
JKI
JKI
SEAL LOADER
JAVASEAL KERNEL
CLASS LOADER
SEAL n
JAVA VIRTUAL MACHINE
SEAL LOADER
SECURITY MANAGER
Figure 6: The JavaSeal system architecture. We now detail some of the noteworthy points of this design. Section 5.1 outlines the implementation of protection domains. Channels are checked primitives, and Section 5.2 discusses how security policies are programmed with this mechanism. Section 5.3 then looks at security of the kernel interface. Section 5.4 looks at object nalization since this allows denial of service attacks. Section 5.5 concludes with some comments on the trade-o of mechanisms for good object design and for security.
16
5.1 Implementing protection domains
In JavaSeal, namespaces are used to implement protection domains: each seal's namespace is a set of classes and objects. Mediation of communication between domains uses checked primitives (provided by the Channel class). Thus, the ARM can aord not to check method calls and object and class creations that occur inside of domains. Achieving domain isolation is dicult because the JDK core classes are shared and can contain storage channels. The main source of storage channels are the static (class) variables which store virtual machine state. These variables are present throughout the JDK4 . The problem is that if a class C provided by a library has a static instance variable v, then agents may use v to exchange objects. One solution to this problem is to simply prevent agents from linking C . This is not sucient however since there may well be another JDK class C that somehow side-eects onto v. Proving the absence of storage channels requires an analysis of the entire JDK. Even if no breach exists at one point in time, adding a single class to the JDK requires analyzing the entire library anew. Domain isolation in JavaSeal is based on the separation oered by the distinct type spaces of class loader spaces. Its enforcement uses static access control checks performed at domain creation. A domain is created when the kernel is asked to create a new seal from an instance of SealCap. A seal capsule contains a serialized object graph and a signed class archive. The creation operation is performed by a new instance of class SealLoader which instantiate a fresh copy of the classes contained in the archive. Currently this is done eagerly, but lazy loading is also possible. The object graph is then deserialized. The SealLoader imposes the following static constraints: (a) classes retrieved from an archive may not extend JDK classes, thus preventing Trojan horse attacks on the kernel, and (b) deserialized objects are resolved by the SealLoader without delegation to the system loader, thus guaranteeing that they are of dierent types to objects instantiated for the same class in other seals. Java typing is then relied upon to isolate this object from another seal domain. Domains are terminated by simply stopping all of their strands and setting all domain speci c kernel pointers to null. Memory will eventually be reclaimed by the garbage collector. The resources used by a seal are relatively easily accounted for. They include classes loaded by the seal's loader and objects reachable from the seal class. Thread objects are accountable through the strands. In the current version of JavaSeal, precise control over memory and CPU is not provided. It would be fairly straightforward to approximate resource usage by instrumentation of the bytecode [8], but a cleaner approach would be to extend the JVM interface with hooks for that purpose. 0
5.2 Channel Security
The JavaSeal message passing interface (MPI) is implemented by three main abstractions: the Channel, Portal and Capsule classes. To enforce the ARM requirements, channel communication must be mediated and controlled by a security policy. 4
JDK1.1 had over 800 shared variables in all of its classes.
17
Every seal has a set of channels on which it may receive messages. Channel identi ers can be given freely to other seals. Every seal also maintains a list of portals. A portal is an access right for a given subseal and a channel. Portals may be opened by specifying a seal and channel pair. Portals may also be closed, thus revoking the associated access right. Communication between a sender seal A and receiver seal B is illustrated in Figure 7. We assume that A and B agree on a channel name, initial agreement is achieved using a set of \standard" channel names. A strand executing within A invokes the send primitive on the channel with a capsule as argument. A's strand blocks until the communication completes. Seal B must at some point (possibly after the call to send) open a portal for A on the agreed channel. Then B must invoke a recv operation on the channel. This primitive blocks until a matching oer appears. In this case, the communication proceeds, rst the portal is closed, then the capsule is copied into B , nally both strands are noti ed that the communication was successful. seal A
Channel
Portal
seal B open()
send() capsule
recv()
close()
capsule
Figure 7: Channel based communication oered by the MPI. It is a matter of policy to decide which portals can be opened. By using dierent communication channels for dierent tasks, it is possible to obtain ne grain control over requests originating from subseals. Another feature is that all the values exchanged by subseals may be inspected by the parent, since the parent mediates their communication. Portals can be opened for a given number of messages or opened with an unbounded capacity. There are three attacks that the checked primitive reference monitor abstraction, implemented by Channel communication, must handle: Creation of aliases across protection domains that can lead to the checked primitives being bypassed. Trojan horse attacks where malicious code is transferred to a domain. Flooding of a target seal with messages. The rst attack is prevented by including a kernel mode deep copy operation in the implementation of capsules. The copy must be done in the trusted kernel to avoid the risk of user code creating an alias to one of the objects being 18
copied. Currently, the implementation relies on serialization, but a more ecient implementation is possible [19]. Trojan horse attacks may occur if a message capsule includes a malicious subtype of one of the classes used by the receiver. The mandatory policy used throughout JavaSeal is that capsules are only opened in the namespace of a seal, and if this namespace does not already have the classes necessary to unpack the capsule's content, the operation fails. Thus it is not possible to add classes to a seal. In fact, the set of classes that implement a seal is signed before transportation meaning that any alteration on the network will be detected. Some trivial denial of service attacks are prevented by the fact that communication requires both a portal and a strand ready to receive the message. The sender blocks if these two conditions are not met, thus preventing ooding. As for resource accounting, capsules are held in the sender while waiting for the communication to take place. It is thus easy to account for these resource (they still belong to the sender). The MPI enforces another aspect of domain isolation through Strands. Strands only exist in a single seal. Thus we prevent attacks that rely on killing a thread while it executes within some other protection domain [18]. The current implementation of messages maps one thread to each strand. This is overly pessimistic, especially when the MPI is used to mimic method invocation, in the future we plan to implement transparent thread sharing [18, 19].
5.3 Kernel Security
As mentioned in the analysis of JDK security, con nement cannot be completely implemented for protection domains since core JDK classes are shared.5 . In the JavaSeal context, kernel classes are also shared. This sharing is a security worry since it can be the source of storage channels. For this reason, JavaSeal speci es a well-de ned interface | the JavaSeal Kernel interface (JKI) | for accessing these shared classes. From a security point of view, the JKI possesses the following two properties: The kernel must be protected from attack by seals; for instance, seals should not be able to exploit the visibility of kernel classes to implant Trojan horses into the kernel or the underlying JVM, e.g., introducing a Channel object that does not conform to the JavaSeal speci cation. The possibilities of storage channels existing in the kernel which could lead to bypassing the channel checked primitive must be eliminated. The basic idea is to have a well-de ned and small interface, and to use a combination of access modi ers and type abstraction (c.f. 3.3.1) [24] to ensure that this interface is correctly used. The JKI is restricted to 8 JavaSeal kernel types and 25 standard Java types most of them exceptions (this includes classes Object, String and StringBuer, as well as interfaces java.io.Serializable and java.lang.Runnable). All arguments and return values of these types are also part of the JKI. These classes have no static variables and instances have no accessible elds that are not instance of JKI types. 5
Some core classes are hardwired in the JVM implementation and cannot be loaded by a thus it is not possible to create entirely disjoint type environment within a JVM.
SealLoader,
19
The only point at which a seal is allowed to give arbitrary objects to the kernel is as an argument to a capsule. However, the capsule is opened in the receiving environment and can at no time reference a kernel object. This, and the restriction on the types directly returnable from the JKI is the key to eliminating the possibility of direct inter-seal aliasing as can happen in JDK1.2. The nal part of the kernel security is obtained by selective access modi ers. We extend the standard Java access modi ers with a more ne-grained version enforced at load time by the SealLoader. We introduce directives that specify selective access modi ers. An example directive sequence is the following: see java.lang.Object; nal seal.sys.Capsule; private java.lang.Object.getClass();
The rst directive speci es that class Object is visible. In other words, the running seal object may link against this (shared) class. The second speci es that a class is to be treated as nal; thus no subclasses are allowed in the seal. The last directive speci es that an attribute of a class cannot be used within a seal. These modi ers are read in by the SealLoader and all classes loaded are checked to conform to these restrictions.
5.4 Finalization
The Java Virtual Machine is in charge of memory management. Regularly the JVM performs a sweep over all of the objects in the memory to identify those which are not referenced, such unreachable objects are garbage collected. The Java language allows programmers to perform some clean-up actions before reclaiming an object, this procedure is called nalization. Finalization usually involves releasing some resources to leave the system in a coherent state, but the nalization code is an arbitrary user written routine. Current implementations of the JVM use a single Java thread to perform garbage collection and nalization. So, a potent attack against a JVM is to put a non-terminating loop in the body of a nalizer. For example class F f public void nalize() f while(true)f g; g
g
When run, this piece of code will tie up the garbage collector and cause the JVM to crash in a time proportional to the allocation rates of the running applications. Empirically, within seconds to minutes the JVM will abort. An interesting point is that nalization may happen after an agent has been terminated. Thus, we must extend our notion of termination to include nalization. A program terminates when all of its threads have been stopped and all of its objects have been nalized. JavaSeal prevents such malicious code by imposing a fairly stringent restriction on nalizers: they are forbidden from containing loops or calls to 20
methods, as the latter could be an invocation of a non-terminating method. These constraints are enforced by SealLoader by a simple static analysis of the byte-code instructions in the body of nalizer methods. A more sophisticated analysis could be used to allow more behavior in nalizers but we have not yet encountered practical cases where this is needed in applications written for JavaSeal.
5.5 Security and Software Engineering
We mentioned in the critique of Section 3.3.1 that access modi ers are a software engineering technique, and are not well suited to security. The JavaSeal highlighted for us several examples of the tension between security and software engineering (good object oriented design). A good illustration of the problem is the Capsule class. In the implementation that class has three subclasses and a number of private methods. We do not want to allow user code to extend the Capsule type because that would allow users to create subtypes containing potentially malicious code. Yet to declare Capsule a nal class would require folding the code of all three implementations in the base class, which is bad object oriented design. Further, some methods in the interface such as in the class Object the method getClass would allow seal to reach outside of the JKI and access forbidden types. On the one hand, the method should be either private or package-protected, but on the other hand not only is it not possible to change existing code for backwards compatibility reason, but also because many other components in the JVM and the JDK use the method quite securely. The solution to this problem proposed for JavaSeal was to introduce a second set of access modi ers | the directives. These are visibility constraints that could be veri ed by the loader. In particular, the directives could be varied for each namespace.
6 Related Work Research into programming-language based approaches to security was active in the 1970s. Liskov & Jones introduced the notion of quali ed typing [20]. In this approach, the declaration of a variable of type T in a program is quali ed by the set of operations that can be executed on an object bound to that variable. For instance, List, faddg: v means that only the operation add may be invoked on an object of type List bound to v. This approach is a form of operation hiding through types, and we implement a variant of this in JavaSeal when preventing types that subtype the kernel classes. Another approach to security studied in the context of programming-languages is information ow control [9]. This 1970s work on programming language security remained at the research level. One problem with these approaches was that they did not scale to programs the size of operating systems which they were intended for. It was more pragmatic to achieve security by inserting access controls at the level of inter-process communication. Another reason for its non-use was that the techniques were unsuitable for assessing the security of the unsafe assembly language programmed components of a system. The 1990s however saw a revival of programming language based security techniques. The main reason is that systems 21
were no longer static monolithic structures, but began to be composed of software components from dierent sources, and these components could be added dynamically to the system. Such structuring approaches for systems include customization e.g., SPIN and mobile code, e.g., Java [15]. A supplementary security requirement in these systems arose: that of verifying security of software components dynamically added to the system. Several agent platforms based on Java now exist, e.g., Mole [3], Voyager and Aglets [23]. A comparison of these agent systems with JavaSeal is made in [38]. None of these systems consider the security issues posed by storage channels in the underlying kernel, nor do they seek a clean implementation of the reference monitor. Another approach often suggested for implementing checked primitives is using objects as capabilities [43]. Here, a reference to a server object is never published. Rather, a reference to a proxy object is published. This proxy object can implement a security policy. The methods of the object are the checked primitives of this architecture and the reference acts like a capability. The problem is that checked primitives do not guarantee security without any notion of protection domain behind this since one cannot ensure that capabilities do not accidentally leak a reference for the object they are guarding. Also, to support an expressive set of security policies, requires the facility to revoke the capabilities and to verify their transfer. This is not obtained because aliasing is hard to control. Guarded Objects [14] are a version of this approach; the guarded object is hidden within an encapsulating object. Wallach et al. discuss two software protection mechanisms for Java: stack introspection and controlled namespaces [43]. The failings of these approaches have been mentioned in section 3 and in [41]. The J-Kernel oers capabilities though with tighter guarantees [19]. A protection domain in the J-Kernel is also a name-space, implemented using a class loader. Communication between domains is achieved by invoking a method on a capability object which acts as a mini-RMI stub. Parameters are deep-copied between domains and only capabilities and core classes are shared. The design of JavaSeal diers to the J-Kernel in that the seal protection domain hierarchy allows for a layered set of security policies, and has a more stringent kernel interface for controlling storage channels in the kernel.
7 Conclusions This paper has overviewed techniques for implementing security using programming language based techniques. The analysis conducted led us to highlight weaknesses in the current JDK security architecture, which we took into account in the design of the JavaSeal mobile agent system. The design of this system and its security model was also presented in this paper. We consider JavaSeal as an experiment in agent system design. Working on the system has shown that it is possible to implement a comprehensive security architecture without having to change the Java virtual machine. Future directions for this work include obtaining an ecient implementation of JavaSeal and including support for accurate resource control. Further, we plan to lift some of the restrictions placed on classes shared in order to provide users with a richer development environment. To do this, we require techniques to analyze 22
the safety of individual classes for security. Programming languages techniques may be used for building secure systems. Security concerns must be addressed at the outset, by the de nition of suitable abstractions in the language itself. These abstraction are not the same abstractions oered to achieve software engineering. Many ideas used in our work originated in operating systems research. The interaction of these elds is promsing. Also interesting is on-going work on static interpretation of programs for security, e.g. [42], as well as proof carrying code [29]
References
[1] A. Acharya, M. Ranganathan, and J. Saltz. Sumatra: A language for resourceaware mobile programs. Lecture Notes in Computer Science, 1222:111{??, 1997. [2] G. Back, P. Tullmann, L. Stoller, W. C. Hsieh, and J. Lepreau. Java operating systems: Design and implementation. Technical Report UUCS-98-015, University of Utah, Department of Computer Science, Aug. 6, 1998. [3] J. Baumann, F. Hohl, K. Rothermel, and M. Strasser. Mole - Concepts of a mobile agent system. World Wide Web, 1(3):123{137, 1998. [4] B. N. Bershad, S. Savage, P. Pardyak, E. G. Sirer, M. Fiuczynski, D. Becker, S. Eggers, and C. Chambers. Extensibility, safety and performance in the SPIN operating system. In Proceedings of the 15th Symposium on Operating Systems Principles, pages 267{284, Copper Mountain, Colorado, Dec. 1995. [5] L. Cardelli and A. D. Gordon. Mobile Ambients. In M. Nivat, editor, Foundations of Software Science and Computational Structures, number 1378 in LNCE, pages 140|155. Springer-Verlag, 1998. [6] J. Chase, H. Levy, M. Baker-Harvey, and E. Lazowska. Opal: A single address space system for 64-bit architectures. In Proceedings of the Fourth Workshop on Workstation Operating Systems, pages 80{85, 1993. [7] Commission of the European Communities. ITSEC { Information Technology Security Evaluation Criteria. Version 1.2, June 1991. [8] G. Czajkowski and T. von Eicken. JRes: A resource accounting interface for Java. ACM SIGPLAN Notices, 33(10):21{35, Oct. 1998. [9] D. Denning. A lattice model of secure information ow. Communications of the ACM, 19(5):236{243, May 1976. [10] J. Dennis and E. VanHorn. Programming semantics for multi-programmed computations. Communications of the ACM, 9(3):236{243, 1966. [11] DOD. Tcsec: Trusted computer system evaluation criteria. Technical Report 5200.28-STD, U.S. Department of Defense, Dec. 1985. [12] B. Ford, M. Hibler, J. Lepreau, P. Tullman, G. Back, and S. Clawson. Microkernels meet recursive virtual machines. In USENIX, editor, 2nd Symposium on Operating Systems Design and Implementation (OSDI '96), October 28{31, 1996. Seattle, WA, pages 137{151, Berkeley, CA, USA, Oct. 1996. USENIX. [13] L. Gong. Java security architecture (JDK 1.2). Technical report, JavaSoft, July 1997. Revision 0.5. [14] L. Gong. Guarding objects. In G. Vigna, editor, Mobile Agents and Security, volume 576 of LNCS, pages 1{23, Berlin, Germany, Aug. 1998. Springer. [15] J. Gosling, B. Joy, and G. L. Steele. The Java Language Speci cation. The Java Series. Addison-Wesley, Reading, MA, USA, 1996.
23
[16] R. Grimm and B. N. Bershad. Security for extensible systems. In Proceedings of 6th Workshop on Hot Topics in Operating Sytems, pages 62{66, Cape Cod, Massachusetts, May 1997. [17] B. Hailpern and H. Ossher. Extending objects to support multiple interfaces and access control. IEEE Transactions on Software Engineering, 16(11):1247{1257, Nov. 1990. Also technical report RC 14016 (#69273), IBM Research Division, T. J. Watson Research Center, March, 1990. [18] G. Hamilton and G. Kougioris. The spring nucleus: a micro-kernel for objects. In Proc. of the Usenix Summer Conference, pages 147{159, Ohio, USA, June 1994. [19] C. Hawblitzel, C.-C. Chang, G. Czajkowski, D. Hu, and T. von Eicken. Implementing Multiple Protection Domains in Java. Technical Report 97-1660, Cornell University, Department of Computer Science, 1997. [20] A. Jones and B. Liskov. A language extension for expressing constraints on data access. Communications of the ACM, 21(5):358{367, May 1978. [21] M. B. Jones. Interposition agents: Transparently interposing user code at the system interface. Technical Report MSR-TR-93-19, Microsoft Research, Redmond, WA, Dec. 1993. [22] Lampson. A note on the con nement problem. In Communications of the ACM, volume 16. 1973. [23] D. B. Lange and M. Oshima. Programming and Deploying Mobile Agents with Java Aglets. Addison-Wesley, Reading, MA, USA, Sept. 1998. [24] X. Leroy and F. Rouaix. Security properties of typed applets. In Conference Record of POPL '98: The 25th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, pages 391{403, San Diego, California, 19{21 Jan. 1998. [25] H. Levy, editor. Capability Based Computer Systems. Digital Press, 1984. [26] S. Lucco, O. Sharp, and R. Wahbe. Omniware: A Universal Substrate for Web Programming. World Wide Web Journal, 1(1):359{368, Dec. 1995. [27] J.-H. Morin. HyperNews: a Hyper{Media Electronic Newspaper based on Agents. In Proceedings of HICSS-31, Hawai International Conference on System Sciences, pages 58{67, Kona, Hawaii, January 1998. [28] G. Muller, E.-N. Volanschi, and R. Marlet. Scaling up partial evaluation for optimizing commercial RPC protocol. ACM SIGPLAN Notices, 32(12):116{??, Dec. 1997. [29] G. C. Necula and P. Lee. Safe, untrusted agents using proof-carrying code. Lecture Notes in Computer Science, 1419:61{??, 1998. [30] G. Nelson, editor. Systems Programming with Modula-3. Prentice-Hall, Englewood Clis, NJ 07632, USA, 1991. [31] J. K. Ousterhout, J. Y. Levy, and B. B. Welch. The Safe-Tcl security model. Lecture Notes in Computer Science, 1419:217{??, 1998. [32] R. Pike, D. Presotto, K. Thompson, H. Trickey, and P. Winterbottom. The use of name spaces in Plan 9. Operating System Review, 27(2):72{76, Apr. 1993. [33] D. Spoonhower, G. Czajkowski, C. Hawblitzel, C.-C. Chang, D. Hu, and T. von Eicken. Design and evaluation of an extensible web & telephony server based on the J-kernel. Technical Report TR98-1715, Cornell University, Computer Science, Nov. 4, 1998. [34] A. Synder. Encapsulation and inheritance in object-oriented programming languages. In ACM, editor, Proc. of Object Oriented Programming, Systems, Languages and Applications, Portland, OR, 1986.
24
[35] A. Tanenbaum, S. Mullender, and R. van Renesse. Using Sparse Capabilities in a Distributed Operating System. In The 6th International Conference on Distributed Computing Systems, pages 558{563. IEEE, May 1986. [36] J. Tardo and L. Valente. Mobile agent security and Telescript. In IEEE CompCon, 1996. [37] D. Tennenhouse. Active networks. In USENIX, editor, 2nd Symposium on Operating Systems Design and Implementation (OSDI '96), October 28{31, 1996. Seattle, WA, pages 89{??, Berkeley, CA, USA, Oct. 1996. USENIX. [38] J. Vitek, C. Bryce, and W. Binder. Designing JavaSeal: or How to make Java safe for agents. In D. Tsichritzis, editor, Electronic Commerce Objects. University of Geneva, 1998. [39] J. Vitek and G. Castagna. Towards a Calculus of Secure Mobile Computations. In Workshop on Internet Programming Languages, Chicago, Ill., May 1998. [40] J. Vitek, M. Serrano, and D. Thanos. Security and communication in mobile object systems. In D. Tsichritzis, editor, Objects at Large. University of Geneva, 1997. [41] J. Vitek, M. Serrano, and D. Thanos. Security and Communication in Mobile Object Systems. In Mobile Object Systems: Towards the Programmable Internet, volume 1222 of Lecture Notes in Computer Science. Springer-Verlag, April 1997. [42] D. Volpano, C. Irvine, and G. Smith. A sound type system for secure ow analysis. Journal of Computer Security, 4:167{187, May 1996. [43] D. Wallach, D. Balfanz, D. Dean, and E. Felten. Extensible Security Architectures for Java. In Proceedings of the 16th Symposium on Operating System Principles, 1997. [44] J. E. White. Telescript technology: The foundation for the electronic marketplace. White paper, General Magic, Inc., 2465 Latham Street, Mountain View, CA 94040, 1994. [45] F. Yellin. Low level security in java. In Fourth International Conference on the World-Wide Web, MIT, Boston, Dec. 1995.
25