1. Problem
1.1. Context
You are developing an application or service which includes functionality requiring high security privileges together with a similar or greater proportion of functionality which can run with lesser or no security privileges.
1.2. Summary
It is more difficult to find and fix security
vulnerabilities in components which include both security-critical and
other code ('economy of mechanism').
Developers
of components which require high security privileges should minimize
any liability they may be exposed to due to errors in design or
implementation ('least privilege').
You wish to make it as easy as possible to create and distribute updates to your code.
When
using any security privilege, but especially the
device-manufacturer-approved capabilities, you are required to ensure
that the privileges are not leaked and unauthorized software cannot use
them.
1.3. Description
The most straightforward architecture for an
application or service may be to structure it as a single Symbian OS
process or device driver; however if some of its functions require
device manufacturer capabilities such as TCB, AllFiles or DRM
there are likely to be longer-term drawbacks in having the highly
trusted parts of your component executing in the same process context as
the less critical parts.
When both highly privileged and non-privileged code
are intermingled in the same process context, any analysis looking for
security vulnerabilities has to consider all of the code as equally
likely to be a potential source of a vulnerability.When all of the code is running with the same set of capabilities, a
vulnerability anywhere in the code could potentially be exploited to use
the most sensitive APIs and data accessible by the process. Also, when
all the code is packaged for certification and signing as a single
component, changes anywhere in the code may lead to the entire component
needing to be recertified as requiring the device manufacturer
capabilities and not just the highly privileged code. This is true even
if the code is separated out into different DLLs since the DLL loading
rule would require them to have any device manufacturer capability that
the process that loads them does.
1.4. Example
The Symbian OS software installer (SWI) is one of the
most critical parts of the platform security architecture. It has, in
effect, the highest level of security privileges
and therefore needs to be trusted at the highest level; it is part of
the Trusted Computing Base (TCB) [DoD, 1985] and malicious functionality
injected into the TCB can do anything it wants.
At the same time, the software install subsystem
covers a lot of functionality that should not be security-critical; it
is desirable for clients without device manufacturer capabilities to be
able to invoke the software installer and supply a package using various
communication and storage mechanisms; it is only necessary that the
package being installed correctly passes security checks. The client is
also likely to make use of DLLs which are not granted the TCB
capability, and thus could not be linked in to a process which has been
assigned that capability. This is because almost all the DLLs provided
by Symbian OS have all capabilities except for TCB, precisely so that they can be widely used but do not have to be audited for use within the TCB.
2. Solution
Divide your software component into two or more
components, with at least one component containing code which is
security-critical, and one or more components containing code which is
not security-critical. Security-critical code should include both code
which requires high security privileges to execute and code which checks the correctness of input data affecting security-related operations.
For example, non-critical code could include user-interface components
and application logic which only requires access to the application's
own private data.
Dividing your software component into two or more
components which execute as separate processes allows each component to
be given only the capabilities which it needs to perform the subset of
functionality which it implements. By introducing additional processes,
each with their own isolated memory spaces, you minimize the damage that
can result from a security vulnerability in one component ('principle
of least privilege').
Such separation enables closer scrutiny and analysis
of the security-critical code, increasing confidence that
vulnerabilities will not be found after the software has been deployed.
This practice of minimizing the size of highly trusted code is a
well-established security principle ('economy of mechanism').
If your component is distributed as an installable package (a SIS file), it will need to be signed by a trusted authority to be granted capabilities. When high security privileges are needed, such as TCB
or other device manufacturer capabilities, there are additional steps
in the signing process. Packaging the security-critical parts of your
component in a separate SIS file allows updates to, and new versions of,
the noncritical parts to be created and distributed without requiring
the overhead of device manufacturer approval for the new code and hence
avoids any additional cost or time so expediting the deployment of
urgent fixes.
2.1. Structure
Non-critical code and security-critical code are
separated into two communicating processes to create two different
memory spaces that are isolated from outside interference (see Figure 1). This can be achieved with an architecture including two user-mode processes, perhaps using Client–Server
(see page 182) for the communication between them or, where it is
necessary to have code execute in kernel space, implementing the
security-critical code as a device driver which communicates with
non-critical components running in a user-space process.
The process containing the security-critical
components should never simply expose a public API equivalent to an
existing API requiring high security privileges to access; if this were
the case then any client of that API would have to be just as trusted as
the secure agent itself, since any vulnerability in the client could be
exploited by an attacker to do the same thing as they could by
exploiting a vulnerability in the agent.
2.2. Dynamics
Malicious software may attempt to call the secure
agent's API as a way of gaining access to protected services or data.
The interface between the security-critical and non-critical components
must therefore be carefully designed to ensure that communication across
the process boundary does not allow unauthorized use of the privileged
operations performed by the security-critical components. This
undesirable consequence is known as 'privilege leakage', and there are
two measures which can be implemented inside the security-critical
components to avoid it occurring:
Ensuring that only authenticated clients are
allowed to invoke the security-critical components, for example, by
checking that the client has a particular SID from the protected range,
mitigates the risk of malicious software impersonating an authorized
client and taking advantage of the APIs exposed by the security-critical
components.
Validating
the parameters passed to the security-critical components to ensure
that they are within expected ranges mitigates the risk of security
vulnerabilities caused by unexpected values whether they come from logic
errors, user input errors or deliberately exploited security
vulnerabilities in an authorized client. Such validation would also
include, for example, checking digital signatures on data if the data is
expected to be coming from a trusted source.
Figure 2 shows the interaction of the non-critical and security-critical processes.
The client process first creates a session with the
secure agent which may cause it to be started, if it is not already
running. The secure agent can perform a one-off set of checks on the
authenticity of the client at this point or can check each service
request individually. These checks can include verification of the
identity of the client process, via its SID or VID, or that it has been
trusted with sufficient capabilities.
When the client invokes the secure agent API to
perform actions on its behalf, it can pass parameters with the request
that might include, for example, basic types, descriptors, and file
handles. For each action, the secure agent is responsible for checking
the validity of the parameters and any input data read from locations,
such as files, provided by the client.
2.3. Implementation
Client
The MMP file for the client should look something like this:
TARGET myapp.exe
TARGETTYPE exe
UID 0 0x200171FD // Protected-range SID
CAPABILITY NetworkServices, ReadUserData // User capabilities
SOURCEPATH .
SOURCE myapp_ui.cpp
SOURCE myapp_logic.cpp
SOURCE myapp_ipc.cpp
USERINCLUDE .
SYSTEMINCLUDE \epoc32\include
LIBRARY euser.lib
In this example, the client part of the application
requires only user-grantable capabilities which do not require to be
signed for. Note, however, that it specifies a protected-range SID which
the secure agent can use to authenticate the client, and therefore it
needs to be delivered in a signed SIS file.
Secure Agent
The MMP file for the secure agent should look something like this:
TARGET secagent.exe
TARGETTYPE exe
UID 0 0x200171FE // Protected-range SID
CAPABILITY AllFiles // Device manufacturer capability
SOURCEPATH .
SOURCE secagent_api.cpp
SOURCE secagent_validation.cpp
SOURCE secagent_operations.cpp
USERINCLUDE .
SYSTEMINCLUDE \epoc32\include
LIBRARY euser.lib
LIBRARY efsrv.lib // Includes APIs requiring AllFiles
The secure agent is built as a separate executable. In this example, it requires a device-manufacturer-approved capability, AllFiles,
so it must either be pre-installed in the device ROM or delivered in a
signed SIS file that is approved by the device manufacturer.
Inter-Process Communication
Implementing this pattern requires the introduction
of a secure IPC mechanism. Symbian OS provides a number of mechanisms
which you can use to provide this, such as Publish and Subscribe (see page 114), if you need a relatively simple, event-driven style of communication, or Client–Server
(see page 182), which supports a more extensive communication style.
Which of the various mechanisms you use depends on exactly what needs to
be communicated between the client and the secure agent.
2.4. Consequences
Positives
It is easier to concentrate review and
testing on the security-critical code, making it more likely that any
security vulnerabilities are found before software has been deployed
hence reducing your maintenance costs.
The
amount of code running with high security privileges is reduced, making
it less likely that there will be undiscovered security
vulnerabilities.
It is easier for signing
authorities to assess functionality and grant sensitive capabilities
because the amount of code requiring special approval for signing is
reduced.
It is possible to develop and
quickly deploy updates to non-critical components without needing to
resubmit code for signing approval.
Negatives
Increased development effort is required at
the design stage to divide the functionality into suitable components
and to define appropriate APIs for communication between the
non-critical and security-critical parts.
Debugging may be more complex because of the need to trace the flow of control between multiple processes.
An additional attack surface has been added which can reduce the security benefits.
The code size of your components will be increased to manage IPC and validate parameters.
RAM usage will be increased due to the addition of an extra process (a default minimum RAM cost of 21–34 KB),
the increased code size (to implement the IPC channel and the
validation of the IPC messages), and the additional objects required in
your components and in the kernel to support the IPC.
Creation
of a separate process, marshalling of IPC, parameter validation and
additional context switching between the client and the secure agent
decreases execution speed.20
2.5. Example Resolved
The Symbian OS software installer uses this pattern (see Figure 3)
to separate out the less-trusted software install functionality, such
as reading, decompressing and parsing the SIS file and handling the user
interface, to be run as part of the client process. The more-trusted
functionality that requires TCB privileges, to write the contents of a
SIS file to the file system and update the registry of installed
applications, is run as a separate process.
The client process, which only needs the TrustedUI capability, communicates with the SWI Server using Client–Server (see page 182) to provide the IPC between the two processes (see Figure 4).
The main purpose of the SIS Helper component in the client is to
facilitate this communication. The SWI Server runs with all capabilities
including TCB, DRM and AllFiles as these are needed in order to extract install binaries into the system directories on the mobile device.
3. Other Known Uses
Malware Scanners
Malware
scanners are applications that include an engine to scan for malware as
well as components that manage the over-the-air update of malware
signature databases and user-interface functionality such as control
panels. Only the scanning engine requires high privileges and so it is
separated from the other functionality which resides in its own process.
The scanning engine however is loaded into the Symbian OS file server
process as a PXT plug-in via Buckle
(see page 252). As the file server is part of the Symbian OS TCB, DLLs
it loads, such as these plug-ins, are required to have the TCB capability. Malware scanner vendors typically package the file server scanning engine plug-in in a SIS file signed with TCB
capability, and deliver other components in a separate SIS file that
can be updated frequently and easily. So that only a single package
needs to be delivered, the scanning engine SIS file can be embedded
inside the SIS file for the full package without the need for the outer
SIS file package to be signed with device manufacturer capabilities.
Note
that the scanning engine is responsible for making sure that any
updates done to its database are legitimate before accepting them;
end-to-end security of such data updates is typically done by validating
a digital signature on the update.
Symbian Debug Security Server
A
further example of this pattern that includes highly trusted code
running in the kernel is the Symbian Debug Security Server, introduced
in Symbian OS v9.4. The low-level operations of the debugger (reading
and writing registers and memory belonging to the debugger process,
suspending and resuming threads, etc.) are implemented in a Logical
Device Driver (LDD) called the Debug Driver, which represents the Secure
Agent as described in this pattern. The device driver provides the
low-level functions to a process running in user mode called the Debug
Security Server. When started, the Debug Driver checks the SID of the
client to ensure it is the Debug Security Server so that only this
authorized process can access it. The Debug Security Server in turn
checks the SID of its clients to ensure that it only provides debug
services to authorized debuggers. The Debug Security Server uses a
security policy based on access tokens and process capabilities to
ensure that a debugger client is only able to access those processes it
is authorized for.
4. Variants and Extensions
Separating Security-Enforcing Code from Highly Privileged Code
In
some ways, the desire to group together security-critical code, such as
the TCB, to allow in-depth security evaluation is in tension with the
desire to isolate code that requires high security privileges to perform
its function according to the principle of least privilege. To resolve
this tension it may be helpful to consider separating a software
component into three subsets (see Figure 5).
It is possible to extend the Secure Agent
pattern to architect an application or service as three communicating
processes. The benefits of separately considering the security
characteristics of the trusted code (including both security-enforcing
and highly privileged code) are maximized by being able to exclude the
non-critical code from security review. The benefits of the 'least
privilege' principle are maximized by separating out the
security-enforcing code that does not need high security privileges from
the code that must be highly privileged to perform its function.
Factoring the component into three
processes, however, magnifies all of the negative consequences listed
above so this more complex approach is best reserved for situations
where security is of paramount importance. Something analogous to this
can be seen in the Symbian OS platform security architecture [Heath,
2006], where the TCB is limited to that functionality which absolutely
has to run with the highest privileges and a lot of security-enforcing
functionality is provided by system servers in the TCE, which run with
only the specific system capabilities they need.
Open-Access Secure Agent
Where
the secure agent can safely provide services to any client, the
authentication step can be omitted. One example of this could be a
service which allows unprivileged clients to initiate playback of
DRM-protected content without giving the client any access to the
protected data itself. The secure agent still needs to include
validation checks on the parameters and other data passed to it to
ensure that malicious software cannot exploit any vulnerabilities
resulting from processing of out-of-range or other unexpected data. The
security implications of such a design should also be carefully
considered; if, in our DRM example, the protected content has a
restriction on the maximum number of times it can be used (a limited
'play count'), malicious software could perform a denial-of-service
attack by repeatedly requesting playback until all the rights are used
up.