Integrating a System Server with UPS

Introduction

User Prompt Service (UPS) functionality is provided by the server process upsserver.exe. The process starts on demand when system servers make calls to the UPS client library. Only applications with a capability of ProtServ or higher can access upsserver.exe.

A system server should only invoke the UPS for security decisions that a typical phone user will understand (For example, it is inappropriate to ask the user for permission to change a low level network setting.) .

Device creators can write a system server that invokes the UPS, if a compatible dialog creator is available and the policies are delivered in an appropriately signed SIS file. They can also write a policy evaluator, but a default policy evaluator is provided by the Symbian platform.

If the device creators do not specify any policy file, the UPS uses the platform security check results to decide whether to access requested services. Thus not supplying policy files effectively turns UPS off.

Required background

Configuring the system server

For each system server that accesses the UPS, you need to do the following:

  1. Create one RUpsSession per server.

    Typically, an RUpsSubession object is created for each connection by a client to the system server and can only have one outstanding call to RUpsSubsession::Authorise().

  2. RUpsSubsession::Authorise() completes immediately and uses the result of its aServerCheckOk parameter to set its aDecision parameter, if a policy file is not defined for a service. Therefore, the default behaviour of an unconfigured UPS is compatible with the existing platform security implementation.

  3. Close each RUpsSubsession.

Considerations

Consider the following points before integrating the UPS to a system server

  • Even though the APIs for the UPS are relatively small, the integration is slightly more complicated than might be expected. This is because existing synchronous security checks must become asynchronous since interaction with the device user is inherently an asynchronous operation. For example, it would be undesirable to block the entire comms server while waiting for the user to respond to a dialog.

  • Provide an informed security decision the user may need to know about the request they are authorizing. Consequently, it may be necessary to extract the parameters from the client application at the security check stage.

  • Finally, depending on the service and the security requirements of the manufacturer, an operator user prompts may be in addition to the platform security (e.g. capabilities) checks. Alternatively, user prompts could be used to allow applications without capabilities limited access to a protected service. Because most implementations of the CPolicyServer fail the client immediately if the platsec check fails these checks may have to be modified.

Note: For an example of how to include the above, see the example code later in this document.

Integration of UPS

If the system server uses the CPolicyServer framework, there are three candidates for the main integration point.

Function

Description

CPolicyServer::CustomSecurityCheckL()

This allows an arbitrary and even asynchronous check to be made instead of using static security policies.

CPolicyServer::CustomFailureActionL()

This virtual method is invoked if the client fails the static security checks. The failure may be overridden or deferred. Asynchronous operations are possible.

CSession2::ServiceL()

The static policy checks could be deferred until the session is created provided that the server connection API is not guarded by user prompts.

Use one or a combination of above functions as required to implement UPS APIs

Procedure

This demonstrates how user permission prompt support (UPS) may be added to a system server.

  1. An RUpsSession object has been added to the CMsgServer class. This is initialized at startup.

  2. CMsgServer::iPolicyElements was changed to cause CustomFailureActionL to be invoked if the static security policy check fails instead of failing the client.

  3. CMsgServer::CustomFailureActionL() notifies the session object that the platsec check failed. This is important because RUpsSubsession::Authorise() requires this information to allow unconfigured systems to be compatible with platsec. It also increases the flexibility of the UPS policy definitions.

  4. An RUpsSubsession instance is created for each CSession2 object, i.e. requests from different clients are authorized independently. This could be modified so that there is one UPS sub-session for each client process instead of each connection from a client process.

  5. An extra state (iAuthorising) is added at the start of the CMsgProcessor state machine.

  6. The RMessage2 parameters from the client API are now extracted in the authorization stage to enable the information to be passed to the UPS.

  7. CMsgProcessor::DoCancel is updated to cancel the call to RUpsSubsession::Authorise, if the client cancels the sending of the message or disconnects.

  8. CMsgProcessor::RunL now checks whether the request was authorized before changing from the authorization to the sending state.

The following code snippet explains how the UPS APIs can be called.



using namespace UserPromptService;

TUpsDecision AuthoriseL(RThread &aClientThread, TDesC aDestination)
    {
 // Create and connect the session.
 // In a real server this should be done once at server startup as it is a relatively
 // expensive operation.
 RUpsSession session;
 User::LeaveIfError(session.Connect());
 CleanupClosePushL(session);

 // Initialise the subsession
 // One of these is required per a concurrent Authorise request.
 // Note that it only connects to the UPS if it has to.
    RUpsSubsession subsession;
 subsession.Initialise(session, aClientThread);
 CleanupClosePushL(subsession);

 TServiceId serviceId = {43};
 // Out "service id", typically a constant
 // Variable for the result, typically a member variable because it must exist until
 // the Authorise request completes.
 TUpsDecision decision = EUpsDecYes;

 // Issue the Authorise request
 TRequestStatus rs;
 subsession.Authorise(EFalse, // Did out platsec checks pass?
                         serviceId, 
                         aDestination, 
                         decision, 
                         rs); // Would typically be the iStatus of a CActive object

    // Wait for the request to complete
 User::WaitForRequest(rs);

 CleanupStack::PopAndDestroy(&subsession);
 CleanupStack::PopAndDestroy(&session);
 return decision;
 }


TInt E32Main()
 {
 __UHEAP_MARK;

 // allocating a cleanup stack also installs it
 CTrapCleanup* tc = CTrapCleanup::New();
 if (tc == 0)
  return KErrNoMemory;

 RThread thd;
 TRAPD(err, AuthoriseL(thd, _L("destination")));
 if(err != KErrNone)
  {
  User::Panic(_L("example failed: "), err);
  }
 delete tc;

 __UHEAP_MARKEND;
 return KErrNone;
 }