Access Protection in a Distributed Smalltalk-System
Andreas Gündel
Informatik V
Universität Dortmund
Postfach 500 500
D-4600 Dortmund 50
(uncp:guendel@exunido)
Forschungsbericht Nr. 271,30.9.1988

Abstract

On the domain of object-oriented programming there has been an evolution from single-user to distributed systems.

Using as example Smalltalk-80, this paper discusses a flexible design of access rights, needed to protect private calculations of involved users from each other and to realize differently privileged access to shared objects.

We describe two strategies of checking access rights: the first one tests incoming messages at a systems boundary, the second one distinguishes between locally and remotely caused processes and tests every message send triggered by the latter ones. Both strategies are fully integrated into the object-oriented environment with each object taking on its own protection against unauthorized access. This results in applications which are easy to survey and which are therefore secure.

An implementation of the second, more general, mechanism called "Subsequent Checkng" and its performance of handling remote activation is presented. Finally we study the possibility of increasing its performance by employing the concept of reliable methods. The realization of the proposed concepts has also profited from the object oriented context. As the only way to access an object is to send it a message, only the corresponding mechanism had to be adopted to the needs of access protection.
 
 
Contents
1 Introduction
2 Related Work
3 Coupling Smalltalk-80
4 Foundations of Access Protection
4.1 Organization of Access-Rights
4.2 Identification of Partners during Access
4.3 Access Protection under Migration
5 Checking Access Rights
5.1 Check at the Systems Boundary
5.2 Checks on all Subsequent Activation
6 Subsequent Checking, its Implementation and Efficiency
7 Discussion
8 Future Research
9 Acknowledgments
References
Footnotes

1 Introduction

Smalltalk-80 [GR83] is an object-oriented programming system, originally developed for stand-alone use on single user workstations.

Direct communication between running Smalltalk-80 systems is not provided for in the language or in the system's description, but there is the possibility of transferring objects to an external representation, which can be saved in a file in the underlying operating system. Another system can read these files and change them back into living objects. During the last few years various attempts have been made to enable direct co-operation between running Smalltalk-80 systems. To achieve this the Smalltalk paradigm, sending messages to objects, was extended. Objects in other systems can be addressed in a similar way as local objects.

An important requisite for the use of communication between Smalltalk-80 systems in practice is a flexible design of access rights. It should be possible here to distinguish between different partners. The data-oriented view "partnerX has no/read only/write access to objectY" is to be replaced by the object-oriented view "partnerX may/may not send messageZ to objectY".

After presenting related work in section 2, section 3 gives a brief review how Smalltalk-80 systems can be coupled. Section 4 investigates methods of applying access rights and briefly deals with the problem of access protection in connection with migration. Section 5 gives the basic ideas for realizing two concepts of checking access rights, while section 6 looks at the implementation of the more general one and its efficiency. A discussion of the achieved framework (section 7) and some objectives of future work (section 8) finish this paper.

2 Related Work

The first extension of the system to be mentioned was done by Vegdahl [Veg86]. The aim of his investigations was the conversion of larger, potentially cyclic object-structures into an external representation, which could later be read in again. The work focuses on the treatment of important special cases (unique objects, global variables). Although this in itself was not a step towards direct communication between running Smalltalk-80 systems, it is one of the foundations of the other investigations, presented here. Decouchant [Dec86] presented a distributed object manager for Smalltalk-80. It realizes direct communication on the level of the virtual machine, i.e. the sending of messages to objects in remote systems, as well as the possibility of transferring objects to other systems (migration). For the mean of access protection Decouchant associates two additional fields with each object. One of them represents read and write capabilities on this object by foreign users. The discrimination between reading and writing is possible viewing access to an object at the level of the virtual machine. The second one is the name of the owner system. There is no way to distinguish between different partners, so all foreign systems have the same rights. It would be very difficult to improve this in a flexible manner at this level. The concept of an encapsulator, presented by Pascoe[Pas86], is the basis for realizing connecting Smalltalk-80 systems on the level of the image. It makes possible to intercept messages to a particular object and to replace or supplement their execution with other message sends. On this basis, at the level of the virtual image similar realizations of the message-send to remote objects were developed by McCullough [McC87],Bennett (Ben87] and in [Gün87]. A so-called proxy object, containing the address of the real object, takes the place of a remote object in the local system. According to Pascoe's principle messages to this proxy object are intercepted and redirected to the real object and the corresponding answer is waited for. More details on these mechanisms are presented in section 3.

John Bennett addresses the problem of "Node Autonomy" by introducing an object called RemoteObjectTable whose entry for an object states whether remote access to it is enabled or not. Again this holds for all partners at the same time, but on image-level this restriction can be easily improved. The mechanism proposed by Bennet nearly complies with the "Check at the systems boundary" described as the less general strategy in section 5.1 of this paper, but it is probably the better concept to let each object take on its own protection as it is done there. A nice idea of Bennett is to support the insertion of "agent objects" to the RemoteObjectTable presenting a message interface to the remote user similar to that of the objects being protected. This can be combined with the mechanisms described here. As Bennett states, his system does not protect certainly one user against shoots of an other.

3 Coupling Smalltalk-80

As a basis for the consideration of access protection in distributed Smalltalk-80 systems this section presents the main concepts of coupling object oriented systems. For reasons of shortness we will not look at (or compare) the single design decisions taken in the papers [McC87, Ben87, Gün87], which do not differ too much in this area. But in opposition to [Dec86] we will consider a coupling at the image-level, an object oriented world, and not at virtual memory level, a world of "dead" data.

The introduction has already set the aim of coupling object oriented systems: it shall be made possible to send messages to objects resident in a remote system. This ought to be done in a transitive manner so that existing methods can handle remote objects (e.g. as parameters) without requiring any changes.

Further requirements are:

  1. the migration of objects (changing their system of residence) has to be possible, at least on demand by the user. An additional property would be system-initiated migration for means of automatic load balancing (transferring objects and processes) or to decrease communication overhead (transferring objects to processes).
  2. a system needs access to the partner's global variables (surely restricted by means of access protection) as a starting point for communication after establishing a connection.
  3. the management of remote references has to ensure that objects referenced by a partner can not be released and has to perform network-wide garbage-collection.
An object oriented concept of coupling Smalltalk-80 systems is based on representing the remote system by an object inside the local system. Therefore an abstract class called RemotePartner is used, whose subclasses define for example the assignment of access rights to the partners their instances will represent as well as the own identification at partners during the connection's establishment. The mechanism used for access protection may also be chosen from different possibilities (as discussed later) in each subclass. A system playing different parts at the same time (e.g. server for someone, client of someone) therefore uses one subclass of RemotePartner for each part it plays. The class RemotePartner itself specifies three important interfaces: the class interface for instance creation and connections management, an instances interfaces to local objects and one to the instance of RemotePartner residing in the partner's system.

To handle remote objects in a transparent manner they have to be represented by local objects (of class RemoteObject) which forward all messages to the object they represent in the remote system. This takes place invisibly for the sender of the message by using the mechanism of encapsulation [Veg86] which can not be presented here in full detail. It is based on the fact that the message doesNotUnderstand: <aMessage is sent to an object, knowing no method for a received message, by the method-lookup mechanism. In our context the instances of RemoteObject do not understand any message3 and implement doesNotUnderstand: <aMessage by forwarding the message along the associated instance of RemotePartner to the requested remote object. The property of not understanding any message is realized by a little hack: RemoteObject is no direct or indirect subclass of Object, as usual Smalltalk objects are, but resides beside it in the class hierarchy. This is easy to achieve in a system like Smalltalk, where the user may manipulate everything.

For readers familiar with the language of Smalltalk-80 we present a part of the class definitions of RemoteObject and RemotePartner.
 
 
nil subclass: #RemoteObject "My instances represent objects in remote systems"
instanceVariableNames:' "They consist of:"
partner "a reference to the object representing the partner"
key' "a key identifying the represented object there"
classVariableNames: ' '
poolDictionaries: ' '
category: 'Kernel-Objects' "Same category as Object"
RemoteObject methodsFor:'message handling' "Focus of this class"
doesNotUnderstand:aMessage "Called up by the virtual machine"
^partnersendMessage:aMessage selector "Forward incoming message with its"
withArguments: aMessage arguments "arguments to the object represented"
toObject: key " by me in the remote system"
Migrate "Transfer the object represented by me to the"
^partnermigrateObject:key " local system"
RemoteObject methodsFor: 'testing' "Some messages understood by remote objects"
" as well as by normal ones. The given example"
" is needed because remote objects forward the"
" 'class' message"
isRemote "Obviously I am a remote object"
^true "Instances of Object answer false"
RemoteObject methodsFor:'private' "Special messages for remote objects"
keyRO "There should be no method for local objects"
^key " in the system with the same selector"
Object subclass: #RemotePartner "My instances represent remote systems"
InstanceVariableNames: ' "They consist of:"
socket "an object managing network communication"
userKey "a key holding the partner's access lights"
lList "a list of local objects references by the partner"
rList "a list of remote objects locally referenced"
"(for garbage collection)"
servingProcess "a process serving the partners requests"
reqPend ' "a list of uncompleted requests"
classVariableNames: ' '
poolDictionaries: ' '
category: 'Coupling-stuff'
"My instances respond to"
RemotePartner methodsFor:'accessing' "a) local requests to the represented partner"
accessGlobal:aString "to create a reference to aglobal's value"
" Similar methods to set a global to an"
" object or to remove it"
RemotePartnermethodsFor:'RO access' "b) requests of remote objects concerning the"
" object represented by them"
migrateObject:rObj "transfer the partner's object identified by the"
" key to the local system"
sendMessage:selwithArguments:argstoObject: key "transmit the specified message to the partner's"
" object identified by key"
RemotePartner methodsFor: 'RemoteMessages' "c) requests from the partner via the network to"
accessRM:aProtoUnit "create a reference to a global variable. Similar"
" methods to set or remove a global"
answerRM:aProtoUnit "an answer from the partner to a previous request"
garbageRM:aProtoUnit "release objects from lList which are not longer"
" referenced by the partner"
migiateRM: aProtoUnit "migrate an object to the partner's system"
sendRM:aProtoUnit "send a message to an object"
terminateRM:aProtoUnit "terminate the connection"
RemotePartner methodsFor:'access control' "d) requests from the previous methods whether"
" they may perform their activity"
mayAccess:aGlobal "Same for mayCreate:,mayRemove: and maySet:"
self subclassResponsibility "Access protection is defined in subclasses"
mayMigrate:anObject
self subclassResponsibility
whatKindOfPerform:obj sel: sel withArgs:args key: aKey "Decides how a remote message send has to be"
self subclassResponsibility " handled. See section 5"
RemotePartner class "The class object itself"
instanceVariableNames:' "I consist of:"
AccPro "a process serving connection requests"
UserTable "characteristics of possible partners"
LocalUser' "characteristics of the part my instances play"
RemotePartner class methodsFor:'initializing'
init "This concerns access protection"
selfsubclassResponsibility "and is managed by my subclasses"
"A typical example (Database server) would be:"
self localUser: 'DBServer' "Symbolic name DBServer"
adr: '1111'. "Waiting on port 1111 for requests"
selfaddUser:'Guendel' "A possible user"
adr: '1112' "using port 1112 for connections"
passwIn:'guendel' "User identifies itself by password"
passwOut:'guendel' "Database identifies itself too"
userKey: 4 "Key for access rights of this user"

To give an idea of the mechanisms in the context briefly considered above to readers which are not familiar with this language we will describe the execution of a remote message-send as an example. By the way we will look at a part of the data structure and methods of RemotePartner's instances. In this example rObj holds a reference to an Array in a remote system, i.e. a reference to a local instance of RemoteObject with instance variables partner (referring to the object representing the remote system) and key (unique in the scope of the connection between the two involved systems, identifying this special Array). In our example there are as yet no established remote references to the components of the Array.

The execution of
 
 
rObj at: 5 results in (Þ )
rObj doesNotUnderstand: <at: 5 Þ
partner sendMessage: #at:
withArguments: (Array with: 5)
toObject: key Þ

The partner sends a ProtocolUnit through the data connection including a key identifying the protocol unit, a message-send request, the object's key, the message-selector and the parameter 5 which, as an instance of SmallInteger, is transferred by value and not as a new reference to an object remote for the partner. Afterwards the triggering activation is blocked and, together with the units key, inserted into a list of pending requests, an instance variable of the object partner. In the partner's system an activation (of that instance of RemotePartner representing there the senders system) waiting at the data connection receives the ProtocolUnit. The local object identified by key is found in the instance variable lList of RemotePartners and the message is forwarded to this object.

(lList at: key) at: 5 After returning, a newKey is computed and inserted to lList together with the result of the above message-send. Through the data connection an answer-ProtocolUnit is sent to the triggering partner including the key identifying the original unit and the result's newKey. Receiving this, a new instance of RemoteObject is created there, which holds the newKey and a reference to the object partner and which is passed to the triggering activation, unblocked now. The message-sends to partner and rObj are thereby finished, their result is the new instance of RemoteObject.
 

4    Foundations of Access Protection


4.1 Organization of Access-Rights

This section describes different organizations of access rights, roughly categorized as follows:

  1. Hierarchical access rights (with the special case of equal or even unlimited rights for all partners).
  2. Property rights (the sets of usable objects of the partners are disjunctive).
  3. Rights are assigned arbitrarily (according to a partner-object table).
  4. Dynamic management (The activation of certain methods can change the rights).
They are all well known from Operation-Systems research, here their employment in object oriented Systems is studied. Using only one of those variants in our context a satisfactory protection mechanism cannot be developed for the following reasons.

In an application with one server and different clients, using a hierarchical classification of rights, protection of simultaneous calculation of different clients against mutual interference is not possible. This is possible only through property rights on objects. However, in their pure form they do not allow access by various partners to commonly needed objects (e.g. class definitions).

The most common case of a static classification of rights is a complete partner-object table according to variant 3. Reading or writing access are typically entered into such a table in systems with passive data. Such a distinction on a higher level is not possible in an object-oriented system like Smalltalk. Here, the smallest unit of "object-processing" is the method's activation of an object, i.e. a message-send. Whether a message changes ("writes") the object, depends on the function of the primitives called up. If rights to "read" or "write" an object are conferred independently, the existing primitives can be classified accordingly. Checks can be limited to their call-up. This method has the grave disadvantage of classifying rights on a very low level, which can be compensated by instead entering in the table all those methods which the partner may activate. The effort this needs prevents the exclusive use of this very flexible administration of access rights.

Variant 4 is an extension of this concept, enabling the dynamic change (most of the time it will be an extension) of access rights, if the partner uses methods which are known to be reliable relative to undesirable subsequent access. After executing the method the old access rights are valid once more. Gehringer [Geh82] gives a comprehensive introduction into the use of dynamic access rights in object oriented systems. He also presents existing systems, which use capabilities. [LSW78] gives a short presentation of an implementation of an efficient capability mechanism in surroundings with small objects.

To summarize: on their own, variants I and 2 are not flexible enough, while variants 3 and 4 often demand too much effort, because of the high number of (small) objects and their frequent creation and release in the Smalltalk system. The aim is now to use the flexibility of variant 3 where it is necessary and otherwise to use the smaller effort of the first two possibilities. Afterwards we will consider dynamic changes in the resulting concept. In any system there will be many objects whose protection does not depend on the respective partner, but only on the hierarchical value of his access rights. The corresponding column in a table of access rights would contain considerable redundancies, especially in those cases where either no partner or all partners may access an object by sending defined methods. If this is true for all objects of a class (e.g. numbers or texts), all columns of a table with objects of the same class will show the same value. This observation promotes a class specific organization of access protection.

If the protective measures are strict enough for the objects (e.g. database elements) which can be accessed directly, all other objects do not need to be protected explicitly. The protection mechanism must then ensure that a remote partner does not gain references, via initially available objects, to objects which ought to be preserved, but which are not explicitly protected.

Access to an object by only one partner is another special case for the entry in a column in an access table. This will frequently be the case with objects on the level of applications, as shown in variant 2. In such cases the entry in the table can easily be replaced by an owner's field for this objects, defined in application-specific classes.

By utilizing these special cases (hierarchy, property) and the protection of objects, implied by their accessibility, the largest part of the table can be left out without loss of flexibility. The rest could either be contained in a list of accessible objects, referring to a partner, or in a list of legitimate partners, referring to an object.

Further, consideration has to be given to the possibilities of integrating dynamic changes (extensions) of access rights during the activation of reliable methods. Database elements have already been used above as an example: objects representing the components of these elements are implicitly protected if they can be reached only via reliable methods of the above elements.

In the section on the checking of access rights (see 5) we will look at a variation, based solely on this method:

from outside, the partner has only the right to activate reliable methods

afterwards, the subsequent actions have unlimited access rights

A partial extension of a partner's rights can also be realized. For this, a reliable method can either extend the access key of a partner or lengthen the list of objects he can reach. This presupposes a partner who cannot change methods and who is thus unable to extend his access rights uncontrollably.

4.2 Identification of Partners during Access

If a system receives a message from outside, the sender must be identified and his access attributes must be attached to him.

There are two possibilities of determining a partner:

  1. The system, from which the message was sent, is used.
  2. An attempt is made to identify the original origin of the message.
This difference will be explained with an example.

If system A activates a method in partner B and if the execution of this method leads to the activation of an object in system C, then in case

  1. the rights of system B
  2. the rights of system A
will be used.

In the special case of system A and system C being the same, solution I can result in a system preventing access to one of its own objects, if it is accessed via system B. Nevertheless, this solution must be used, because there is no possibility of preventing system B from pretending that the activation was the result of a previous message-send from system A.

4.3 Access Protection under Migration

The question of whether the set of partners which have access to an object can be maintained after the migration of this object to another system and how to do this is part of the considerations on access protection. It can be shown that this aim cannot be reached by a mechanism which uses hierarchical rights and in addition allows only one owner per object. In a system of access protection, working with a list of entitled partners for each object, it is possible with the following simple procedure. During a migration a reference object is created in the old system of residence. This object "inherits" the list of entitled partners of the migrated object. In the new system of residence the list of the migrated object is created, extending by the code of the previous system of residence the list of the reference object which triggered the migration and which is afterwards replaced by the transferred object.

The question is undecided whether it is possible to achieve that the partners retain their access rights to objects which could be reached via the migrated object and how to do this. An example of this case is the migration of an Array whose components may remain at the previous system of residence. If a system tries to access one of these components via the migrated object) e.g. by executing (thisArray at: 4) printString, sending of the message printString is routed via the Array's new system of residence. Therefore, the access rights of this system concerning the component object are needed, as we have seen in section 4.2, and it is obviously not possible to make general statements about these. In order to solve this problem a migration should be allowed only if the system of destination possesses access rights for all (indirectly) referenced objects, which will be addressed by other partners in this way. In practice, large-scale checks are necessary in order to fulfill this demand.

5    Checking Access Rights

Protection against unauthorized access via a distant message-send can take one of two forms:
  1. Check each received message once at the systems boundary before the message is transmitted to the addressed object.
  2. Distinguish between locally and remotely caused activation and check the access rights of the triggering partner in respect to each object to which a message is sent during the execution of the latter.
5.1 Check at the Systems Boundary

This mechanism is the application of a special case of the theory of dynamically adjustable access rights, presented in section 4.1. In that case, all methods which could be activated by a partner were regarded as reliable, extending a partner's rights to all objects. From the point of efficiency this realization offers greater freedom than the second one, because only one check of access rights takes place per remote message-send. Further, protection design is therefore flexible, because it takes place on Smalltalk level, before the message is transmitted to the local message-send mechanism.

As a common frame of protection against unauthorized access each object takes on its own protection in a special method:

remoteMessageReceived: message In this context the communication mechanism has the sole task of changing the parameters and the destination object of the message received into local objects and to send the latter the above message.

The method remoteMessageReceived: can be implemented specifically according to the class. Its use will be demonstrated with the help of an example, in which a Smalltalk system offers the use of a database to its partners. They are prevented from activating any other object or any method which does not belong to the user-interface of the database. In order to achieve this, all remote access is forbidden in the class Object. This also applies for all subclasses of Object, if they do not implement the method themselves:

remoteMessageReceived: message
    ^nil "or a special object marking errors"
In the class DataBaseService all methods which may be activated from outside can be marked with their name. In the example the distant activation of a method of this class is allowed only if the selector contains the given part of the name. remoteMessageReceived: message (message selector includes: 'DBS') ifTrue: [^self perform: message selector

withArguments: message arguments ]

if False:

[^nil] "or a special object marking errors"

If a partner oriented protection of the objects of a database is required as well, the local communication mechanism will transmit a recognition code of the triggering partner in addition to the message. Then, the more general form of the method remoteMessageReceived: is: remoteMessageReceived: message fromPartner: partner 5.2 Checks on all Subsequent Activation

As described above, access protection at the systems boundary is easy to realize, because it takes place on the Smalltalk level. There is the disadvantage however, that every method of accessible objects has to be checked relative to its ability of accessing, via subsequent activation, other objects, which should really be protected.

Therefore, access protection has to be very restrictive at the systems boundary between the reception of a message from a network and its transmission to the activated local object, as for example the method remoteMessageReceived: in the class Object. It was pointed out in the previous subsection that there are applications, which need access to only a few objects or to instances of few classes in a partner. The simpler protection mechanism will then be quite sufficient.

We will here consider feasible access protection, which allows checks on the activation of an object through partner systems, independently of whether a partner addresses it directly or whether the activation takes place via another object. This cannot be realized completely on a Smalltalk level, because at least the differentiation between a locally and a remotely caused activation must take place with each message-send. For efficiency reasons this can be done only inside the virtual machine.

After receiving a message from the network, it is the task of the communication mechanism to start a Smalltalk process, marked as "remotely caused". The created process will then send the message received to the object addressed. The pattern for dealing with a message-send in the machine is thus:

  1. Check whether the process is remotely caused, else Þ 3.
  2. Form the message remoteMessageReceived: <message.
  3. Handle the message as usual.
For efficiency reasons not every activation of a method should be treated in this way. Messages from an object to itself are executed without protection, thereby preventing a cyclical call-up of the protecting method. This does not comply with the ideas about the dynamic extension of rights when reliable methods are called up! The rights of a partner, concerning subsequent activation, are extended to all methods of the object addressed, but only if these methods were called up directly, without detours via other objects. With this extension the pattern inside the machine is as follows:
  1. Check whether the process is remotely caused, else Þ 4.
  2. Check whether the message originates from another object, i.e. whether it was not addressed to self or super. Else Þ 4.
  3. Form the message remoteMessageReceived: <message,
  4. Handle the message as usual.
The effort for locally caused processes is negligibly small, as only one additional test (remotely caused?) is required on machine level.

The above examples can be used for the implementation of the method remoteMessageReceived:, with the difference that the activating partner can be found out via the active process.

Possible subsequent activation of other objects need not be considered here (they are checked automatically). Only the method become:, calling up the primitive that belongs to it, must additionally check the access right of a process to its parameter, as it is the only method of a system which changes an object without sending it a message.
 
 

6 Subsequent Checking, its Implementation and Efficiency

In section 5.2 a mechanism was proposed which realizes access protection for all subsequent activation of a remote message-send. As stated there, for efficiency reasons the differentiation between locally- and remotely caused activation has to take place inside the virtual machine. Detecting the latter case a Smalltalk method is called up, which checks the desired access rights and, if this is successful, activates the original message. So the only thing the RemotePartner, known from section 3, has to do after receiving a message from outside, is to create a new process for its execution and to set the effAccessKey and realAccessKey of this process to the sender's parameter.

The basis for the realization is the extension of the class Process by the two instance variables effAccessKey and realAccessKey, which contain either the rights of the partner or (for locally caused processes) the value nil. With each activation of an object, with the exception of self, super and instances of SmallInteger, the virtual machine carries out a check between effAccessKey and nil. If the two do not correspond, a Smalltalk method is called up to check the user rights. Before that, effAccessKey is set to nil to prevent further, recursive call-ups during its execution. The Smalltalk method takes the rights of the user from realAccessKey and copies it back into the effAccessKey before transmitting the desired activation.

During the extension great importance was attached to the capability of the virtual machine to handle images, not modified for the inclusion of remote systems and which therefore do not possess additional instance variables for the class Process. They are accessed, as described above, only if the user explicitly demands it. The control for this is realized with the help of the global variable selectorProtectMessage in the virtual machine. It is initialized to nil at the start of the machine and contains the selector of the Smalltalk method that is to be called up when access protection is "switched on". A primitive has been implemented to change this variable, its parameter giving the new value. Activating the primitive by sending the message Process protectMessage: #remoteAccess: switches the check on and makes the method remoteAccess:, defined in an object's class or superclasses, responsible for its protection against unauthorized access. Sending Process protectMessage: nil switches off the check. The sending of the method given by selectorProtectMessage to the addressed object was realized on analogy of the call-up of doesNotUnderstand:, the desired message is similarly transmitted as a parameter.

To investigate the efficiency problems, the Smalltalk-80 standard benchmarks were called up by a process, running under access protection (i.e. effAccessKey ~= nil), but having unlimited rights. The method remoteAccess: used for these benchmarks is presented with some simplifications.
 
 
Object methodsFor:'access protection' "The method given below typically is valid"
" for all objects in the system, while"
" accessABowed:arguments:key: is class specific"
remoteAccess: aMessage "called up by the virtual machine with"
" effAccessKey = nil. Thereby this method"
" itself is not executed under access protection"
| sel args key |
key := Processor activeProcessrealAccessKey. "the access key of the process"
sel := aMessage selector. "the selector of the desired activation"
ugs := aMessagearguments. "the necessary parameters"
argsisNilifTrue:[args := Array new: 0].
selisPerform "if the selector is contained in the set perform:,"
ifTrue: " perform:with:, ... , perform:withArguments:"
[sel := argsselectorToPerform. "Fetch the selector and arguments"
args := args argumentsToPerform] " of the message to be performed"
(self "For class specific access protection"
accessAllowed:sel "During the benchmarks the result has always"
arguments: args " been true"
key: key)
ifTrue: "if the message send is allowed"
[Processor activeProcesseffAccessKey: key. "reactivate access protection"
^self perform: sel withArguments: args] "execute the desired activation"
ifFalse: "otherwise"
[key partner error: key identarg:'...' "notify the remote partner"
Processor activeProcess terminate] "abort the process"

In order to investigate the efficiency of the mechanism the benchmarks were carried out in four different configurations of the system

  1. with the basic, virtual machine without extensions
  2. with the extended machine, where selectorProtectMessage had the value nil, i.e. it did not access the effAccessKey of the process
  3. with selectorProtectMessage equal to remoteAccess:, but effAccessKey equal to nil, therefore the method was not called up
  4. like 3., but effAccessKey unequal nil, i.e. access-rights were checked
No difference in efficiency could be measured between the first three variants: the extension fulfilled the demand of not interfering with the execution of local activation.

The measuring was done using methods of the standard class Benchmark. It includes two kinds of benchmarks: micro-benchmarks testing the performance of & small basic activity and macro-benchmarks evaluating typical user actions as textformatting and compilation. In the former category the loss of efficiency running under access protection differs very much between the single benchmarks. No message send actions, like loading an instance variable, and message sends to instances of class SmallInteger which are not protected for efficiency reasons are obviously not effected. Message sends invoking a primitive of instances of other classes are seriously slowed down depending on the primitive's execution time compared to the constant time needed for access protection by the method remoteAccess:. Here the factor measuring the loss of efficiency lies between 12 for a time consuming floating point addition and 54 for simply asking a string for its size.

A more realistic evaluation is given by the macro-benchmarks listed in the table below. It contains the standard name of the benchmark, its performance in situations I to 3, the performance running under access protection (situation 4) and the factor measuring the loss of efficiency. For reasons of lucidity this factor is given as quotient of the lower and the higher performance, telling that running without access protection is x-times as efficient as running under access protection. Calculating the other way round results in values of 0.xxx which are hard to read for humans. The table ends with the overall performance rating calculated by Smalltalk-80 as geometrical mean of the benchmarks.
 
 
Benchmark 1-3 4 factor
BitBLT 39.12 35.00 1.12
TextScanning 195.00 111.43 1.75
ClassOrganizer 69.54 4.69 14.82
PrintDefinition 68.55 4.67 14.68
PrintHierachy 76.92 10.87 7.08
AllCallsOn 44.12 7.98 5.53
AllImplementors 50.78 7.16 7.09
Inspect 79.82 11.21 7.12
Compiler 57.47 7.05 8.15
Decompiler 74.70 8.84 8.45
KeyboardLookAhead 118.06 13.89 8.50
KeyboardSingle 167.69 19.06 8.80
TextDisplay 46.38 8.89 5.22
TextFormatting 191.67 15.84 12.10
TextEditing 99.53 12.10 8.23
Performance Rating 80.36 12.07 6.66

Table: Efficiency of access protection

By the way: running the benchmarks with a variation of the method remoteAccess: has shown that it is called up 322672 times. The resulting loss of the overall performance (factor 6.66) can be tolerated for remote activation, whose time for executions is not too large. Particularly complex remote calculations demand more efficient methods, like the execution of reliable methods without checking their subsequent activation. In order to do so, in the related method remoteAccess: the effAccessKey has to be reset to key after transmitting the message to the object, as stated there in a footnote. Therefore the result of the message accessAllowed:arguments:key: has to take one of three forms: reject the message, execute it under subsequent access protection or consider it as reliable. This classification strongly depends on the desired application as the resulting performance does.
 

7 Discussion


In the previous sections we have seen different concepts of access protection in the context of object oriented systems as well as the realization of the most general one in Smalltalk. We now have to discuss how easy to use they are and whether the resulting system is secure against unauthorized access or not.

Therefore it has to be mentioned that the effort of establishing access rights (and, as a consequence, the resulting security) strongly depends on the desired application. If remote users may only access instances of a small number of (typically user defined) classes, it is not hard to manage the corresponding access rights, using the presented mechanisms. Their structure (each object keeps track of its own protection in a class specific way) then allows an evolution of access protection parallel to the evolution of the application itself. The advantage of object oriented programming for constructing large software systems communicates itself to an easy management of access rights, especially if the latter is integrated into the application development from the beginning. This will result in a system that is easy to survey and which is therefore secure, if attention is paid to some Smalltalk peculiarities. The realization of the proposed concepts has also taken profit of the object oriented context. As the only way to access an object is to send it a message, only the corresponding mechanism had to be adopted to the needs of access protection.

It is of course much more difficult to give remote users controlled access to some of the objects making up the Smalltalk system. According to the previous discussion the development process of this system therefore has to be followed or the internal structure has to be fully reflected at least. An example of this is to allow remote debugging: it has to be studied what special classes are involved, what messages to objects of any class are needed and how their use can be restricted to the means of debugging. Changes of methods or instance variables which may affect further access protection have to be forbidden. As debugging is typically used during the phase of software development and not by users of the software it will take place under control of one (or a team of) developer(s). No access protection is thus needed here at all; the usual context for the employment of the presented mechanisms will be the one described in the previous paragraph.

As mentioned before, some Smalltalk peculiarities have to be taken into consideration, for example the method SmallInteger asObject which results in a reference to an object if the receiver arbitrarily is a valid object table entry or the method ClassXYZ allInstances. These may evade implicit access protection which only handles objects that can be reached from initially available ones. Remote activation of these methods therefore has to be forbidden in this context. The method object become: anotherObject is another peculiarity; its handling has been described at the end of section 5.2.

8 Future Research

The next tasks to be tackled are:

Concerning the last point: One of the strong points of Smalltalk-80, the adaptability of the whole system to the requirements of the user, has disadvantages, too. The user can hardly protect himself against erroneous destruction of the system. With the extension of the virtual machine by the management of protection mechanisms, locally different access rights could in principle be given to the users of the system. The demands on the efficiency of the protected system are much higher: therefore, checking each message-send (except those to self and super) by calling up a Smalltalk method would need too much effort. A boundary between the direct actions of the user (mouse movements, keyboard) and the actions instigated, where rights can be checked, must therefore be found. This pattern would hardly influence the efficiency of the system, but its feasibility will have to be checked.

9 Acknowledgments

My sincere gratitude goes to Professor Ganzinger for supervising my master's thesis and for suggesting the writing of this article as well as to Georg Heeg for his helpful criticism.

The prerequisite for embedding parts of the access protection mechanism into the virtual machine was the availability of its source-code. This was the case because a machine of the Xerox-Corporation was adapted to other computers at Dortmund University. I feel obliged to the Xerox-Corporation for making the source available. Hans-Martin Mosner, a member of the adaptation-team, kindly helped with the embedding, a short but difficult task for the uninitiated. Thomas Birke helped to prepare this text.

References
 
 
[Ben87] J.Bennett. The Design and Implementation of Distributed Smalltalk. In Proceedings of the OOPSLA'87 conference, pp. 318-330, Orlando, Florida, 1987.
[Dec86] D.Decouchant. Design of a Distributed Object Manager for the Smalltalk-80 System. In Proceedings of the OOPSLA'86 conference, pp. 444-452, Portland, Oregon, 1986.
[Geh82] E.F.Gehringer. Capability architectures and small objects. Ann Arbor, Michigan, UMI Research Press, 1982.
[GR83] A. Goldberg and D. Robson.Smalltalk-80: The Language and its Implementation. Addison-Wesley, Reading, MA, 1983.
[Gün87] A. Gündel. Coupling of Object-Oriented Systems by the Example of Smalltalk-80. Master's thesis, University of Dortmund, 1987. Language: German.
[LSW78] D. Lanciaux, L. Schiller, and W.A. Wulf. Supporting Small Objects in a Capability System. Pittsburgh, Pennsylvania, Carnegic-Mellon University, 1978.
[McC87] P. McCullough. Transparent Forwarding: First Steps. In Proceedings of the OOPSLA'87 conference, pp. 331-341, Orlando, Florida, 1987.
[Pas86] G.A.Pascoe.Encapsulators: A New Software Paradigm in Smalltalk-80. In Proceedings of the OOPSLA'86 conference, pp. 341-346, Portland, Oregon, 1986.
[Veg86] S.R.Vegdahl. Moving Structures between Smalltalk Images. In Proceedings of the OOPSLA'86 conference, pp. 466-471, Portland, Oregon, 1986.

Footnotes

  1.   Smalltalk-80 is a trademark of ParcPlace Systems, Inc.
  2.  all Smalltalk-80 terms are printed boldface in this paper
  3.  excluding those for their management, made disjunctive by special names including the pattern "RO"
  4.  here we assume that the result does not belong to a class whose instances are transferred by value
  5.  the letter mechanism could be called "multiple ownership"
  6.  the order of the last two actions may be changed under some circumstances: send the message first and reset the key after its completion. See below.