Symbian OS Platform Security/07. Sharing Data Safely

From Franklin Heath Ltd Wiki
Jump to: navigation, search
by Will Palmer Reproduced by kind permission of John Wiley & Sons. Prev.   Contents   Next

Introduction to Sharing Data

This chapter presents the key questions you need to ask – and some possible answers – to share your data in a secure way with Symbian OS platform security. It also shows you techniques you can use to implement data sharing in the Symbian operating system. Files and other resources were previously freely accessible to all running processes; this has led in some cases to bad modeling and design, to shortcuts, and, of course, to the creation of malware. With the advent of platform security this is no longer the case – access to files and other resources is now strictly controlled and managed, so developers need to consider how to share and access data securely.

We will consider all data that is accessed by more than one process. Although sharing data might involve, for example, several messaging applications accessing data from a message server, it also includes participating in a backup operation, or data being remotely provisioned through a device-provisioning server.

You should be sure of your need to share data. If your application’s data does not need to be shared, then it is safest to hide it by default. You do this by putting it into your process’s private directory, without implementing any accessor methods – and then you’ll have no need for this chapter.

Considerations in Sharing Data

When sharing your data with more than one process, there are a number of key questions to consider. These will all be developed in further sections.

Do you need to protect the integrity of your data?

You may need to guard against abnormal behavior of your application or of other applications that are a client of your data. Although you should always make your application robust in the face of unexpected or corrupted data, you may have data whose integrity is relied upon by a client to perform a sensitive action – for example, a certificate or secure URL.

Do you need to protect the confidentiality of your data?

Your data may need to be guarded against ‘prying eyes’ – for example, if you are storing a password to use for remote access. You might choose never to reveal the plain text of that password, or perhaps to allow the password to be retrieved only if the user supplies a PIN.

What attacks should you care about?

There are several types of attack on data, both on its confidentiality and its integrity. Some of these we have seen exploited in releases of Symbian OS prior to the enhancement of platform security, but new measures are provided to protect against all of them.

When should your data be available?

Do you want to share your data synchronously or asynchronously? You may want your data to be accessible even if your process is not running. You usually do this by making use of Symbian OS system services.

How much data should you share?

You should examine the granularity of your data below the file level. Often data is a collection of information, and you may want to police the elements separately.

There are other important considerations that are more implementation specific, and relate to ‘best practice’ when developing your application. For example, if you are implementing a server, what are the consequences of allowing concurrency of access to your data and how do you permit this to occur; what are the consequences of too weak or too strong an access policy? We will also cover best practice in more detail in our discussion about levels of trust.

Categories of Data

What’s Out of Scope

Data is a very general term, and encompasses anything from messages (signals, semaphores, datagrams) to settings (persistent configuration, transient property) to files (databases, resources, media ‘BLOBs’).

We restrict discussion in this chapter to settings and files. The primary mechanism for inter-process message passing is the Symbian OS client–server mechanism, discussed in Chapter 5. Other mechanisms for communicating information between processes, such as shared memory, semaphores and message queues, are also touched on in that chapter. Similarly, getting other processes to undertake tasks on your behalf – for example, the Task Scheduler and SendAs server – is outside the scope of what we discuss here.

What’s Out of the Platform’s Control

It is worth briefly touching on this topic, in order to clearly understand where the boundaries are. This is important to enable us to look at the ‘end-to-end’ security of our data. We have already mentioned in Chapter 1 how controls at the network boundary are becoming inadequate because of the trend towards seamless information flow between networks. There is, therefore, a need for a mobile phone to protect the information on it in a robust way, and also to protect any networks that it connects to.

A secure platform allows you to protect your data according to a policy that you choose. Capabilities that allow the use of local and remote network services are carefully assigned to trusted processes to control whether an application can communicate sensitive data across a network, but platform security does not control where a process sends data to or receives data from. We must rely on other technologies to provide end-to-end communications security – for example, a secure data channel protocol such as TLS/SSL, with authentication at each end of the link using digital certificates. By assigning a level of trust to a particular process, we have determined the policy for the type of information it can send from the mobile phone – for example, address book contact information that is synchronized to the PC. The synchronization process has capabilities ReadUserData and LocalServices, and we trust it to send on request only the subset of data that is policed by this capability.

We also cannot control how the data is used once it is received by an external entity. There can be no guarantee that the external entity will respect, or even understand, the security policy applied to the data. Once the data is off the Symbian OS mobile phone, we have no control over the level of privacy applied to the data.

Consider the ramifications of providing a backup mechanism in Symbian OS. Once the backup data is on the remote device – the PC, for example – it is the responsibility of the owner of the device to ensure that the backup data is not tampered with. On restore, Symbian OS can, of course, detect any evidence of tampering using hashing, but cannot prevent it. This is similar to the way it deals with removable media (see Chapter 8). Additionally, the backup architecture provides hooks that mobile phone manufacturers can use, at their discretion, to implement encryption of the backup data to assist with confidentiality. The story remains the same though – once data is off the mobile phone it is out of our control.

Persistency

Persistency of data describes the length of time for which data exists on the mobile phone. Data may either be transient – held in temporary memory and not surviving a reboot – or persistent – written to disk and, therefore, surviving a reboot. As examples of these different types, consider an instant messaging (IM) application. This application might have transient data (friends who are currently online, location information) that exists, at most, for the lifetime of the application, and persistent data (a list of friends, the configuration of the user interface) that is written to disk for use when the application restarts.

Once created, persistent and transient data may have a constant value – for example, mobile phone attributes such as manufacturer and firmware version. Resource files, such as fonts and branding elements (bitmaps, ring tones, wallpapers, etc.), are also in this category. Data may also be dynamic (i.e. changing in value), such as a setting indicating whether the Bluetooth service is available, or indicating the currently active window.

Data Added by the User

Perhaps the most common use of the term ‘data’ on a mobile phone is when it is applied to persistent information that a user stores. We refine this by defining ‘user data’ as that which is personal information stored by a user. Address book contacts and calendar entries are examples of user data and Symbian OS defines capabilities for protecting exactly this type of data. Contrast this with data such as downloaded ring tones – this is a type of data that a user might add to the mobile phone, but it is not regarded in platform security terms as user data.

Data added by the user may be considered as two types – public, which the user has no problem with any person reading, and private, which the user does not want any unauthorized person or process reading. Symbian OS platform security does not differentiate between these, since it is content-dependent – data added by the user may be either public or private user data.

As a result of this, it may be necessary to employ other protection techniques in the case of private user data. A piece of text in a notebook application, for example, might be something as innocuous as a scribbled note to oneself or it might be a debit card PIN. In this case it is the responsibility of the application managing this data to enable protection mechanisms such as encryption and password protection. This protection may simply be achieved at the user interface level; an example of this is a calendar entry that might be a public holiday or a medical appointment.

User data capabilities are usually policed symmetrically – that is, both reading and writing the data requires a capability. User data does not have to be policed symmetrically though; a media server is an example of this, where the server might be quite happy for anyone to read files in order to play them, but it certainly doesn’t want anybody writing to them, potentially corrupting the files. Note also that, in this example, the policing of an application wishing to add files would probably be applied differently too, but this would depend on the content of the data.

Data Resident as Part of the Mobile Phone

We describe this as ‘system data’ – data that is vital to the integrity of the system, and that, if compromised, could cause a network or device service to fail. Examples of this type of data are network availability (a transient property), network access point (persistent) and IMEI number (persistent and constant value). The hardware abstraction layer (HAL) in the OS kernel contains system data, some of which has a constant value (ECPUSpeed), and some of which is variable (EBacklightState).

Here though, the type of data does not automatically determine the capabilities it will be policed on – system data capabilities are often non-symmetrical. Write access might be policed by the SID of a system process, in the case where there should only be one process updating the value. (This is generally preferable as it reduces the need for more processes to possess a system capability – this is discussed further in the next section). Read access, in many cases, does not require any capability – anyone can read the data – because confidentiality is not the prime issue, as the description of this type of data shows. An example of this is network signal strength or current call status. In the small number of cases where confidentiality is required in addition to integrity, read access is policed with ReadDeviceData and we term this ‘device data’ since the policy is then symmetrical. An example of this would be an IMAP server address.

Compound data often has both user and system parts – for example, a user account for a SyncML server. The server’s address is clearly system data, as changing it would cause the service to fail; however the display name for the account is considered public data.

Capability Summary

Here we summarize the policy that you would apply to the different types of data that we have discussed in this section. The storage mechanism has no bearing on the policy.

Table 7.1 Data Access Capabilities
Type of Data Confidentiality Required: access policy Integrity Required: access policy
User data ReadUserData or none WriteUserData
System data None WriteDeviceData, SID of process that modifies the value
Device data ReadDeviceData WriteDeviceData

Deciding the Level of Trust

So you have some data that you want to share – how do you share it safely? You must decide the level of trust to apply. Thismeans undertaking a threat analysis. Analyze the value of your assets (the data), identify the attack surface, consider the threats to those assets, and then decide on an appropriate measure.

Symbian’s platform security architecture is designed to empower the developer to decide the level of trust to apply. In practical terms this means the capabilities to assign to the policy for sharing your data. Note that the ability to write data can encompass creating, adding and deleting data as well. It may well be that you need to police access to these differently, depending on the content of your data. For example, a process that wishes to log events may treat adding entries simply as ‘noise’ but deleting entries as removing an audit trail.

It is sometimes easier to apply an inverse test and ask, ‘Who do I not want to share my data with?’. This might help you to decide how much you would care if the data was corrupted or deleted, either intentionally or unintentionally, or stolen by an unauthorized entity. If you applied the wrong capability policy, could a phone number be modified to a premium rate number, for example? You must also think about the consequences to subscribers of your data – what is at stake for them? In fact, who are the clients of your data?

Bear in mind that when we asked users what security they expect from a mobile phone, one answer repeatedly made was ‘I want my private data to stay private’ (as reported in Symbian OS Internals [Sales 2005]).

Identification for Authorization

There are sometimes practical requirements for access control based on identity, and so we have chosen to support this as a complement to the authorization model. You may choose to mediate access using the SID or the VID of the process because you are only concerned with identity.

Policing your data using the VID may be considered less secure than using the SID, as the scope of processes with a particular VID is ‘open-ended’ – new software may be installed that has that VID, which may introduce new security problems. By contrast, only one EXE on the mobile phone can possess a particular SID, so the access control is more precisely defined. When using the VID, it may be useful to combine it with a capability check, in order to constrain further the potential scope of access being granted. The best approach, in this case, is to keep it simple. In many cases policing by the SID or the VID can be considered overengineering, and by restricting access you could reduce the mass-appeal of your software.

Getting the Right Balance

In defining the policy for your data, you should consider that too strong a policy could be as bad as too weak a policy. The authorization model relies on the correct use of capabilities to be effective.

If you don’t protect your data sufficiently you are at greater risk of an attack. If you protect your data too much you may create side effects:

  • You limit the number of applications that can use your service, so you decrease its potential market value. Clients will have to pass more stringent tests to obtain this level of trust.
  • You weaken the protection of other data policed by this capability by increasing the number of processes requiring it. We call this ‘dilution of capabilities’.

Think of it in terms of ‘communities’ of services and data. The level of trust required to get access to the community protected by a capability is in practice likely to be reduced to that required for the least sensitive service or piece of data.

Attacks on Data and Countermeasures

Having created a threat model you will have a good idea of the kind of attacks that could occur on your data. We categorize the most common forms of attack below, and focus primarily on intentional attacks directed against our data assets. Unintentional compromises, however, can be just as damaging unless we implement robust behavior.

We include some common threats to data storage and management services, since many problems occur as a result of concurrency issues – multiple clients accessing data at the same time [Anderson 2001].

We also discuss specific countermeasures for each type of attack. The platform security architecture gives you the primary countermeasures to threats against your data. In particular, data caging and memory protection, through process isolation, are integral countermeasure tools that you get ‘for free’. However there are two things that you must do:

  • Implement the countermeasures, made available through the platform security architecture that you have decided on to harden your software against attack.
  • Apply ‘best practices’ as a complement to platform security, to make your software robust in the face of unexpected data. These are critical elements of security engineering in diminishing the threat of attack.

Unfortunately, many instances of compromised data occur as a result of the unintentional behavior of the clients of that data. As a client of shared data, you have responsibilities too. Capability implies trust – trust not to leak data that you have privileged access to. This means that you must ensure that you do not pass this information on to any other process without first checking that it has the appropriate capabilities. In accepting responsibility for privileged access, you must also accept responsibility for policing access.

Data Capture

This is an attack on confidentiality and is the most commonly considered attack on data. Here are some ways in which it can occur:

  • Malware tricking a process into giving it data – for example, by

intentionally passing inappropriate data to glean sensitive information from any output. Alternatively it could use brute force – for example, by cracking a password.

  • Snooping on data passed between two legitimate entities (man-inthe- middle). Temporary files and public memory spaces are common attack vectors.
  • Unintentionally giving access to files, or data within them, that should not be shared. Often data is part of a collection of information. It may be a set of elements in a table, lines in a file, a typed list of elements, or a set of properties of a class. Sharing more data than you need to leaves that data vulnerable to attack. Log files are an obvious example here, especially if they are recorded to a public location.

Countermeasures

  1. Place your sensitive persistent data in the private data cage of your process, and keep your sensitive transient data within its private memory space.
  2. Mediate read access to your data by policing APIs exported by your process.
  3. Use a system service to mediate read access on your behalf, and register a read policy with it for your data. Your data is hidden within the system service’s data cage or process memory space.
  4. Share individual files from your data cage with trusted processes using shared handles.
  5. Identify and implement different read policies, where necessary, for collections of data. Consider the individual elements of the collection and only share what is necessary.

Best practices

Some best practices to help keep data secure from prying eyes are:

  1. Temporary files are often a loophole in security – don’t use a public store to temporarily hold sensitive data derived from the original.
  2. Don’t write data belonging to your own process to a public log.
  3. Don’t write another application’s data to a public log.
  4. Protect user-added data with sensitive content with passwords or encrypt the data.
  5. When sharing files from your data cage, open only the files you wish to share and no others. Any files that are open may be accessible by the receiving process through the shared file session. Close the session at the first opportunity after the file handle has been received by the process you are sharing with.
  6. Where possible, minimize the amount of time for which data is shared.

Tampering

This is an attack on the integrity of data and, where system or device data is involved, on the integrity of the mobile phone itself. Here are some ways in which it can occur, some reasons and some effects:

  • Most importantly, the attack could be launched to cause instability through corrupted data. This might result in a process crash, or stopping the mobile phone from booting.
  • It may be launched to coerce the owner into undertaking an alternative path of action.
  • It may lead to service denial – for example, if dialup connection information is altered.
  • It may cause the user to unwittingly spend money – for example, by altering a phone number to a premium rate one.
  • It may lead to data capture, if connection information is altered.
  • Data on removable media is at high risk from this kind of attack – as we have already noted, this is out of our control when the media is removed from the mobile phone.
  • Some attacks of this kind are specific to server processes that allow concurrency of access. These occur when inconsistent updates are performed on dependent compound data. There is a risk of being left in an unknown state unless atomic updates are executed.

Countermeasures

  1. Place your sensitive persistent data in the private data cage of your process, and keep your sensitive transient data within its private memory space.
  2. Mediate write access to your data by policing APIs exported by your process.
  3. Use a system service to mediate write access on your behalf, and register a write policy with it for your data. Your data is hidden within the system service’s data cage or process memory space.
  4. Share individual files from your data cage with trusted processes using shared handles.
  5. Take advantage of the static access policy that is applied to the special directory \resource for your resource files. This directory allows you to share your resources publicly but without compromising their integrity. Essentially it is a read-only directory for non-TCB processes. Examples of data you might install here are bitmaps, fonts, RSC and help files.

Best practices

  1. Make your process fault tolerant – that is, resistant to corrupted or unexpected data. Create a recovery strategy for all the situations you have identified in your threat model. This may be as simple as resetting the data back to a ‘default’ configuration for corrupted backup data. Choose a resilience strategy that is both simple and effective for your process, and also right for the type of data in question. If you are implementing a media player, you are likely to be accepting data from off the mobile phone (OTA or removable media). If such a file were corrupt you would simply not play it.
  2. You may refuse unsolicited data but instead only use data that has been installed through the Software Install process. You can do this by not implementing support for a \import directory in your private data cage – see Chapter 8 for more information on import directories.
  3. Return errors to clients of your data in a timely fashion, but make sure that a client can process the error. For example, you must not return an error for a method such as CancelTransaction().
  4. Threats to server processes associated with inconsistent updates can be mitigated by using a locking transaction model. However, we do not recommend this – instead build this into your recovery strategy. For example, the use of client-side caching of data is advocated in Symbian OS C++ for Mobile Phones [Harrison 2003] – this could be used to roll back to a consistent state.

Denial of Service

This is an attack on availability of data and is especially important to address for the system services presented next in this chapter. Here are some ways in which it can occur:

  • Changing a setting, resulting in the user being deprived of a service, such as a setting on a POP account stopping a user from picking up email.
  • Removal of an application’s data file resulting in an application not running.
  • Sending data too large to be handled by the receiving process – resulting in buffer overflow.
  • Causing a priority inversion – an attack on a server process – where a high-priority thread waiting on that service fails to run because a low-priority thread has locked the resource it wishes to access. A resource can be locked for an extended period of time, for example, if a large transaction takes place (or indeed if a locking transaction never completes), or if frequent and extensive searches through the data are made.

Countermeasures

  1. Place your sensitive persistent data in the private data cage of your process and keep your sensitive transient data within its private memory space.
  2. Police the transaction APIs correctly on your server process to guard against denial of service through locking.

Best practices

  1. Make your process fault tolerant and create a good recovery strategy.
  2. Implement a ‘non-serialized optimistic’ transaction model on your server process in order to achieve high concurrency of access. This model allows any number of clients to start a transaction (optimistic) at any time (non-serialized), which eliminates the threat of blocking access to shared data.

Physical Loss and Damage

Lastly, there is, of course, what we might consider as the biggest threat against data by users themselves – data being lost through losing or breaking a mobile phone.

Best practices

Recovery of lost data can be achieved if processes back up their data. You should consider using the Symbian OS secure backup service – being robust in this way will certainly make your application more attractive to the user. Another way of backing up data is through synchronization (for example, SyncML), although this has the added complication of the remote software (server) needing to understand the MIME type of your data.

Threats and Countermeasures Summary

Here we summarize the threats and their countermeasures that we have discussed in this section. Abbreviations used in the table are as follows: data capture (DC), tampering (TA), and denial of service (DoS).

Table 7.2 Threats and Countermeasures
Threat scenario and type Countermeasure
Tricking of APIs by malware (DC) Data cage all sensitive data, or keep in private memory space
Snooping (DC) Data cage and implement API read policy (use system service if practical) Use shared file handles
Unintentionally allowing access (DC) Implement read policies at the right level of granularity
Modification of data to cause unexpected behavior, including failing to execute (TA, DoS) Data cage and implement API write policy (use system service if practical)

Use shared file handles Use \resource directory for your resources

Altering dial-up information (TA, DoS, DC) Data cage and implement API write policy
Inconsistent update made (TA, DoS) Police transactional APIs accordingly
Causing a buffer overflow (DoS) Implement fault tolerance (best practice only)
Causing a priority inversion or locking of a resource (DoS) Police (transactional) APIs accordingly

Using System Services

We now look at how you choose the right mechanism for sharing your data. We recommend that you use existing system services where possible, unless:

  • your security policy does not fit these services
  • your data should only be accessible when your process is running
  • your data type is not supported.

In this case you should implement a custom server to tailor the shared service to your needs (see Chapter 5). This may be appropriate for BLOBs, for example, such as media files, for which there is no system service at the time of writing.

System services that are currently available for you to share your data safely are:

  • publish and subscribe
  • DBMS
  • file handles.

We will discuss the security features of these services and show how to use them. Additionally, we will look at security in the central repository – a system service that has restrictions on who can use it, but we will look at how you can make use of it. Lastly, we will touch on a few other mechanisms that are pertinent to this topic, and review the implications of their use.

Publish and Subscribe

Publish and subscribe is a state-oriented service for asynchronous distribution of information where the latest value of a setting is the only one relevant. We term each setting held in this service a property. Key functional points are:

  • It provides a means to store and broadcast transient data at run time.
  • It can provide a run-time data cage into which each process can publish its properties, providing protection from spoofing or denial of service.
  • Under certain circumstances it provides bounded execution time for critical tasks that require real-time guarantees. Guarantees are made for:
    • publishing a property of up to 512 bytes in length via a handle, if the new data length does not exceed a buffer size that you can pre-allocate
    • reading the value of a property of up to 512 bytes in length using a handle to the property.

The run-time data cage is the point of interest in platform security terms.

Here is the prototype for defining a property:

IMPORT_C static TInt Define(TUint aKey, TInt aAttr,
                const TSecurityPolicy& aReadPolicy,
               const TSecurityPolicy& aWritePolicy,
                             TInt aPreallocated=0);

When a process defines a property it is stored in a category with a value equal to the SID of the process. No other processes can define a property in this keyspace, so the property can’t be spoofed – the net effect being a run-time data cage. At the point of definition, the process must also supply a read and write policy, which allows the service to limit access to trusted clients you have specified.

Note that the lifetime of the property defined in the service is not necessarily linked to the lifetime of the defining process. The property will exist until either the mobile phone is switched off or the defining process deletes it. This provides flexibility in how long your shared data is available. It could be:

  • for the length of time that your process is running (for example, a state flag during a task execution phase)
  • for the lifetime of your process (for example a ‘friend online’ Instant Message signal)
  • or, from the point that it is defined to when the mobile phone is switched off (for example, system properties such as network signal availability and battery strength).

Consider an example from an instant messaging application, whose design is split between a server and a UI application (see [Harrison 2003]) to allow, among other things, different UI applications to be built. A function of this application might be to notify a user when a ‘friend’ goes online or offline. This could be achieved by the server defining a property ‘new friend event’:

static const TInt32 KIMServerSID = 0x89ABCDEF;
static _LIT_SECURITY_POLICY_S0 (KFriendsListChangePolicy,
                                KIMServerSID);

void CIMServer::ConstructL()
  {
  ...
  User::LeaveIfError(
             RProperty::Define(KKeyFriendsListHasChanged,
                                         RProperty::EInt,
                                       KAlwaysPassPolicy,
                              KFriendsListChangePolicy));
  ...
  }

There are a couple of interesting points that come out of this. Firstly, we simply define a property to publish the fact that the friends list has changed, rather than trying to say ‘you have a new friend’. If you look back to our definition of ‘Publish and Subscribe’ you can see why – we state that ‘the latest value of a setting is the only one relevant’. This is because it cannot be guaranteed that every event will be received by a subscriber. Consequently your behavior as a subscriber should always be to react based on the current value, not a change in state.

The following code snippet shows how the server publishes to the property when a friend’s online status changes:

void CIMServer::OnCommunicationEventL(TEvent aEvent,
                       TEventData aBuf, TInt aError)
  {
  switch (aEvent)
    {
    case EFriendOnline:
    case EFriendOffline:
      {
      UpdateFriendsListL(aBuf);
      User::LeaveIfError (RProperty::Set(KIMServerSID,
                            KKeyFriendsListHasChanged,
                               iFriendsList.Count()));
      }
    ...
    }
  }

Consider the client code for the UI application. First, in its ConstructL() the application creates an instance of an active object that tracks ‘friends online change’ events from the server:

void CIMApplicationUI::ConstructL()
{
// connect to the server
User::LeaveIfError(iIMSvr.Connect());
// create the active object that tracks events
iWatcher = CFriendEventWatcher::NewL(*this);
...
}

The following code shows how the active object handles and acts on these server events:

void CFriendEventWatcher::ConstructL()
  {
  // attach to the property
  User::LeaveIfError(iProperty.Attach(KIMServerSID,
                       KKeyFriendsListHasChanged));
  CActiveScheduler::Add(this)
  // initial subscription and process current property value
  RunL();
  }

void CFriendEventWatcher::RunL()
  {
  // re-subscribe to help prevent missing notification of updates
  iProperty.Subscribe(iStatus);
  SetActive();
  
  // test availability of property
  TInt numfriends;
  TInt err = iProperty.Get(numfriends);
  if (KErrNotFound == err)
    {
    // server is not running
    User::Leave(KErrIMServerNotRunning);
    }
  else if (KErrNone == err)
    {
    if (numfriends)
      {
      // enable friends online icon
      }
    else
      {
      // disable friends online icon
      }
    }
  ...
  }

Here we are checking the latest value in order to enable or disable a ‘you have friends online’ icon. If a friend came online at the same time as another went offline, there would be two published events – however, the UI application might only receive one. This is why the event could not be an increment (‘new friend online’) in its own right. Imagine a toggle switch property – if a subscriber missed one event and didn’t check the value, it would have its logic reversed.

The second interesting point is with regard to the ‘friends online’ icon. This is a good technique for avoiding polling overhead – an application could ask the server for the complete list of friends each time it is notified, however, if it only does this in response to a ‘show friends’ command, processing is minimized.

Finally, note that the UI application does not need any capabilities to read this property as we consider it public information – we only protect what needs to be protected – however, only the IM server can write a value to it. This is an example of non-symmetrical policing.

Good practices

Multiple publishers of values to a property are generally discouraged. There are two reasons for this. Firstly, it encourages the, potentially unnecessary, proliferation of ‘write policy’ capabilities. Secondly, it could give rise to race conditions, especially with respect to Booleans or bit masks. An exception to this might be publishing to properties that represent events where the subsequent processing of subscribers is simply in response to the event.

Client code should not perform substantial processing on each notification for properties that change value frequently. Publishing to a property could potentially lead to multiple clients servicing the notification – if this property’s value has a high frequency of change, it can lead to a large amount of processing. It is, therefore, a good idea to document the expected update frequency of your property.

Sometimes data represents the current state of part of a mobile phone. An example might be the current volume level of the speaker, the value of which may be highly volatile while it is being changed through the user interface. The publish and subscribe service should be used for just such transient data. Note that once the target volume is reached (it’s final state), the property ceases to be volatile. In this case the value is written to disk in order to save the initial state of the volume should the mobile phone be rebooted or media application restarted.

Central Repository

The central repository provides a centralized service for safe, secure persistence of shared settings. It is implemented in the OS to enhance ease of programming through a unified settings API, to promote a common policy for platform security, and to allow simpler customization, factory reset and data backup of the mobile phone. Key functional points are:

  • It uses simple partitioning of data using a UID-pair to create a two-tier structure: up to 232 top-level ‘keyspaces’ or repositories, within which up to 232 settings of any simple type, may be defined.
  • It provides the ability to define separate read and write access policies to settings within a keyspace at three levels: a default policy for the whole keyspace, a policy for a range of settings and a policy for individual settings.
  • It provides the means to change the read and write access policy of a setting at run time.
  • It provides non-serialized optimistic transactional support for reading and writing multiple settings – ensuring consistency between multiple values within a keyspace.

Policies are defined in a keyspace initialization file held in the data cage of the central repository server, and these may reside in ROM, or be installed via a SIS package. A new keyspace may only be created through software install; it cannot be created at run time, which prevents the potential problem of unchecked bloat through badly behaved applications not deleting keyspaces that are no longer used.

It is important to note that the central repository is not generally available for use as a storage mechanism at the time of writing. Third parties with sufficient capabilities can find and read from settings, and be notified of any changes, but cannot write their own changes. This restriction is likely to be lifted in the future.

The INI file may also contain settings and default values for that keyspace. Here is a simple example, an instant messaging server’s initialization file:

# 89ABCDEF.cre
# Instant Messaging Server keyspace

cenrep
version 1

[platsec]
# default capabilities for this keyspace
cap_rd=ReadUserData cap_wr=WriteUserData
# define a range for server private data
0x100 0x200 sid_rd=0x89ABCDEF sid_wr=0x89ABCDEF
# define a range for public data
0x1000 0x1100 cap_rd=AlwaysPass cap_wr=AlwaysPass

[defaultMeta]
0x1000000

[main]
# protected by default capabilities
# autologin: specifies whether to login on startup of application
0x1 int 1
# usesslport: specifies whether to use SSL
0x2 int 0
# anonsearch: specifies whether to undertake anonymous searches
0x3 int

# server private data
# imhostname: host address of server
0x100 string “host1.imserver”
# defport: default port number
0x101 int 5269
# sslport: ssl port number
0x102 int 5223

# account data (device data) - using individual policy
# username
0x200 string “username” cap_rd=ReadDeviceData
cap_wr=WriteDeviceData
# password
0x201 string “password” cap_rd=ReadDeviceData
cap_wr=WriteDeviceData

# public data
# onlineicon: user profile icon
0x1000 string “defUser.gif”
# displayfont
0x1001 string “swissA”
# fontsize
0x1002 int 10
#timeout: timeout value (min) for inactive chat sessions
0x1003 int 10

The ability to define access policies at different levels makes this service very flexible. This is particularly important as you can store up to 232 settings in a single keyspace. Note that the policies are governed by the standard rules applied by the TSecurityPolicy class. The policies at successive levels supersede any more general policy defined, and these cannot be applied or changed dynamically. This does not prevent the policy applied to the setting being changed at run time, as we stated.

In the previous example we defined a range of keys with a read and write access policy equal to the owning process’s SID. The central repository supports the ability to move settings within a keyspace, given that the process that moves the settings has the write capabilities applying to both the source and target keys. In this example, settings could be moved into this key range, effectively hiding them from all but the owning process. This can be used to achieve temporary sharing (or hiding) of data. There is a potential drawback to this mechanism – the potential to ‘downgrade’ the policy on a setting. So, for instance, a client may have added what was considered as user data, and another client makes it available to anyone. Also, the clients of some shared settings may not be robust enough to deal with data they expect to be available suddenly disappearing. The trust relationship between processes sharing settings is evident here, as well as the need for resilience to unexpected events.

The transaction model implemented in the central repository achieves high concurrency – a number of transactions are allowed to be opened on a keyspace, however, the first to commit causes all others to fail with KErrLocked. Consequently, clients are rewarded for keeping their transactions short since they are less likely to fail. Clients that must guarantee success place their transaction in a do-while KErrLocked loop. Concurrency is further guarded by checking that the capabilities of the requesting client were at least equal to the default policy for the keyspace – making sure that people without the ability to read and write settings cannot start a transaction.

DBMS

DBMS is a service that provides a general interface for access to relational databases. The service provides two different implementations: a clientside service and a client–server service. The former gives you DBMS functionality to use within your own data cage and you would police access through your own API. The latter is of more interest in this context, and provides you with a way to share your data asynchronously. Key functional points are:

  • It provides the ability to define cumulative separate read and write access and schema manipulation policies on two levels: a default policy for the database and a policy for individual tables.
  • It provides a lightweight client-side implementation, enabling processes to store data in their own data cage, and police it accordingly.

Databases may be manipulated either through a subset of SQL or through a Symbian OS C++ API. This API has been extended to support creation and manipulation of secure, shared databases and also allows a thread to query the policy applied to a database or table. This can be useful for a middleware server, for example, that provides querying functionality on top of DBMS, and which polices this API according to the data it is acting on in DBMS. However, this will not be a very common pattern – most clients would use the DBMS API directly.

Access to secure DBMS databases is controlled through pre-registered policy files, each of which has a UID, and is applied to the database by its creator. The policy file states the read, write and ‘modify schema’ policies to apply to a database and it can be used to add capabilities for accessing tables. Note that the policies are governed by the standard rules applied by the TSecurityPolicy class and that this table-level security can only strengthen the database policy, rather than override it. Here is an example policy file that an instant messaging server might register to be used to police access to an IM ‘friends’ database:

; dbms policy file for Instant Messaging ‘friends’ database
; 87654321.spd

[database]
; friends are considered to be user data. The tables containing
; contact information will be policed on these default policies
read
capability = ReadUserData
write
capability = WriteUserData
schema
; only the server process can change the schema
SID = 89ABCDEF

[table]
; this is a table relating to friends, and contains data
; private to the server process, for example, local UIDs etc
; the policies below strengthen the default policies
name = metadata
; only server process can read or write metadata
read
SID = 89ABCDEF
write
SID = 89ABCDEF

There is no concept of ownership in the context of a policy file, and hence there is no limit to the number of databases that can use one. This is certainly the case for the Contacts Model, which allows a user to create more than one database – for instance, one for personal contacts and a separate corporate directory.

It is important to note here that access policies are only read from ROM at the time of writing. This restriction is likely to be lifted in the future.

In order to make the creation and manipulation of secure shared databases available to third parties, a mobile phone manufacturer could choose to embed a ‘default’ policy file in ROM that polices access on user capabilities:

; default security policy file for third party use
; 12345678.spd

[database]
read
capability = ReadUserData
write
capability = WriteUserData
schema
capability = WriteUserData

Given that the UID for the policy file is published, third parties could then use the DBMS secure shared database extension API to create their own database with a policy based on this file:

RDbs dbms;
User::LeaveIfError(dbms.Connect());
CleanupClosePushL(dbms);
RDbNamedDatabase file;
file.Create(dbms, _LIT(“c:myDb.db”), _L(“secure[12345678]”));
CleanupClosePushL(file);
...

Obviously a table-level policy is not possible in this case, since you must write the name of the table in the policy file.

File Handles

Symbian OS supports the means for a process to temporarily share a file with another process under controlled conditions. The kernel provides the basic support for handle-sharing across the process boundary, and the file server builds on this to provide specific support for sharing file handles. Some key points to note:

  • The owning process opens a file in a mode that cannot be changed by the receiving process under normal circumstances.
  • The receiving process accesses the file through the shared handle it receives, but does not get access to the shared file’s private directory.
  • The owning process shares a file server session with the receiving process.
  • Revocation of sharing a file is not possible.

Note that file handles are not handles in the sense of a reference to a kernel object, but, instead, they are a unique identifier of an open file within a file server session.

Files can be shared in this way using the following family of methods published on the RFile API:

RFile::TransferToClient() and RFile::AdoptFromClient()
RFile::TransferToServer() and RFile::AdoptFromServer()
RFile::TransferToProcess() and RFile::AdoptFromProcess()

These methods allow transfer of a file handle using the client server IPC mechanism, and also from one process to another.

We said that the mode of a file cannot normally be changed. The only change allowed is to toggle between the modes EFileShareExclusive and EFileShareReadersOnly, and this is only allowed under limited circumstances that would not compromise the use of the file by the other process you are sharing with.

Our example IM server could make good use of file sharing if it receives a file as an attachment during a chat session and needs to hand the file on to an appropriate application or recognizer. The server needs to share that file and no other. The IM server code might look as follows:

void CIMServer::HandleNewAttachmentL(const TDesC& aName)
  {
  // create a new session to the file server specifically for
  // sharing just the attachment
  RFs fs;
  User::LeaveIfError(fs.Connect());
  CleanupClosePushL(fs);
  // allow session to be shared by receiving process
  User::LeaveIfError(fs.ShareProtected());

  // open file where data has been previously stored
  RFile attachment;
  User::LeaveIfError(attachment.Open(fs, aName, EFileRead));
  CleanupClosePushL(attachment);
  RProcess handler;
  User::LeaveIfError(handler.Create(_LIT(“Handler.exe”),
                                            KNullDesC));
  CleanupClosePushL(handler);

  // transfer file to process
  User::LeaveIfError(attachment.TransferToProcess(handler, 1, 2);
  TRequestStatus status;
  handler.Rendezvous(status);
  if (KRequestPending != status.Int())
    {
    handler.RendezvousCancel(status);
    handler.Kill(0);
    User::Leave(status.Int());
    }
  handler.Resume();
  User::WaitForRequest(status);
  User::LeaveIfError(status.Int());

  // safe to close all handles including shared session
  CleanupStack::PopAndDestroy(3, fs);
  }

Notice that the IM server opens the attachment in read-only mode for sharing. Notice also that the file server session is open specifically for sharing, and the only file opened using this shared session is the attachment. This ensures that the handler process only has access to that file and no others in the IM server’s private directory. If any other files were opened using this session there is a possibility that the receiving process could also open it – by speculatively incrementing the handle number, for instance. Finally, we see that the session is open only for as long as required for the handler process to receive the handle, and no longer. This is achieved with the Rendezvous code, which allows the IM server to be notified when the handler has received the attachment. Here is the corresponding code in the handler process:

LOCAL_C void MainL()
  {
  // adopt the file
  RFile attachment;
  User::LeaveIfError(attachment.AdoptFromCreator(1, 2));
  CleanupClosePushL(attachment);

  //signal transfer complete
  RProcess::Rendezvous(KErrNone);

  // use the file
  ...
  // close the file
  CleanupStack::PopAndDestroy(&attachment);
  }

GLDEF_C TInt E32Main()
  {
  __UHEAP_MARK;
  CTrapCleanup* cleanup = CTrapCleanup::New();
  CActiveScheduler* scheduler = new CActiveScheduler;
  TInt ret = KErrNoMemory;
  if (cleanup && scheduler)
    {
    CActiveScheduler::Install(scheduler);
    }
  delete scheduler;
  delete cleanup;
  __UHEAP_MARKEND;
  return ret;
  }

The client can use the file through a hidden shared file server session inside the RFile object, removing the need for a separate RFs object to be maintained. This session is automatically closed when RFile::Close() is called on the attachment.

The trust relationship between the two processes is evident here once again; there is no chance of revocation on the part of the sharing process. Similarly, the receiving process has a duty to protect the file it has received (note, though, that there is no technical reason why this process cannot pass the file on to another process).

Services Summary

The table below summarizes the system services and the categories of data with which they are best used. ‘Custom server’ in the table denotes the implementation of your own dedicated server to manage access to the data.

Table 7.3 System Data Services Characteristics
Service Characteristic
Persistency Volatility Size Exchange
Publish and subscribe Transient Any Prefer <512 bytes Asynchronous
Central repository Persistent Non-volatile <2 KB Asynchronous
DBMS Persistent Non-volatile Any Asynchronous
File handles Persistent Non-volatile Any Synchronous
Custom server Any Any Any Synchronous

Summary

Getting the level of trust correct for your data is essential in order to ensure it is secure enough, and to maintain a strong and balanced capability model on the mobile phone. Creating a threat model is crucial in achieving this outcome.

We have looked at different categories of data, and shown how confidentiality and integrity should be treated, with respect to each of these categories. As a client of shared data, the granting of a capability should be seen as bestowing privileged access. With privileges come responsibilities – once access has been granted, the responsibility for policing access has to be accepted also.

Your decision on how to implement secure sharing of your data will depend on its type and the characteristics of that data. We noted that system services should be used where possible, in preference to duplicating their functionality.

The benefits of sharing data safely are clear: in a world where more and more information is stored electronically, and where much of it may reside on or pass through a mobile phone and where the abuse of electronic communications to perpetrate fraud is rapidly increasing, mediated access to your data for trusted clients is paramount.


Copyright © 2006, Symbian Ltd.