Programmable Active Services for SIP - CiteSeerX

1 downloads 0 Views 330KB Size Report
[13] Patrick Tullmann, Mike Hibler, and Jay Lepreau. Janos: A ... [17] Ian Welch and Robert Stroud Using Bytecode rewriting to add Behavioral Reflection to Java ...
Programmable Active Services for SIP Jean Deruelle, M. Ranganathan and Doug Montgomery [email protected], [email protected], [email protected]

Advanced Networking Technologies Division National Institute of Standards and Technology1 100 Bureau Drive, Stop 8920, Gaithersburg, MD 20899

Abstract

SIP-Based IP Telephony offers the promise of rapid service creation and dynamic deployment. SIP Services are fragments of code that are triggered by SIP Messages and can perform actions on behalf of registered users. We present Programmable Active Services for SIP (PASS), a technique that uses Java 2 Security and Java Bytecode re-writing to allow un-trusted services to run on SIP Signaling Servers. Our technique allows users to write and upload services as Java classes with no a priori constraints on the structure or content of the programs. This generality permits users to leverage the extensive Java libraries and to program new SIP servicesin familiar environments. We define an extended, SIP specific, Java security model that restricts the behavior of the executing SIP service and that constrains the computational resources that it consumes.

1.0 Introduction

Internet telephony initially had the appeal of being a low cost way of making voice phone calls. Users were willing to tolerate degraded quality or reduced function for lower cost. However, with cost differentials rapidly disappearing, the driving force for IP Telephony is evolving toward rapid service deployment – especially 3rd party service creation and user customizable services. In this paper we explore a technique that enables un-trusted users to upload safe services to SIP Servers. The Session Initiation Protocol (SIP) [1] can be used for a variety of peer-to-peer applications that run on Internetconnected hosts including IP Telephony, conferencing, Instant Messaging and networked games. Its primary function it to set up a session between communicating peers. A User Agent (UA) is a signaling termination point (i.e., a SIPenabled phone or device controller). The basic protocol objects in SIP are Message, Transaction and Dialog. Messages may be Request or Response messages. As in HTTP, a Request has a Request method and a request URI that identifies the resource to which the Request is directed. SIP Requests and Responses also have From and To headers that perform an analogous function as the identically named Mail headers. While being routed towards the resource identified by the request URI, Requests may traverse multiple intermediate nodes known as Proxy Servers. Responses in SIP maintain enough state to match them with outgoing requests. Responses are classified as provisional or final and should match an outgoing request in order to be sensibly interpreted by an application. A SIP Transaction 1 2

This work is a contribution of the U.S. Government and, as such, is not subject to copyright. This work identifies certain commercial products and standards so as to describe our work adequately. The National Institute of Standards and Technology neither recommends nor endorses these products or standards as the best available for the purpose.

1

consists of a SIP Request, followed by zero or more provisional responses followed by a final response. SIP is a Transaction oriented protocol. A Transaction may be created by the underlying SIP Stack upon sending an outgoing Request (Client Transaction) or when an incoming request is received (Server Transaction). For a given Transaction, the SIP maintains a Transaction State Machine (TSM) for which state transitions are based on the current state, the incoming message and a timer tick. A SIP Dialog is an end to end association between two communicating endpoints. Each endpoint establishes a Dialog on successful call setup. The purpose of call setup is essentially to establish such a Dialog to Dialog association. A SIP Service is a fragment of code that can intervene in SIP call setup and can be used to perform actions on behalf of a registered user. One of the attractive features of SIP is the ability to easily integrate call processing with other widely deployed protocols such as HTTP and Mail resulting in converged services. For example, if a proxy server detects that a phone is busy, it may redirect the caller to email the intended recipient instead. In order to provide such services, it would be advantageous to provide a developer the ability to access other APIs such as Mail and HTTP dynamically based on call state and to have a security mechanism that integrates SIP’s usage and security models and these other protocols. To illustrate with an example, a user B1 may wish to install a service (myBusyReferService) on Proxy P1 that can transfer the call on his behalf to his secretary (User B2) when he is busy and also forward mail to his email account alerting him that he missed a call. The call flow for such a service is shown in Fig. 1. [2]. User A attempts to call User B1 through a SIP Proxy server that forwards the call setup INVITE request towards user B1. User B1 is on the phone so his UA replies 486 Busy Here. The user B1 has uploaded a service to the proxy server myBusyRefer Service that Acks the busy response (F5), then forwards the INVITE to user B2 (F6) and sends Mail to user B1 indicating that user A called. User B2’s phone rings and returns the Ringing response to the Proxy which is forwarded to User A . User B2 answers the phone and his user agent sends the Proxy server a 200 OK message, which is acknowledged by A’s user agent. The Acknowledgement is forwarded to User B2’s user agent completing the call establishment. In setting up the call, User B1 creates a Client INVITE Transaction. Each message arrival at B1 sends the underlying Transaction State machine to a new state. The final response (200 OK) terminates the INVITE transaction. At this point the communicating endpoints both have a Dialog for the conversation. When either party wants to release the call, it sends a Bye message, identifying the call to be released which results in the Dialogs for the call being released at each of the User Agents. The above is a short overview of the protocol and omits many details. The reader is referred to [1] for a detailed description of SIP and to [2] for examples of the type of services that can be created using SIP. Because SIP is a symmetric and stateless protocol, there is a great deal of flexibility on where services may be deployed. Typically, such services run in a User Agent (UA) residing on the user’s own service platform (e.g., PC or IP Phone). The service can collaborate with a SIP Proxy server on behalf of the user to intervene in the call flow. This of course assumes that the user agent is connected to the SIP-enabled network and that the proxy is set up to interact with the UA where the service is deployed. While the symmetrical nature of the protocol allows flexibility, it is sometimes necessary to install new, or customized, services at a central rendezvous point for reasons of reliability, connectivity or function. For example, a user may wish to register a service that blocks calls when the user is in a meeting in the company of another user. Such services are best deployed on a presence server or at least such a service should have access to the presence information that is part of the state information stored by a presence server. The service may be thought of as a remotely executing autonomous agent that performs actions on behalf of the registered user in response to SIP messages. In the case where the service needs to reside on a central rendezvous point, it is conceivable that the service could be installed (uploaded to the server) by an un-privileged user. In the case where the service resides on the user agent on the user’s PC or dedicated IP phone, it is conceivable that the service will be written by a third party and deployed on the device or PC in a manner similar to a Java applet. Uploadable and downloadable SIP services have the same set of problems - either the server or user agent needs to trust software and content that has been written by a third party.

2

myBusyRefer Service Installed by B1

Proxy

Send B1 mail “A called”

User

B1

User

User A Invite F1 (100 Trying)

B2

Invite F2 486 Busy Here Ack F5 Invite F6 180 Ringing

180 Ringing

200

200 OK ACK

Bye

ACK Two Way RTP Established Bye 200 OK

200 OK

Fig. 2. A SIP Call flow consists of a causal sequence of messages exchanged between signaling components in setting up a SIP Session. A SIP Service is a fragment of code that intervenes in the call flow and performs actions on behalf of the user who installed the service.

In this paper we focus on uploaded services that run on a Proxy Server or central rendezvous point, but the concepts could be applied equally to UAs running downloaded services. Malicious attacks on server and client resources and masquerading and spamming attacks launched from proxy servers or SIP enabled phones become a real possibility in both cases. Two basic approaches exist to dealing with these problems, the first is to severely restrict the programming model (e.g., define the service as a limited set of XML tags). A careful implementation of the state machine defined by these tags will allow administrators to define policies that prevent attacks. A second approach is to allow general programs to implement new services, but to employ sandboxing to constrain the behavior of the running code. In this paper we consider the second approach. We describe a technique that enables new services to be developed as custom Java User Agents that use a standard Java API for SIP Signaling [3]. The compiled Java class files for these programs are dynamically uploaded and instantiated on our server. We use Java bytecode re-writing and the Java security mechanisms for containing and restricting the actions that may be performed by the uploaded code. We argue that our technique allows security policies to be defined for converged services. In the sections that follow, we examine the state of the art in SIP service creation, describe our approach and its implementation in detail, and discuss its advantages and disadvantages as compared to other service creation environments.

3

2. Approaches to SIP Service Creation There is a large and growing commercial interest in enabling dynamic service creation in Internet telephony environments. Several methods for writing and deploying SIP services have been devised and standardized including SIP CGI [4] SIP Servlets [5], JAIN SIP and SIP CPL [6]. We compare them with our approach in the related work section. From the point of view of SIP Signaling, a SIP Service needs to perform the following four basic functions [4]: (1) Proxying of Requests: Receiving a request, adding or modifying any of the headers, deciding on a set of servers to forward the request to, and forwarding it to them. (2) Returning Responses: Receiving a response, adding or modifying any of the headers, and passing the response towards the client. (3) Generating Requests: Creating a new request, originating at the server, placing headers and a body into the message, and sending it to a server. (4) Generation of Responses: Receiving a request, generating a response to it, and sending it back to the client. In addition to these signaling actions, a SIP Service may perform actions that are not directly related to SIP, like dynamic generation of web pages, playing a pre-recorded voice or music from a specified URL, etc. This flexibility and customization was previously unavailable on conventional telephone networks. Other than SIP-CPL none of the other service creation environments discussed above directly support dynamic uploading of programmable SIP Services. SIP-CPL allows unprivileged users to upload services. However, it restricts the programming model to simple if then else constructs, thus limiting the types of services that can be built. In this paper, we propose a more general approach in that we do not place any explicit constraints on the programming model. Our solution depends upon the existence of an underlying standard API for SIP Signaling and the ability to perform rewriting operations on uploaded services. We base our approach upon JAIN-SIP [3], a low level Java protocol wrapper for a SIP Stack. The JAIN-SIP API defines protocol abstractions as Java Classes, which map directly to the protocol abstractions defined in the SIP RFC. For example, there are Java classes that map to Transactions, Dialogs and Messages. Protocol specific events of interest are delivered to the application as Java Events. A service in our model consists of a Java Program that utilizes the JAIN-SIP API and also has restricted access to other java APIs such as java Mail and HTTP. To create a safe programmable environment, we use an approach that is very similar to the Java sandbox approach that is typically used to restrict the behavior of Applets. We describe our approach in detail in the following sections. 3 Outline of Proposed Solution Our solution is specific to the Java programming language and relies on the Java Runtime model and Java Security. We leverage the Java Sandbox model and the existence of a standard API (i.e., JAIN) for SIP Signaling. We assume our service is a SIP Signaling application built on top of a trusted JAIN-SIP stack implementation. Each service runs in its own JVM process. The diagram below shows the architecture of such an environment. JAIN-SIP exposes an event-driven model to applications.3 The application implements the javax.sip.SipListener Java interface. JAIN-SIP is defined entirely using JAVA interfaces, thus providing a clean separation between design and implementation and supporting application level portability between different JAIN-SIP implementations. Applications create requests, URIs and headers using Factory objects. Applications use the SipProvider to perform protocol-relevant actions such as creating transactions and sending messages. The application receives event notifications from the stack via the SipProvider. The architecture is sketched in Fig. 2.

3

The Events are essentially the interaction between the Transaction Unit (TU) and the stack in the Transaction State Machine described in the SIP RFC 3261.

4

JAIN-SIP App/Service (un-trusted)

JAIN-SIP stack (Trusted)

SipListener

SipListener SIP Messages

SIP Events

SIP Messages

SIP Events

SipProvider

SipProvider

Listening Point

Listening Point

Stack

Stack Network

Fig. 2. Architecture of system. The application implements the SipListener interface and interacts with the stack using the Provider and Transaction and Dialog objects. The SIP Stack is trusted. The listener class is un-trusted and runs the user’s service code. The listener is a general piece of java code that has resource usage restrictions placed on it at run-time.

The SipListener reacts to events posted to an event queue and can create SIP Transactions, create and send outgoing Requests and create and send outgoing Responses. It performs these actions through the SipProvider and the Transaction and Dialog protocol objects that it creates. There is a single instance of SipListener per SipStack Instance and a single SipStack instance for a given IP address. A ListeningPoint corresponds to a port and transport at which the stack listens for incoming requests and responses. The code fragment below shows the javax.sip.SipListener interface, for which the user provides an un-trusted implementation that is uploaded and run (see [3] for further details) : public interface SipListener extends EventListener { public void processRequest (RequestEvent requestEvent); public void processResponse(ResponseEvent responseEvent); public void processTimeout (TimeoutEvent timeoutEvent); }

Although JAIN-SIP allows the implementer to implement the listener in another process than the stack, we will initially assume, for simplicity, that the SipListener runs in the same JVM as its stack and we assume that each service runs in its own JVM along with its own stack instance. This allows us to use operating system process isolation and standard JAVA security mechanisms to contain the actions of the process as a whole and to distribute load over a farm of machines if necessary for scalability. Operating system security mechanisms, however, are not sufficiently fine grained for IP telephony services. For example, the administrator may wish to allow specific users the ability to dial out only to specific SIP URLs. This translates into the ability to create socket connections to specific addresses which are not known a-priori because there may be a SIP Registrar lookup process involved in determining the address. As a second example, the administrator may wish to restrict the user installed service to send mail only to a restricted set of addresses (for example the same domain as the SIP URL). These are permissions that are specific to SIP and SIP services that are difficult to realize at a lower layer. Our assumptions are that the stack is trusted and can have unrestricted access to machine resources subject to the Java platform security policy whereas the Application that runs on top of the stack in the same JVM (ie. the SipListner) must be subject to restrictions which are finer grained and specific to the SIP call flow. If not subject to such restrictions, the un-trusted application can launch

5

spamming attacks from the server or use excessive server resources, thus denying resources use to other services/users. As described above, the service is a long running process that houses some user written classes. It may need to be remotely started or stopped by administrators or the user that installed the service. We need to provide this facility for uploaded applications. Finally, we need to do this in a portable fashion, so that our solution may apply to any JAINSIP implementation. Later, we will show how our design is adapted to work in the context of a managed environment such as the Java Service Logic Execution Environment [7] To implement these restrictions, we use bytecode re-writing and the Java Security Framework to define a new set of permissions for un-trusted JAIN SIP applications. We focus our approach on the SipListener implementation which is the un-trusted application. When the SipListener class is loaded to the SIP stack, the class loader rewrites the class bytecodes to call a security policy manager before every interaction with the SipProvider. This mechanism ensures that the Listener can only interact with the Provider in a restricted fashion. Using this technique we are also able to control interactions between the listener and other restricted JAVA APIs for which a suitable security models have not currently been architected and to restrict the classes which the Listener is allowed to access. For example, the Listener cannot use certain classes in the java.net hierarchy directly whereas the stack can. Bytecode rewriting allows us to insert callouts to the policy monitor in any part of the Listener and gives us the flexibility to make the policy and its enforcement application-specific. Bytecode rewriting is also used to control runtime behavior not traditionally covered by Java Security. For example, we leverage bytecode rewriting to monitor the memory allocation of running uploaded services. If the amount of memory allocated by a service exceeds a specified bound, a runtime exception is thrown. This is done by monitoring heap-size periodically while the service is in execution. We do not re-write the bytecodes of the Trusted Computing Base (i.e. the java class libraries and the JAIN SIP Stack itself). We assume that we can extract enough semantic information from the arguments passed to the invoked functions to not necessitate further run-time checking when these are in execution.

4. Implementation

We use a simple web server as the front end to our system. Users of the system are subject to authentication and must have an account on the system in order to upload their services. Once authenticated, a user is assigned to a group that defines the resource allowances granted for their uploaded services. Each new service class file is uploaded to the web server through a web form.. The web server uploads the service from a user specified URL, re-writes the listener to restrict its access to machine resources as described above and runs the modified service class in its own trusted stack. Every group will have a set of both standard Java permissions and our own customized SIP permissions. The Java permissions use well-known “policy” files to can control many things such as access to system or network resources. While this suffices for resources such as file access, it is not expressive enough to be able to restrict resource usage at the level of a SIP Call Flow. To restrict resource usage at this level, we define additional permissions that are specific to SIP (e.g., blocking outgoing calls to one or more specific domains). We need to define permissions in terms of highlevel concepts such as the permission to make outgoing calls or to receive incoming calls from specified URLs, etc. These general permissions need to be mapped to JAIN SIP method invocations. We present an example of such permissions in Fig. 3.

6

grant codeBase "file:D:/Uploads/guest/-"{ permission java.net.SocketPermission "*:1-", "accept,connect,listen,resolve"; permission gov.nist.security.permissions.SipStackPermission "create"; permission gov.nist.security.permissions.SipCallPermission "sip:*@nist.gov", "outgoing,incoming"; permission gov.nist.security.permissions.MailPermission "[email protected]", "outgoing"; };

Fig. 3. Policy File Example that allows a user to only make calls to the nist.gov domain.

Once the new service class is uploaded, its bytecode rewritten to insert appropriate security and resource monitors, it can be loaded and run in a JVM on top of a trusted JAIN-SIP stack. The re-written service (SipListener implementation) class interacts with the trusted stack using the Provider/Event mechanism previously outlined. In addition to the uploaded service, we add monitor classes to the service that restrict access to the SIP API. Checking methods of these security monitor classes are invoked before access to each JAIN-SIP method. These need to be inserted into the uploaded class before running it. This is accomplished as follows: A given high-level call permission maps to one or more underlying JAIN-SIP calls. To make our approach modular, we define a mapping file (details omitted here) that maps a given high level call permission to underlying JAIN-SIP calls. This mapping provides an easily interpreted semantic layer between the high-level call permissions and the underlying JAIN-SIP implementation. It specifies the argument patterns that should be checked for each Listener call to a JAIN SIP method so as to enforce a given high level resource restriction. This mapping is used during the bytecode re-writing step to insert calls to the appropriate security policy enforcement code. The policy enforcement code checks the arguments to the JAIN-SIP method by using the specified argument pattern. This mechanism allows us to specify patterns for parameters to SIP calls and may be structured by a SIP expert. This approach facilitates extensibility of the security framework. This mapping file is input to a bytecode re-writing tool based on BCEL [8] that re-writes the submitted class file and inserts code fragments that check the parameters of the invoked JAIN-SIP API method. For example, if we wanted to restrict the application so that the Listener can only accept calls from the nist.gov domain, we would put security checking around the portion of the listener that sends the response to an incoming SIP INVITE request. Here is the code to send a Response in the original Listener: serverTransaction.sendResponse(response); After re-writing and insertion of security hooks this fragment of code would look as follows: SecurityWrapper.checkSendResponse(response); serverTransaction.sendResponse(response);

7

Note in the example above that the call would not complete unless the response is sent by the stack fielding the incoming request and hence it makes sense to wrap the portion of the code that sends the response. checkSendResponse checks the arguments to the JAIN-SIP serverTransaction.sendResponse method using an argument pattern that specifies the permissible response pattern. Clearly, bytecode re-writing is not strictly necessary to accomplish the above. It would also be possible to build such restrictions by incorporating a security policy layer into the SIP Stack itself. The real case for using bytecode rewriting arises when different JAVA APIs are used to build converged services and when the resources that are being restricted do not come under the normal set of resources addressed by the standard JAVA security model. As previously mentioned, interesting services may be built by combining SIP Signaling with other Java service APIs such as HTTP and mail. To facilitate this, we define additional permissions for selected APIs such as the Java Mail API. As standard security permissions do not exist for all such API, we use our technique of bytecode rewriting to wrap access to such API. The approach is essentially the same as the approach for JAIN-SIP above. For example, the MailPermission in Fig. 3 allows the uploaded service to send mail but only to [email protected]. Such policies are fairly simple to implement; however, more complex scenarios are possible where access to the Mail API depends on previous SIP signaling messages. Thus, for example, a user may wish to design a service which attempts to contact the recipient by mail if he is not available by phone. However, to prevent the un-trusted service from launching a spamming attack we may specify that the service may only send mail to the address specified in the To header of the SIP INVITE request. To implement such a restriction, the security enforcement mechanism must watch for the response to the outgoing INVITE request. If the response indicates that the called phone is busy, the user’s service must be restricted to only allow mail to be sent to the name and address specified in the To header of the busy response. The implementation of such a dependent security polices is relatively easy to accomplish using the technique of bytecode re-writing but would be difficult to achieve using a static technique because there is no direct coupling between SIP and Mail and thus the Mail API and SIP API are impervious to each other. We illustrate the re-writing operation that needs to be performed for this service with the code fragment below: After bytecode re-writing the code in the processResponse method of the un-trusted SipListener that sends mail to the address of the busy recipient is shown below: public void processResponse (ResponseEvent responseEvent ) { // response is the received response extracted from responseEvent //Check the response code of the response event and if the ToHeader toHdr = SipURI toUri

(ToHeader) response.getHeader(To.NAME);

= (SipURI) toHdr.getAddress().getURI();

// Construct the SIP URI from the To address of the response String toAddress = toUri.getUser()+”@”+fromUri.getHost(); FromHeader fromHdr = (FromHeader) response.getHeader(From.NAME); SipURI fromUri = (SipURI) fromHdr.getAddress().getURI(); String fromAddress = fromUri.getUser()+”@”+fromUri.getHost(); // callee is busy forward mail to his To address if (response.getStatusCode() == Response.BUSY_HERE) { // Get a Mail Session object and set up the outgoing mail // server etc. (code omitted) // Construct the outgoing mail message javax.mail.Message msg = new MimeMessage(session); msg.setFrom(new InternetAddress(fromAddress)); msg.setRecipient(javax.mail.Message.RecipientType.TO,new InternetAddress(toAddress) );

8

// Set the msg subject and date for the outgoing message //This code is inserted by the bytecode rewriting step //It will verify that the service code can only send messages //to the address specified in the To header SecurityWrapper.checkMailPermission(responseEvent,msg); Transport.send(msg); } …… } The MailPermission.checkMailPermission(Response,Message) method implements a check that the Mail message To header corresponds to the SIP Response To Header and the Mail message From header corresponds to the SIP Response From header, the SIP Transaction is in the COMPLETED state . The From and the To headers of the SIP Request are logical headers and it may be argued that this solution still leaves room for spamming. However, this is no worse than the kind of spamming attack that can be launched from the user’s machine and further, the outgoing mail server and incoming mail server on the receiver end can be configured to thwart such attacks. Clearly, we may have also sent mail from the callers machine rather than from the service platform, assuming the caller had such a capability on his device. To make this entirely general, we need a way to describe the causal sequence of SIP messages for call setup as a template and be able to match that template with what actually happened during the SIP call setup. For this work we adopt a static framework that is less descriptive and more coarse grained. The checkMailPermission would look at the SIP response and ensure that the appropriate constraint is met. We may further want to restrict the application such that it may only send mail when it is participating in a SIP call setup and when the stack fields a 486 (Busy) response and the transaction state at that time is COMPLETED. Note further, that we can specify a constraint on the domain to which mail can be set as a separate mail permission. The security policy for the restriction that we desire is expressed as follows: permission gov.nist.security.permissions.MailPermission “*.nist.gov”, “EventType = ResponseEvent, statusCode==486, transactionState==COMPLETED_STATE, Mail.from=response.From, Mail.to=response.to” To place bounds on the amount of CPU cycles that can be consumed by un-trusted code, we proceed as follows (1) parse the uploaded bytecode in the class file, looking for branching constructs, thus logically dividing the class file into basic blocks that contain no branching constructs (2) count the number of Bytecode instructions in each basic block and (3) insert a callout to a Bytecode resource accounting monitor at the end of every basic block, passing as a parameter the number of bytecode instructions in that basic block. The resource monitor accounting function tracks the bytecode usage since the beginning of the Listener invocation by keeping a global variable which is initialized to 0 at the start of the SipListener method invocation. If the instruction limit is exceeded during execution, the service exits at the next check. This basic approach could be subverted by unrolling loops – ie. writing a long linear list of instructions instead of a loop. We can prevent such artificially unrolled code by simply limiting the size of the class file uploaded. Note that we restrict our check only to the un-trusted service code and hence do not re-write the JDK class libraries or the JAIN SIP implementation. Memory resources are limited by checking the heap size at the end of every allocation request. The base heap size is read when the service is initialized and is subsequently checked dynamically when the service executes. Before the check is made, if the heap limit for the service is close, we invoke the garbage collector synchronously. We then invoke the System Java method to calculate the amount of available heap space. Synchronous garbage collection is of course not precisely accurate and frequent invocations of the garbage collector can slow the system down and thus this method adds a high overhead. However, it is not our aim to provide fine grained control. Instead using this technique

9

we can detect programs that try to subvert the service platform by deliberately or inadvertently allocating unlimited amounts heap space. We can also apply more complicated policy over memory usage such as memory residency time to deal with spikes in heap usage or call the memory check only when the allocated size is large. Note that, the technique of counting bytecode instructions prevents runaway stack allocations. That is, a listener which indefinitely recurses will eventually run into its Bytecode limit and be terminated. A ListeningPoint in JAIN-SIP corresponds to the IP Address, Port combination through which the stack listens for incoming messages. When the ListeningPoint of the application initializes, it binds to a local IP port. This presents another vulnerability – the uploaded code can try to successively bind to ports on the server and use up the available ports or check if reserved ports are being used. This information may be used by malicious code to effectively do a port scan. To prevent this kind of attack, we restrict the number of ListeningPoints that the uploaded application can create and the ports to which these listening points can be bound. Table 1 describes the new security permissions we have defined and the utility of each permission. The Stack permission allows the user’s application to instantiate a SIP stack. JAIN-SIP allows applications to load a customized router. The Router permission allows applications to load such a router. JAIN SIP applications may act either statefully or statelessly. Stateless behaviour implies that the underlying stack does not create and manage transactions on behalf of the application. The application has a lot more freedom when it is operating statelessly and further, composite security policies that are dependent on the underlying transaction state machine may not be specified. The SipDialogPermission allows an application to create a specified number of Dialogs (thus limiting the ability of the application to fork). We have described the function and use of the other permissions earlier.

New JAIN-SIP Permission

Use

gov.nist.security.permissions.SipStackPermission gov.nist.security.permissions.SipRouterPermission gov.nist.security.permissions.StatelessBehavior gov.nist.security.permissions.SipDialogPermission gov.nist.security.permissions.SipPortPermission gov.nist.security.permissions.SipCallPermission gov.nist.security.permissions.MailPermission gov.nist.security.permissions.InstructionResourcePermission gov.nist.security.permissions.AllocationPermission

can create a SIP Stack can load a Router. can send stateless messages can create pre-specified # of dialogs can bind to pre-specified # of ports can initiate calls incoming / outgoing can send mail to specified address can run # of bytecodes in listener can allocate given # of bytes

4.1 Uploading, Registering and Controlling the Service On our prototype platform, uploaded services are submitted to the server via a web interface. The function of the web front end is to authenticate the user, rewrite the bytecode as described above, assign a port on which the service can run and provide an interface by which the user can remotely control the service. Before starting the service, the web server decides on which port the service gets to run. Incoming requests to the service have no idea what port has been assigned to the service so how can they advertise themselves? Fortunately SIP provides a mechanism to get around this problem. The web server also runs a SIP Stateless Proxy Server and SIP Registrar as a trusted service. We refer to these collectively as our Application Server. Any registered user can upload a service, start it and initiate a call toward another user through his service. The newly uploaded service has been bytecode rewritten to ensure the safety of our own server and block misbehaving actions as we have already described. The stateless proxy server is running at port 5060, which is a standard port for the SIP protocol. Its function is to forward incoming requests for registered users towards their service. The registrar and redirect server do not keep persistent state on behalf of users. The table entries for registered services that run on the registrar are timed out and need to be periodically refreshed. The scheme is shown in Fig. 4 below.

10

Fig. 4. JAIN-SIP Application Server overview. The Application server uses a well known port and the SIP Redirection mechanism to deal with dynamic port allocation.

As part of the bytecode re-writing step we extend the uploaded service by adding a trusted Monitor service class. The Monitor runs a thread that reads incoming control messages from the front-end web-server, thus allowing the user or administrator remote control over the service. When a user uploaded service initializes, the Monitor sends a REGISTER request to the Redirect server and periodically refreshes the registration. The web server registrar / redirect, thus does not need to maintain persistent state information about the liveness of services. The Monitor thread also listens on a localhost socket for incoming requests that may be issued to it by the web server via a Servlet. This allows the user to start/stop his service and query about the liveness of his service remotely. The user may do this by visiting the control web page corresponding to the installed application and issuing control messages via a Servlet to the service that he has uploaded.

5. Example Applications

To test the viability of our approach, we constructed a number of uploadable services and evaluated them. We describe some of these applications in this section. Our first test application is an uploadable SIP service that sends and email to the user when a call to his phone goes unanswered. It uses the Java Mail API to send the message. We use the mail permission discussed above to allow the user to only send mail to a pre-defined location thus preventing unscrupulous users from launching spamming attacks. This service is easily created using JAIN-SIP and the Java Mail API. The permissions file for the service is shown below: grant { permission java.io.FilePermission "D:\\Uploads\\nist-sip-dev\\deruelle\\-", "read,write";

11

permission java.net.SocketPermission "*:1-", "accept,connect,listen,resolve"; permission gov.nist.security.permissions.SipStackPermission "create"; permission gov.nist.security.permissions.SipDialogPermission "20","create"; permission gov.nist.security.permissions.SipPortPermission "2","create"; permission gov.nist.security.permissions.SipCallPermission "nist.gov", "outgoing,incoming"; permission gov.nist.security.permissions.InstructionResourcePermission "100000", "allowed"; permission gov.nist.security.permissions.MailPermission "nist.gov", "outgoing"; permission gov.nist.security.permissions.AllocationPermission "4000000", "allocation"; }; The ability to initiate a communication session independent of the endpoints connected to the session’s bearer channels is generally referred to as third-party call control (3PCC). This is our second test application; it allows a third party to initiate a call between two users. When a person registers with this service, and the user who uploaded this service registers, a call is initiated between the two people. This example of 3PCC can be easily upgraded in a click-todial application from a Web page, wherein a request to establish a voice session between two parties is initiated. The third-party call controller initiates communications via a signaling interface to each of the endpoints involved in the session. Our third test application is a proxy server upon which we restrict Call Flows to a fixed number of call legs (Dialogs). A user may use the proxy server to register a specialized proxy and registrar. When an incoming INVITE comes into the proxy, it can be forwarded to the location from which the user is actually registered, thus permitting user mobility. Note that a proxy server may normally wish to forward messages statelessly. However, in this case we want to restrict the proxy to only stateful behavior to prevent it from launching spamming attacks to domains other than the restricted set of domains which it is allowed to handle. Our fourth test application is an uploadable SIP service that forwards the call to a phone location through a PSTN gateway or a user agent location server when the user is not registered or busy. For example if the service is started, and someone tries to call this SIP address, they will be redirected to another user agent (e.g., a secretary’s phone). These simple examples illustrate the types of services that can be built and deployed using our service deployment platform. We have extended these ideas to work in a container based architecture such as the Service Logic Execution Environment (SLEE). SLEE exposes an event driven model to the application. The application consists of one or more container-managed Service Building Blocks (SBBs). SLEE services subscribe to Activities. An Activity is a related stream of Events. The overall architecture of the SLEE is shown in Fig. 5.

12

Resource Exception Resource Monitor Undeploy SBB

Fig. 5. JAIN-SLEE architecture with added resource monitor for the uploaded service. Note the additional monitor that is deployed along with the service that undeploys the service if resource exceptions are encountered.

When using SIP as a resource adapter, the SLEE becomes the listener and manages application logic which is deployed on the SLEE. SLEE allows the application to access the SipProvider and handle Server and Client transactions as shown in the code fragment below: Context myEnv = (Context) new InitialContext().lookup(“nist.gov”); acif = (JainSipActivityContextInterfaceFactory) myEnv.lookup("slee/resources/jainsip/1.1/acifactory"); fp = (JainSipFactoryProvider) myEnv.lookup("slee/resources/jainsip/1.1/provider"); provider = fp.getSipProvider(); addressFactory = fp.getAddressFactory(); headerFactory = fp.getHeaderFactory(); messageFactory = fp.getMessageFactory();

The one problem we encounter is that services cannot be un-deployed by just exiting the SLEE as that would cause all the managed services of the SLEE to be destroyed. Further, SLEE SBBs cannot passivate themselves so we have to modify our approach slightly. We rely on bytecode rewriting again, but now we need an external agent which is notified when the SBB misbehaves so as to request the SLEE to un-deploy the SBB in question. This is shown in Fig. 5. The utility of our approach is evident from the range of applications we have been able to build. These are fairly general and realistic JAIN-SIP applications and indeed, any existing JAIN-SIP application can be deployed as a service in this system. We evaluated the performance of our scheme by measuring the relative performance of several example services, both with and without our security and resource control mechanisms. Our experiments were performed on a 1.8 Ghz Intel Pentium with 1 Gb of RAM. As an example of one fairly substantial application, we tested the relative performance of an uploadable SIP proxy server. As a typical service, we instrumented the JAIN-SIP proxy server and concluded that the overhead was minimal. The overhead for the SipListener with bytecode

13

rewriting as compared to the original application for processing the REGISTER message was approximately 1.5% and the overhead for processing INVITE messages was approximately 1.6% with all security checking enabled.

6. Related Work and Conclusions SIP and SIP Services have been the focus of a great deal of industry activity and several standardization efforts. Among the several proposed methods of building general purpose SIP services, SIP CGI, and SIP Servlets have significant momentum in industry. SIP CGI [4] is similar to HTTP CGI. It works as follows: When a request arrives at a SIP server, initiating a new transaction, the server will set a number of environment variables, and call a CGI script. The script is passed the body of the request through stdin. The script returns, on stdout, a set of SIP action lines, each of which may be modified by CGI and/or SIP headers. The action lines allow the script to specify any of the actions defined in Section 2. SIP CGI is not language specific. The disadvantage of SIP CGI is that it there is no associated security or resource control model. Once a CGI script gets control, it has access to the entire resources of the machine. Thus, CGI scripts are typically installed by trusted users. SIP Servlets API [5] is a java extension API (javax.sip.servlet) that is similar to the HTTP Servlet API. The Servlet engine matches incoming SIP messages to a set of rules in order to decide which Servlet to pass control to. The API gives full control to the Servlet to handle SIP messages. The Servlet has full access to headers and body. The Servlet can proxy or redirect requests and can respond to or reject requests as well as forwarding messages upstream. The Servlet can also initiate requests. The Servlet engine can be collocated on the same host as the SIP Proxy server or can be hosted on a different machine, process or server. Servlets offer some advantages over SIP-CGI. There is no need to fork a new process for every call, thus reducing the latency of call setup. Servlets have full access to the Java API. SIP CPL [6] takes an XML based approach to service uploading. A SIP Service is defined using an XML Script and pattern matching primitives that allow the user to define actions under certain conditions. Because of the restricted programming environment, SIP CPL constrains what a service can do on the server. Within this restricted programming model a number of interesting services can be built and moreover, the use of XML makes the approach language neutral. This is an advantage of this approach. However, SIP CPL does not directly address the question of the other actions that a SIP Service may perform in addition to SIP Signaling such as invoking APIs from other libraries. Our approach of using bytecode rewriting for extensible Java Security is a popular technique that has been extensively researched [9]. Czajkowski and Eicken used this technique for resource accounting in java applications [10]. Their system (J-Res) was motivated by the application of developing extensible web servers. In contrast to J-Res we decided to run our services as separate processes rather than as separate threads within a given process. This limits the scalability of our system if all services are run on the same machine; however, it offers several advantages such as the ability to load balance the system by using a server farm to run the services if necessary. As we are allowing generalized access to application libraries, services may block in kernel calls for extended periods of time. Killing threads that are blocked in kernel calls is not portable. We would like to build on top of existing operating system mechanisms for process isolation that are portable across platforms. Finally, with minor modifications, we could run the system as a JAVA Isolate (a separate address space in the same JVM) using the Java Isolation API [11][12][13]. Rudys et. al. [14] describe the use of bytecode rewriting for runtime checking of Java Applications. A difficult problem that is encountered is safe termination of loops in an executing program. We do not have the problems of soft termination and rollback referred to in their paper because in our scheme, service runs in its own JAVA process and hence killing a service can be accomplished by just killing the service process. In the case of the SLEE application, the listener just exits and is removed. The container thus takes care of the cleanup issues. Note that SIP is Stateless and therefore, simply killing the service process is an acceptable termination solution – the end points that use the service will just time out. Villazon and Binder [15] describe a scheme where bytecode instruction counts are used to restrict the CPU resources used by Mobile Agents. This is very similar to the scheme we have followed. Using bytecode modification for Java security is also explored in the work by Chander et al. [16] where the authors examine various

14

denial of service attacks and how they may be prevented using bytecode rewriting. Kava [17] is a reflective Java that uses bytecode rewriting to enforce run-time security checks. Reflection is used to insert security checks into the compiled application to avoid re-compilation. Kava proposes a general model where pre-and post checking code may be placed before each security sensitive call. The Kava Metaobject framework could be used to implement the ideas proposed in this paper. We emphasize that the primary contribution of this work is in the use of bytecode rewriting to implement a composite security framework specific to SIP and SIP services rather than to explore the use of bytecode rewriting as a general technique. We have previously discussed the alternative ways of creating SIP Services and contrasted our technique with CPL. It should be apparent that we have imposed no restriction on the structure of the uploaded service and therefore the class of services we can support using our approach is larger than the class supportable using CPL. Defining a technique to deploy such general purpose code as a SIP service is the chief contribution of this work. Complex applications such as an entire stateful proxy can be uploaded and constrained without needing to manually modify the code. A tag-based approach such as CPL is language independent; however, extending the CPL framework involves defining and learning new tags. Our security framework is flexible and easily customized, thanks to the JAVA sandbox model -- you can change the different permissions applied on the services without restarting the server, just the service. One critique of our technique could be that although the permissions are fairly intuitive it still requires some experience to know what underlying API calls to map these to. While we have reduced the complexity somewhat using an intuitive mapping scheme, it could still be a bit confusing. We have addressed this problem to a limited extent by defining the mapping as an XML template file (not described here). Thus new permissions can be easily defined by experts who understand the SIP protocol and JAIN-SIP API without having to dig through the implementation. Our future work in this area is to define a set of XML templates for entire call flows so that SIP services can be defined by merely customizing the XML templates with user supplied code. This would hide the API altogether and make the scheme independent of JAIN-SIP. In conclusion, we have shown that the availability of a trusted stack and standard API can be combined with Java Security and bytecode re-writing to allow for user uploaded SIP services that can be written without restricting the programming model. Using this technique and the extensive Java class libraries, powerful SIP services can be deployed on end systems by un-privileged users, both in managed environments such as SLEE as well as in standalone environments where each service runs in its own process. Using the technique of bytecode rewriting, we are able to impose dependent restrictions on converged SIP services that may use the wealth of JAVA API for building such services. The work described in this paper may be downloaded from http://jain-sip-appserver.dev.java.net.

7. Acknowledgements This work was supported by the DARPA Active Networks Program and by NIST’s Information Technology Laboratory. The authors are grateful to Stephen Quirolgico for reviewing this material and providing detailed feedback to improve its presentation and readability.

References [1] J. Rosenberg, G. Camarillo, A. Johnston, J. Peterson, R. Sparks, M. Handley, E. Schooler, RFC 3261: SIP : Session Initiation Protocol, http://www.ietf.org/rfc/rfc3261.txt [2] SIP Service Examples IETF Draft http://www.ietf.org/internet-drafts/draft-ietf-sipping-service-examples-05

15

[3] Phelim O Doherty and M. Ranganathan, JSR 32 JAIN-SIP Specification and RI http://jain-sip.dev.java.net/

[4] J. Lennox, H. Schulzrinne and J. Rosenberg, RFC 3050: Common Gateway Interface for SIP, http://www.ietf.org/rfc/rfc3050.txt [5] Anders Kristensen, JSR 116 - SIP Servlet Specification http://www.jcp.org/aboutJava/communityprocess/final/jsr116/ [6] J. Lennox and H. Schulzrinne, RFC 2824: Call Processing Language Frramework and Requirements, http://www.ietf.org/rfc/rfc2824.txt [7] Java Service Logic Execution Environment http://jainslee.org/ [8] Markus Dahm The Byte Code Engineering Library (BCEL) http://jakarta.apache.org/bcel/ [9] Don Batory, Charles Consel and Walid Taha, Altering Java Semantics via Bytecode Manipulation, Proceedings of the 1st ACM SIGPLAN/SIGSOFT Conference on Generative Programming and Component Engineering (GPCE 2002), Pittsburg, PA Oct 2002

[10] Grzegorz Czajkowski and Thorsten Von Eicken, JRes Resource Accounting for Java, OOPSLA 1998 [11] Pete Soper, JSR 121 - JAVA Isolation API http://www.jcp.org/en/jsr/detail?id=121 [12] Godmar Back and Wilson C. Hsieh and Jay Lepreau Processes in KaffeOS: Isolation, Resource Management, and Sharing in Java, Proceedings of the 4th Symposium on Operating Systems Design and Implementation, October 2000. [13] Patrick Tullmann, Mike Hibler, and Jay Lepreau. Janos: A Java-oriented OS for Active Networks . Appears in IEEE Journal on Selected Areas of Communication. Volume 19, Number 3, March 2001. [14] Algis Rudys, Dan S. Wallach, John Clements 2001 Termination in Language-based systems Network and Distributed Systems Security Symposium (San Diego, CA), February 2001 [15] A. Villazon and W. Binder, Portable Resource Reification in Mobile Agent Systems, proceedings of The Fifth IEEE International Conference on Mobile Agents (MA'01), Atlanta, Georgia, USA, December 2-4, 2001 [16] Ajay Chander, John C. Mitchell, Insik Shin, Mobile code security by Java bytecode instrumentation, Proceedings of the DARPA Information Survivability Conference & Exposition, DISCEX-II 2001, Anaheim, CA, June 12-14, 2001 [17] Ian Welch and Robert Stroud Using Bytecode rewriting to add Behavioral Reflection to Java , Proceedings of USENIX Conference on Object-Oriented Technology, 119-130 (2001).

16