Resource Access and Mobility Control with Dynamic ... - CiteSeerX

4 downloads 0 Views 434KB Size Report
Oct 23, 2003 - We present the language and a capability-based type system that ... Global Computing initiative, project MIKADO IST-2001-32222, and by ...
Resource Access and Mobility Control with Dynamic Capabilities Acquisition∗ Daniele Gorla Rosario Pugliese Dipartimento di Sistemi e Informatica, Universit`a di Firenze e-mail: {gorla,pugliese}@dsi.unifi.it October 23, 2003

Abstract We introduce a process language that permits modelling networks of immobile and mobile components, i.e. nodes and processes, interacting through multiple distributed tuple spaces. We present the language and a capability-based type system that enables specification and dynamic modification of security policies for controlling processes activities (namely mobility of code, creation of and access to resources). We exploit a combination of static and dynamic type checking, and of in-lined reference monitoring, to guarantee absence of run-time errors due to lack of capabilities and prove two type soundness results: one involves whole nets, the other is relative to subnets of larger nets. We illustrate the usefulness of our framework by using it for implementing a simplified but significant e-commerce scenario. Finally, we show how the framework can be easily tailored for dealing with different forms of capability acquisition and loss, thus enabling dynamic variations of security policies both by immobile and mobile components.

Keywords: Process Calculi, Code Mobility, Type Systems, Resource Access Control, Mobility Control

∗ This paper is an extended and revised version of [20]. This work has been partially supported by EU FET Global Computing initiative, project MIKADO IST-2001-32222, and by MIUR project NAPOLI. The funding bodies are not responsible for any use that might be made of the results presented here.

1

CONTENTS

2

Contents 1 Introduction

3

2 Syntax 2.1 Processes and Nets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Capabilities, Types and Capability Specifications . . . . . . . . . . . . . . . . . . . .

6 7 8

3 A Capability-based Type System

9

4 Operational Semantics

13

5 Type Soundness 17 5.1 Global Type Soundness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 5.2 Local Type Soundness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 6 Example: Subscribing On-line Publications

21

7 Discussion 22 7.1 Variations on Capabilities Acquisition . . . . . . . . . . . . . . . . . . . . . . . . . . 23 7.2 Managing Loss of Capabilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 7.3 Language Design Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 8 Related Work

32

9 Conclusions

34

A Proofs of Technical Results

35

B An Alternative Static Type Checking

37

1 INTRODUCTION

1

3

Introduction

Code mobility is a fundamental aspect of global computing systems, like e.g. open systems and the Internet; however it gives rise to a lot of relevant security problems. Security, i.e. secrecy and integrity of data and program code, is a key issue in the development of mobile distributed systems/applications. In such a scenario, other than attacks to inter-process communication over the communication channels (e.g. traffic analysis, message modifications/forging), several other kinds of attacks could take place. For instance, one can easily imagine malicious mobile processes attempting to access private information, or modifying private data, of the network nodes hosting them. Hence, a server receiving a mobile process for execution needs to impose strong requirements to ensure that the incoming process does not violate the secrecy and jeopardize the integrity of local information. Similarly, mobile processes need tools to ensure that their execution at the server node does not compromise their integrity (e.g. modification of process code) or secrecy (e.g. leak of sensitive data). Several alternative approaches have been exploited to enforce security policies in distributed computing systems. The approaches may differ in the level of trust required, the flexibility of the enforced security policies and their costs to components producers and users. A comprehensive security framework could result from the combination of complementary features. Approaches like code signing and sand-boxing (for instance, consider the Java implementation of these concepts [21, 18]) have low costs but cannot enforce flexible security policies (signed components may behave in arbitrary ways and the user must trust the component producer, while sand-boxed components are isolated and cannot interact with each other). Recently, a number of languages for mobile processes have been designed that come equipped with security mechanisms (at compile-time and/or at run-time) based on, e.g., type systems [12, 5, 25], control and data flow analysis [30, 29, 13, 24] and proof carrying code [28]. Some sensible and flexible language-based security techniques are investigated in [37]. We address here the problem of protecting hosts from attacks or misbehavior of mobile processes in distributed and open systems. These systems are distinguishable because their overall structure, along with the entities involved, cannot be completely known statically and can change dynamically in unpredictable ways. Our starting point is Klaim (Kernel Language for Agents Interaction and Mobility, [10]), an experimental language specifically designed to program distributed systems made up of several mobile components interacting through multiple distributed tuple spaces, and its capability-based type system [12] for controlling access to and creation of resources and mobility of processes. Klaim has been implemented [2] by exploiting Java and has proved to be suitable for programming a wide range of distributed applications with agents and code mobility [10, 11]. Let us now briefly illustrate the main features of Klaim and thus explain why Klaim has driven the design of our process calculus. The Klaim programming paradigm aims at clearly separating the programmer level and the net administrator level. Programmers write processes, while administrators write nets, hence manage the initial distribution of processes and set the security policies for controlling access to and creation of resources and mobility of processes. Thus, differently from other programming notations enabling process distribution and mobility, in Klaim the network infrastructure is clearly distinguishable from user processes and explicitly modelled, which we believe gives a more accurate description of

1 INTRODUCTION

4

the computer systems we are interested to. Klaim communication mechanism rests on an extension of the basic Linda coordination model [16] with multiple distributed tuple spaces. A tuple space is a multiset of tuples that are sequences of information items. Tuples are anonymous and associatively selected from tuple spaces by means of a pattern-matching mechanism. Interprocess communication is asynchronous because producer (i.e. sender) and consumer (i.e. receiver) of a tuple does not synchronize. Klaim handles multiple distributed tuple spaces that are placed on nodes of a net. The nodes of a net can be thought of both as physically distributed machines and as logical partitions of the same machine. Each node can be accessed through its locality; it contains a single tuple space and processes in execution. Processes can be executed concurrently both at the same node or at different nodes and can perform a few basic operations over tuple spaces and nodes: retrieve/place tuples from/into a tuple space, send processes for execution on (possibly remote) nodes, and create new nodes. The Linda coordination model, originally proposed for parallel programming on isolated machines, has a number of properties that make it appealing also for network computing environments (see, e.g., [9, 7, 14]), where, in general, connections are not stable and host machines are heterogenous. Indeed, the model permits time uncoupling (data life time is independent of the producer process life time), destination uncoupling (the producer of a datum does not need to know the future use or the destination of that datum) and space uncoupling (communicating processes need to know a single interface, i.e. the operations over the tuple space). Multiple, possibly distributed, tuple spaces have been advocated [17] to improve in modularity, scalability and performance thus making the obtained model suitable for open distributed systems and large-scale, multi-users applications. As shown in [14], where several messaging models for mobile processes are examined, the blackboard approach, of which tuple space based models are variants, is one of the most appreciated, also because of its flexibility. Indeed, general evidence of the success gained by the tuple space paradigm is given by the many tuple space based run-time systems, both from industries (e.g. SUN JavaSpaces [39, 1] and IBM T Spaces [40]) and from universities (e.g. PageSpace [8], WCL [36], Lime [33] and TuCSoN [31]). In conclusion, we think it is worthwhile to investigate the Klaim paradigm, also because its peculiar aspects about interprocess communication and network modelling distinguish it from the most popular and studied process languages. The major contribution of this paper is the introduction of a calculus, called µKlaim, and of a relative type system for controlling process activities. µKlaim is at the core of Klaim and has a simpler syntax and operational semantics (without higher-order communication, with only one kind of node addresses, without allocation environments that map logical addresses to physical ones, and with recursion in place of parameterized process definitions). The µKlaim type system is cleaner and more powerful than the Klaim one, in that it permits dynamic modifications of security policies and process capabilities, thus enabling more flexible type usages. In this paper we focus rather on the type system than on the language, and leave an analysis of the language expressive power (e.g. recursion vs. parameterized process definitions in a context with code mobility) for future investigation. We believe that the design principles of our type system can easily be exploited for developing similar theories for other languages for global computing. The idea of controlling program executions through types dates back in time. Traditionally, types have been exploited to statically enforce an important property, called type safety, that

1 INTRODUCTION

5

guarantees that every datum will be used consistently with its declaration during program execution (e.g., an integer variable will always be assigned integer values). To better cope with the problems raised by global computing systems, standard types have been generalized to behavioural types. Intuitively, behavioural types are abstractions of process behaviours and supply information about the capabilities/intentions of processes, namely the operations processes can/intend to perform. However, static checking hardly fits in such a highly dynamic systems as those for global computing, because it could restrict capabilities more than needed. To deal with open systems, a certain amount of dynamic checking is needed (e.g. mobile processes should be dynamically checked at run-time when they migrate), also for taking into account that in these environments typing information could be partial, inaccurate or missing. Furthermore, dynamic checking together with mechanisms supporting modifications at run-time of security polices and process capabilities turn out to be essential for dealing with pervasive network applications, like e.g. those for e-commerce. Behavioural types are used in µKlaim to express security policies of nodes; hence types are part of the language for configuring the underlying network architecture. Each node comes equipped with a type, specified by a net administrator in terms of capabilities (there is one capability for each process operation): the policy of node l describes the actions processes located at l are allowed to execute. Type checking will guarantee that only processes whose intentions match the rights granted by administrators can proceed. A static inference system allows processes to be first partially verified and then executed in a more efficient and flexible way, rather than to run inefficiently because of massive run-time checks. Moreover, node security policies must be taken into account when defining the operational semantics because they can dynamically change and because they control and affect the bahaviour of nets. The type system we propose reflects the great dynamicity and open endedness of wide area networks. Indeed, it relies on both compile-time and run-time local checkings/updatings, and on in-lined reference monitoring for postponing the verification of some process actions at run-time. Due to process migration and dynamic variation of capabilities, the µKlaim framework largely relies on dynamic checks. However, to increase efficiency, static checks are performed everywhere possible. For the sake of presentation, we will first introduce the basic type system and then argue on how enriching it to deal with further desirable features. To softly introduce the reader to the µKlaim programming style and its dynamic types, we informally present a simple but significant e-commerce example where our setting turns out to be expressive and elegant; this informal presentation will be refined throughout the paper. Let lU be the address of a node representing the server of a given department and let lP be the address of a node representing the publisher of some on-line publications. We want to implement a protocol through which the department chief subscribes a ‘licence’ enabling all the department members to access the on-line publications. In terms of access control, this means that the protocol must extend the policy of node lU (expressing the operations that processes hosted by lU are allowed to perform over the net) with the capability of reading papers from lP . Hence, if the department server is assigned policy δ, upon completion of the protocol lU ’s policy should become δ[lP 7→ {r}] (this notation means that processes in lU can read tuples from lP ’s tuple space, while still being enabled to perform those actions enabled by δ). Then, all the department members can spawn code

2 SYNTAX

6

over lU and thus retrieve lP papers by simply using the process eval( read(paperT itle, !x)@lP .out(paperT itle, x)@lM )@lU Action eval(·)@lU spawns code · for execution at lU . Action read(paperT itle, !x)@lP looks for a tuple matching the template hpaperT itle, !xi, i.e. a paper whose title is paperT itle and whose body is a text B; if such a paper is found, x is replaced by B in the continuation process. Then, action out(paperT itle, B)@lM inserts in lM ’s tuple space a tuple containing the paper required by the department member whose address is lM . In a more realistic scenario, the capability ‘read’ over lP will not be delivered forever to lU . To model this situation, we will also take into account means of managing capabilities loss. The rest of the paper is organized as follows. Section 2 defines the syntax of µKlaim, Section 3 its type system, and Section 4 its operational semantics. Section 5 presents two type soundness results: one involves whole nets, the other is relative to a subnet of a larger net. Section 6 gives more details on the subscription example described above. Section 7 presents a few variants dealing with processes acquiring capabilities for themselves and with management of capabilities loss, and argues on some language design issues. Section 8 contains comparisons with related work, while Section 9 concludes the paper. Appendix A contains the proofs of some technical results, while Appendix B presents an alternative static type checking. With respect to the extended abstract [20], this paper mainly differs because it deals with tuples of any length, handles recursive processes in a simpler way, contains all the formal definitions and proofs, and argues thoroughly on variants of the framework and on a few language design issues.

2

Syntax

This section presents the syntax of µKlaim as reported in Table 1. The syntax of the calculus is parameterized with respect to the following syntactic sets, which we assume to be countable and pairwise disjoint: X , of process variables, ranged over by X; L, of localities, ranged over by l; U, of locality variables, ranged over by u. We use ` to range over L ∪ U , V over basic values, x over value variables, π over sets of capabilities, δ over types, µ over capability specifications and eµ over evaluated capability specifications. The exact syntax of expressions, e, is deliberately not specified; we just assume that expressions contain, at least, basic values and variables. Localities, l, are the addresses (i.e. network references) of nodes and are the syntactic ingredient used to express the idea of administrative domain: computations at a given locality are under the control of a specific authority. Tuples, t, are sequences of actual fields. These contain expressions, localities or locality variables. A field of the form ` : µ points out a capability specification µ that permits dynamically determining the set of capabilities delivered along with address `. Templates, T , are patterns used to select tuples in a tuple space. They are sequences of actual and formal fields. Formal fields are written ! x or ! u : π (the set of capabilities π constraints the use of the address dynamically bound to u) and are used to bind variables to values. Some language design issues will be discussed in Section 7.3; among them, we will illustrate the possibility to charge the burden of deriving π in ! u : π to the static inference system.

2 SYNTAX

N ::=

Nets δ

l :: C N1 k N2 C ::=

a ::=

single node net composition (Node) Components

heti P C1 | C2 P ::=

7

located tuple process



(Process) Actions in(T )@`

input

read(T )@`

read

out(t)@`

output

eval(P )@`

migration

newloc(u : δ)

node creation

component composition

T ::= t ! x ! u : π T1 , T2

Templates

Processes

t ::= e ` : µ t1 , t2

Tuples

et::= V l : eµ et1 , et2

Evaluated Tuples

e ::= V x . . .

Expressions

` ::= l u

Localities & Loc.Var.

nil

null process

a.P

action prefixing

P1 | P2

parallel composition

X

process variable

rec X.P

recursion

Table 1: µKlaim Syntax

2.1

Processes and Nets

Processes are the µKlaim active computational units. They are built up from the null process nil, that does not perform any action, and from five basic operations, called actions, over nodes and tuple spaces by using action prefixing, parallel composition and recursion. The informal semantics of process actions is as follows. Action in(T )@` evaluates T and looks for a matching tuple t in the TS located at `; if t is found, it is removed from the TS, in the continuation process the variables in the formal fields of T are then replaced with the corresponding values of t and the operation terminates. If no matching tuple is found, the operation is suspended until one is available. Action read(T )@` differs from in(T )@` only in that the selected tuple is not removed from the TS. Action out(t)@` adds the tuple (resulting from evaluating) t to the TS located at `. Action eval(Q)@` sends process Q for execution at `. Action newloc(u : δ) dynamically creates a new node in a net whose address is bound to u and whose security policy is specified by type δ. Notice that newloc is the only action not indexed with an address because it always acts locally; all the other actions explicitly indicate the (possibly remote) locality where they will take place. Variables occurring in process terms can be bound by action prefixes or by recursion. More precisely, prefixes in(T )@`.P and read(T )@`.P bind the variables in the formal fields of T , prefix newloc(u : δ).P binds u and rec X.P binds X; in all these cases, P is the scope of the bindings. A variable that is not bound is called free. The sets bv(P ) and fv(P ) (of bound and free variables, resp., of P ) are defined accordingly, and so is α-conversion, denoted ≡α . We say that a process is closed if it does not contain free variables. In the sequel, we shall assume that bound variables in processes are all distinct and different from the free variables (by possibly applying α-conversion, this requirement can always be satisfied).

2 SYNTAX

(Com) (Alpha)

8

N1 k N2 ≡ N2 k N1 P ≡α P 0 δ

δ

l :: P ≡ l :: P

0

(Ass)

(N1 k N2 ) k N3 ≡ N1 k (N2 k N3 )

(Abs) l ::δ C ≡ l ::δ (C|nil)

Table 2: Nets Structural Congruence Remark 2.1 To describe iterative or arbitrary long (and possibly infinite) behaviours, process calculi with mobility often use the replication construct, ! P , which represents an unbounded number of copies of process P put in parallel. This treatment, although simpler than recursion, seems not to be adequate in case of migrating processes because in some sense the definitions of processes would be immobile and process migration should be modelled by requiring instantiation of new processes via (possibly remote) communication with the appropriate process definition (this is in fact the way in which replication is encoded through recursion in the π–calculus, see e.g. [32]). In our view, this is in sharp contrast with the autonomy that migrating processes should have. Located tuples are inactive components representing tuples in a tuple space (TS, for short) that either have been inserted in the initial configuration or along a computation by executing an action out. The TS located at l results from the parallel composition of all evaluated tuples located at l. Nets are finite collections of nodes where processes and tuple spaces can be allocated. A node is a triple l ::δ C, where locality l is the address of the node, C is the parallel composition of all components (i.e. processes and tuples) located at l and δ is the type of the node, i.e. the specification of its security policy. We will identify nets which intuitively represent the same net. We therefore define structural congruence ≡ to be the smallest congruence relation over nets that satisfies the laws in Table 2. The laws say that k is commutative and associative, that α-convertible processes can be replaced each other, and that process nil can be absorbed/spawned. Notice that ≡ identifies only nets whose equality is immediately obvious from their syntactical structure and has nothing to do with the semantics of nets (which has still to be introduced and shall rely on structural congruence). If not differently specified, in the sequel we shall only consider well-formed nets, i.e. nets whose nodes only contain closed processes and where pairwise distinct nodes have different addresses. The requirement that all processes be closed reflects our view that open processes should be considered as programming errors (this is similar to many programming languages whose compilers reject programs with undeclared variables). The requirement that all node addresses be different guarantees that each network node has one security policy. However, our main results (see Section 5) do not rely on this last hypothesis and still hold for generic nets.

2.2

Capabilities, Types and Capability Specifications

Capabilities are elements of the set {r, i, o, e, n}, where each symbol corresponds to the operation whose name begins with it; e.g. r denotes the capability of executing a read operation. We use Π, ranged over by π, to denote the power set of {r, i, o, e, n}. Types, δ, are functions of the form δ : L ∪ U →fin Π, where →fin means that the function maps only a finite subset of its domain to non-empty sets. Notation [`i 7→ πi ]`i ∈D stands for the type δ

3 A CAPABILITY-BASED TYPE SYSTEM

9

such that δ(`) is πi if ` = `i ∈ D and is ∅ otherwise. The extension of δ1 with δ2 , written δ1 [δ2 ], is the type δ 0 such that δ 0 (`) = δ1 (`) ∪ δ2 (`) for each ` ∈ L ∪ U. Intuitively, when the access policy of node l is set to δ, the processes running in l can perform over l0 only the actions enabled by δ(l0 ). To simplify notation, we permit that n ∈ δ(l0 ) also if δ is the type of a node l 6= l0 ; indeed, the fact that n ∈ δ(l0 ) or not is of no importance from a security point of view because the syntax prescribes to execute action newloc always locally. Capability specifications, µ, are partial functions with finite non-empty domain of the form µ : L ∪ U ∪ {others} * Π ∪ Π, where Π = {π : π ∈ Π} and others is a reserved keyword. For capability specifications, we adopt a notation similar to that used for types, but now [`i 7→ ρi ]`i ∈D (where ρi ∈ Π ∪ Π) stands for the capability specification µ such that dom(µ) = D and µ(`i ) = ρi . Capability specifications are used, mainly in out operations, to identify sets of capabilities that depend on the type at run-time of the node where processes run. In fact, when a process P running, say, at l wants to output a location l0 along with some capabilities, it is important to guarantee that P cannot deliver larger capabilities over l0 than those owned by l. Of course, it is perfectly reasonable to deliver all the capabilities over l0 owned by l. Since, in general, the latters can be determined only at run-time (because they depend on the capabilities acquired by l over l0 during the computation), capability specifications provide a way to statically express this fact. For example, process out(l0 : [l00 7→ π , others 7→ π])@l.nil running, say, at l wants to give l00 all the capabilities over l0 owned at run-time by l except those contained in π (e.g., if l is allowed to perform input and output over l0 when out is executed and π = {i}, then l00 will receive only the output capability over l0 ) and wants to give all the capabilities in π to any other node that will access the tuple. If π is ∅ then l gives l00 all the capabilities over l0 dynamically owned by l itself and gives no capability to the other nodes of the net; however, [l00 7→ ∅ , others 7→ ∅] is different from [l00 7→ ∅] because the last capability specification only enables l00 , instead of all the nodes of the net, to access the tuple (see the matching rules in Table 7 and Remark 4.1 for more details). We write eµ to denote evaluated capability specifications, i.e. partial functions with finite nonempty domain of the form eµ : L ∪ {others} * Π, that result from run-time evaluation called for by the operational semantics. In particular, evaluated tuples may only contain evaluated capability specifications. Remark 2.2 Although they are similar (thus, e.g., we can use the same notation for extension, once we let ` to also assume the value others), there is a subtle semantical difference between a type δ and a capability specification µ. A type describes the actions that a process running at a locality is allowed to perform over the net localities, while a capability specification describes the actions that the net localities can perform over a specific locality. This distinction will be made clearer in the next sections.

3

A Capability-based Type System

We now formally present a capability-based type system for µKlaim. Our understanding is that the development of µKlaim applications proceed in two phases. In the first phase, administrators assign policies to the nodes of the net, and processes are programmed while ignoring the capabilities

3 A CAPABILITY-BASED TYPE SYSTEM

{i} vΠ {r}

10

π1 ⊆ π2

π1 vΠ π10

π2 vΠ π20

π2 vΠ π1

(π1 ∪ π2 ) vΠ (π10 ∪ π20 )

Table 3: Capability Ordering Rules of the hosting nodes. In the second phase, processes are allocated over the nodes of the net and, after having type checked their intentions against the policy of the hosting node, they start executing. We start this section by defining a subtyping relation. Then, we describe the static type inference system for processes and finally we introduce the notion of well-typed nets. The dynamic part of our type system is deferred to Section 4. The subtyping relation, ¹, relies on an ordering over sets of capabilities, vΠ , namely a hierarchy over capabilities. The ordering vΠ is the least reflexive and transitive relation induced by the rules in Table 3. It relies on the assumptions that if a process owns a set of capabilities π then it also owns any subset of π and that if a process is allowed to perform an in operation then it is also allowed to perform a read1 . Thus, if π1 vΠ π2 then π1 enables at least the actions enabled by π2 . Remark 3.1 Here, the ordering vΠ is borrowed from [12]. However, the type theory we develop is completely parametric with respect to the used ordering over capabilities (e.g. in [20], vΠ is the reverse subset inclusion). Definition 3.2 δ1 is a subtype of δ2 , written δ1 ¹ δ2 , if δ2 (`) vΠ δ1 (`) for each ` ∈ L ∪ U. This means that the subtyping relation ¹ is the standard preorder over functions and that if δ1 ¹ δ2 , then δ1 is less permissive than δ2 . In the sequel, if δ1 ¹ δ2 , we shall also say that δ2 is a supertype of δ1 . The following proposition (that could be easily proven by induction on the number of elements in the domain of δ2 associated to non-empty sets) gives a characterization of ¹ in terms of type extension. Proposition 3.3 δ1 ¹ δ2 if and only if there exists a type δ3 such that δ2 = δ1 [δ3 ]. Let us now present the static inference system. Informally, for each node of a net, say l ::δ C, the inference system determines if the actions that C intends to perform when running at l are enabled by the access policy δ or not. For example, capability e can be used to control process mobility: a process in C can migrate to l0 only if [l0 7→ {e}] is a subtype of δ. However, because the node can dynamically acquire capabilities when C performs actions in/read, some actions that can be permissible at run-time could be statically illegal. For this reason, if C intends to perform an action not allowed by δ, the static inference system cannot reject the process since the capability necessary to perform the action could in principle be dynamically acquired by the node. In such cases, the inference system simply marks the action to require its dynamic checking. The marking mechanism never applies to actions whose targets are locality variables bound by in/read, because such actions can be statically checked, thus alleviating the burden of dynamic 1 The rationale behind this choice is that if a process may consume a tuple then it should be enabled to just read the tuple because consuming a tuple is more dangerous than reading it. Therefore, read operations cannot be expressed in terms of in and out, and this is why µKlaim provides two different operations for accessing tuples in tuple spaces.

3 A CAPABILITY-BASED TYPE SYSTEM

11

checking and improving system performance. In fact, according to the syntax, whenever a locality variable u is bound by an action in/read, u is annotated with a set of capabilities π that specifies the operations that the continuation process is allowed to perform by using u as the target address. Moreover, π also specifies the ‘minimal’ set of capabilities that the executing node must own or acquire over the net locality that at run-time will replace u. Hence, our type system will reject node 0 l1 ::[l 7→{r}] read(!u : {o})@l0 .read(!x)@u because r does not belong to the annotation of u, while 0 it will accept node l2 ::[l 7→{r}] read(!u : {o})@l0 .out(t)@l0 because action out(t)@l0 can be marked and checked at run-time. In fact, if u is dynamically replaced with l0 , l2 will acquire capability o over l0 and the process running at l2 can proceed; otherwise, the process will be suspended. In our system, the dynamic acquisition of capabilities is exploited exactly for relaxing the static type checking and admitting nodes like l2 while requiring on (part of) them a dynamic checking. To enable the static inference to modify the code of processes, we extend the µKlaim syntax to include processes willing to perform marked actions, where a marked action is a normal µKlaim action which is underlined to require a dynamic checking of the corresponding capability. Formally, we extend the syntax as follows P ::= . . . | a.P We will write P (C and N , resp.) to emphasize that process P (component C and net N , resp.) may contain marked actions. A type context Γ generalizes a type in that it also keeps track of used process variables. Formally, Γ is a finite partial mapping Γ : L ∪ U ∪ X → Π (where process variables will always be associated to ∅). In the sequel, we shall use Γ as a standard type; thus Γ[Γ0 ], δ[Γ] and Γ[δ] are defined in the expected way. To update a type context with the type annotations specified within a template, we use the auxiliary function upd that behaves like the identity function for all fields but for template formal fields binding locality variables. Formally, it is defined by:   if T = T1 , T2  upd(upd(Γ, T1 ), T2 ) upd(Γ, T ) = Γ[u 7→ π] if T = ! u : π,   Γ otherwise Hence, if T is a tuple, then upd(Γ, T ) = Γ. To have more compact inference rules for judgments, we found it convenient to extend function upd to encompass the case that the second argument is a process and let upd(Γ, P ) = Γ. Type judgments for processes take the form Γ| l C . C. In Γ, the bindings from localities to non-empty sets implement the access policy of the node with address l, the bindings from locality variables to non-empty sets record the type annotations for the variables that are free (i.e. have been freed) in C, while the bindings from process variables records the free (i.e. freed) process variables of C. Intuitively, the judgment Γ| l C . C states that, within the context Γ, when C is located at l, the unmarked actions in C are admissible w.r.t. Γ and the free variables occurring in C are defined in Γ. Instead, the marked actions in C cannot be deemed legal at compile time but could become permissible at run-time, after dynamic acquisition of the necessary capabilities (via execution of actions in/read performed at l). Notation 3.4 Given an action a, we use arg(a) to denote its argument, tgt(a) its target location and cap(a) the capability corresponding to a. For example, if a is out(t)@l, then we have arg(a) = t,

3 A CAPABILITY-BASED TYPE SYSTEM

(T1 ) (T3 )

(T5 )

(T7 )

Γ| l nil . nil

(T2 )

X ∈ dom(Γ)

(T4 )

Γ| l X . X Γ| l C1 . C1

Γ| l C2 . C2

Γ| l C1 | C2 . C1 | C2 Γ(l) vΠ {n}

12

δ ¹ Γ[u 7→ Γ(l)]

(T6 )

Γ| l heti . heti Γ[X 7→ ∅]| l P . P Γ| l rec X.P . rec X.P cap(a) 6= n

upd (Γ, arg(a))| l P . P

Γ| l a.P . markΓ (a).P

Γ[u 7→ Γ(l)] | l P . P

Γ| l newloc(u : δ).P . newloc(u : δ).P

Table 4: µKlaim Type Inference Rules tgt(a) = l and cap(a) = o. Of course, tgt(newloc(u : δ)) returns the locality where the action is performed. Type judgments are inferred by using the rules in Table 4. The function markΓ (·) for marking process actions is defined as follows ( a if Γ(tgt(a)) vΠ {cap(a)} markΓ (a) = a if Γ(tgt(a)) 6vΠ {cap(a)} and tgt(a) ∈ L where 6vΠ denotes the negation of vΠ . Condition tgt(a) ∈ L distinguishes actions using localities as target from those using variables, marking the formers and rejecting the latters (as previously explained). The rules in Table 4 should be quite explicative, we only remark a few points. Rule (T2 ) says that evaluated tuples always successfully passes the static type checking, regardless their contents. This corresponds to taking the point of view that tuples existing in the initial configuration have been made available by the net administrator and thus are reliable, while tuples produced by processes during a computation are checked when they are inserted in the target TS. Rules (T3 ) and (T4 ) deal with recursive definitions and only accept processes whose process variables are all bound by a rec. Rule (T5 ) deals both with process composition and with component composition. Rule (T6 ) deals with action prefixing. Notice that, in case of action eval, the argument process is not statically checked because the locality where the process will be sent for execution, and hence the access policy against which the process has to be checked, cannot be, in general, statically known. Action newloc is dealt with differently from the other actions by rule (T7 ) and is always statically checked (i.e. it is never marked). Indeed, newloc is always performed locally and the corresponding capability cannot be dynamically acquired. Notice also that the creating node is assumed to have over the created one all the capabilities it owns on itself. Moreover, to enable the creation, the specified access policy δ must be in agreement with the access policy of the node executing the operation extended with the ability of performing over u all the operations allowed locally. This is needed to prevent a malicious node from forging capabilities by creating a new node with more powerful capabilities where sending a malicious process that takes advantage of the capabilities not owned by the creator. We now state (the proof is given in Appendix A) an important property of the type inference system of Table 4, namely that it is decidable.

4 OPERATIONAL SEMANTICS    E[[ e ]] T [[ t ]]δ = l : [[ µ ]]δ(l)−{n}   T [[ t1 ]]δ , T [[ t2 ]]δ

13

if t = e if t = l : µ if t = t1 , t2

   T [[ T1 ]]δ , T [[ T2 ]]δ T [[ T ]]δ = T [[ t ]]δ   T

if T = T1 , T2 if T = t otherwise

Table 5: Tuple/Template Evaluation Function [[ [` 7→ π 0 ] ]]π

=

[ ` 7→ (π ∩ π 0 )]

if ` ∈ L ∪ {others}

[[ [` 7→ π 0 ] ]]π

=

[ ` 7→ (π − π 0 )]

if ` ∈ L ∪ {others}

[[ µ1 [µ2 ] ]]π

=

( [[ µ1 ]]π )[ [[ µ2 ]]π ]

Table 6: Capability Specifications Evaluation Function Proposition 3.5 (Decidability) For any Γ, l, C and C 0 it is decidable to determine whether the judgement Γ| l C . C 0 holds true or not. The proof of Proposition 3.5 is constructive in that it also gives an algorithm that, for any Γ, l and C determines the C 0 with the smallest number of marked actions such that the judgement Γ| l C . C 0 holds. The complexity of the algorithm is linear in the number of operators in C. We end this section by formalizing the notion of well-typed net. Definition 3.6 A net is well-typed if for each node l ::δ C there exists a component C such that δ| l C . C.

4

Operational Semantics

A first ingredient we need for defining the operational semantics is a mechanism to evaluate tuples and templates. Indeed, only evaluated tuples can be added to a TS and, similarly, templates must be evaluated for being used to retrieve tuples. The tuple/template evaluation function T [[ · ]]δ , formally defined by the rules in Table 5, takes as a parameter the type (i.e. access policy specification) of the node where the evaluation will take place. As the rules state, evaluation consists of evaluating the contained expressions by using function E[[ · ]] (which depends on the kind of allowed expressions and, hence, is left unspecified) and of properly evaluating the contained capability specifications; formal fields are left unchanged. In the sequel, we will write eT to emphasize that a template T has already been evaluated. The evaluation function for capability specifications [[ · ]]π used by function T [[ · ]]δ is defined by the rules in Table 6. [[ · ]]π is parameterized with respect to the set π of capabilities owned by the node where the evaluation takes place over the locality which the capability specification being interpreted is associated to and returns an evaluated capability specification. Function [[ · ]]π is invoked in Table 5 in such a way that the parameter π does never contain n. This is necessary because actions newloc are always performed locally, and thus the corresponding capability n should never be transmitted. As concerns the rules defining [[ · ]]π , the first one ensures that no more capabilities over a given ` than those owned by l are delivered, the second one replaces π 0

4 OPERATIONAL SEMANTICS

(M1 ) matchδl (V, V ) = h[ ], ²i

(M3 )

(M5 )

{l, others} ∩ dom(µ) 6= ∅ matchδl (l0 : µ0 , l0 : µ) = h[ ], ²i matchδl (eT1 , et1 ) = hδ1 , σ1 i

14

(M2 ) matchδl (! x, V ) = h[ ], [V /x]i if

l ∈ dom(µ)

(M4 )

then else

δ(l0 ) ∪ µ(l) vΠ π δ(l0 ) ∪ µ(others) vΠ π 0

matchδl (! u : π, l0 : µ) = h[l0 7→ π], [l /u]i matchδl (eT2 , et2 ) = hδ2 , σ2 i

matchδl ( (eT1 , eT2 ) , (et1 , et2 ) ) = hδ1 [δ2 ], σ1 ◦ σ2 i

Table 7: Matching Rules with the complement of π 0 with respect to π, and the last one says that extension and evaluation of capability specifications do commute. Of course, for a capability specification to be evaluable it cannot contain locality variables. Another ingredient we need is a formal way to say that a template and a tuple do match. The pattern-matching function matchδl , used to select (evaluated) tuples from a tuple space according to (evaluated) templates, is defined by the rules in Table 7. Function matchδl is parameterized with the locality l and the security policy δ of the node where it is invoked. A successful matching returns a type, used to extend the type of the node executing the matching with the capabilities delivered by the (producer of the) tuple, and a substitution, used to assign values to variables in the (continuation of the) process invoking the matching. More specifically, the returned substitution replaces the variables contained in the formal fields of the template with the values contained in the corresponding actual fields of the accessed tuple. We use σ to range over substitutions (with finite domain) of values for variables, ² to denote the ‘empty’ substitution and ◦ to denote substitutions composition. As usual, substitution application may require α-conversion to avoid capturing of free names. Let us now briefly comment on the pattern-matching rules. The first two rules say that two values match only if they are identical and that value variables match any basic value. Rule (M3 ) requires that, for a matching to take place, the locality of the node where the read/in is executing must be authorized by the capability specification associated to the locality being accessed. Rule (M4 ) ensures that if a read/in executing at l looks for a locality where to perform the actions enabled by π, then, for selecting locality l0 , it must hold that the union of the capabilities over l0 owned by l and of the capabilities over l0 delivered to l by the tuple enables all the actions enabled by π. The capabilities delivered by the tuple are then used to enrich the capabilities of l over l0 . Finally, rule (M5 ) says that an evaluated template matches against an evaluated tuple if both have the same number of fields and corresponding fields do match. Remark 4.1 In rules (M3 ) and (M4 ), if both µ(l) and µ(others) are undefined, then l is not allowed to access the tuple and the matching fails. This feature permits controlling ‘immediate access’ to tuples (see Section 6), i.e. constraining the nodes from where tuples can be accessed. Function matchδl satisfies the following property, whose proof can be easily done by induction on the number of fields of the first argument of the function.

4 OPERATIONAL SEMANTICS

15

Proposition 4.2 If matchδl (eT, et) = hδ 0 , σi with dom(σ) = {ui }i∈I ∪ {xj }j∈J then δ 0 = [li 7→ πi ]i∈I where, for every i ∈ I, ! ui : πi is a field of eT , li : µi is the corresponding field of et (for some µi such that {l, others} ∩ dom(µi ) 6= ∅) and σ(ui ) = li . Now, the µKlaim operational semantics is given by a net reduction relation, Â− → , which is the least relation induced by the rules in Table 8. Net reductions are defined over configurations of the form L ` N , where L is such that loc(N ) ⊆ L ⊂ L and function loc(N ) returns the set of localities occurring in N . Function loc(·) is defined inductively on the syntax of terms by: ( {l, l1 , . . . , ln } ∪ loc(C) if N = l ::[l1 7→π1 ,...,ln 7→πn ] C • loc(N ) = loc(N1 ) ∪ loc(N2 ) if N = N1 k N2   loc(et) if C = heti  • loc(C) = loc(P ) if C = P   loc(C1 ) ∪ loc(C2 ) if C = C1 |C2  if P = nil or P = X ∅     loc(a) ∪ loc(Q) if P = a.Q or P = a.Q • loc(P ) =  if P = Q|R loc(Q) ∪ loc(R)    if P = rec X.Q loc(Q) ( {l1 , . . . , ln } if a = newloc(u : [li 7→ πi , uj 7→ πj0 ]j=1,...,m i=1,...,n ) • loc(a) = {tgt(a)} ∪ loc(arg(a)) otherwise where loc(T ) returns the localities contained in T and in the capability specifications therein. In a configuration L ` N , L keeps track of the localities occurring in N and is needed to ensure global freshness of new addresses. For the sake of readability, when a reduction does not generate any fresh addresses we write N Â− → N 0 instead of L ` N Â− → L ` N 0. Let us comment on the rules in Table 8. Rule (Out) says that, before adding a tuple to a tuple space, the tuple must be evaluated according to the access policy δ of the node where the process performing the out runs. Rule (Eval) says that a process is allowed to migrate only if it successfully passes a type checking against the access policy of the target node. During this preliminary check, some process actions could be marked to be effectively checked when being executed. Rules (In) and (Read) say that the process performing the operation can proceed only if pattern-matching succeeds. In this case, the access policy of the receiving node is enriched with the type returned by the matching mechanism and the substitution returned along with the type is applied to the continuation (and the type annotations therein) of the process performing the operation. In rule (New) the set L of localities already in use is exploited to choose a fresh address l0 for naming the new node. Notice that, once created, the address of the new node is not known to any other node in the net, thus it can be used by the creating process as a sort of private resource. Rule (Rec) says that recursive processes can be freely unfolded. Rule (Mark) says that the in-lined security monitor stops execution whenever the capability for executing a is missing. Rule (Split) transforms a parallel over components into a parallel over net nodes2 . There is a subtlety with rule 2 This

permits splitting the parallel components running at a node thus enabling the application of the main reduction rules that, in fact, can only be used when there is only one process running at l. Moreover, by possibly using axiom (Abs) in Table 2, (Split) enables the use of axioms (Out), (Eval), (In) and (Read) also for execution of local operations. In conclusion, (Split) permits a compact and general formulation of the reduction rules without the need of explicitly considering all the parallel components running at a node and of having different rules for local and remote operations.

4 OPERATIONAL SEMANTICS

(Out) (Eval) (In)

(Read) (New)

et = T [[ t ]]δ δ

0

0

0

0

l :: out(t)@l .P k l ::δ C 0 Â− → l ::δ P k l0 ::δ C 0 |heti δ 0 | l 0 Q . Q0 0

0

l ::δ eval(Q)@l0 .P k l0 ::δ C 0 Â− → l ::δ P k l0 ::δ C 0 |Q0 matchδl (T [[ T ]]δ , et) = hδ 00 , σi 0

l ::δ in(T )@l0 .P k l0 ::δ heti Â− → l ::δ[δ

00

0

]

P σ k l0 ::δ nil

matchδl (T [[ T ]]δ , et) = hδ 00 , σi 0

l ::δ read(T )@l0 .P k l0 ::δ heti Â− → l ::δ[δ

00

0

]

P σ k l0 ::δ heti

l0 6∈ L 0

0

0

0

L ` l ::δ newloc(u : δ 0 ).P Â− → L ∪ {l0 } ` l ::δ[l 7→δ(l)] P [l /u] k l0 ::δ [l /u] nil l ::δ rec X.P Â− → l ::δ P [rec X.P/X ]

(Rec) (Mark)

16

l0 = tgt(a)

0

δ(l0 ) vΠ {cap(a)}

l ::δ a.P k l0 ::δ C 0 Â− → N 0

l ::δ a.P k l0 ::δ C 0 Â− → N 0

(Split)

(Par) (Struct)

L ` l ::δ C1 k l ::δ C2 k N Â− → L0 ` l ::δ C10 k l ::δ C20 k N 0 0

L ` l ::δ C1 |C2 k N Â− → L0 ` l ::δ C10 |C20 k N 0 L ` N1 Â− → L0 ` N10 L ` N1 k N2 Â− → L0 ` N10 k N2 N ≡ N1

L ` N1 Â− → L0 ` N 2

N2 ≡ N 0

L ` N Â− → L0 ` N 0 Table 8: µKlaim Operational Semantics

(Split): in fact, the nets in the premise of the rule are not well-formed according to the definition of Section 2.1 because they contain nodes with the same address (locality l). However, the transition in the conclusion preserves well-formedness. Rules (Par) and (Struct) are standard: the former says that, if part of a composed net evolves, the whole net evolves accordingly and the latter says that structural congruent nets have the same reductions. We end this section by stating a few properties of the operational semantics whose proofs can be found in Appendix A. The first result relates the set of localities L in a configuration L ` N to the localities occurring in the net obtained after a reduction step; the second states that the reduction relation preserves nets well-formedness. As usual, we shall write Â− →∗ to denote the reflexive and transitive closure of Â− →. Proposition 4.3 If L ` N Â− → L0 ` N 0 and loc(N ) ⊆ L then loc(N 0 ) ⊆ L0 . Proposition 4.4 If N is well-formed and loc(N ) ` N Â− →∗ L0 ` N 0 then N 0 is well-formed.

5 TYPE SOUNDNESS

5

17

Type Soundness

In this section, we first present a type soundness result that involves whole nets; then, we point out the modifications needed to get a local type soundness result relative to a subnet of a larger net.

5.1

Global Type Soundness

We start introducing the notion of executable nets that, intuitively, are nets already containing all necessary marks (as if they have already passed a static type checking phase). Definition 5.1 A net is executable if for each node l ::δ C it holds that δ| l C . C, where for inferring the type judgement, in addition to the rules in Table 4, one can also use the rule (T8 )

upd(Γ, arg(a))| l P . P Γ| l a.P . a.P

that allows a process to already contain marked actions. For the sake of readability, in the sequel the judgement Γ| l C . C will be written as Γ| l C. Notice that executable nets are well-typed. Our main results will be stated in terms of executable nets; indeed, due to the dynamic acquisition of capabilities, well-formed nets that are statically deemed well-typed can still give rise to run-time errors. However, by marking those actions that should be checked at run-time, well-typed (and well-formed) nets can be transformed into executable nets that, instead, cannot give rise to run-time errors (see Corollary 5.7). We first prove some standard results for type systems, i.e. weakening and substitutivity. Lemma 5.2 (Weakening) If Γ| l C then Γ[Γ0 ]| l C. Proof: The proof consists in mimicking for Γ[Γ0 ]| l C the derivation of Γ| l C. The process actions not enabled by Γ0 will have the same type judgements w.r.t. both Γ and Γ[Γ0 ]; on the contrary, actions enabled by Γ0 (but not by Γ) will be typed w.r.t. Γ[Γ0 ] using rule (T8 ) in place of rule (T6 ). Lemma 5.3 (Substitutivity) 1. If Γ| l C then Γσ| l Cσ, for any substitution σ. 2. If Γ[X 7→ ∅]| l C and Γ| l Q then Γ| l C[Q/X ]. Proof: 1. The proof is by induction on length of the inference of the type judgement. The base cases (i.e., rules (T1 ), (T2 ) and (T3 )) are obvious. Let us examine the case in which the last rule used is (T6 ) (the cases for (T4 ), (T5 ), (T7 ) and (T8 ) are similar or easier). By hypothesis, for some process Q and action a such that Γ(tgt(a)) vΠ {cap(a)}, we have that C = a.Q, Γ| l a.Q and, thus, upd(Γ, arg(a))| l Q. Without loss of generality, we can assume that dom(σ) does not contain the variables bound by a (otherwise, if this is not the case, we could rename such variables) thus we have (a.Q)σ = aσ.Qσ. Now, by induction, we have that

5 TYPE SOUNDNESS

18

(upd(Γ, arg(a)))σ| l Qσ, i.e. that upd (Γσ, arg(aσ))| l Qσ (indeed, it is easy to prove that (Γ1 [Γ2 ])σ = (Γ1 σ)[Γ2 σ]). Finally, by applying rule (T6 ), we conclude that Γσ| l aσ . Qσ, i.e. that Γσ| l (a.Q)σ. 2. The proof proceeds by induction on length of the inference of the type judgement. The base cases (i.e., rules (T1 ), (T2 ) and (T3 )) are obvious. If the last used rule is (T4 ), the thesis easily follows by induction (recall that substitution application may require renaming of bound variables to avoid capturing free names). If the last used rule is (T5 ), the thesis follows by induction using the fact that (P |Q)σ = P σ | Qσ. The cases when the last rule used is (T6 ), (T7 ) or (T8 ) are similar; we examine only the first one. By hypothesis, for some process P and action a such that Γ(tgt(a)) vΠ {cap(a)}, we have that C = a.P and Γ[X 7→ ∅]| l a.P ; thus, for the premises of rule (T6 ), we have that (upd(Γ, arg(a)))[X 7→ ∅]| l P (indeed, upd (Γ[X 7→ ∅], arg(a)) = (upd(Γ, arg(a)))[X 7→ ∅]). By hypothesis, Γ| l Q thus, by using Lemma 5.2, we have that upd (Γ, arg(a))| l Q. Then, by using the induction hypothesis we get that upd(Γ, arg(a))| l P [Q/X ] and, by applying rule (T6 ), we obtain Γ| l a.(P [Q/X ]), that is Γ| l (a.P )[Q/X ]. Now we prove that the property of a net of being executable is an invariant both of the structural congruence and of the reduction relation. Lemma 5.4 If N is executable and N ≡ N 0 then N 0 is executable. Proof: By case analysis on the axioms in Table 2. The cases of axioms (Com) and (Ass) trivially follows by definition, while the case for (Alpha) follows from the fact that the static type inference is not affected if we coherently rename bound variables within a net. The case for (Abs) is easy. By hypothesis, δ| l C; moreover, by axiom (T2 ), we have that δ| l nil. Thus, by rule (T5 ), δ| l C | nil. Theorem 5.5 (Subject Reduction) If N is executable and loc(N ) ` N Â− → L0 ` N 0 then N 0 is executable. Proof: The proof proceeds by induction on the length of the inference of loc(N ) ` N Â− → L0 ` N 0 . 0 Notice that the sets of localities loc(N ) and L do not play any role (namely, they do not affect the definition of executable net) and will be ignored in the rest of the proof. Base Step:

We reason by case analysis on the axioms (i.e. the first six rules) of Table 8.

(Out). Since by hypothesis N is executable, we have that δ| l out(t)@l0 .P . Due to the form of the process involved, rule (T6 ) has been the last one applied to deduce the type judgement; hence we also have that δ| l P . Moreover, by applying (T5 ) to δ 0 | l0 C 0 (that holds by hypothesis) and to δ 0 | l0 heti (axiom (T2 )), we get that δ 0 | l0 C 0 | heti. This suffices to conclude that N 0 is executable. (Eval). By reasoning like before, we can prove that δ 0 | l0 P 0 | Q0 . This easily follows by applying (T5 ) to δ 0 | l0 P 0 , that holds by hypothesis, and to δ 0 | l0 Q . Q0 , that is the premise of rule (Eval).

5 TYPE SOUNDNESS

19

(In). Since it holds that δ 0 | l0 nil, we are only left to prove that δ[δ 00 ]| l P σ. Now, by hypothesis, we have that δ| l in(T )@l0 .P , where rule (T6 ) has been the last one applied to infer the judgement; hence we also have that upd(δ, T )| l P . By definition, if {ui }i∈I are the locality variables bound by T and, for every i ∈ I, πi are the capabilities associated to ui in T , we have that upd(δ, T ) = δ[ui 7→ πi ]i∈I . Moreover, by the premise of rule (In) and by Proposition 4.2, we have that matchδl (T [[ T ]]δ , et) = hδ 00 , σi, where δ 00 = [li 7→ πi ]i∈I , {ui }i∈I ⊆ dom(σ) and σ(ui ) = li for i ∈ I. Now, upd(δ, T ) = δ[ui 7→ πi ]i∈I implies upd(δ, T )σ = δ[li 7→ πi ]i∈I = δ[δ 00 ]. Thus, by applying Lemma 5.3.1, from upd (δ, T )| l P by using σ we conclude that δ[δ 00 ]| l P σ. (Read). Similar to the previous case. (New). By hypothesis we have that δ| l newloc(u : δ 0 ).P , where rule (T7 ) has been the last one applied to infer the judgement. Hence we also have that δ[u 7→ δ(l)]| l P . The thesis follows 0 by taking σ = [l /u] and by reasoning like in the case of rule (In). (Rec). By hypothesis, δ| l rec X.P . This implies, by the premise of rule (T4 ), that δ[X 7→ ∅]| l P ; then, by Lemma 5.3.2, it holds that δ| l P [rec X.P/X ]. Inductive Step:

We reason by case analysis on the last applied operational rule of Table 8.

(Mark). By hypothesis, we have that δ| l a.P . Due to the form of the process involved in the judgement, rule (T8 ) has been the last one applied to deduce the judgement; hence we also have that upd (δ, arg(a))| l P . By the premise of (Mark), we have that, when the reduction takes place, δ(tgt(a)) vΠ {cap(a)}. Hence, by applying (T6 ), we can derive δ| l a.P , that, 0 together with the hypothesis δ 0 | l0 C 0 , implies that l ::δ a.P k l0 ::δ C 0 is executable. The thesis now follows by induction. (Split). By hypothesis, we have that δ| l C1 |C2 . Due to the form of the process involved in the judgement, rule (T5 ) has been the last one applied to deduce the judgement; hence we also have that δ| l C1 and δ| l C2 . Thus, we have that the net l ::δ C1 k l ::δ C2 k N is executable. 0 By induction, we get that l ::δ C10 k l ::δ C20 k N 0 is executable. From the fact that the types of nodes never decrease during net reductions (that could be easily proven by induction on the operational rules), we have that δ ¹ δ 0 and thus, by Proposition 3.3, δ 0 = δ[δ 00 ] for some δ 00 . Now, the thesis directly follows by using Lemma 5.2. (Par). By hypothesis, N1 k N2 is executable, hence N1 and N2 are executable too. Now, by induction, N10 is executable and hence N10 k N2 is executable. (Struct). From the hypothesis, N is executable and N ≡ N1 ; by Lemma 5.4, it follows that N1 is executable too. Now, by induction, we get that N2 is executable. From this fact and from the hypothesis N2 ≡ N 0 , again by Lemma 5.4, it follows that N 0 is executable too. Now, we introduce the notion of run-time error and state type safety, i.e. that executable nets do not give rise to run-time errors. Run-time errors are defined by the rules in Table 9 in terms of predicate N ↑ l that holds true when, within the net N , a process P located at a node with

5 TYPE SOUNDNESS

(ErrAct)

20

δ(tgt(a)) 6vΠ {cap(a)}

(ErrPar)

l ::δ a.P ↑ l

N ↑l N k N0 ↑ l

(ErrStr)

N ≡ N0

N0 ↑ l

N ↑l

Table 9: Run-Time Error address l attempts to perform an action that is not allowed by the access policy of the node. The rules are straightforward. Notice that, of course, marked actions do not give rise to run-time errors because they are checked whenever they are going to be performed. At most, when their execution is not permissible, the process that is trying to execute them blocks waiting for the acquisition of the corresponding capabilities by a parallel process running at the same node. Theorem 5.6 (Type Safety) If N is executable then N ↑ l for no l ∈ loc(N ). Proof: We proceed by contradiction and prove, by induction on the length of the inference of N ↑ l , that if N ↑ l for some l ∈ loc(N ) then N is not executable. Base Step: In this case, the error is generated by using axiom (ErrAct). This means that N is a node of the form l ::δ a.P and δ(tgt(a)) 6vΠ {cap(a)}. Therefore, node l ::δ a.P , and hence N , is not executable otherwise action a should have been marked (see rule (T6 ) in Table 4). Inductive Step:

By case analysis on the last error rule used.

(ErrPar). From the premise N ↑ l of the rule, by induction, we have that N is not executable. Hence, by definition, N k N 0 is not executable too. (ErrStr). From the premise N 0 ↑ l of the rule, by induction, we have that N 0 is not executable. Then the thesis follows from the premise N ≡ N 0 by using Lemma 5.4. Therefore, executable nets cannot immediately give rise to run-time errors. Now, by combining together the results shown so far, we get that executable nets never generate run-time errors along sequences of reductions. Corollary 5.7 (Global Type Soundness) If N is executable and loc(N ) ` N Â− →∗ L0 ` N 0 0 0 then N ↑ l for no l ∈ loc(N ). Proof: The proof proceeds by induction on the length of loc(N ) ` N Â− →∗ L0 ` N 0 . The base step is Theorem 5.6, while the inductive step follows from Theorems 5.5 and 5.6.

5.2

Local Type Soundness

Type soundness is one of the main goal of any type system. However, in our framework it is formulated in terms of a property requiring the typing of whole nets. While this could be acceptable for LANs, where the number of hosts usually is relatively small, it is unreasonable for WANs, where in general hosts are under the control of different authorities. When dealing with larger nets, it is certainly more realistic to reason in terms of parts of the whole net. Hence, we put forward a more local formulation of our properties and results. To this aim, we define the restriction of a net N to a set of localities S, written NS , as the subnet obtained from N by deleting all nodes whose addresses are not in S. The wanted local type soundness result can be formulated as follows.

6 EXAMPLE: SUBSCRIBING ON-LINE PUBLICATIONS

21

Theorem 5.8 (Local Type Soundness) Let N be a net and S ⊆ loc(N ). If NS is executable and loc(N ) ` N Â− →∗ L0 ` N 0 then for no l ∈ S it holds that N 0 ↑ l . Notice that in the previous statement, no assumption on the whole net N is made. The proof is similar to the proof of Corollary 5.7. In fact, the local type soundness is enforced by the dynamic checking performed when processes migrate, which prevents ill-typed processes to get into NS .

6

Example: Subscribing On-line Publications

In this section, we take up the simple example presented in the Introduction and use it to show the µKlaim programming style and to illustrate how exploiting its type system for establishing security policies. For programming convenience, we shall assume integers and strings to be basic values of the language and omit trailing occurrences of process nil. Moreover, to suitably identify and refer to processes, we shall use notation A , P to assign the name A to the (not necessarily closed) process P . Suppose that a user U wants to subscribe a ‘licence’ to enable accessing on-line publications by a given publisher P . To model this scenario we use three localities, lU , lP and lC , respectively associated to U , P and to the repository containing P ’s on-line accessible publications (this slightly differs from the informal scenario sketched in the Introduction — we explain later the reason why node lC is needed). First of all, U sends a subscription request to P including its address (together with an ‘out’ capability) and credit card number; then, U waits for a tuple that will deliver it the capability r needed to access P ’s publications and proceeds with the rest of its activity. The behaviour described so far is implemented by the process AU , out(“Subscr”, lU : [lP 7→ {o}], CrCard)@lP .in(“Acc”, !u : {r})@lU .R where process R may contain operations like read(. . .)@lC . P , once it has received the subscription request and checked (by possibly using a third party authority) the validity of the payment information, gives U a ‘read’ capability over lC . P ’s behaviour is modelled by the following process. AP , rec X.

( in(“Subscr”, !u : {o}, !y)@lP . check credit card y of u and require the payment . out(“Acc”, lC : [u 7→ {r}])@u | X )

In real situations, the capability r will be delivered to U for a limited period of time (for example, annual subscriptions would obtain read capabilities valid for one year) or for a limited number of accesses. In Section 7.2 we shall present some simple ways to implement these features in our setting. For processes AU and AP to behave in the expected way, the underlying net architecture, namely distribution of processes and security policies, must be appropriately configured. A suitable net is: lU lC

::[lU 7→{o,i,r,e,n}, lP 7→{o}] AU k lP ::[lP 7→{o,i,r,e,n},lC 7→{o,i,r}] AP ::[ ] hpaper1i | hpaper2i | . . .

k

where we have intentionally used AU to emphasize the fact that the static type checking might have marked some actions occurring in AU , e.g. those actions read(. . .)@lC in R. Upon completion of

7 DISCUSSION

22

the protocol, the net will be lU lC

::[lU 7→{o,i,r,e,n},lP 7→{o},lC 7→{r}] R k lP ::[lP 7→{o,i,r,e,n},lC 7→{o,i,r},lU 7→{o}] AP ::[ ] hpaper1i | hpaper2i | . . .

k

To conclude this section, we want to remark three features of this example that shed light on some peculiarities of our framework. 1. P ’s papers cannot be safely put in lP ’s TS because otherwise the integrity of P ’s publications could be compromised by the execution in lU of the legal process out(not− a− P− paper)@lP (see Section 7.3 for a deeper discussion on what kind of security policies µKlaim types can enforce). Indeed, differently from [19], our types are not so refined to, e.g., restrict the kind of tuples over which actions can operate thus enabling out(“Subscr”, lU : [lP 7→ {o}], CrCard)@lP and disabling out(not− a− P− paper)@lP . 2. The knowledge of address lC is not enough for reading papers: the ‘read’ capability is needed. Indeed, security in the µKlaim framework does not rely on name knowledge but on security policies. Moreover, once the ‘read’ capability over lC has been acquired, all processes eventually spawned at lU can access P ’s on-line publications. In other terms, U obtains a sort of ‘site licence’ valid for all processes running at lU . This is different from [12], where, by using the same protocol, U would have obtained a sort of ‘individual licence’ for process R. In the next section we will present variations of our framework that permit delivering different capabilities to processes running at the same node. 3. The licence delivered by P to U can be used only at lU since the capability specification associated to lC only delivers lU capability r over lC . Moreover, no denial-of-service attack could be mounted through the access of tuple h“Acc”, lC : [lU 7→ {r}]i located at lU by processes running at remote sites of the network because only processes running at lU can retrieve the tuple (see rules (M3 ) and (M4 ) in Table 7 and Remark 4.1). A similar argument holds for the tuple h“Subscr”, . . .i inserted by AU at lP .

7

Discussion

Up to now, capabilities are always acquired by the node hosting the process performing actions in/read, and not by the process itself. This may be adequate in some situations, e.g. when a department subscribes a ‘site licence’ (i.e. valid for all its members), and unrealistic in others, e.g. when a mobile process has to buy a good on behalf of its owner. Moreover, capabilities can only increase; this does not fit well to control wastable resources where one usually wants to count the number of times a given resource is used or to deliver accesses for a limited period of time. In the next two subsections, we will show that our framework can be smoothly tailored for taking into account these different scenarios, while the results of Section 5 are still valid. Finally, in the last subsection we will argue on some more general language design issues.

7 DISCUSSION

7.1

23

Variations on Capabilities Acquisition

Acquisition by Processes. We start modifying our framework in such a way that capabilities, in particular those dynamically acquired, can be associated to processes. To this aim, we annotate located processes with a type that specifies the capabilities they own. Thus, a process, in addition to the capabilities of the executing node that are shared by all co-located processes, can also use its own private capabilities. Now, a µKlaim node is of the form l ::δ AC, where AC is an annotated component generated from the following auxiliary syntactic productions ¯ ¯ ¯ ¯ AC ::= heti ¯ {{ P }}δ ¯ AC 1 |AC 2 Notice that only process components can be annotated. The structural congruence is modified by adding rule (Distr)

l ::δ {{ P |Q }}δ0 ≡ l ::δ {{ P }}δ0 |{{ Q }}δ0

that permits distributing the annotation of a parallel process over its components thus enabling application of the reduction rules. Moreover, rules (Alpha) and (Abs) in Table 2 are replaced with rules P ≡α P 0 (Alpha0 ) l ::δ {{ P }}δ0 ≡ l ::δ {{ P 0 }}δ0 (Abs0 )

l ::δ AC



l ::δ AC|{{ nil }}δ0

The static semantics does not need to be modified. However, when checking well-typedness, process type annotations must be used to augment the type of the node where the process is allocated. Thus, for a net to be well-typed, for each node with locality l and type δ, and for each annotated process {{ P }}δ0 located at the node, there must exist a process P 0 such that δ[δ 0 ]| l P . P 0 . Instead, the operational semantics is changed for managing the acquisition of capabilities that now causes the increasing of process type annotations while leaves types of nodes unchanged. In the initial configuration, all processes could have assigned the same empty type or could have assigned different and more informative type annotations, reflecting different capabilities for the processes. Table 10 presents the modified reduction rules (the rest of the rules are those in Table 8 with the obvious replacement in (Split) of C1 and C2 with AC 1 and AC 2 , resp.). The rules should be self-explicative. Notice that in (Mark0 ) only the type associated to the process is used to check admissibility of action a; this because the type of the node does never change (i.e. it is statically known) and has already been used in the type inference phase. Let us now briefly revise the subscription example. If in the initial configuration all processes have assigned the empty type, the evolution of the net according to the modified semantics leads to lU lC

::[lU 7→{o,i,r,e,n},lP 7→{o}] {{ R }}[lC 7→{r}] k lP ::[lP 7→{o,i,r,e,n},lC 7→{o,i,r}] P k ::[ ] hpaper1i | hpaper2i | . . .

where now R is the only process having the capability to access the papers stored at lC . Moreover, notice that the capability o over lU delivered by AU to AP disappears upon completion of the parallel component running at lP that handles AU ’s request. Indeed, at the end of its task such a component becomes {{ nil }}[lU 7→{o}] and hence can be removed by using rule (Abs0 ) above.

7 DISCUSSION

(Out0 ) (Eval0 )

24

et = T [[ t ]]δ1 [δ] 0

0

l :: {{ out(t)@l .P }}δ1 k l ::δ AC 0 Â− → l ::δ {{ P }}δ1 k l0 ::δ AC 0 |heti δ

0

0

δ 0 [δ1 ]| l0 Q . Q0 0

0

l ::δ {{ eval(Q)@l0 .P }}δ1 k l0 ::δ AC 0 Â− → l ::δ {{ P }}δ1 k l0 ::δ AC 0 |{{ Q0 }}δ1 matchδl (T [[ T ]]δ1 [δ] , et) = hδ 00 , σi

(In0 )

0

0

l ::δ {{ in(T )@l0 .P }}δ1 k l0 ::δ heti Â− → l ::δ {{ P σ }}δ1 [δ00 ] k l0 ::δ nil matchδl (T [[ T ]]δ1 [δ] , et) = hδ 00 , σi

0

(Read )

0

0

l ::δ {{ read(T )@l0 .P }}δ1 k l0 ::δ heti Â− → l ::δ {{ P σ }}δ1 [δ00 ] k l0 ::δ heti l0 6∈ L

(New0 )

L ` l ::δ {{ newloc(u : δ 0 ).P }}δ1 Â− → 0 0 0 0 0 L ∪ {l } ` l ::δ[l 7→δ(l)] {{ P [l /u] }}δ1 [l0 7→δ1 (l)] k l0 ::δ [l /u] nil l ::δ {{ rec X.P }}δ1 Â− → l ::δ {{ P [rec X.P/X ] }}δ1

(Rec0 ) (Mark0 )

l0 = tgt(a)

δ1 (l0 ) vΠ {cap(a)}

0

l ::δ {{ a.P }}δ1 k l0 ::δ AC 0 Â− → N 0

l ::δ {{ a.P }}δ1 k l0 ::δ AC 0 Â− → N Table 10: Acquisition by Processes: Operational Semantics

Acquisition by Nodes and Processes. In real situations, a (mobile) process could acquire some capabilities and, from time to time, decide whether it wants to keep them for itself or to share them with other processes running at the same node. An example of the first situation is a mobile process that buys a book and obviously wants the book to be delivered to the process owner. An example of the second situation is a process that subscribes a ‘site licence’ to enable accessing some on-line publications to be shared with all members of a given department. A simple way to model both cases is to use different acquisition actions depending on whether the acquisition should be made on behalf of the node or of the process. Hence, we could leave the operational semantics of actions in/read unchanged (i.e. as given in Section 4) apart for the replacement of processes with annotated processes, add actions inpr(T )@` and readpr(T )@` to the syntax, and model their operational semantics by using rules akin to (In0 ) and (Read0 ) in Table 10. In such a way, actions in/read would increase the type of the node where they are executed while actions inpr/readpr would increase the private type of the executing process. Therefore, in this variant of the calculus, processes are more powerful because, whenever capabilities are acquired, they can decide to share these capabilities with the other co-located processes or not. Of course, to enable controlling the new actions, we also need to introduce the corresponding capabilities and to extend the capability ordering relation. The static semantics can be smoothly extended (in particular, rule (T6 ) can deal with actions inpr/readpr too). Finally, notice that, since node types can dynamically change (like in the original semantics), in rule (Mark0 ) the hypothesis δ1 (l0 ) vΠ {cap(a)} must be replaced by δ1 (l0 ) ∪ δ(l0 ) vΠ {cap(a)}. Indeed, a marked action can be enabled both by the capabilities accumulated by the process and by the capabilities offered by the hosting node.

7 DISCUSSION

7.2

25

Managing Loss of Capabilities

In this subsection, we deal with some scenarios where capabilities can be lost. The three settings we shall present mainly differ in the formal definition of capabilities (more precisely, in the definition of the set Π) and in the way in which capabilities are lost. The main common feature is that the static inference is weakened since there are a lot of ingredients that can dynamically change. As it could be expected, more flexibility requires more run-time checks. Since in this subsection we need to express capabilities removal, we introduce notation δ = δ1 , δ2 to mean that δ = δ1 [δ2 ] and that δi (`) = δ(`)−δj (`) for each ` ∈ L∪U and {i, j} = {1, 2} (and similarly for capability specifications). The last requirement informally means that δi is obtained from δ by exactly removing all the capabilities delivered by δj . Consumption. If we interpret the ‘acquisition of capabilities’ as the ‘purchase of services/goods’, it seems natural that a process will lose the acquired capability once it uses the service. For example, by paying the price of a book a user purchases one copy of the book; if he wants another copy, he has to pay again. To enable multiple acquisitions and consumptions of capabilities, we should be able to count the number of capabilities that nodes/processes have over each resource (this is somehow similar to ‘affine’ capability types of [5]). To this aim, we modify our semantic framework by working with multisets of capabilities, instead of sets; in particular, Π now denotes the set of the multisets built upon {i, e, r, o, n}. All the operations over and relations between sets used in this paper (i.e., union, subset inclusion, ...) must be considered as operations over and relations between multisets. We start considering the case of dynamic acquisition and consumption of capabilities only by processes (this means that node types are statically known and left unchanged by the operational semantics). Differently from Section 7.1, process rights do not play any role in the static inference: indeed, since they can also decrease, it is statically impossible to rely on them to determine whether a given action will be legal at run-time or not. As an example, consider the net l ::[ ] {{ P |Q }}[l0 7→{o}] , where P , out(t)@l0 and Q , out(t0 )@l0 . In this case, exactly one between P and Q will be able to perform the action out while the other process will be blocked, depending on the execution order. However, it is impossible to statically tell which one will evolve and which one will get stuck (and hence both of them have to be marked). Moreover, the static semantics differs from that presented before because it has to mark all the actions, except those directly enabled by the access policy of the node where the inference takes place (i.e. those actions a such that [tgt(a) 7→ {cap(a)}] ¹ δ, 0 where δ is the type of the node). This is necessary to properly handle such nodes as l ::[l 7→{i}] in(!u : {o})@l0 .out(·)@l0 .out(·)@u, where the action in should be the only unmarked one after the static inference. Indeed, if we would use the static inference of Section 3, the second action out would not be marked; but now this generates a run-time error because if u is replaced by l0 upon execution of the in then the acquired capability o, that enables the execution of the second action out, is consumed for performing the first action out. As concerns the operational semantics, three major modifications need to be done to the rules of Table 10. Firstly, in rule (Mark0 ), capabilities must be decreased when they are used. The new formulation of the rule should be

7 DISCUSSION l0 = tgt(a)

26 0

l ::δ{{ a.P }}δ10 k l0 ::δ AC 0 Â− → N

δ1 = δ10 , [l0 7→ {cap(a)}] 0

l ::δ {{ a.P }}δ1 k l0 ::δ AC 0 Â− → N Secondly, since capabilities are numbered, when a new process is created, the capabilities of the creating process must be split between the two processes3 ; hence, the new formulation of rule (Eval0 ) is δ1 = δ10 , [δ100 ] δ 0 | l 0 Q . Q0 0

0

l ::δ {{ eval(Q)@l0 .P }}δ1 k l0 ::δ AC 0 Â− → l ::δ {{ P }}δ10 k l0 ::δ AC 0 |{{ Q0 }}δ100 Finally, since repeatedly accessing a tuple via actions read could arise a form of ‘capability forging’ (because, in general, each time the tuple is accessed, some capabilities are delivered), we also need to modify rule (Read0 ) that becomes matchδl (T [[ T ]]δ1 [δ] , et) = hδ 00 , σ, et0 i 0

0

l ::δ {{ read(T )@l0 .P }}δ1 k l0 ::δ heti Â− → l ::δ {{ P σ }}δ1 [δ00 ] k l0 ::δ het0 i where now function match also returns the tuple obtained by removing from et all the capabilities contained in δ 00 . The new formulation of function match relies on the following modification of rule (M4 ) if l ∈ dom(µ) then else

δ(l0 ) ∪ µ(l) vΠ π δ(l0 ) ∪ µ(others) vΠ π

∧ µ = µ0 , [l 7→ (π − δ(l0 ))] ∧ µ = µ0 , [others 7→ (π − δ(l0 ))] 0

matchδl (! u : π, l0 : µ) = h[l0 7→ (π − δ(l0 ))], [l /u], l0 : µ0 i where only the capabilities delivered by the tuple that are not already owned by the executing node are used to enrich the policy of the executing process (this is needed to avoid delivering the process capabilities already in δ). As concerns the other rules, (M1 ), (M2 ) and (M3 ) are modified to additionally return the tuple passed as second argument to function matchδl , while (M5 ) is modified to additionally return the tuple resulting from the concatenation of the two tuples returned by its premises. Taking up the example of Section 6, we can now program the acquisition (and the consumption) of a fixed number of capabilities r over the on-line repository. The user explicitly requires a number k of capabilities r and the publisher will charge on U ’s credit card the cost of k accesses to its publications. The processes implementing these behaviours are AU , out(“Subscr”, lU : [lP 7→ {o}], CrCard, k)@lP .in(“Acc”, !u : {k × r})@lU .R AP , rec X.

( in(“Subscr”, !u : {o}, !y, !z)@lP .

check credit card y of u and charge the cost for z accesses . out(“Acc”, lC : [u 7→ {z × r}])@u | X ) where { × r} stands for the multiset with occurrences of capability r. Finally, let us now briefly consider the general setting where both processes and nodes can dynamically acquire and consume capabilities. This scenario is the most expensive because a static inference phase cannot be exploited at all and all actions must be checked at run-time. In fact, since also node types can dynamically change, it is impossible to statically determine if a given action will have the necessary capabilities at run-time. Moreover, both the type associated to a 3 Similarly, when a tuple is generated, the capabilities delivered by the tuple must be removed from those of the process performing the out. Thus also rule (Out0 ) should be properly modified.

7 DISCUSSION

27

process and the type of the node where the process is running can provide the process with the capability necessary to perform a given action. In this case, the capability can be removed from the type of the node or from the type of the process, and a strategy must be implemented. The operational rules can be easily modified to enable the control on capabilities and the removal of the used capability, but, to save space, we do not show the modified rules. Validity duration. Another possible way of modelling capability lost is by introducing duration (as we already mentioned in the example of Section 6). Each capability can be assigned a validity duration by indexing it with a number representing a period of time during which the capability is valid, i.e. can be used; thus a capability is available until its validity has not been expired. For example, [l 7→ {i10 , o5 }] expresses the fact that it is possible to perform over l actions in for 10 time units and actions out for 5 time units. A ‘persistent’ capability, i.e. a capability whose validity never expires, is written without index (like the capabilities used so far). Notice that we can statically control only the operations that are enabled by persistent capabilities; all the other operations have to be marked, since it is not possible to exactly know when they will be performed. Moreover, for a process to be able to deliver a capability with duration τ , in the type enabling the action the capability must occur either as a persistent one or with a duration greater than or equal to τ (this is necessary to avoid forging capability durations). We leave out the details, since they are simple modifications of the previous theory, and finish by just hinting at an easy way of modifying our operational semantics to model time passing. For pointing out the passing of τ time units, we label net reductions with τ . All the rules in Table 8, except (Par) and (Struct), represent computational steps and are assumed to be instantaneous, thus the reductions occurring therein are labelled with ‘0’. Of course, this choice is far from being realistic but it allows us to incorporate durations in our framework with very small modifications and can however encode all real situations. The reductions contained in rules (Par) (that additionally requires that the net in the premise be well-formed) and (Struct) are instead labelled with a generic label τ . Because of the intrinsic asynchronous nature of our nets, we assume that time can pass differently in different parts of the net but, at each node, time passes uniformly for all the processes running there. This behaviour is implemented by adding to the rules in Table 8 the following one τ

l ::δ C Â− → l ::δ−τ C where δ−τ is inductively defined as [ ]−τ [l 7→ c]−τ [l 7→ cτ 0 ]−τ (δ[δ 0 ])−τ

= [] = [l 7 c] (→ [l 7→ c(τ 0 −τ ) ] = [] 0 = δ−τ [δ−τ ]

if c ∈ {i, r, o, e, n} and τ 0 > τ otherwise

Revocation. We shall now touch upon a new scenario where capabilities can be revoked, i.e. a node can delete capabilities of other nodes. To rule out obvious denial-of-service attacks, we allow l to remove a type δ from l0 only if l has previously passed a supertype of δ to l0 (notice that this

7 DISCUSSION

28

complies with standard trends in discretionary access control models). In doing so, we have also to take into account the fact that several nodes could have passed δ to l0 . We let S to be the set of the finite subsets of L and we let s, s0 , . . . to range over S. We now annotate capabilities, ranged over by c, with the identity of the deliverers, thus obtaining the set of annotated capabilities Π0 , ranged over by p. Formally, Π0 is the set of the subsets of {r, i, o, e, n} × S such that if (c1 , s1 ) ∈ p and c1 = n then s1 = ∅ and that if (c1 , s1 ) ∈ p and (c2 , s2 ) ∈ p, for some s1 6= s2 , then c1 6= c2 . Statically assigned capabilities take the form (c, ∅) and are abbreviated as c. We let the preorder vΠ0 on annotated capabilities to be defined by the following rules: s2 ⊆ s1 ∨ s1 = ∅

s2 ⊆ s1 ∨ s1 = ∅

p1 ⊆ p2

{(c, s1 )} vΠ0 {(c, s2 )}

{(i, s1 )} vΠ0 {(r, s2 )}

p2 vΠ0 p1

p1 vΠ0 p01

p2 vΠ0 p02

(p1 ∪ p2 ) vΠ0 (p10 ∪ p02 )

The key rule is the first one where, apart for subset inclusion between annotations, it is checked whether capability c has been statically assigned. The rest of the rules are obvious modifications of those defining vΠ . Capability specifications are left unchanged, while types now use annotated capabilities. We use γ to range over these annotated types that, formally, are functions of the form γ : L ∪ U →fin Π0 . E.g., the type [l 7→ { (i, {l1 }) , (o, {l2 , l3 }) }] used as security policy of node l0 enables actions in/out from l0 over l, and records that the capability i has been delivered by l1 while the capability o has been delivered by both l2 and l3 . The subtyping relation between annotated types, ¹0 , is defined like ¹ but of course relies on vΠ0 instead of vΠ . If γ1 and γ2 are annotated types, the extension γ1 [γ2 ] is the annotated type γ 0 such that γ 0 (`) = γ1 (`) + γ2 (`) for each ` ∈ L ∪ U, where p1 + p2 is inductively defined as ∅+p = p ( {(c, s)} + p =

{(c, s ] s0 )} ∪ p0 {(c, s)} ∪ p

if (c, s0 ) ∈ p and p0 = p − {(c, s0 )} if (c, ) 6∈ p

({(c, s)} ∪ p) + p0 = {(c, s)} + (p + p0 ) and s1 ] s2 is s1 ∪ s2 if both si 6= ∅, ∅ otherwise. Underlying the definition of ] there is the assumption that, if a capability has been statically assigned to a certain node (and hence one of the si is the empty set), then no other node will ever be allowed to revoke it; a similar motivation inspired us the definition of vΠ0 . When capabilities are passed via an action out, they must be annotated with the identity of the node where the out is being executed in order to enable their possible future revocation. Hence, the tuple/template evaluation function used in the operational semantics now takes the form {l} l l T [[ · ]]γ and is defined like T [[ · ]]δ except for clause T [[ l0 : µ ]]γ = l0 : ([[ µ ]]γ(l0 )−{n} ) , where µ{l} is defined as µ{l} (`) = µ(`) × {l}, for any ` ∈ dom(µ). To enable capabilities revocation, we add the operation revoke(δ)@` to the syntax of µKlaim actions. We do not use a specific capability to enable revoke: the operation is enabled only if l has previously delivered δ to l0 . Hence, the static inference system is modified by the addition of the following rule (T9 )

Γ| l P . P Γ| l revoke(δ)@l0 .P . revoke(δ)@l0 .P

The operational semantics of the new operation is given by the rule

7 DISCUSSION

29

(Revoke)

γ 0 = γ 00 , [δ {l} ] 0

00

l ::γ revoke(δ)@l0 .P k l0 ::γ C 0 Â− → l ::γ P k l0 ::γ C 0

where, for any ` ∈ L ∪ U, we let δ {l} (`) = δ(`) × {l}. We now show two possible uses of revoke in the example of Section 6. The first use consists in an alternative way of implementing the subscription for a fixed period of time d. Indeed, if we do not introduce validity durations as previously shown, we can let P to manage timing information: once U ’s capability r has expired, P can revoke it. A simplified process AP implementing this behaviour is AP

, rec X.

( in(“Subscr”, !u : {o}, !y, !d)@lP . check credit card y of u and require the payment for duration d.

out(“Acc”, lC : [u 7→ {r}])@u.out(u, T oday(), d)@lP0 .B | X ) B , rec Y. read(u, !s, !d)@lP0 .out(“check”, u, T oday(), T oday() ≥ s + d)@lP0 . ( in(“check”, u, T oday(), true)@lP0 .revoke([lC 7→ {r}])@u.in(u, s, d)@lP0 | in(“check”, u, T oday(), false)@lP0 . Y ) where lP0 is a reserved locality where P stores timing information (we have silently used basic values representing dates and booleans, together with some obvious operations over them). Another possible use of revoke in our example consists in revoking the access capability to a misbehaved user, e.g. a user that sold the acquired capability r to a third part at a lower price (thus being a tricky contender of P ). Notice, however, that the evidence of U ’s crime cannot be implemented in the calculus (but this is reasonable, since also in real settings to discover the crime and inform the publisher should be devolved to an external authority). The theory developed so far is perhaps the simplest way to model capabilities revocation. We now conclude by sketching more elaborated scenarios but, to save space, we will omit details. • According to rule (Revoke), the process revoke(δ)@l0 .P is stuck if only a subtype of δ is present in γ 0 . If we want to avoid this block, we can define the type γ1 u γ2 to be the greatest common subtype (w.r.t. ¹0 ) of both γ1 and γ2 , and redefine (Revoke) to be γ 0 = γ 00 , [γ 0 u δ {l} ] 0

00

l ::γ revoke(δ)@l0 .P k l0 ::γ C 0 Â− → l ::γ P k l0 ::γ C 0 Thus, we can remove from γ 0 the greatest subtype of δ delivered by l. • The proposed formulation rules out direct denial-of-service attacks like, e.g., the execution of action revoke(δ)@l0 by a process running in l, where l did not delivered δ to l0 . However, one can easily imagine a situation in which l spawns such a malicious process over an l00 that delivered δ to l0 . A simple way to avoid this is to define two inference systems: the first one is | l , the other one, denoted by || l , is defined as the first one but without rule (T9 ). We still use | l in the definitions of well-typed and of executable nets, while we use || l in rule (Eval) (in this way we block incoming agents containing actions revoke). This solution can however be over-restricting: a better (but more complex) solution is to define || l in a way such that revoke(δ)@l0 is deemed legal only if it is syntactically preceded by an action out delivering l0 some supertype of δ.

7 DISCUSSION

30

• The last scenario we consider is when l1 delivers δ to l2 and then l2 delivers δ to l. Should it be legal for l1 performing an action revoke over l? In our framework this is not the case. However, we could model this situation by annotating capabilities with subsets of S; each such subset represents all the (unordered) paths leading to the acquisition of the capability. E.g., if δ is annotated with the set { {l1 , l2 } , {l10 , l20 , l30 } } in the annotated type of l, then δ has been delivered to l through l1 and l2 and, independently, through l10 , l20 and l30 . The semantics has to be modified accordingly (to enable actions revoke over l performed by all the li and lj0 ).

7.3

Language Design Issues

Type information in the syntax. As we said in the Introduction, in µKlaim types play the role of security policies, hence they are part of the language. More specifically, apart from occurring in the specification of a node, type related information occur in three different syntactic constructs: • in newloc(u : δ) (node creation), δ specifies the security policy of the new node, • in ` : µ (locality passing), µ specifies the access rights delivered along with locality `, and • in ! u : π (locality reception), π specifies the access rights corresponding to the operations that the receiving process wants to perform at u. In the first two cases, type related information are not strictly necessary but are used to increase the flexibility of the language. Otherwise, some kind of ‘default value’ should be used in place of these information, like, e.g., the security policy of the creating node and the access rights currently owned by the executing node, respectively. In the third case, the information could be removed, thus relieving the programmer from the charge of specifying them. In fact, the set of access rights corresponding to the operations that the receiving process wants to perform could be statically inferred by using the alternative type inference system presented in the Appendix B. Types expressiveness. A somewhat related topic is what kind of security policies µKlaim types can express. When a remote action is performed, a network node could be involved in the action as the hosting node (i.e. the node where the action is executed) or as the target node (i.e. the node where the action acts). Since in general a remote node could be untrusted, a hosting node might want to control the remote operations a process can perform while running at it, for example, for preserving the secrecy of its information (in case of out/eval operations) or their integrity (in case of in/read operations). Conversely, a target node might want to control the remote operations acting over it, for example, for preserving the integrity of its information (in case of out/eval operations) or their secrecy (in case of in/read operations). µKlaim types directly enable a node to set up its security policy as a hosting node, but what if the node wants to set up its security policy as a target node? There are two obvious ways in which this could be implemented: one consists in modifying the operational rules and performing appropriate run-time checks according to the target node security policy (like, e.g., in the l sdπ-calculus [34]), the other consists in statically checking a whole (sub)net (like, e.g., in the original type system for Klaim [12]). In a global computing scenario both these two solutions have drawbacks: the former is inefficient and requires remote

7 DISCUSSION

31

synchronization, the latter is hardly implementable because network nodes are usually handled by different authorities. Our setting also provides a partial solution; indeed, as we notice in Remark 4.1, the matching rules (M3 ) and (M4 ) in Table 7 permit constraining the nodes from where tuples can be accessed. The point is that, to be able to set up its security policy as a target node, a node should be able to take under its control which other network nodes know its address, and which capabilities these nodes own over it. In general this is not possible because the set up of security policies in the initial configuration is not modelled and no assumptions on the level of trust in the administrators/principals that have fixed such initial policies are made. However, by exploiting the following µKlaim mechanisms: 1. node creation and setting up of its initial security policy, 2. communication of a node locality along with the capabilities to use it, it is possible to trace and take under control the relevant information for all those dynamically created nodes. Therefore, a node willing to protect its sensitive resources, without relying on the security policies set up in the initial configuration, should create a new node, where all sensitive resources must be moved, and should carefully deliver the locality and the capabilities over this new node. Capabilities management mechanisms. Our type theory is largely independent from the underlying language. More specifically, given a language with the following features • a set of possibly remote process operations and a set of corresponding capabilities, • an ordering relation over capabilities, and • some linguistic primitives for exchanging capabilities, it is then possible to define a type theory similar to the one for µKlaim we have presented in this paper. Notice that, however, several choices were possible when defining most of the functions used throughout the paper; we shall only comment upon a couple of them. • When a tuple is produced, no check is made to control that the capabilities delivered through the tuple do agree with the security policy of the node where the tuple is being inserted. Such ‘compatibility’ check is desirable if the point of view is taken that a node is responsible for the information and capabilities it provides and it would give rise to a different notion of well-typed net. The check could be implemented by appropriately modifying the definition of tuple/template evaluation function so that also the security policy of the target node is taken into account when evaluating a tuple. • In rule (M4 ) only the capabilities actually required by the process enrich the type of the node performing the action in/read. An alternative could be to enrich the type with all the capabilities delivered to the receiving node by the producer of the tuple. In this way we could simply implement a mobile process A that collects all the capabilities over a node l owned by the nodes of a given subnet. Indeed, process A has to travel within the subnet and, for each visited node l0 , it has to perform two tasks:

8 RELATED WORK

32

– to collect the capabilities owned by l0 over l through the sequence of actions out(l : [l0 7→ ∅])@l0 .inpr(! u : ∅)@l0 (the first action determines the capabilities over l owned by l0 , while the second one assigns them to A); – to look for the next node to visit (whose address is properly stored in the TS of l0 ). This behaviour is not easily implementable with the formulation of (M4 ) in Table 7 (indeed, action inpr(! u : ∅)@l0 would have delivered no capability to A) that, however, seems to provide a better way to control delivering of capabilities.

8

Related Work

There is a lot of work on type systems for security in calculi with process distribution and mobility; however, to the best of our knowledge, the type system we have presented in this paper is the first one that permits dynamic modification of security policies. We conclude by surveying more strictly related work. The research line closest to ours is that on the Dπ–calculus [25, 23, 35], a distributed version of the π–calculus equipped with a type system to control capabilities of mobile processes over located resources (i.e. communication channels). Like µKlaim, the Dπ–calculus relies on a flat net architecture; however, differently from µKlaim, communication is local and channel-based, and node types describe permissions to use local channels (this is in sharp contrast with µKlaim types that aim at controlling the remote operations that a network node can perform over the other network nodes). Moreover, when processes migrate, at the level of the operational semantics no check is performed to verify a priori existence of the target node (that, if not present, is automatically created). However, existence of the target node is guaranteed by resorting to the type system. For establishing well-typedness, the type system in [25] needs considering the whole net while that in [23] only needs local information (because processes are dynamically checked whenever they migrate), and thus is similar to our approach. In [35], the global knowledge about the net is split into several parts (that are updated independently, but still in accordance with the global knowledge, via dynamic knowledge acquisition through communication) and, to reduce the amount of dynamic controls, a relation of trust among nodes is exploited (thus, no process coming from a trusted node is type checked). To better clarify similarities and differences between Dπ and µKlaim types, we shall now compare how mobility control is implemented in both settings by using types. More recently [22] a capability denoted moveS has been introduced in the type of locality k that allows all processes coming from a locality l ∈ S to be spawned over k.4 . This is different from the µKlaim mobility control: indeed, if the type of k contains [l 7→ {e}], then k can spawn processes at l. This example throws light on the differences between Dπ and µKlaim: in the former, the type of a locality is used to control the operations performed over the locality itself, while in the latter the type is used to control the operations performed by the locality. From a security point of view, the first approach is preferable; however, this use of types is possible in Dπ because it only implements local operations. 4 The

moveS capability generalizes the go capability of [25] that enables a process coming from a generic node of the net to migrate over k: indeed, the go capability is encoded in [22] as move∗ .

8 RELATED WORK

33

Hence, if a process running at l wants to perform an action over a remote locality k, then it has to migrate there and to perform the action locally: this fact enables the receiving locality k to typecheck the incoming process and to refuse it whenever it would not behave properly. In µKlaim this use of types is not possible because operations might be performed remotely. [41] presents Dπλ, a process calculus that results from the integration of the call-by-value λcalculus and the π–calculus, together with primitives for process distribution and remote process creation. Apart from the higher order and channel-based communication, the main difference with µKlaim is that Dπλ localities are anonymous (i.e. not explicitly referrable by processes) and simply used to express process distribution. Dπλ comes equipped with a type system that controls how processes use resources (i.e. channels) by guaranteeing that in well-typed systems processes that intend to perform inputs at a given channel are co-located. In [42], a fine-grained type system for Dπλ is defined that permits controlling the effect of transmitted process abstractions (parameterized with respect to channel names) over local channels. Processes are assigned fine-grained types that, like interfaces, record the channels to which processes have access together with the corresponding capabilities, and process abstractions are assigned dependent functional types that abstract from channel names and types. This use of types is akin to µKlaim one, though the differences between the underlying languages still remain. Confined-λ [26] is a higher-order functional language that supports distributed computing by allowing expressions at different localities to communicate via channels. In Confined-λ, authors of code can assign regions (i.e. subsystems) to values in order to limit the part of a system where a value can freely move; a secrecy-oriented type system is defined that guarantees that each value can roam only within the corresponding region. Communication is channel based, the transmissible process abstractions can be parameterized with respect to channel names, and the types of transmissible values permit restricting the subsystem where a value can freely move. A simplified version of such control can be performed in the setting we presented in this paper by properly programming direct access to tuples via capability specifications (see Remark 4.1). Finally, we want to mention some proposals for the Mobile Ambients calculus and its variants, albeit their network models and mobility mechanisms are very different from those of µKlaim. Among those more strictly related to security we recall the works disciplining the types of the values exchanged in communications [5, 3], those for controlling ambients mobility and ability to be opened [5, 27, 15, 6] and that for controlling resources access via policies for mandatory access control based on ambients security levels [4]. However, the Mobile Ambients calculus and its variants are quite different from µKlaim, mainly because they assume an hierarchically organized net architecture where nodes (i.e. ambients) and the part of the net therein enclosed move as a whole, and because communication is restricted within ambients boundaries. As an example, we want to mention some differences between the mobility control in Ambient and in µKlaim deriving from their different programming choices. Mobility in µKlaim is, using Ambient terminology, objective (in that the moving process is spawned by an external process), while most of the work on Ambient–derived calculi uses subjective moves (explicitly programmed in the moving entities). The mobility control defined in [27] for the Safe Ambient Calculus (similar to that presented in [5] for the Mobile Ambient calculus) consists in identifying those ambients, called immobile, that cannot be opened and autonomously move, but can host local message exchange and can receive

9 CONCLUSIONS

34

incoming ambients (that, however, cannot be opened therein). A somehow similar behaviour can be implemented in µKlaim by setting the policy of a node l to be [l 7→ {r, i, o, e, n}]; however, this type cannot rule out remote communications using l as the target node since l’s type cannot restrict actions remotely performed. [15] extends [27] by introducing ambient security levels: the types used there rule out the movement through (and the opening of) ambients of lower security levels. We believe that this feature can be easily introduced in the µKlaim setting, but we leave this point for a future work. Considerations similar to those made for Ambient-like calculi also apply to [38] that presents a distributed process calculus that combines the channel-based communication mechanism of the π–calculus with the hierarchical organization and mobility of localities of the Mobile Ambients calculus, together with a type system for controlling whether the channels are used for input/output remotely or locally.

9

Conclusions

We presented µKlaim, a process calculus where mobile processes communicate via multiple distributed tuple spaces, and its capability based type system that, by dynamically exchanging capabilities, enables the control of accesses to distributed resources and of process mobility. The type system presented in this paper has been designed to reflect the great dynamicity and open endedness of wide area networks. This has been obtained by using both compile-time and run-time local checkings/updatings. According to the terminology used in [37], the µKlaim framework exploits a combination of static and dynamic checking, and of in-lined reference monitoring implemented by adding marks to process actions that need run-time verification. Due to process migration and dynamic variation of capabilities, the µKlaim framework largely relies on dynamic checks. However, to increase efficiency, static checks are performed everywhere possible. A few variants of the µKlaim framework that enable processes to acquire capabilities for themselves, thus supplying processes running at the same node with different capabilities, and take into account also capabilities lost, have also been presented. We conclude by touching upon some directions for future research. We aim at providing our setting with features that better reflect real system security needs. We plan to introduce the possibility of grouping nodes into clusters having different capabilities and representing different roles; nodes should be able to dynamically change the cluster to which they belong and the type system would enable implementations of role-based access control. Furthermore, we intend providing mechanisms for tightly controlling and restricting process mobility within confined areas: this could prevent, for example, migrations on malicious nodes. Similar mechanisms could also be applicable to control propagation of sensitive data/tuples (like in Confined-λ [26]). Finally, we would like to integrate in µKlaim other security mechanisms, like e.g. those based on cryptographic techniques, both for the establishment of secure channels, and for ensuring process code integrity and authentication.

A PROOFS OF TECHNICAL RESULTS

A

35

Proofs of Technical Results

In this section we shall prove some technical results stated in the paper, namely Propositions 3.5, 4.3 and 4.4. Proposition A.1 (Proposition 3.5) For any Γ, l, C and C 0 it is decidable to determine whether the judgement Γ| l C . C 0 holds true or not. Proof: We firstly introduce the function ν(C) that gives an upper bound to the number of type inference rules that must be applied to establish the validity of a judgement Γ| l C . C 0 .   if C = heti or C = nil or C = X  1 ν(C) = 1 + ν(P ) if C = a.P or C = a.P or C = rec X.P   1 + ν(P1 ) + ν(P2 ) if C = P1 |P2 Notice that ν(C) is always linear in the number of operators occurring in C, hence it is finite and does not depend on Γ. We then prove the following lemma that trivially implies the thesis. Lemma A.2 For any Γ, l, C and C 0 the validity of judgement Γ| l C . C 0 can be established in at most ν(C) inference steps. In particular, exactly ν(C) rules are needed to validate the judgement, while a strictly lesser number is needed to disprove it. Proof: The proof is by induction on ν(C). The key observation is that the inference of the judgement Γ| l C . C 0 is driven by the syntax of C itself; hence, at any step at most one rule can be applied. Base case: ν(C) = 1 We reason by case analysis on the syntax of C. C = heti. In this case, the only applicable static inference rule is (T2 ) that permits deducing Γ| l heti . heti. Thus, the judgement Γ| l C . C 0 is valid if, and only if, C 0 = heti and this can be established in one step. C = nil. The proof proceeds similarly, once we replace heti with nil and (T2 ) with (T1 ). C = X. If C 0 6= X or if X 6∈ dom(Γ), then no static inference rule can be applied and the judgement does not hold. Otherwise, the only applicable static inference rule is (T3 ) and, again, the validity of the judgement can be proven in one step. Inductive case: ν(C) > 1 We reason by case analysis on the syntax of C. C = a.P . We further distinguish the case a is an action newloc from the case a is another action. a = newloc(u : δ). Due to the syntax of C, the only static inference rule that could be applied is (T7 ). For (T7 ) to be applicable it must hold that Γ(l) vΠ {n} and δ ¹ Γ[u 7→ Γ(l)]; otherwise, Γ| l C . C 0 does not hold. Moreover, it must hold that C 0 = newloc(u : δ).P 0 for some P 0 such that Γ[u 7→ Γ(l)]| l P . P 0 . Since, by definition, ν(C) = 1 + ν(P ), by induction we conclude that:

A PROOFS OF TECHNICAL RESULTS

36

• if such P 0 does not exist, then this can be determined by using less than ν(P ) steps, and hence we can confute Γ| l C . C 0 by using less than ν(C) steps; • otherwise, ν(P ) steps are needed for P and one step is needed to apply (T7 ). Thus, we can validate Γ| l C . C 0 by using ν(C) steps. a 6= newloc(u : δ). In this case, we can only apply rule (T6 ) that requires C 0 = markΓ (a).P 0 for some P 0 such that upd(Γ, arg(a))| l P . P 0 . The thesis then follows by induction. C = a.P . This case proceeds like the case a 6= newloc(u : δ) but using (T8 ) in place of (T6 ). C = C1 |C2 . The only inference rule that can be used in this case is (T5 ). To this aim, C 0 must be of the form C10 |C20 for some C10 and C20 such that Γ| l Ci . Ci0 for i = 1, 2. By using a straightforward induction on the latter judgements, the thesis follows. C = rec X.P . The only inference rule that can be used in this case is (T4 ). To this aim, C 0 must be of the form rec X.P 0 for some P 0 such that Γ[X 7→ ∅]| l P . P 0 . The thesis follows by induction. Proposition A.3 (Proposition 4.3) If L ` N Â− → L0 ` N 0 and loc(N ) ⊆ L then loc(N 0 ) ⊆ L0 . Proof: We firstly prove a technical lemma. Lemma A.4 If L ` N Â− → L0 ` N 0 and loc(N ) ⊆ L then L ⊆ L0 and loc(N 0 ) − 0 loc(N ) = L − L. Proof: That L ⊆ L0 immediately follows from the definition of the reduction rules because the set of localities on the left of ` never decreases along reduction. To show that loc(N 0 ) − loc(N ) = L0 − L we reason by induction on the length of the proof of L ` N Â− → L0 ` N 0 . The only significant base case is when rule (New) is used: in such case, L0 is obtained by adding to L the newly created locality l0 , which is the only locality in loc(N 0 ) − loc(N ). The inductive step is straightforward; when considering rule (Struct), notice that, if N ≡ N 0 , then loc(N ) = loc(N 0 ) (indeed, renaming only affects bound variables). Now, of course loc(N 0 ) ∩ loc(N ) ⊆ loc(N ) (notice that loc(N ) − loc(N 0 ) might be not empty because some localities occurring in N but not as addresses of network nodes may disappear in N 0 due to inter-process communication). Hence, if we take L = loc(N ), from Lemma A.4 we get loc(N 0 ) = (loc(N 0 ) − loc(N )) ∪ (loc(N 0 ) ∩ loc(N )) ⊆ (L0 − L) ∪ loc(N ) ⊆ (L0 − L) ∪ L0 = L0 . Proposition A.5 (Proposition 4.4) If N is well-formed and loc(N ) ` N Â− →∗ L0 ` N 0 then N 0 is well-formed. Proof: It is easy to prove, by induction on the rules, that the structural congruence ≡ preserves well-formedness of nets and that the reduction relation preserves closedness of processes located

B AN ALTERNATIVE STATIC TYPE CHECKING

37

over nodes of a net. Thus, we are only left to prove that the reduction relation does never transform a well-formed net into a net where two distinct nodes have the same address (indeed, the reduction rules could also be applied to nets that do not satisfy this property). To this aim, we first prove a Lemma stating that a single reduction step from a net N (that only contains closed processes) preserves the number of nodes having the same address. This property is expressed by using clone(N ) to denote the least number of nodes that should be removed from N in order the remaining nodes have different addresses (i.e. the remaining net to be well-formed). To formally define function clone(·), we exploit the auxiliary function mnl(·) (mnl stands for ‘multiset of node localities’), that when applied to a net returns the multiset of localities naming the nodes of the net, and is inductively defined over the syntax of nets as follows: mnl(l ::δ C) = {|l|}

mnl(N1 k N2 ) = mnl(N1 ) q mnl(N2 )

where {|l0 , . . . , ln |} denotes the multiset with elements l0 , . . . , ln and q denotes multiset union. Now, for any µKlaim net N , we can define clone(N ) as the cardinality of the multiset obtained by removing from mnl(N ) one occurrence of each different locality occurring in it. Lemma A.6 If L ` N Â− → L0 ` N 0 then clone(N ) = clone(N 0 ). Proof: We reason by induction on the length of the proof of the reduction L ` N Â− → L0 ` N 0 . The base case (i.e. one of the axioms (Out), (Eval), (In), (Read), (New) or (Rec) has been used) is obvious. In the inductive case, we reason by case analysis on the last rule applied. The cases of rules (Mark), (Par) and (Struct) (it can be easily seen that ≡ preserves clone(·)) easily follow by induction. Suppose now that the last applied rule is (Split) and let L ` N1 Â− → L0 ` N2 be its premise. Then, due to the form of the nets involved in the rule, we have clone(N1 ) = clone(N ) + 1 and clone(N2 ) = clone(N 0 ) + 1. Since the proof of L ` N1 Â− → L0 ` N2 is shorter than that 0 0 of L ` N Â− → L ` N , we can apply induction and deduce that clone(N1 ) = clone(N2 ), from which it follows that clone(N ) = clone(N 0 ) that proves the thesis. To conclude, note that a net N that only contains closed processes is well-formed if, and only if, clone(N ) = 0. Hence, by using Lemma A.6 and by a straightforward induction on the length of reduction sequences, the thesis easily follows.

B

An Alternative Static Type Checking

In this section we assume that templates are T ::= t | ! x | ! u | T1 , T2 , i.e. that information about access rights are no longer bound to locality variables. Since these information are necessary for establishing template vs. tuple matching at run-time, the task of correctly annotating a formal field binding a locality variable is left to the static type inference system. For example, if we have l ::δ in(! u)@l0 .P and P wants to perform actions out and eval at u, then the static inference records this fact by replacing ! u with ! u : {o, e}. Therefore, the µKlaim syntax must be enriched with annotated template fields. Formally, we have T ::= . . . | ! u : π

B AN ALTERNATIVE STATIC TYPE CHECKING

| l hΓ; nili . hΓ; nili

38

X ∈ dom(Γ)

| l hΓ; hetii . hΓ; hetii

| l hΓ; Xi . hΓ; Xi

| l hΓ[X 7→ ∅]; P i . hΓ0 [X 7→ ∅]; P 0 i | l hΓ; rec X.P i . hΓ0 ; rec X.P 0 i | l hΓ; C1 i . hΓ0 ; C1 0 i

| l hΓ0 ; C2 i . hΓ00 ; C2 0 i

| l hΓ; C1 | C2 i . hΓ00 ; C1 0 | C2 0 i cap(a) ∈ {o, e}

| l hΓ; P i . hΓ0 ; P 0 i

tgt(a) ∈ L

| l hΓ; a.P i . hΓ0 ; markΓ (a).P 0 i cap(a) ∈ {o, e}

| l hΓ; P i . hΓ0 ; P 0 i

tgt(a) 6∈ L

| l hΓ; a.P i . hΓ0 [tgt(a) 7→ {cap(a)}]; a.P 0 i cap(a) ∈ {i, r}

tgt(a) ∈ L

blv(arg(a)) = u e

| l hΓ; P i . hΓ0 ][u^ 7→ π]; P 0 i

!u : π/f !u]).P 0 i | l hΓ; a.P i . hΓ0 ; markΓ (a[ ^ cap(a) ∈ {i, r}

tgt(a) 6∈ L

blv(arg(a)) = u e

| l hΓ; P i . hΓ0 ][u^ 7→ π]; P 0 i

!u : π/f | l hΓ; a.P i . hΓ0 [tgt(a) 7→ {cap(a)}]; a[ ^ !u].P 0 i Γ(l) vΠ {n}

δ ¹ Γ[u 7→ Γ(l)]

| l hΓ; P i . hΓ0 ][u 7→ π]; P 0 i 0

Γ(l) vΠ π

0

| l hΓ; newloc(u : δ).P i . hΓ ; newloc(u : δ).P i

Table 11: µKlaim Revised Type Inference Rules A type judgment now takes the form | l hΓ; Ci . hΓ0 ; C 0 i and intuitively states that successfully checking C located at l within context Γ modifies C into C 0 (by appropriately adding action marks and annotations for locality variables) and modifies Γ into Γ0 (by adding, possibly temporarily, annotations for locality variables). Type judgments are inferred by using the rules in Table 11. There, blv(T ) denotes the locality variables bound by T and Γ]Γ0 denotes the function resulting from the disjoint union of Γ and Γ0 (i.e. Γ]Γ0 is the type Γ00 such that Γ00 (`) = Γ(`) ∪ Γ0 (`) and Γ(`) ∩ Γ0 (`) = ∅, for each ` ∈ L ∪ U). Moreover, we redefine function markΓ (·) to be ( a if Γ(tgt(a)) vΠ {cap(a)} markΓ (a) = a otherwise and use it only when tgt(a) ∈ L. Indeed, there is no reason now to block the inference (i.e. fail) if tgt(a) 6∈ L because no declarations are initially made when locality variables are bound about their use. The inference rules clearly distinguish the case that the target of an action is a locality from the case that it is a locality variable. In the former case, it is checked if the action should be marked. In the latter case, the type context in enriched to record that the current action is intended to be performed at a locality variable. This information is deleted from the context, and put in a template, !u : π/f when the action in/read binding the variable is inspected. Notation a[ ^ !u] denotes the action

REFERENCES

39

resulting from appropriately annotating the locality variables u e bound in a. Action newloc is always statically checked (i.e. it is never marked) and the premises | l hΓ; P i . hΓ0 ][u → 7 π]; P 0 i and Γ(l) vΠ π ensure that u is used according to the set of access rights Γ(l). Now, well-typed nets are defined as follows. Definition B.1 A net is well–typed if for each node l ::δ C there exists a component C 0 such that | l hδ; Ci . hδ; C 0 i. Like in Section 5, subject reduction and type safety could be stated and proved by relying on the corresponding notion of executable net, which is again a net resulting from the static inference phase (i.e. a net whose processes already contain all necessary action marks and locality variable annotations).

References [1] K. Arnold, E. Freeman, and S. Hupfer. JavaSpaces Principles, Patterns and Practice. Addison-Wesley, 1999. [2] L. Bettini, R. De Nicola, and R. Pugliese. Klava: a Java Package for Distributed and Mobile Applications. Software — Practice and Experience, 32:1365–1394, 2002. [3] M. Bugliesi, G. Castagna, and S. Crafa. Boxed ambients. In TACS 2001, number 2215 in LNCS, pages 38–63. Springer, 2001. [4] M. Bugliesi, G. Castagna, and S. Crafa. Reasoning about security in mobile ambients. In Concur 2001, number 2154 in LNCS, pages 102–120. Springer, 2001. [5] L. Cardelli, G. Ghelli, and A. D. Gordon. Types for the ambient calculus. Journal of Information and Computation, 177(2):160–194, 2002. [6] G. Castagna, G. Ghelli, and F. Z. Nardelli. Typing mobility in the seal calculus. In Concur 2001, number 2154 in LNCS, pages 82–101. Springer, 2001. [7] S. Castellani, P. Ciancarini, and D. Rossi. The ShaPE of ShaDE: a coordination system. Technical Report UBLCS 96-5, Dip. di Scienze dell’Informazione, Univ. di Bologna, Italy, 1996. [8] P. Ciancarini, R. Tolksdorf, F. Vitali, D. Rossi, and A. Knoche. Coordinating multiagent applications on the WWW: A reference architecture. IEEE Transactions on Software Engineering, 24(5):362–366, 1998. [9] N. Davies, S. Wade, A. Friday, and G. Blair. L2 imbo: a tuple space based platform for adaptive mobile applications. In Int. Conference on Open Distributed Processing/Distributed Platforms (ICODP/ICDP’97), 1997. [10] R. De Nicola, G. Ferrari, and R. Pugliese. Klaim: a Kernel Language for Agents Interaction and Mobility. IEEE Transactions on Software Engineering, 24(5):315–330, 1998. [11] R. De Nicola, G. Ferrari, and R. Pugliese. Programming Access Control: The Klaim Experience. In C. Palamidessi, editor, Proc. of the 11th International Conference on Concurrency Theory (CONCUR’00), volume 1877 of LNCS, pages 48–65. Springer-Verlag, 2000. [12] R. De Nicola, G. Ferrari, R. Pugliese, and B. Venneri. Types for Access Control. Theoretical Computer Science, 240(1):215–254, 2000.

REFERENCES

40

[13] P. Degano, F. Levi, and C. Bodei. Safe ambients: Control flow analysis and security. In ASIAN Computing Sciece Conference - ASIAN’00, volume 1961 of LNCS, pages 199–214. Springer, 2000. [14] D. Deugo. Choosing a Mobile Agent Messaging Model. In Proc. of ISADS 2001, pages 278–286. IEEE, 2001. [15] M. Dezani-Ciancaglini and I. Salvo. Security types for mobile safe ambients. In ASIAN Computing Sciece Conference - ASIAN’00, volume 1961 of LNCS, pages 215–236. Springer, 2000. [16] D. Gelernter. Generative Communication in Linda. ACM Transactions on Programming Languages and Systems, 7(1):80–112, 1985. [17] D. Gelernter. Multiple Tuple Spaces in Linda. In J. G. Goos, editor, Proceedings, PARLE ’89, volume 365 of LNCS, pages 20–27, 1989. [18] L. Gong. Inside Java 2 platform security: architecture, API design, and implementation. AddisonWesley, Reading, MA, USA, 1999. [19] D. Gorla and R. Pugliese. Enforcing Security Policies via Types. In Proc. of Security in Pervasive Computing (SPC’03), volume 2802 of LNCS, pages 88–103. Springer-Verlag, 2003. [20] D. Gorla and R. Pugliese. Resource Acces and Mobility Control with Dynamic Privileges Acquisition. In J. Parrow, editor, Proc. of the 30th International Colloquium on Automata, Languages and Programming (ICALP’03), volume 2719 of LNCS, pages 119–132. Springer-Verlag, 2003. [21] G. M. Graw and E. Felten. Securing Java. John Wiley and Son, 1999. [22] M. Hennessy, M. Merro, and J. Rathke. Towards a behavioural theory of access and mobility control in distributed systems. In Proceedings of FoSSaCS ’03, volume 2620 of LNCS, pages 282–299. Springer, 2003. Full version as COGS Computer Science Technical Report, 2002:01. [23] M. Hennessy and J. Riely. Type-Safe Execution of Mobile Agents in Anonymous Networks. In J. Vitek and C. Jensen, editors, Secure Internet Programming: Security Issues for Distributed and Mobile Objects, number 1603 in LNCS, pages 95–115. 1999. [24] M. Hennessy and J. Riely. Information flow vs. resource access in the asynchronous pi-calculus. In U. Montanari, J. Rolim, and E. Welzl, editors, Proceedings of ICALP 2000, volume 1853 of LNCS, pages 415–427. Springer, July 2000. [25] M. Hennessy and J. Riely. Resource Access Control in Systems of Mobile Agents. Information and Computation, 173:82–120, 2002. [26] Z. D. Kirli. Confined mobile functions. In 14th IEEE Computer Security Foundations Workshop (CSFW ’01), pages 283–294, Washington - Brussels - Tokyo, June 2001. IEEE Computer Society. [27] F. Levi and D. Sangiorgi. Controlling interference in ambients. In Proceedings of POPL ’00 , pages 352–364. [28] G. Necula. Proof-Carrying Code. In Proceedings of POPL ’97, pages 106–119. ACM, 1997. [29] F. Nielson and H. R. Nielson. Shape analysis for mobile ambients. In Proceedings of POPL ’00 , pages 135–148. [30] F. Nielson, H. R. Nielson, R. R. Hansen, and J. G. Jensen. Validating firewalls in mobile ambients. In J. C. Baeten and S. Mauw, editors, Proceedings of CONCUR ’99, volume 1664 of LNCS, pages 463–477. Springer, 1999. [31] A. Omicini and F. Zambonelli. Coordination for internet application development. Autonomous Agents and Multi-agent Systems, 2(3):251–269, 1999. Special Issue on Coordination Mechanisms and Patterns for Web Agents.

REFERENCES

41

[32] J. Parrow. An introduction to the pi-calculus. In J. Bergstra, A. Ponse, and S. Smolka, editors, Handbook of Process Algebra, pages 479–543. Elsevier Science, 2001. [33] G. Picco, A. Murphy, and G.-C. Roman. Lime: Linda Meets Mobility. In D. Garlan, editor, Proc. of the 21st Int. Conference on Software Engineering (ICSE’99), pages 368–377. ACM Press, 1999. [34] A. Ravara, A. G. Matos, V. T. Vasconcelos, and L. Lopes. A Lexically Scoped Distributed π-Calculus. Technical Report TR02-4, Department of Computer Science, University of Lisbon, 2002. [35] J. Riely and M. Hennessy. Trust and partial typing in open systems of mobile agents. In Proceedings of POPL ’99 , pages 93–104. Full version to appear in Journal of Automated Reasoning, 2003. [36] A. Rowstron. WCL: A web co-ordination language. World Wide Web Journal, 1(3):167–179, 1998. [37] F. B. Schneider, G. Morrisett, and R. Harper. A language-based approach to security. In Informatics: 10 Years Ahead, 10 Years Back. Conference on the Occasion of Dagstuhl’s 10th Anniversary, number 2000 in LNCS, pages 86–101. Springer, 2000. [38] P. Sewell. Global/local subtyping and capability inference for a distributed pi-calculus. In K. G. Larsen, S. Skyum, and G. Winskel, editors, Proceedings of ICALP ’98, volume 1443 of LNCS, pages 695–706. Springer, July 1998. Full version as Technical Report 435, Computer Laboratory, University of Cambridge. [39] Sun Microsystems. Javaspace specification. available at: http://java.sun.com/, 1999. [40] P. Wyckoff, S. McLaughry, T. Lehman, and D. Ford. TSpaces. IBM Systems Journal, 37(3):454–474, 1998. [41] N. Yoshida and M. Hennessy. Subtyping and locality in distributed higher order processes. In J. Baeten and S. Mauw, editors, Proc. of the Int. Conf. on Concurrency Theory (CONCUR), volume 1664 of LNCS, pages 557–572. Springer-Verlag, 1999. [42] N. Yoshida and M. Hennessy. Assigning types to processes. Information and Computation, 174(2):143– 179, 2002.

Suggest Documents