50 Conditional Permission Admin Service Specification

50.1 Introduction

The OSGi security model is based on the powerful and flexible Java security architecture, specifically the permission model. This specification adds several new features to the Java model to adapt it to the typical use cases of OSGi deployments.

Key aspects of this security management API is the use of policies. Policies contain a set of permissions that are applicable when the related conditions are met. A policy can both allow (the Java model) as well as deny access when the permissions are implied. Deny permissions can significantly simplify security management. The real time management of Conditional Permission Admin enables management applications to control the permissions of other applications with immediate effect; no restart is required.

Policies are based on the very general concept of conditions. Conditions guard access to the policy's permissions. If they are not satisfied, then the permissions are not applicable. Conditions can be based on the bundle signer, the bundle location, as well as on user-defined conditions. The advantage of this model is that groups of permissions can be shared based on signers or locations. Conditions can also be used to enable or disable a group of permissions when an external condition is true, for example, an inserted SIM card, an online connection to the management system is established, a specific roaming area, or a user has approved a permission after prompting. This model allows an operator to create and enforce a dynamic security policies for its devices.

This specification defines a Conditional Permission Admin that supersedes the Permission Admin (albeit its relation to Permission Admin is well-defined in this specification).

50.1.1 Essentials

  • Policies - Provide a security policy system where conditions control the actual permissions that bundles have at a certain moment in time to be allowed or denied access.

  • Java Security - Provide full compatibility with the existing Java security model, existing applications must not require modifications.

  • Delegation - Support a management delegation model where an Operator can delegate part of the management of a device to another party in a secure way.

  • Digital Signatures - Support the use of digital signatures in a bundle's policy decisions.

  • Real Time - Changes in the environment must be reflected immediately in the bundle's permissions.

  • Operator Specific Conditions - It must be possible for operators, manufacturers, selected developers, and others to provide custom conditions.

  • User Confirmation - The policy model must support end user prompting and confirmations.

  • Allow/Deny Policies - It must be possible to both allow access as well as specifically deny access.

  • Ordering - Policies must be ordered in a table and evaluated in the given order, which is from index 0 upwards.

  • Backward Compatibility - The model must be backward compatible with the Permission Admin of earlier releases.

50.1.2 Entities

  • Conditional Permission Admin - The administrative service that provides the functions to manipulate the policy table.

  • Policy - Provides the information to allow or deny access to a resource. A policy contains a name, an access type, a set of conditions that must all be satisfied and a set of permissions of which at least one should be implied to specifically allow or deny access. A policy is encoded in a Conditional Permission Info.

  • Policy Table - A conceptual table containing all the Conditional Permission Infos.

  • Conditional Permission Info - The encoded form of a Policy.

  • Conditional Permission Update - Holds a temporary copy of the Policy Table so that a number of changes can be committed as a whole.

  • Permission Info - Holds a string based encoding of a Permission object.

  • Condition Info - Holds a string based encoding of a Condition object.

  • Condition - A Condition object is associated with a single Bundle Protection Domain. It abstracts an external condition that can be evaluated. A condition can be mutable or immutable as well as immediate or postponed.

  • Bundle Location Condition - An immutable Condition object that is satisfied when the associated bundle has the given location.

  • Bundle Signer Condition - An immutable Condition object that is satisfied when the associated bundle is signed by a certificate that matched the given DN.

  • Permission - An object that defines a certain permission type. Same as the Java Security model.

  • Bundle Protection Domain - The class that implements the Protection Domain of a bundle, this specification does not define an interface for this class, but it plays an important role in this specification.

Figure 50.1 org.osgi.service.condpermadmin package

org.osgi.service.condpermadmin package

50.1.3 Synopsis

A Conditional Permission Admin service maintains a system wide ordered table of ConditionalPermissionInfo objects. This table is called the policy table. The policy table holds an encoded form of conditions, permissions, and their allow/deny access type. A manager can enumerate, delete, and add new policies to this table via a ConditionalPermissionsUpdate object.

When a bundle is created, it creates a Bundle Protection Domain. This protection domain calculates the system permissions for that bundle by instantiating the policy table, potentially pruning any policies that can never apply to that bundle and optimizing entries that always apply.

A bundle can have local permissions defined in a Bundle Permission Resource. These are the actual detailed permissions needed by this bundle to operate. A bundle's effective permissions are the intersection of the local permissions and the system permissions. During the permission check of the Java Security Manager, each Protection Domain is first checked for the local permissions, if this fails, the complete check fails.

Otherwise, the Bundle Protection Domains of the calling bundles are consulted to see if they imply the requested permission. To imply the requested permission, the Bundle Protection Domain must find a policy in its policy table where all conditions are satisfied and where one of the policy's permissions imply the requested permission. If one of the permissions is implied, then the policy's access type decides success or failure.

Certain conditions must postpone their evaluation so that their evaluation can be minimized and grouped to prevent unwanted side effects. Postponed conditions can leverage a Dictionary object to maintain state during a single permission check.

50.2 Permission Management Model

The Conditional Permission Admin provides a flexible security model for bundles. However, the price of this flexibility is additional complexity. The amount of configuration necessary to setup a working system can easily become overwhelming. It is therefore necessary to be very careful implementing a deployment security model. This section defines a series of possible deployment security models while simultaneously defining the terminology that is used in later sections.

50.2.1 Local Permissions

A good working principle is to minimize permissions as much as possible, as early as possible. This principle is embodied with the local permissions of a bundle. Local permissions are defined by a Bundle Permission Resource that is contained in the bundle; this resource defines a set of permissions. These permissions must be enforced by the Framework for the given bundle. That is, a bundle can get less permissions than the local permissions but it can never get more permissions. If no such permission resource is present then the local permissions must be All Permission. The Bundle Permission Resource is defined in Bundle Permission Resource.

For example, if the local permissions do not imply ServicePermission[org.osgi.service.log.LogService,GET], then the bundle can never get the LogService object, regardless of any other security setup in the device.

The fine-grained permissions allowed by the OSGi framework are very effective with the local permissions because they can be defined by the developer instead of the deployer. The developer knows exactly what services are needed, what packages the bundle requires, and what network hosts are accessed. Tools can be used that analyze bundles and provide the appropriate local permissions to simplify the task of the developer. However, without detailed knowledge of the bundle's intrinsics, it is very difficult to create the local permissions due to their fine-grained granularity.

At first sight, it can seem odd that a bundle carries its own permissions. However, the local permissions define the maximum permissions that the bundle needs, providing more permissions to the bundle is irrelevant because the Framework must not allow the bundle to use them. The purpose of the local permissions is therefore auditing by the deployer. Analyzing a bundle's byte codes for its security requirements is cumbersome, if not impossible. Auditing a bundle's permission resource is (relatively) straightforward. For example, if the local permissions request permission to access the Internet, it is clear that the bundle has the potential to access the network. By inspecting the local permissions, the Operator can quickly see the security impact of the bundle. It can trust this audit because it must be enforced by the Framework when the bundle is executed.

An Operator that runs a fully closed system can use the local permissions to run third party applications that are not trusted to run unchecked, thereby mitigating risks. The Framework guarantees that a bundle is never granted a permission that is not implied by its local permissions. A simple audit of the application's local permissions will reveal any potential threats.

This scenario is depicted in Figure 50.2. A developer creates a bundle with local permissions, the operator verifies the local permissions, and if it matches the expectations, it is deployed to the device where the Framework verifies that the local permissions are never exceeded.

Figure 50.2 Local permissions and Deployment

Local permissions and Deployment

Summarizing, the benefits of local permissions are:

  • Fine-grained - The developer has the knowledge to provide the fine-grained permissions that are necessary to minimize the sandbox of the bundle without constraining it.

  • Auditable - The Operator has a relatively small and readable file that shows the required sandbox. It can therefore assesses the risk of running a bundle.

  • Sandboxed - The Operator has the guarantee from the Framework that a bundle cannot escape its local permissions.

50.2.2 Open Deployment Channels

From a business perspective it is sometimes too restrictive to maintain a fully closed system. There are many use cases where users should be able to deploy bundles from a CD, via a PC, or from an Internet web sites. In those scenarios, relying on the local permissions is not sufficient because the Framework cannot verify that the local permissions have not been tampered with.

The de facto solution to tampering is to digitally sign the bundles. The rules for OSGi signing are defined in Digitally Signed JAR Files. A digital signing algorithm detects modifications of the JAR as well as provide the means for authenticating the signer. A Framework therefore must refuse to run a bundle when a signature does not match the contents or it does not recognize the signer. Signing makes it possible to use an untrusted deployment channel and still rely on the enforcement of the local permissions.

For example, an Operator can provision its applications via the Internet. When such an application is downloaded from an untrusted site, the Framework verifies the signature. It should install the application only when the signature is trusted or when it has default permissions for untrusted bundles.

Figure 50.3 with signing

with signing

50.2.3 Delegation

A model where the local permissions are secured with a signature works for an Operator that fully controls a device. The operator must sign all bundles before they are provisioned. In this case, the Operator acts as a gatekeeper, no authority is delegated.

This can become expensive when there are third parties involved. For example, an Enterprise could provide applications to its employees on a mobile phone that is managed by an Operator. This model is depicted in Figure 50.4. If the Enterprise always has to contact the Operator before it can provision a new version, bottlenecks quickly arise.

Figure 50.4 Delegation model

Delegation model

This bottleneck problem can also be solved with signing. Signing does not only provide tamper detection, it can also provide an authenticated principal. The principal can be authenticated with a certificate chain. The device contains a set of trusted certificates (depending on implementation) that are used to authenticate the certificate of the signer.

The operator can therefore safely associate a principal with a set of permissions. These permissions are called the system permissions. Bundles signed by that principal are then automatically granted those system permissions.

In this model, the Operator is still fully in control. At any moment in time, the Operator can change the system permissions associated with the principal and thereby immediately deny access to all bundles of that principal, while they are running. Alternatively, the Operator can add additional system permissions to the principal if a new service has become available to the signer's applications. For example, if the Operator installs a org.tourist.PointOfInterest service, it can grant the ServicePermission[org.tourist.PointOfInterest,GET] and PackagePermission[org.tourist,IMPORT] to all principals that are allowed to use this service. The Operator can inform the involved parties after the fact, if at all. This model therefore does not create a bottleneck.

Using digital signing to assign system permissions can therefore delegate the responsibility of provisioning to other parties. The Operator completely defines the limits of the permissions of the principal, but the signing and deployment can be done by the other parties.

For example, an Operator can define that the ACME company can provision bundles without any intervention of the Operator. The Operator has to provide ACME once with a signing certificate and the Operator must associate the ACME principal with the appropriate system permissions on the device.

The key advantage of this model is the reduced communication between ACME and the Operator: The Operator can modify the system permissions of ACME applications and be in control at any moment in time. The ACME company can develop new applications without the need to coordinate these efforts in any way with the Operator. This model is depicted in Figure 50.5, which also shows the possible sandboxes for Daffy Inc. and unsigned bundles.

Figure 50.5 Typical Delegation model

Typical Delegation model

The local permissions can still play an important role in the delegation model because it provides the signer the possibility to mitigate its risk, just as it did for the Operator. Signers can verify the local permissions before they sign a bundle. Like the Operator in the earlier scenario, the signer can quickly verify the security requirements of a bundle. For example, if a game bundle requests AdminPermission[*,*], it is unlikely that the bundle will pass the security audit of the signer. However, in the unlikely case it did, it will not be granted this permission unless the Operator gave such a permission to the signer's principal on the device.

50.2.4 Grouping

The grouping model is traditionally used because it minimizes the administration of the security setup. For example, an operator can define the following security levels:

  • Untrusted - Applications that are not trusted. These applications must run in a very limited security scope. They could be unsigned.

  • Trusted - Applications that are trusted but that are not allowed to manage the device or provide system services.

  • System - Applications that provide system services.

  • Manage - Applications that manage the device.

The operator signs the bundle with an appropriate certificate before it is deployed, when the bundle is installed, it will be automatically be assigned to the appropriate security scope.

However, the behavior can also be obtained using the local permissions of a bundle.

50.2.5 Typical Example

This example provides a simple setup for a delegation model. The example is intended for readability, certain concepts will be explained later. The basic syntax for the policies is:

policy      ::= access '{' conditions permissions '}' name?
access      ::= 'ALLOW' | 'DENY'       // case insensitive 
conditions  ::= ( '[' qname quoted-string* ']' )*
permissions ::= ( '(' qname (quoted-string 
                         quoted-string?)? ')' )+
name        ::= quoted-string

For readability, package prefixes that can be guessed are replaced with "..".

The following policy has a condition that limits the permissions to bundles that are signed by ACME. The permissions given are related to managing other bundles.

ALLOW {
   [ ..BundleSignerCondition "* ; o=ACME" ]

    ( ..AdminPermission "(signer=\* ; o=ACME)" "*" )
    ( ..ServicePermission "..ManagedService" "register" )
    ( ..ServicePermission "..ManagedServiceFactory" "register" )
} "1"

The next permission policy is for bundles signed by the operator. The operator bundles get full managing capabilities as well as permissions to provide any service.

ALLOW {
    [ ..BundleSignerCondition "*; o=Operator" ]
    ( ..AdminPermission "*" "*" )
    ( ..ServicePermission "*" "get,register" )
    ( ..PackagePermission "*" "import,exportonly" )
} "2"

The following block indicates that all bundles not signed by ACME will not have access to the com.acme.secret package, nor can they provide it. In this case, only bundles that are signed by ACME may use the com.acme.secret.* packages. Instead of explicitly specifying all allowed packages for bundles not signed by ACME, it is easier to deny them the protected packages. The exclamation mark ('!' \u0021) is a parameter for the Bundle Signer Condition to reverse its normal answer. This facility is also available in the Bundle Location Condition.

That is, the following policy specifies that bundles not signed by ACME will be denied permission to package com.acme.secret.*.

DENY {
   [ ..BundleSignerCondition "* ; o=ACME" "!" ]
    ( ..PackagePermission "com.acme.secret.*" 
            "import,exportonly" )
} "3"

Basic permissions define the permissions that are available to all bundles. The basic permissions therefore have no conditions associated with them so all bundles will be able to use these permissions. All bundles may use the Log Service as well as use any package that was not yet denied earlier.

ALLOW { 
    (..ServicePermission "..LogService" "get" )
    (..PackagePermission "*" "import" )
} "4"

The resulting permissions are summarized in Table 50.1. The + indicates allow, the - means deny. The number is the deciding policy.

Table 50.1 Assigned Permissions. + indicates allow, - deny.

    Unsigned ACME Operator
..LogService get +4 +4 +2
..ManagedService* register - +1 +2
..ManagedService* get - - +2
com.acme.FooService get - - +2
com.acme.secret import -3 +4 +2
com.acme.secret.bar exportonly -3 - +2
com.acme.foo import +4 +4 +2
bundle signed by ACME start - +1 +2
bundle signed by Operator start - - +2

50.3 Effective Permissions

Once a bundle is installed, it gets Java permissions associated from the framework. Some of these permissions are implied. Implied permissions are given by the framework because they are required for normal operation, for example every bundle has the File Permission to read and write the bundle data area. See Implied Permissions.

A framework can also provide an administrative service to associate a set of permissions with a bundle. The set of permissions given by such an administrative agent to a bundle are called the system permissions. For example, the Permission Admin service and the Conditional Permission Admin service can be used by a managing application to define the system permissions. Additionally, a bundle can carry its own permissions; these are called the local permissions. All these permission sets interact in a non-trivial way to give the effective permissions.

The purpose of the local permissions is to mitigate the bundle signer's risk. The Framework guarantees that a bundle's effective permissions are always smaller or equal than the local permissions because the effective permissions are the intersection of the local permissions with the system permissions, except for the implied permissions that are always granted.

Effective = ( Local  System)  Implied

The system permissions have two possible sources. The system permissions can be bound via the Permission Admin service to a location. This mechanism is provided for backward compatibility only. New management applications should use the Conditional Permission Admin service if possible.

If the Permission Admin location is not bound, all the conditional permissions from Conditional Permission Admin act as the system permissions. The relationship between the system permissions and local permissions is depicted in Figure 50.6.

Figure 50.6 System, Local and Security permissions

System, Local and Security permissions

50.4 Conditional Permissions

The conditional permissions provide a very general model that is related to the Java Security model. The Java Security model assigns a set of permissions to a code base or signer. During the permission check, this set is consulted for permissions that imply the checked permissions. If the checked permission is implied, access is granted.

The Conditional Permission Admin service model assumes a more general approach. It conceptually has a system wide policy table, that is shared between all bundles.

A policy consists of:

  • An access type (ALLOW or DENY)

  • A set of conditions

  • A set of permissions

  • A name

During a permission check, the table is traversed in ascending index order and each policy is evaluated. The first policy that is matching controls the outcome of the permission check as given by its access type. A policy is only matching a permission P when:

  • All of the policy's conditions are satisfied

  • At least one of its permissions implies P, as defined by Java security.

For example, assume the following setup for bundle A:

ALLOW { 
  [ ...BundleSignerCondition "cn=*, o=ACME, c=US" ]
  [ com.acme.Online ]
   (...AdminPermission "*" "*") 
}

In the example, both the BundleSignerCondition must be satisfied as well as the com.acme.Online condition before Admin Permission is granted.

Deny policies significantly simplify the security configuration setup because they handle the common case of an exception to a general rule. For example, a bundle that is allowed to use all package imports and exports except packages in the com.acme.secret.* namespace. The set of all possible packages is impossible to enumerate because it is an infinite set. In this case, * cannot be used because the com.acme.secret.* should not be included. With a deny policy it is possible to first deny access to com.acme.secret.* for all bundles bar the exception, and then later allow * for everybody. The policies for this look like:

DENY {
    [...BundleSignerCondition "cn=*, o=ACME" "!" ]
    (...PackagePermission "com.acme.secret.*" 
            "import,exportonly" )
}
ALLOW {
    (...PackagePermission "*" "*" )
}

50.4.1 Encoding versus Instantiation

The system wide policy table does not contain instances, it contains encoded forms of the permissions and conditions. The policy table acts as a template for each Bundle Protection Domain; the Bundle Protection Domain creates instances with the associated bundle as their context.

It is a dynamic template because a Bundle Protection Domain must track the changes to the framework's policy table immediately and update any instances from the new encoded forms. Once the atomic commit() method of the update object has successfully returned, all subsequent use of Bundle Protection Domains must be based on the new configuration. See Permission Management for more information of how to manage this table.

The conditions and permissions of the policy table must be instantiated before the conditions can be checked. This instantiation can happen, when a Bundle Protection Domain is created, or the first time when the conditional permissions are needed because of a permission check. Figure 50.7 shows the central table and its instantiation for different Bundle Protection Domains.

Figure 50.7 Instantiation of the policy table

Instantiation of the policy table

Condition objects must always belong to a single Bundle Protection Domain and must never be shared.

50.5 Conditions

The purpose of a Condition is to decide if a policy is applicable or not. That is, it acts as a guard for the permissions. The conditions must therefore be evaluated when a Permission object is checked against the effective permissions of a bundle.

The state of a Condition object can be obtained with its isSatisfied() method. A condition that returns true to this method is called to be satisfied. If the method throws an Exception, this should be logged and treated as if the condition is not satisfied.

Certain Condition objects could optimize their evaluations if they are activated multiple times in the same permission check. For example, a user prompt could appear several times in a permission check but the prompt should only be given once to the user. These conditions are called postponed conditions, conditions that can be verified immediately are called immediate conditions. The isPostponed() method can inform if the condition is immediate or postponed. A Condition must always return the same value for the isPostponed method so that the Conditional Permission Admin can cache this value. If this method returns false, the isSatisfied() method must be quick and can be called during the permission check, otherwise the decision must be postponed until the end of the permission check because it is potentially expensive to evaluate. Postponed conditions must always be postponed the first time they are evaluated.

For example, a condition could verify that a mobile phone is roaming. This information is readily available in memory and therefore the isPostponed() method could always return false. Alternatively, a Condition object that gets an authorization over the network should only be evaluated at most once during a permission check to minimize the delay caused by the network latency. Such a Condition object should return true for the isPostponed method so all the Condition objects are evaluated together at the end of the permission check.

Condition objects only need to be evaluated multiple times when the answer can change. A Condition object that can vary its satisfiability is called mutable, it can be checked with the isMutable() method. If the condition is immutable, the Condition object must always return the same result for the isSatisfied() method. The isMutable() method answers the mutability of the next isSatisfied() method. The answer of the next call to the isSatisfied method could still differ from the previous call, even if the isMutable method returns true.

A mutable Condition can become immutable over time but it cannot go from immutable to mutable. Optimizations can take advantage of the immutability by, for example, caching the result of the isSatisfied() method.

Except for immediate conditions, the isSatisfied method must only be called inside a permission check.

For example, the Bundle Protection Domain can prune any policy from its view of the policy table that contains a Condition object that is immutable and not satisfied. The Bundle Signer Condition and Bundle Location Condition are examples of immutable conditions that can easily be discarded when they do not match the context bundle when the policy table is instantiated. See Optimizing Immutable Conditions for more information about optimizing immutable conditions.

50.6 The Permission Check

The Java security model has both a Security Manager and an Access Controller to perform a permission check. The core functionality is located in the AccessController and the AccessControlContext classes that cooperate with ProtectionDomain objects to detect if a permission is allowed or denied. In the OSGi Framework, each bundle must have a single Bundle Protection Domain that holds the instantiated policy table.

The Access Controller provides the full functionality for checking a permission. However, all permission checks should be tunneled through the SecurityManager checkPermission methods. The Security Manager can be replaced by a custom implementation, unlike the Access Controller (it is a final class). This model is depicted in Figure 50.8.

Figure 50.8 in OSGi bundles

in OSGi bundles

50.6.1 Security Manager checkPermission Method

A permission check starts when the Security Manager checkPermission method is called with permission P as argument. The current Security Manager must be implemented by the Framework and is therefore called the Framework Security Manager; it must be fully integrated with the Conditional Permission Admin service.

The Framework Security Manager must get the Access Control Context in effect. It must call the AccessController getContext() method to get the default context if it is not passed a specific context.

The AccessControlContext checkPermission method must then be called, which causes the call stack to be traversed. At each stack level the Bundle Protection Domain of the calling class is evaluated for the permission P using the ProtectionDomain implies method. The complete evaluation must take place on the same thread.

50.6.2 Bundle Protection Domain implies Method

Permission P must be implied by the local permissions of the Bundle Protection Domain. If this is not the case, the complete check must immediately end with a failure. Local permissions are described in Local Permissions and Bundle Permission Resource.

The permission check now depends on the instantiated policy table, called Ts. During the Bundle Protection Domain implies method, the goal is to decide if the permission P is denied, or can progress because it is potentially allowed. Potentially, because the table can contain postponed conditions that need to be executed after all protection domains are checked.

The policy table must therefore be traversed in ascending index order until the first policy is matching that can give an immediate access type. If this access type is DENY, the implies method fails and aborts the check. If an ALLOW is found, the next domain must be checked. To ensure that there is at least one immediate matching policy in the table, a virtual DENY { (AllPermission) } is added at the end of the table. This virtual policy has the effect of making the default policy DENY when no matching entries are found.

During the traversal, an optimized policy list per bundle is constructed containing the postponed conditions and at the end a matching policy. This list is evaluated after all the protection domains are checked and none of them failed.

Therefore, the following definitions begin the Bundle Protection Domain implies method's algorithm:

Ts = instantiated policy table + DENY {(AllPermission)}
PL = {}

PL will be copied from Ts until the first policy that matches. A matching policy has all of its conditions immediately satisfied and one of the permissions implies permission P. If a policy can never be matched because it has an immediate condition that cannot be satisfied, then it is not copied to PL. At the end, PL contains zero or more postponed policies followed by exactly one matching policy.

In pseudo code:

policy: 
for each policy T in Ts
    for each condition C in T conditions
        if C is immediate and C is not satisfied 
            continue policy

    found = false
    for permission X in T permissions
        found |= X implies P

    if not found
        continue policy

    add T to PL

    if T has no postponed conditions
        break

PL must now be optimized to remove superfluous policies. A postponed policy can be removed if it cannot change the outcome from the last (which is an immediate) policy. That is, if the immediate policy at the end has the same access type as the previous policy in PL, then it can be ignored. The following pseudo code removes these superfluous postponed conditions.

while PL length > 1
    if PL[PL length -2] access = PL[PL length -1] access
        remove PL[PL length -2]
    else
        break

After discarding these superfluous postponed conditions, the contents of PL has the structure outline in Figure 50.9, where Tp(x) is a postponed policy with a access type x, and Tm is a matching policy, ! is the not operator for the condition.

Figure 50.9 Structure of Postponed List PL

Structure of Postponed List PL

If PL contains only one policy and it is a DENY policy, then the Bundle Protection Domain can directly answer false from the implies method, which will abort the complete permission check evaluation. In all other cases, it must remember the postponed list PL for the given bundle, and return true.

if PL = {DENY{...}}
    return false
Bundle.pl = PL
return true

50.6.2.1 Example Bundle Protection Domain Check

This example demonstrated the per bundle evaluation aspect of the Bundle Protection Domain's implies method. Assume the following policies are active:

DENY {
    [ BundleSignerCondition "cn=ACME" "!" ]
    ( FilePermission "/tmp/acme/-" "READ,WRITE" )
} "0"
ALLOW { 
    ( FilePermission "/tmp/-" "READ,WRITE" )
} "1"
ALLOW {
   [ PromptCondition "Allowed to Read?" ]
   ( FilePermission "*" "READ" )
} "2"
DENY {
    [ PromptCondition "Deny Writing?" ]
     ( FilePermission "*" "READ,WRITE" )
} "3"

This setup reserves unconditionally the /tmp/acme/- file system for bundles signed by ACME because the first line denies any bundle not signed by ACME access to this part of the file system. Reading and writing of the remainder of the /tmp file tree is allowed for all. For the rest of the file system, read access is prompted to allow, and write access is prompted to deny.

Assume that a bundle signed by ACME wants to read /etc/passwd. Policy 0, and 1 do not match because the File Permission in these policies are not applicable. Policy 2 has a permission that implies this file and its condition is postponed, so it will be postponed and policy 3 will also included. There is no matching policy, so a virtual matching DENY policy (D) will be included as the last entry. This makes PL: 2, 3, and D.

Tp(ALLOW)       # 2
Tp(DENY)        # 3 
Tm(DENY)        # virtual (D)

In this case, there is a superfluous Tp(DENY) #3 because it can not change the final answer of the last matching DENY. It is therefore removed. The list is thus:

Tp(ALLOW)       # 2
Tm(DENY)        # virtual

This list must be saved for later evaluation when all the Bundle Protection Domains have finished successfully.

50.6.3 Postponed Evaluation

If all protection domains have been checked and none has denied the permission by returning false, then each checked Bundle Protection Domain has a postponed list.

This per bundle postponed list contains one or more policies that must now be evaluated in sequence. The first policy in the list that can satisfy all its postponed conditions decides the access. If this policy has an access type of ALLOW, the list of the next domain is evaluated otherwise the evaluation fails.

The evaluation always ends because the last entry in each of the postponed lists is guaranteed to be a matching policy. As a special case, a postponed list with one entry indicates success. This must be a matching ALLOW because an immediate DENY would have failed earlier.

For example, if bundle A postponed policy Tp1 and bundle B postponed policy Tp2, and bundle C was immediately satisfied for ALLOW, then the constellation would like Figure 50.10.

Figure 50.10 Evaluation of postponed policies

Evaluation of postponed policies

The Conditional Permission Admin provides a type specific Dictionary object to all evaluations of the same postponed Condition implementation class during a single permission check. It is the responsibility of the Condition implementer to use this Dictionary to maintain states between invocations. The condition is evaluated with a method that takes an array and a Dictionary object: isSatisfied(Condition[],Dictionary). The array always contains a single element that is the receiver. An array is used because an earlier version of this specification could verify multiple conditions simultaneously.

The Dictionary object has the following qualities:

  • It is specific to a Condition implementation class, different implementation classes will not share this Dictionary object.

  • It is created before the isSatisfied(Condition[],Dictionary) is called for the first time during this permission check.

  • It is only valid during the invocation of a single checkPermission session. That is, it is not maintained between checkPermission invocations.

  • It is shared between invocations of isSatisfied(Condition[], Dictionary) method for different Bundle Protection Domains.

The algorithm for the postponed processing can now be explained with the following pseudo code:

bundle: 
for each bundle B
    policy:
    for each policy T in B.pl
        for C in T conditions
             if C is postponed and
                C is not satisfied with Dictionary
                continue policy

        if T access = DENY 
            return false
        else
            continue bundle
    assert false // can not reach
    
return true

50.6.4 Example

A permission P is checked while bundle A, B, and C are on the call stack. Their security setup is as follows:

  • IC = a condition that is immediately evaluated,

  • PC is a postponed condition,

  • P, Q, and R are permissions.

The situation for C is as follows:

ALLOW {                 (Q)     }  "C1"
ALLOW { [IC0]           (P)     }  "C2"
ALLOW { [PC2]           (P)     }  "C3"

First, the Bundle Protection Domain of bundle C is asked if it implies permission P. Bundle C has three policies. Policy C1 has no conditions, only a permission that does not imply permission P, it is therefore ignored. The second policy has an immediate condition IC0, which is not satisfied. Therefore, the policy's permissions are not even considered. The last policy contains a mutable postponed condition PC2. The permission P is implied by the related permissions. However, it is not possible to make the decision at this moment in time, therefore the evaluation of policy C3 is postponed. The postponed list for bundle C is therefore:

ALLOW {[PC2]}                      "C3"
DENY  {(AllPermission)}

This list can not be optimized because the final access type differs from the earlier access types.

The setup for bundle B is as follows:

ALLOW { [IC1][PC2][PC1] (P) (R) }  "B1"
ALLOW { [PC2]           (P) (R) }  "B2"
DENY  {                 (P)     }  "B3"
ALLOW {                 (Q)     }  "B4"

Bundle B is considered, its first policy has and immediate Condition object that is IC1. This condition turns out to be satisfied. This policy is a potential candidate because it has two postponed conditions left. It is a possibility because its permissions imply permission P. The policy is therefore placed on the postponed list.

Policy B2 is similar, it must also be placed on the postponed list because it implies permission P and it has a postponed condition PC2.

Policy B3 matches, it is therefore placed on the postponed list and the evaluation is stopped because there is an immediate decision, therefore it is not necessary to look at policy B4.

There are 2 policies postponed, the bundle is potentially permitted. Bundle's B postponed list therefore looks like:

ALLOW {[PC2][PC1]}    "B1"
ALLOW {[PC2]}         "B2"
DENY  { } 

This list cannot be optimized because the final access type differs from the earlier postponed conditions.

Last and least, bundle A.

  A:  ALLOW {  [IC1] [PC1]    (P) (Q) }    "A1"
      ALLOW {  [IC2]          (P) (R) }    "A2"
      ALLOW {                 (S)     }    "A3"

Bundle A's IC1 is evaluated first and it is satisfied. Permission P is implied by the policy A1's permissions, therefore this policy is postponed for evaluation.

Policy A2 is also satisfied and it directly implies permission P. This policy is therefore also placed on the postponed list and further evaluation is no longer necessary because it is the first matching policy. That is, policy A3 is ignored. The postponed list looks like:

ALLOW { [PC1] } "A1"
ALLOW { }       "A2"

This list is optimized to:

ALLOW {}        "A2"

After the checkPermission method of the Access Control Context method returns, the Framework Security Manager must evaluate the postponed lists of all the bundles. The list of postponed policies looks like Figure 50.10.

Figure 50.11 Evaluation of postponed policies

Evaluation of postponed policies

The Framework Security Manager must now evaluate the postponed lists for each bundle. In this example, postponed condition PC2 occurs 3 times. All these evaluations of PC2 get the same Dictionary object. If PC2 prompts the users with a specific question, then it should not ask the same question again when another PC2 is evaluated later. The Dictionary object can be used to maintain this state.

Both PC1 and PC2 prompt the user. PC1 will be rejected in this example, and PC2 will be affirmed.

First the postponed list of bundle A is evaluated. This is the trivial case of ALLOW {}, and the postponed list for bundle A succeeds with ALLOW.

For bundle B, policy T1 must prompt for PC2 and PC1. PC2 records the answer in the Dictionary that is specific for PC2. Because PC1 fails, T1 is not applicable and policy T2 must be evaluated. PC2 had recorded its answer so it does not prompt but returns true immediately. Policy T2 is an ALLOW policy and bundle B therefore ends positively.

Last, bundle C requires evaluation of policy T4. PC2 retrieves its answer from the given Dictionary object and succeeds. Policy T4 has an access type of ALLOW and this concludes the complete permission check positively.

50.6.5 Using the Access Control Context Directly

Bundle programmers should always use the Java Security Manager to do security checks. When the Access Controller is used directly (or the Access Control Context) to do the security check instead, then the evaluation cannot handle postponed conditions. Therefore, the postponed conditions must be treated as immediate conditions by the Bundle Protection Domain when the permissions check does not go through the Framework's security manager. The implication of this is that the result of checking a permission can depend on the way the check is initiated.

For example, a bundle on the stack has the needed permission P tied to a User Prompt Condition and another bundle on the stack does not have the Permission P. The check would fail if the Security Manager was called and the user would never be prompted because the failure was detected before the conditional permissions could be evaluated. However, if the Access Control Context was called directly, the user would be prompted and fail even if the user acknowledged the request.

50.7 Permission Management

The policy model provided by the Conditional Permission Admin service requires that the policies in the policy table are ordered. This requires a management interface that allows easy manipulation of the ordered table. The List interface fulfills this requirement, but an OSGi Framework is a dynamic environment and there can be other parties editing the same policy table. Therefore, the Conditional Permission Admin service uses an indirection. If a bundle wants to edit the table, it can get the table in a ConditonalPermissionUpdate object with the newConditionalPermissionUpdate() method from the Conditional Permission Admin service. This method creates a copy of the policy table in the returned update object. This update object provides access to a List object with ConditionalPermissionInfo objects, which are the encoded form of the policies.

New objects can be created with the newConditionalPermissionInfo(String,ConditionInfo[],PermissionInfo[],String) method on the Conditional Permission Admin service, and then added to this list. The method requires a name, an array of ConditionInfo objects, an array of PermissionInfo objects, and the access decision. The name parameter can be null. Each ConditionalPermissionInfo object has a name to distinguish it from others, as well as identifying it to a management server. If the name is null, the Conditional Permission Admin service will automatically create a unique name. Though it is possible to create policies with the same name, during the commit the names will be verified for uniqueness. If a duplicate name appears, an exception will be thrown.

Conditional Permission Infos can also be removed from this list. Modifications like remove, do not change or influence the running system, they only affect the update object.

All changes are effectuated when the commit method is called. If there had been a change in the underlying policy table since the update object was created, the commit method will abort and return false. Otherwise, the actual policy table is atomically updated and true is returned. There is no obligation to ever call commit; a canceled update can just be forgotten.

The data structures of the update model are depicted in Figure 50.12.

Figure 50.12 Structure of the Info objects.

Structure of the Info objects.

Both the ConditionalInfo and PermissionInfo objects can be constructed from encoded strings. The syntax for these strings are:

conditions  ::= ( '[' qname quoted-string* ']' )*
permissions ::= ( '(' qname (quoted-string 
                         quoted-string?)? ')' )+

The strings are converted by getting the class with the qname, and then creating an instance through a constructor that takes the given number of arguments. The exclamation mark is convention for a negated condition, it indicates that the condition should reverse its result. Deny policies often require the negation of the conditions. For example, often a bundle should not be signed by a specific signer to exclude it from a resource.

Both the PermissionInfo and ConditionInfo are concrete classes with appropriate methods to construct them from their encoded form. The encoded form given to a Condition implementation constructor must not contain the exclamation mark for negation.

A policy is specified with the following syntax:

policy      ::= access '{' conditions permissions '}' name?
access      ::= 'ALLOW' | 'DENY'  // case insensitive 
name        ::= quoted-string

The Conditional Permission Admin provides a convenience method to create a ConditionalPermissionInfo object with the newConditionalPermissionInfo(String) method.

The following example is a code snippet the reads a stream with conditional permissions using the encoded form. The method parses the file line by line. Each line is scanned and split with regular expressions. The following example shows how a text string can be parsed and added to the update object.

static Pattern CP_MATCHER = Pattern.compiler(...);
public  void manage(ConditionalPermissionAdmin admin, 
    String text) {
    ConditionalPermissionUpdate update = admin
            .newConditionalPermissionUpdate();
    List list = update.getConditionalPermissionInfos();
    list.clear();

    Matcher m = CP_MATCHER.matcher(text);
    int n = 1;
    while (m.find()) {
        String policy = m.group(1);
        ConditionalPermissionInfo info = 
            admin.newConditionalPermissionInfo( policy );
            
        list.add(info);
    }
    if ( !update.commit() )
        throw new   ConcurrentModificationException(
    "Conditional Permission Admin was updated concurrently");
}

50.7.1 Default Permissions

Conditional Permission Admin does not have a specific concept of default permissions. Default permissions are derived from the policies that do not have any Condition objects. These policies are applied to all bundles, effectively making them default permissions. This is a different from Permission Admin; in Permission Admin default permissions only apply when there are no specific permissions set.

50.8 Implementing Conditions

Condition objects are constructed from ConditionInfo objects when the policy table is instantiated for a Bundle Protection Domain. The ConditionInfo object supports a variable number of arguments.

The Conditional Permission Admin must use reflection to find a public static getCondition method on the Condition implementation class that takes a Bundle object and a ConditionInfo object as arguments. This method must return a object that implements the Condition interface.

However, this does not have to be a new object, the getCondition method can reuse objects if it so desires. For example, a Bundle Location Condition is immutable, it therefore maintains only 2 instances: One for bundles that match the given location and one for the others. In the getCondition method it can verify the bundle's location with argument and return either instance.

This is such a common pattern that the Condition interface provides two such immutable instances:

  • TRUE - A condition object that will always evaluate to true and that is never postponed.

  • FALSE - A condition object that will always evaluate to false and that is never postponed.

If no static getCondition method can be found, the Conditional Permission Admin service must try to find a public constructor that takes a Bundle object and a ConditionInfo object as arguments. For the com.acme.AcmeCondition, the Conditional Permission Admin must look for:

public static Condition com.acme.AcmeCondition.getCondition( 
        Bundle, ConditionInfo )
public com.acme.AcmeCondition( Bundle, ConditionInfo )

If it is not possible to create a condition object, the given condition must be treated as a Condition.FALSE object and an error should be logged.

A Condition object will be unique to a Bundle Protection Domain as explained in Encoding versus Instantiation. Thus, any queries made on a Condition object will be with the given Bundle object as context.

The cheapest Condition objects are immutable; they have almost no overhead. If a Condition object is immutable directly after it is created, then the Framework Security Manager can immediately shortcut future evaluations. That is, if an immutable Condition object is not satisfied, its parent policy can be immediately be discarded; it is not even necessary to instantiate any further Condition or Permission objects for that policy.

Mutable Condition objects must be evaluated during a permission check. Permission checks are common and the evaluation of a permission should therefore be highly optimized and preferably not cause additional security checks on the same thread. A mutable condition is system code, it must be designed to work in a constrained environment. The isSatisfied() method should be designed to quickly return. It should normally base its decision on variables and limit its side effects.

However, side effects are sometimes necessary; a key example is user prompting. As discussed in Security Manager checkPermission Method, the evaluation can be postponed towards the end of the check, where a special version of isSatisfied method is called. The Condition object must always return true for the isPostponed() method to be postponed and it must always return the same value.

Mutable postponed conditions must optimize their evaluation by implementing an instance method isSatisfied(Condition[],Dictionary). This method must only be called with a single element in the array; this element is unrelated to the given instance (except that the class is the same).

The following is the code for a condition that verifies that an action is granted by a network server. This is a postponed condition that groups all requests before it asks the host for authorization. The network code is abstracted in a Host class that is not shown here.

public class HostCondition implements Condition{
    String                  action;
 
    public HostCondition( Bundle, ConditionInfo info ) {
        action = info.getArgs()[0];
    }

    public boolean isSatisfied() { return false; }
    public boolean isPostponed() { return true; }
    public boolean isMutable()   { return false; }

    static Host         host = new Host();

    public synchronized boolean isSatisfied(
        Condition[] conditions, Dictionary state ) {
        Set       granted = (Set) state.get("granted");
        if ( granted == null ) {
            granted = new TreeSet();
            state.put("granted", granted );
        }
        Set     pending = new TreeSet();
        // There will only be one condition
        for ( int i=0; i<conditions.length; i++ ) {
            String a = ((HostCondition)conditions[i]).action;
            if ( ! granted.contains(a) )
                pending.add( a );
        }
        if ( pending.isEmpty() )
            return true;

        if ( ! host.permits( pending ) )
            return false;

        granted.addAll( pending );          
        return true;
    }               
}

The Host Condition has the following Condition Info representation:

[ HostCondition "payment" ]

The majority of the code is in the isSatisfied method which takes an array of Condition. The constructor only stores the action.

This isSatisfied method first gets the set of granted permissions. The first time the method is called this set does not exist. It is then created and stored in the state dictionary for use in later invocations.

Next, a temporary set pending is created to hold all the actions of the conditions that are checked, minus any conditions that were already granted during this invocation of the Security Manager checkPermission method. If the pending list turns out to be empty because all actions were already granted, the method returns true. Otherwise it asks the host. If the host allows the actions, the pending actions are added to the granted set in the state dictionary.

50.9 Standard Conditions

This specification provides a number of standard conditions. These conditions are explained in the following sections.

50.9.1 Bundle Signer Condition

A Bundle Signer Condition is satisfied when the related bundle is signed with a certificate that matches its argument. That is, this condition can be used to assign permissions to bundles that are signed by certain principals.

The Bundle Signer Condition must be created through its static getCondition(Bundle,ConditionInfo) method. The first string argument is a matching Distinguished Name as defined in Certificate Matching. The second argument is optional, if used, it must be an exclamation mark ('!' \u0021). The exclamation mark indicates that the result for this condition must be reversed. For example:

[ ...BundleSignerCondition "* ;cn=S&V,o=Tweety Inc., c=US"]
[ ...BundleSignerCondition "* ;cn=S&V" "!"]

The Bundle Signer Condition is immutable and can be completely evaluated during the getCondition method.

50.9.2 Bundle Location Condition

The Bundle Location Condition matches its argument against the location string of the bundle argument. Bundle location matching provides many of the advantages of signing without the overhead. However, using locations as the authenticator requires that the download locations are secured and cannot be spoofed. For example, an Operator could permit Enterprises by forcing them to download their bundles from specific locations. To make this reasonable secure, at least the HTTPS protocol should be used. The Operator can then use the location to assign permissions.

https://www.acme.com/download/*      Appsfrom ACME
https://www.operator.com/download/*  Operatorapps

The Bundle Location Condition must be created through its static getCondition(Bundle,ConditionInfo) method. The first string argument is a location string with possible wildcard asterisks ('*' \u002A). Wildcards are matched using Filter string matching. The second argument is optional, if used, it must be an exclamation mark ('!' \u0021). The exclamation mark indicates that the result for this condition must be reversed. For example:

..BundleLocationCondition "http://www.acme.com/*"
..BundleLocationCondition "*://www.acme.com/*"

The Bundle Location Condition is satisfied when its argument can be matched with the actual location.

The Bundle Location Condition is immutable and can be completely evaluated during the getCondition method.

50.10 Bundle Permission Resource

Bundles can convey their local permissions using the file OSGI-INF/permissions.perm. This must be a UTF-8 encoded file. The format of the file is line based; lines are not limited in length but must be readable with the BufferedReader readLine method:

    permissions.perm ::= line *
    line             ::= ( comment | pinfo ) ( '\r\n'| '\n' )
    comment          ::= ( '#' | '//' )
    pinfo            ::= '(' qname ( quoted-string 
                                    ( quoted-string )? )? ')'
    // See 1.3.2

Each permission must be listed on its own line using the encoded form of Permission Info. Comment lines are allowed. They consist of lines starting with a # or //, where leading spaces must be ignored. Multiple spaces outside quotes must be treated as a single space.

For example (.. must be replaced with the appropriate package prefix.):

# Friday, Feb 24 2005
# ACME, chess game
( ..ServicePermission "..log.LogService" "GET" )
( ..PackagePermission "..log" "IMPORT" )
( ..ServicePermission "..cm.ManagedService" "REGISTER" )
( ..PackagePermission "..cm" "IMPORT" )
( ..ServicePermission "..useradmin.UserAdmin" "GET" )
( ..PackagePermission "com.acme.chess" "IMPORT,EXPORTONLY")
( ..PackagePermission "com.acme.score" "IMPORT" )

If this resource is present in the Bundle JAR, it will set the local permissions. If it is not present, the local permissions must be All Permission.

50.10.1 Removing the Bundle Permission Resource

An attacker could circumvent the local permission by simply removing the permissions.perm file from the bundle. This would remove any local permissions that were required by a signer of the bundle. To prevent this type of attack the Conditional Permission Admin must detect that the permissions.perm resource was signed, that is, present in the Manifest, but that it is not in the JAR. If the bundle is being installed when this condition is detected, the install must fail with a Bundle Exception.

50.11 Relation to Permission Admin

If the framework provides a Conditional Permission Admin service and a Permission Admin service then a bundle A will receive its permissions according to the following steps:

  1. If the Permission Admin defines the permissions for bundle A (location is set), then these permissions override any Conditional Permission Admin information.

  2. If the Conditional Permission Admin has a non-empty table, then this table is used to calculate the permissions for bundle A.

  3. If the default permissions are set in Permission Admin, then these are given to bundle A.

  4. All Permission is given to bundle A.

The Permission Admin defines a concept of Default Permissions, which is not supported by Conditional Permission Admin. Default permissions are now modeled with an empty set of conditions. Empty sets of conditions apply to all bundles, this in addition to any more specific conditions. This is very different from the Permission Admin service where the default permissions only apply when there is no location bound permission for that bundle. The default conditions of Permission Admin are therefore never used when Conditional Permission Admin is present and its table is non-empty.

New applications should use the Conditional Permission Admin service. The Permission Admin service will be deprecated in a future release.

50.12 Implementation Issues

50.12.1 Optimizing Immutable Conditions

There is a subtle interaction between mutability and postponement. An immutable postponed condition must be treated as a postponed conditions. This first result can then be cached. The following table shows the interaction between mutability and postponement. The Direct column indicates the steps during the permission check, the After column indicates the step when all the permissions are checked and found to allow the requested action.

isMutable isPostponed Direct After
false false isSatisfied() / cache  
false true use cache if exists isSatisfied(Condition[], Dictionary) / cache
true false isSatisfied()  
true true postpone isSatisfied(Condition[], Dictionary) (grouped)

This significant optimization is leveraged by the provided BundleLocationCondition and BundleSignerCondition classes. The Protection Domain will never have to consider conditional permissions that do not match the protection domain's bundle. However, a Condition object can also start as a mutable condition and later become immutable. For example, a user prompt could have the following states:

  • Prompt - The user must be prompted to get the answer, the Conditional Permission Admin will evaluate the answer to detect if it is satisfied.

  • Blanket - The user, during an earlier prompt, has indicated it approves or denies access for that remainder of the lifetime of the bundle. In this state, the Condition object has become immutable.

This specification provides a number of condition classes to bind permission sets to specific bundles. However, custom code can also provide conditions. See Implementing Conditions for more information about custom conditions.

50.12.2 Optimizing the Permission Check

Theoretically, every checkPermission method must evaluate every condition for every bundle on the call stack. That is, the Framework Security Manager must iterate through all bundles on the stack, run through the instantiated policy table of that bundle, evaluate all the conditions, test the permissions, until it finds a permission that is implied. This model would be prohibitively expensive.

Implementations are therefore urged to optimize the evaluation of the permission checks as much as possible. They are free to change the algorithms described in this specification as long as the external effect remains the same.

One optimization is pruning the instantiated policy table. A Condition object can be pruned if it is immutable.

If an immutable Condition object is satisfied, it can be removed from the policy's Condition objects because it cannot influence the evaluation anymore. If it is not satisfied, the corresponding policy can be completely discarded because one of the Condition objects is not satisfied, making it impossible for the policy to be used.

For example, assume the following policy table:

ALLOW {
    [ ...BundleLocationCondition 
          "http://www.acme.com/*" ]
    ( ...SocketPermission "www.acme.com" "connect,accept" )
} 
ALLOW {
    [ ...BundleLocationCondition 
          "http://www.et.com/*" ]
    [ ...Prompt "Phone home?" ]
    ( ...SocketPermission "www.et.com" "connect,accept" )
}

Assume this table is instantiated for a bundle with a location of http://www.acme.com/bundle.jar. The first policy's permissions can be placed in a the special Permission Collection because the Bundle Location condition is immutable and in this case satisfied.

The second policy can be discarded for this bundle because it is immutable and not satisfied for the bundle's location. Any condition that is not satisfied and immutable makes the policy ignorable.

50.12.3 Using Permission Checks in Conditions

If there is a chance that permissions will be checked in code being called by isSatisfied, the implementer of the Condition should use the AccessController doPrivileged to ensure needed permissions. For example, a User Prompt Condition has the potential to cause many permission checks as it interacts with the UI.

However, the same Condition object must not be evaluated recursively. The Framework must detect the recursive evaluation of a Condition object and act as if the second invocation returns an unsatisfied, not postponed Condition object.

For example, if a User Prompt Condition is evaluated and this evaluation accesses the UI, which in its turn checks a permission that causes the evaluation of the same User Prompt Condition, then this second evaluation must not take place and be treated as not postponed and false.

50.12.4 Concurrency

A Condition implementation is guaranteed that all evaluations necessary for a single checkPermission invocation are carried out on the same thread. However, multiple permission checks can take place on different threads. It is the responsibility of the Condition class implementers to handle these synchronization issues.

50.12.5 Class Loading

All conditions must come from the boot class path or from the Framework class loader. This is due to security reasons as well as to prevent the case that there are multiple versions of the implementation packages present. Conditions can still be downloaded with bundles by using a Framework extension bundle, see Extension Bundles.

50.12.6 Condition Life Cycle

Condition objects will get instantiated when the framework is restarted or the Bundle Protection Domain is created. Framework implementations can also use optimizations that cause Condition objects to be created and destroyed multiple times within the lifetime of an instance of a Bundle Protection Domain. An implementation of a Condition class must not make any assumptions about its creation or dereferencing.

50.13 Security

50.13.1 Service Registry Security

50.13.1.1 Conditional Permission Admin Service

The Conditional Permission Admin service should be part of the Framework and therefore has All Permission.

50.13.1.2 Client

ServicePermission   ..ConditionalPermissionAdmin  GET
PackagePermission   ..condpermadmin               IMPORT
AllPermission

Clients of the Conditional Permission Admin service that set permissions must themselves have All Permission because they can give All Permission to any bundle.

50.14 org.osgi.service.condpermadmin

Version 1.1

Conditional Permission Admin Package Version 1.1.

Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest.

Example import for consumers using the API in this package:

Import-Package: org.osgi.service.condpermadmin; version="[1.1,2.0)"

50.14.1 Summary

50.14.2 public class BundleLocationCondition

Condition to test if the location of a bundle matches or does not match a pattern. Since the bundle's location cannot be changed, this condition is immutable.

Pattern matching is done according to the filter string matching rules.

Thread-safe

50.14.2.1 public static Condition getCondition(Bundle bundle, ConditionInfo info)

The Bundle being evaluated.

The ConditionInfo from which to construct the condition. The ConditionInfo must specify one or two arguments. The first argument of the ConditionInfo specifies the location pattern against which to match the bundle location. Matching is done according to the filter string matching rules. Any '*' characters in the first argument are used as wildcards when matching bundle locations unless they are escaped with a '\' character. The Condition is satisfied if the bundle location matches the pattern. The second argument of the ConditionInfo is optional. If a second argument is present and equal to "!", then the satisfaction of the Condition is negated. That is, the Condition is satisfied if the bundle location does NOT match the pattern. If the second argument is present but does not equal "!", then the second argument is ignored.

Constructs a condition that tries to match the passed Bundle's location to the location pattern.

Condition object for the requested condition.

50.14.3 public class BundleSignerCondition

Condition to test if the signer of a bundle matches or does not match a pattern. Since the bundle's signer can only change when the bundle is updated, this condition is immutable.

The condition expressed using a single String that specifies a Distinguished Name (DN) chain to match bundle signers against. DN's are encoded using IETF RFC 2253. Usually signers use certificates that are issued by certificate authorities, which also have a corresponding DN and certificate. The certificate authorities can form a chain of trust where the last DN and certificate is known by the framework. The signer of a bundle is expressed as signers DN followed by the DN of its issuer followed by the DN of the next issuer until the DN of the root certificate authority. Each DN is separated by a semicolon.

A bundle can satisfy this condition if one of its signers has a DN chain that matches the DN chain used to construct this condition. Wildcards (`*') can be used to allow greater flexibility in specifying the DN chains. Wildcards can be used in place of DNs, RDNs, or the value in an RDN. If a wildcard is used for a value of an RDN, the value must be exactly "*" and will match any value for the corresponding type in that RDN. If a wildcard is used for a RDN, it must be the first RDN and will match any number of RDNs (including zero RDNs).

Thread-safe

50.14.3.1 public static Condition getCondition(Bundle bundle, ConditionInfo info)

The Bundle being evaluated.

The ConditionInfo from which to construct the condition. The ConditionInfo must specify one or two arguments. The first argument of the ConditionInfo specifies the chain of distinguished names pattern to match against the signer of the bundle. The Condition is satisfied if the signer of the bundle matches the pattern. The second argument of the ConditionInfo is optional. If a second argument is present and equal to "!", then the satisfaction of the Condition is negated. That is, the Condition is satisfied if the signer of the bundle does NOT match the pattern. If the second argument is present but does not equal "!", then the second argument is ignored.

Constructs a Condition that tries to match the passed Bundle's location to the location pattern.

A Condition which checks the signers of the specified bundle.

50.14.4 public interface Condition

The interface implemented by a Condition. Conditions are bound to Permissions using Conditional Permission Info. The Permissions of a ConditionalPermission Info can only be used if the associated Conditions are satisfied.

Thread-safe

50.14.4.1 public static final Condition FALSE

A Condition object that will always evaluate to false and that is never postponed.

50.14.4.2 public static final Condition TRUE

A Condition object that will always evaluate to true and that is never postponed.

50.14.4.3 public boolean isMutable()

Returns whether the Condition is mutable. A Condition can go from mutable (true) to immutable (false) over time but never from immutable (false) to mutable (true).

true isSatisfied() can change. Otherwise, false if the value returned by isSatisfied() will not change for this condition.

50.14.4.4 public boolean isPostponed()

Returns whether the evaluation must be postponed until the end of the permission check. If this method returns false (or this Condition is immutable), then this Condition must be able to directly answer the isSatisfied() method. In other words, isSatisfied() will return very quickly since no external sources, such as for example users or networks, need to be consulted.

This method must always return the same value whenever it is called so that the Conditional Permission Admin can cache its result.

true to indicate the evaluation must be postponed. Otherwise, false if the evaluation can be performed immediately.

50.14.4.5 public boolean isSatisfied()

Returns whether the Condition is satisfied. This method is only called for immediate Condition objects or immutable postponed conditions, and must always be called inside a permission check. Mutable postponed Condition objects will be called with the grouped version isSatisfied(Condition[],Dictionary) at the end of the permission check.

true to indicate the Conditions is satisfied. Otherwise, false if the Condition is not satisfied.

50.14.4.6 public boolean isSatisfied(Condition[] conditions, Dictionary<Object, Object> context)

The array of Condition objects, which must all be of the same class and mutable. The receiver must be one of those Condition objects.

A Dictionary object that implementors can use to track state. If this method is invoked multiple times in the same permission check, the same Dictionary will be passed multiple times. The SecurityManager treats this Dictionary as an opaque object and simply creates an empty dictionary and passes it to subsequent invocations if multiple invocations are needed.

Returns whether the specified set of Condition objects are satisfied. Although this method is not static, it must be implemented as if it were static. All of the passed Condition objects will be of the same type and will correspond to the class type of the object on which this method is invoked. This method must be called inside a permission check only.

true if all the Condition objects are satisfied. Otherwise, false if one of the Condition objects is not satisfied.

50.14.5 public interface ConditionalPermissionAdmin

Framework service to administer Conditional Permissions. Conditional Permissions can be added to, retrieved from, and removed from the framework. Conditional Permissions are conceptually managed in an ordered table called the Conditional Permission Table.

Thread-safe

Consumers of this API must not implement this type

50.14.5.1 public ConditionalPermissionInfo addConditionalPermissionInfo(ConditionInfo[] conditions, PermissionInfo[] permissions)

The conditions that need to be satisfied to enable the specified permissions. This argument can be null or an empty array indicating the specified permissions are not guarded by any conditions.

The permissions that are enabled when the specified conditions, if any, are satisfied. This argument must not be null and must specify at least one permission.

Create a new Conditional Permission Info in the Conditional Permission Table.

The Conditional Permission Info will be given a unique, never reused name. This entry will be added at the beginning of the Conditional Permission Table with an access decision of ALLOW.

Since this method changes the Conditional Permission Table any ConditionalPermissionUpdates that were created prior to calling this method can no longer be committed.

The ConditionalPermissionInfo for the specified Conditions and Permissions.

IllegalArgumentException– If no permissions are specified.

SecurityException– If the caller does not have AllPermission.

As of 1.1. Use newConditionalPermissionUpdate() instead.

50.14.5.2 public AccessControlContext getAccessControlContext(String[] signers)

The signers for which to return an Access Control Context.

Returns the Access Control Context that corresponds to the specified signers. The returned Access Control Context must act as if its protection domain came from a bundle that has the following characteristics:

  • It is signed by all of the given signers

  • It has a bundle id of -1

  • Its location is the empty string

  • Its state is UNINSTALLED

  • It has no headers

  • It has the empty version (0.0.0)

  • Its last modified time=0

  • Many methods will throw IllegalStateException because the state is UNINSTALLED

  • All other methods return a null

An AccessControlContext that has the Permissions associated with the signer.

50.14.5.3 public ConditionalPermissionInfo getConditionalPermissionInfo(String name)

The name of the Conditional Permission Info to be returned.

Return the Conditional Permission Info with the specified name.

The Conditional Permission Info with the specified name or null if no Conditional Permission Info with the specified name exists in the Conditional Permission Table.

As of 1.1. Use newConditionalPermissionUpdate() instead.

50.14.5.4 public Enumeration<ConditionalPermissionInfo> getConditionalPermissionInfos()

Returns the Conditional Permission Infos from the Conditional Permission Table.

The returned Enumeration will return elements in the order they are kept in the Conditional Permission Table.

The Enumeration returned is based on a copy of the Conditional Permission Table and therefore will not throw exceptions if the Conditional Permission Table is changed during the course of reading elements from the Enumeration.

An enumeration of the Conditional Permission Infos that are currently in the Conditional Permission Table.

As of 1.1. Use newConditionalPermissionUpdate() instead.

50.14.5.5 public ConditionalPermissionInfo newConditionalPermissionInfo(String name, ConditionInfo[] conditions, PermissionInfo[] permissions, String access)

The name of the created ConditionalPermissionInfo or null to have a unique name generated when the returned ConditionalPermissionInfo is committed in an update to the Conditional Permission Table.

The conditions that need to be satisfied to enable the specified permissions. This argument can be null or an empty array indicating the specified permissions are not guarded by any conditions.

The permissions that are enabled when the specified conditions, if any, are satisfied. This argument must not be null and must specify at least one permission.

Access decision. Must be one of the following values:

The specified access decision value must be evaluated case insensitively.

Creates a new ConditionalPermissionInfo with the specified fields suitable for insertion into a ConditionalPermissionUpdate. The delete method on ConditionalPermissionInfo objects created with this method must throw UnsupportedOperationException.

A ConditionalPermissionInfo object suitable for insertion into a ConditionalPermissionUpdate.

IllegalArgumentException– If no permissions are specified or if the specified access decision is not a valid value.

1.1

50.14.5.6 public ConditionalPermissionInfo newConditionalPermissionInfo(String encodedConditionalPermissionInfo)

The encoded ConditionalPermissionInfo. White space in the encoded ConditionalPermissionInfo is ignored. The access decision value in the encoded ConditionalPermissionInfo must be evaluated case insensitively. If the encoded ConditionalPermissionInfo does not contain the optional name, null must be used for the name and a unique name will be generated when the returned ConditionalPermissionInfo is committed in an update to the Conditional Permission Table.

Creates a new ConditionalPermissionInfo from the specified encoded ConditionalPermissionInfo string suitable for insertion into a ConditionalPermissionUpdate. The delete method on ConditionalPermissionInfo objects created with this method must throw UnsupportedOperationException.

A ConditionalPermissionInfo object suitable for insertion into a ConditionalPermissionUpdate.

IllegalArgumentException– If the specified encodedConditionalPermissionInfo is not properly formatted.

ConditionalPermissionInfo.getEncoded()

1.1

50.14.5.7 public ConditionalPermissionUpdate newConditionalPermissionUpdate()

Creates a new update for the Conditional Permission Table. The update is a working copy of the current Conditional Permission Table. If the running Conditional Permission Table is modified before commit is called on the returned update, then the call to commit on the returned update will fail. That is, the commit method will return false and no change will be made to the running Conditional Permission Table. There is no requirement that commit is eventually called on the returned update.

A new update for the Conditional Permission Table.

1.1

50.14.5.8 public ConditionalPermissionInfo setConditionalPermissionInfo(String name, ConditionInfo[] conditions, PermissionInfo[] permissions)

The name of the Conditional Permission Info, or null.

The conditions that need to be satisfied to enable the specified permissions. This argument can be null or an empty array indicating the specified permissions are not guarded by any conditions.

The permissions that are enabled when the specified conditions, if any, are satisfied. This argument must not be null and must specify at least one permission.

Set or create a Conditional Permission Info with a specified name in the Conditional Permission Table.

If the specified name is null, a new Conditional Permission Info must be created and will be given a unique, never reused name. If there is currently no Conditional Permission Info with the specified name, a new Conditional Permission Info must be created with the specified name. Otherwise, the Conditional Permission Info with the specified name must be updated with the specified Conditions and Permissions. If a new entry was created in the Conditional Permission Table it will be added at the beginning of the table with an access decision of ALLOW.

Since this method changes the underlying permission table any ConditionalPermissionUpdates that were created prior to calling this method can no longer be committed.

The ConditionalPermissionInfo for the specified name, Conditions and Permissions.

IllegalArgumentException– If no permissions are specified.

SecurityException– If the caller does not have AllPermission.

As of 1.1. Use newConditionalPermissionUpdate() instead.

50.14.6 public interface ConditionalPermissionInfo

A list of Permissions guarded by a list of conditions with an access decision. Instances of this interface are obtained from the Conditional Permission Admin service.

Immutable

Consumers of this API must not implement this type

50.14.6.1 public static final String ALLOW = "allow"

This string is used to indicate that a row in the Conditional Permission Table should return an access decision of "allow" if the conditions are all satisfied and at least one of the permissions is implied.

1.1

50.14.6.2 public static final String DENY = "deny"

This string is used to indicate that a row in the Conditional Permission Table should return an access decision of "deny" if the conditions are all satisfied and at least one of the permissions is implied.

1.1

50.14.6.3 public void delete()

Removes this Conditional Permission Info from the Conditional Permission Table.

Since this method changes the underlying permission table, any ConditionalPermissionUpdates that were created prior to calling this method can no longer be committed.

UnsupportedOperationException– If this object was created by ConditionalPermissionAdmin.newConditionalPermissionInfo(String) or ConditionalPermissionAdmin.newConditionalPermissionInfo(String, ConditionInfo[] , PermissionInfo[] , String) or obtained from a ConditionalPermissionUpdate. This method only functions if this object was obtained from one of the ConditionalPermissionAdmin methods deprecated in version 1.1.

SecurityException– If the caller does not have AllPermission.

As of 1.1. Use ConditionalPermissionAdmin.newConditionalPermissionUpdate() instead to manage the Conditional Permissions.

50.14.6.4 public boolean equals(Object obj)

The object to test for equality with this ConditionalPermissionInfo object.

Determines the equality of two ConditionalPermissionInfo objects. This method checks that specified object has the same access decision, conditions, permissions and name as this ConditionalPermissionInfo object.

true if obj is a ConditionalPermissionInfo, and has the same access decision, conditions, permissions and name as this ConditionalPermissionInfo object; false otherwise.

1.1

50.14.6.5 public String getAccessDecision()

Returns the access decision for this Conditional Permission Info.

One of the following values:

  • allow - The access decision is "allow".

  • deny - The access decision is "deny".

1.1

50.14.6.6 public ConditionInfo[] getConditionInfos()

Returns the Condition Infos for the Conditions that must be satisfied to enable the Permissions.

The Condition Infos for the Conditions in this Conditional Permission Info.

50.14.6.7 public String getEncoded()

Returns the string encoding of this ConditionalPermissionInfo in a form suitable for restoring this ConditionalPermissionInfo.

The encoded format is:

   access {conditions permissions} name

where access is the access decision, conditions is zero or more encoded conditions, permissions is one or more encoded permissions and name is the name of the ConditionalPermissionInfo.

name is optional. If name is present in the encoded string, it must quoted, beginning and ending with ". The name value must be encoded for proper parsing. Specifically, the ", \, carriage return, and line feed characters must be escaped using \", \\, \r, and \n, respectively.

The encoded string contains no leading or trailing whitespace characters. A single space character is used between access and { and between } and name, if name is present. All encoded conditions and permissions are separated by a single space character.

The string encoding of this ConditionalPermissionInfo.

1.1

50.14.6.8 public String getName()

Returns the name of this Conditional Permission Info.

The name of this Conditional Permission Info. This can be null if this Conditional Permission Info was created without a name.

50.14.6.9 public PermissionInfo[] getPermissionInfos()

Returns the Permission Infos for the Permissions in this Conditional Permission Info.

The Permission Infos for the Permissions in this Conditional Permission Info.

50.14.6.10 public int hashCode()

Returns the hash code value for this object.

A hash code value for this object.

1.1

50.14.6.11 public String toString()

Returns the string representation of this ConditionalPermissionInfo. The string is created by calling the getEncoded method on this ConditionalPermissionInfo.

The string representation of this ConditionalPermissionInfo.

1.1

50.14.7 public interface ConditionalPermissionUpdate

Update the Conditional Permission Table. There may be many update objects in the system at one time. If commit is called and the Conditional Permission Table has been modified since this update was created, then the call to commit will fail and this object should be discarded.

1.1

Thread-safe

Consumers of this API must not implement this type

50.14.7.1 public boolean commit()

Commit this update. If no changes have been made to the Conditional Permission Table since this update was created, then this method will replace the Conditional Permission Table with this update's Conditional Permissions. This method may only be successfully called once on this object.

If any of the ConditionalPermissionInfos in the update list has null as a name it will be replaced with a new ConditionalPermissionInfo object that has a generated name which is unique within the list.

No two entries in this update's Conditional Permissions may have the same name. Other consistency checks may also be performed. If this update's Conditional Permissions are determined to be inconsistent in some way then an IllegalStateException will be thrown.

This method returns false if the commit did not occur because the Conditional Permission Table has been modified since the creation of this update.

true if the commit was successful. false if the commit did not occur because the Conditional Permission Table has been modified since the creation of this update.

SecurityException– If the caller does not have AllPermission.

IllegalStateException– If this update's Conditional Permissions are not valid or inconsistent. For example, this update has two Conditional Permissions in it with the same name.

50.14.7.2 public List<ConditionalPermissionInfo> getConditionalPermissionInfos()

This method returns the list of ConditionalPermissionInfos for this update. This list is originally based on the Conditional Permission Table at the time this update was created. The list returned by this method will be replace the Conditional Permission Table if commit is called and is successful.

The delete() method of the ConditionalPermissionInfos in the list must throw UnsupportedOperationException.

The list returned by this method is ordered and the most significant table entry is the first entry in the list.

A List of the ConditionalPermissionInfos which represent the Conditional Permissions maintained by this update. Modifications to this list will not affect the Conditional Permission Table until successfully committed. The list may be empty if the Conditional Permission Table was empty when this update was created.

50.14.8 public class ConditionInfo

Condition representation used by the Conditional Permission Admin service.

This class encapsulates two pieces of information: a Condition type (class name), which must implement Condition, and the arguments passed to its constructor.

In order for a Condition represented by a ConditionInfo to be instantiated and considered during a permission check, its Condition class must be available from the system classpath.

The Condition class must either:

  • Declare a public static getCondition method that takes a Bundle object and a ConditionInfo object as arguments. That method must return an object that implements the Condition interface.

  • Implement the Condition interface and define a public constructor that takes a Bundle object and a ConditionInfo object as arguments.

Immutable

50.14.8.1 public ConditionInfo(String type, String[] args)

The fully qualified class name of the Condition represented by this ConditionInfo.

The arguments for the Condition. These arguments are available to the newly created Condition by calling the getArgs() method.

Constructs a ConditionInfo from the specified type and args.

NullPointerException– If type is null.

50.14.8.2 public ConditionInfo(String encodedCondition)

The encoded ConditionInfo.

Constructs a ConditionInfo object from the specified encoded ConditionInfo string. White space in the encoded ConditionInfo string is ignored.

IllegalArgumentException– If the specified encodedCondition is not properly formatted.

getEncoded()

50.14.8.3 public boolean equals(Object obj)

The object to test for equality with this ConditionInfo object.

Determines the equality of two ConditionInfo objects. This method checks that specified object has the same type and args as this ConditionInfo object.

true if obj is a ConditionInfo, and has the same type and args as this ConditionInfo object; false otherwise.

50.14.8.4 public final String[] getArgs()

Returns arguments of this ConditionInfo.

The arguments of this ConditionInfo. An empty array is returned if the ConditionInfo has no arguments.

50.14.8.5 public final String getEncoded()

Returns the string encoding of this ConditionInfo in a form suitable for restoring this ConditionInfo.

The encoded format is:

   [type "arg0" "arg1" ...]

where argN are strings that must be encoded for proper parsing. Specifically, the ", \, carriage return, and line feed characters must be escaped using \", \\, \r, and \n, respectively.

The encoded string contains no leading or trailing whitespace characters. A single space character is used between type and "arg0" and between the arguments.

The string encoding of this ConditionInfo.

50.14.8.6 public final String getType()

Returns the fully qualified class name of the condition represented by this ConditionInfo.

The fully qualified class name of the condition represented by this ConditionInfo.

50.14.8.7 public int hashCode()

Returns the hash code value for this object.

A hash code value for this object.

50.14.8.8 public String toString()

Returns the string representation of this ConditionInfo. The string is created by calling the getEncoded method on this ConditionInfo.

The string representation of this ConditionInfo.