Telescript is a software technology for building distributed applications using mobile agents [Wh95a, Wh95b, STAN95, IBM95a]. Telescript extends the concept of remote programming, the ability to upload and execute programs to a remote processor, with migrating processes. A Telescript mobile agent is a migrating process that is able to move autonomously during its execution to a different processor and continue executing there. Mobile agents conceptually move the client to the server, where stationary processes, or places, service their requests. When it is done in a place, an agent might choose to move itself to a different processor, carry results back to where it originated, or simply terminate.
Clearly, security is a major concern in this scenario. The operator of a Telescript processor wants some assurance that nothing bad will come of its decision to admit an incoming agent. The host platform wants to know who is responsible for the agent. The agent, on the other hand, would like to trust that private information it is carrying will not be disclosed arbitrarily. It needs to trust the operator of the platform. The server application responsible for the place might also like to set its own policies.
Telescript provides the necessary security through a combination of mechanisms:
This document is organized as follows. The next section gives a brief introduction to Telescript language and network concepts. The following section introduces general safety and security concepts and terminology. The next four sections describe each of the above mechanism groups, in turn. The last section gives direction for future security work.
Features may be public or private. In contrast to the object models in some other languages, Telescript's private features are visible in sub-classes as well as in the base class. Features, or entire classes, can be sealed so that they cannot be overridden in sub-classes or sub-classed, respectively. Telescript does not permit incompatible signatures (feature overloading) in sub-classes.
The Telescript language reference specifies a number of predefined classes. These must be supported in every implementation of a Telescript interpreter, or engine.
Objects are always instances of some class, and always inherit from the class Object, which is at the top of the object hierarchy. An object encapsulates its properties, specified by the class and directly accessible only to code "inside" the object (and not to sub-classes). Properties reference other objects. Agents and places are also objects, instances of various sub-classes of the predefined abstract class Process.
Process objects provide Telescipt's multi-tasking functionality. Processes are pre-emptively multi-tasked, and scheduled according to priority. Process classes have a live operation which is invoked directly by the engine, on a new thread of control, to animate new process objects.
All objects must be owned by at most one process. Objects that are not owned are subject to being garbage collected. A process "owns" itself. A process can transfer ownership of an object to another process, provided it owns the object and all objects referenced by that object's properties, and, recursively, all objects referenced by objects in that object's closure.
The Agent class, a sub-class of Process, provides the sealed, private go operation. An agent can request the go operation on itself, providing a ticket argument. The ticket object describes the place where the agent is trying to go, with varying levels of specificity. If the engine can figure out where and how to route the agent, and the trip is ultimately successful, the agent "wakes up" executing its next line of code in the destination place.
Agent processes always execute in the context of one or more enclosing place processes. Places usually provide a service API for agents to interact with. Places can be nested within other places, and every engine has an engine place as its outermost place. As one of the last steps in processing an agent's go, the destination engine requests the entering operation on the destination place to give that place the opportunity to deny occupancy. If no place satisfying the ticket will admit the agent, the trip fails with a trip exception.
Telescript engines from General Magic include, in addition to the Telescript interpreter, supporting runtime components such as additional built-in classes, facilities for object persistence, and communications functions for moving agents around. A Telescript engine thus has a number of significant differences from other, more conventional "load and execute" remote programming runtime environments, for example, Java [ JAVA95a], Safe-Tcl [ SAFE-TCL], or Oblique [ OBLIQUE]. A Telescript engine needs to support a multi-user, multi-process operating environment, not unlike a mini operating system unto itself.
A Telescript network consists basically of a set of interconnected Telescript engines. Telescript imposes no limit on the particular network technologies that might be used to transfer encoded agents between engines. Currently, engines from General Magic support agent transfer over TCP/IP, as well as using UDP over PPP for dialup links. An engine provides the platform-specific code that enables agent portability.
Each process has a name, an instance of class Telename, that has two OctetString-valued attributes, its authority and its identity. An authority uniquely identifies a Telescript user. If two processes have Telenames with the same authority, they are running on behalf of the same user.
An engine place runs under what is known as the region authority, analogous to the root account on a UNIX(tm) system. A region can consist of multiple engines with engine places running under the same authority. An agent may travel between places on a single engine, on different engines in the same region, or to an engine in a different region. For an agent to travel between engines, it must be serialized by the sending engine's encoder function, and decoded by the destination engine. The encoding process takes care of encoding the agent's current runtime state, including in it all objects owned by that agent, along with all referenced classes, unless a class is built-in or otherwise known to already exist at the destination. An agent travels self-contained, so to speak.
Anybody who wants to can put up a region. A Telescript network potentially includes regions that not everyone particularly trusts, and which certain agents might want to avoid.
A typical C program can count on relatively few safety guarantees. Only extreme diligence by the programmer catches hazards like accessing uninitialized pointers, referencing discarded objects, referencing beyond the end of an array, pointer arithmetic errors, or copying in a string that is longer than the destination buffer. Fortunately, the damage that such programs do is usually limited by the operating system [DAG95]. However, such hazards in critical programs, such as mail daemons, represent major opportunities for system penetrators.
There is significant conceptual overlap between safety and security. For example, Telescript uses encapsulation, which is usually considered a safety mechanism, as a protection mechanism.
Very little has been written on general techniques for protecting against attacks such as Trojan horses [KAR87]. Generally, people are justifiably reluctant to let anyone run whatever programs they wish on their systems, for fear of viruses or worms, for example. One common approach is to run untrusted programs in a "safe" or restricted environment. This is used, for example, by the Safe-Tcl interpreter, which has the "dangerous" primitives of Tcl removed [ BOR93]. It is also used by the HotJava(tm) class loader [ JAVA95b], which partitions the runtime environment into different namespaces, with "safer" built-in classes available in less-trusted namespaces. Telescript also provides a form of restricted environment, using places and local permits.
Much of Telescript's security is in the form of features whose use is discretionary. Security features permit a place or agent to protect itself against untrusted (and potentially malicious) agents or places. Programs can defend themselves, but they don't necessarily have to. Some Telescript security features are specified in the basic language. Others, especially relating to specific security policies, are implemented by Telescript applications themselves.
The remainder of this document describes the particular safety and security features of Telescript.
All Telescript engines provide:
Basically, all interprocess access is mediated by the Telescript engine. As with a domain (rings of protection) machine, the Telescript engine is like an operation system kernel, isolated and protected from the programs running on it. The engine may be considered part of a "trusted computing base" (TCB) that provides mechanisms for controlled sharing and safe process interaction[TCSEC, SALZ75].
All Telescript engines provide:
A protected reference to an object can be created at any time using the protect operation, which is sealed and defined on the class Object.
By providing public operation wrappers that make access checks around the private features that actually perform the function, programmers can take advantage of encapsulation to implement their own access controls. Telescript provides different ways for identifying the authority and class of the caller (i.e., the requester, or client, from the point of view of the executing code) that are useful in making identity-based access checks. These are obtained directly from the engine in global variables, and include:
Here, hazel and bigwig are both agents that have entered theWarren. Hazel gets a reference to bigwig's Trojan object and naively requests the foo operation on it. However, Trojan, now in control on hazel's thread, tries to get service from theWarren attempting to masquerade as hazel. Bigwig might get away with it if theCarrots did access control checks on the basis of process alone.
The following code fragment provides an example of making an access check:
MyAccessViolation: class(Exception) = (); MySafeClass : class ( Object ) = ( public mySafeOp:op ( theArg: Object ) = {
theSponsor:OctetString = sponsor.name.authority;
theClass:Class = client.class; if (friends.examine(theSponsor) == nil) || (okClasses.examine(theClass) == nil) ) {
throw MyAccessViolation() ; };
myRealOp ( theArg ); // could check the arg, too...
}; private friends: List[OctetString,Equal] = ... ; okClasses: List[Class,Equal] = ... ; myRealOp:op (argument: Object) = { ... }; );
Here, the program maintains a simple private list of references to authorized classes along with a list of authorized authorities to implement an access policy that checks the class and authority of both the requester and the current sponsor (process). It's not expecting a call from a Trojan object .
Had foo been a sponsored operation, "sponsorship" would have passed by the call, so that mySafeOp would sense the sponsor as bigwig. Hazel's class could have been written knowing that foo has to be sponsored, owing to the fact that Trojan was sub-classed from a class where foo was sponsored.
Note that, since Telescript always provides unprotected references to the requester, process, place, and so forth, it is important for agents and places to always make the appropriate access control checks.
Also, attribute features are not simply "passive," but are always implemented using getter and setter operations. Normally, most class definitions simply use the default getters and setters. However, unless sealed, nothing prevents overriding, for example, to perform access control checks. Unfortunately, an untrusted sub-class could put malicious code in there, too.
With regard to installing bogus classes, the Telescript engine won't admit an agent carrying a class that has the same name as one that's already in the engine, unless it's the same class. In other words, within an engine, class names must be unique. Without going into excessive detail, new classes (the ones normally used by the engine during class search) are "installed" in what is known as the process's public and private packages . These private process attributes are collections of "write once" objects, so that only a process can add new class packages to itself . The engine searches for a class either in the private packages of the process instantiating the new object, or in the public packages of that process and its enclosing places. Usually, class definitions are found in the engine place.
Telescript uses four kinds of permits:
The effective permit is computed by the engine by the logical intersection of all applicable permits in a particular situation. For example, in the case of a Boolean-valued capability, the effective permit value is true only if that capability is true in all permits intersected. For an integer-valued attribute, the effective permit value is the minimum specified in any of the permits intersected.
Temporary permits are useful for bounding the damage that suspect code might do, as the following illustrates:
paranoid := Permit(); // create a new instance paranoid.canCharge = false; // No capabilitiesIf the suspect call fails because of a permit violation, the engine raises a Permit Violated exception that can be caught by the calling procedure. If any of the native, local, or regional permits is violated, the engine terminates the process with a Permit Exhausted exception.
paranoid.canCreate = false;
paranoid.canDeny = false;
paranoid.canGo = false;
paranoid.canGrant = false;
paranoid.canRestart = false;
paranoid.canSend = false;
paranoid.age = *.age + 2; // give it two seconds
paranoid.extent = *.size + 1000; paranoid.charges = *.charges + 1000;
try { restrict paranoid { // restrict statement yourObject.yourSuspectCall(); // the suspect call }; catch failed: PermitViolated { ... }; catch ... // other catch clauses
Note that is yourSuspectCall were sponsored, it would be executed under yourObject's permit. Not only would yourObject's owner be charged for resources consumed, but the paranoid permit would not be in effect during that call, nor be needed.
Here's an illustration:
MyParanoidPlace : class ( Place ) = ( public ... // initialize, live, etc. entering: sponsored op( who: protected Telename; theClass: Class; permit: unprotected Permit; // intersection of native, regional, requested ticket: protected Ticket | Nil) Object throws DestinationUnknown, OccupancyDenied = { *.myRestrictEntry(who.copy(), theClass); // local access check // throws OccupancyDenied for us // we'll let this agent in, but with restrictions... try { // could fail in subtle ways permit.canCreate = false; // can't make new agents in here.
permit.canSend = false; // we will let it go out eventually permit.canRestart = false; permit.canCharge = false; // nada... permit.canDeny = false; permit.canGrant = false; permit.age = 120; // live free for two minutes or die! if ((permit.priority == nil)||(permit.priority > *.priority)) { permit.priority = *.priority - 5 ; // make sure lower priority };
permit.authenticity = *.myAuthPolicy(who, class, permit); // set how much we trust this guy // don't care about limiting size or charges
return(nil); } catch Exception { throw OccupancyDenied; // that's it, no fooling around. }; }; private myRestrictEntry: op(name: Telename; theClass: Class) throws OccupancyDenied = { ... }; ... // other stuff here... );
Entering is a sponsored operation that is called by the engine. Inside the entering operation, client evaluates to nil.
Analogous to entering, the engine mediates a meeting protocol between two agents (of classes that have the built-in mix-in Meeting Agent) that gives them the opportunity to accept or reject a meeting, according to their own policy. Agents in a place can't discover the other occupants on their own, but can find out from a cooperating place. Meeting agents meet in places whose classes have the built-in mix-in Meeting Place. Consult the language reference manual for specifics.
An example of a restricted place, normally used in production Telescript regions, is a place called purgatory. The engine place can be programmed to route incoming agents whose trips fail to purgatory. On entering, agents are given a very restricted permit with a relatively short time to live. The intent is to enable them to catch a trip exception and recover on their own.
The engine requires that there be an appropriate Copyright Enforcer for these classes in a region process on the package search path. Usually, the Copyright Enforcer is located in the engine place. This prevents an arbitrary agent from merely supplying its own Copyright Enforcer. Each of these Copyright Enforcers is looked up under a different key, so that different policies can be imposed by the region for each class.
Unauthorized processes, or processes that are not running under the region's authority, cannot create instances of the following classes:
Processes running as region can, for example, modify the regional permit of any process, or instantiate a Control Manager. The default Copyright Enforcers in the engine place usually authorize only the region to create system sensitive objects.
A security regime specifies a set of security services that can be negotiated over a secure channel. Currently, engines only have security regimes for secure communication with Magic Cap communicators over the ATT PersonaLink service. More security regimes can be defined as they are needed.
The simplest security regime is the Identification regime, which only exchanges the authority information without any other protection. The Authentication regime, on the other hand, requires strong mutual authentication using RSA public key encryption [ PKCS], session key negotiation using the Diffie-Hellman algorithm with perfect forward security [DIFFIE], and session encryption using RC4. Export quality encryption is used (e.g., 512-bit RSA keys, 40-bit RC4 keys).
An asymmetric protocol is used in the Authentication regime that permits a device to authenticate using an RSA public key pair that it generated during its initialization. The region uses a public key certificate based approach used to authenticate itself to a device. The certificate, in essence, associates the region's authority with its public key, and must be digitally signed (using the RSA algorithm) by one of the General Magic keys. The corresponding public keys are included in all devices.
Magic Cap devices register their key (and other information, for example, a credit card number) with the ATT PersonaLink service using the Registration security regime. Registration uses session encryption, but doesn't demand a pre-existing relationship with the device as the Authentication regime would. Region policies control which agents are allowed in during registration, for example.
Future versions based on work currently in progress may incorporate: