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).
-
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.
-
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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 |
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.
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
orDENY
) -
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 "*" "*" )
}
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.
Condition
objects must always belong to a single
Bundle Protection Domain and must never be shared.
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.
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.
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.
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.
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
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.
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.
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 thisDictionary
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 betweencheckPermission
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
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
, andR
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.
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.
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.
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.
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");
}
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.
A java.io.FilePermission
assigned to a bundle via a
condition must receive special treatment if the path argument for the
FilePermission
is a relative path name. A relative path name
is one that is not absolute. See the java.io.File.isAbsolute
method for more information on absolute path names.
When a bundle is assigned a FilePermission
for a
relative path name, the path name is taken to be relative to the bundle's
persistent storage area. This allows additional permissions, such as
execute
, to be assigned to files in the bundle's persistent
storage area. For example:
java.io.FilePermission "-" "execute"
can be used to allow a bundle to execute any file in the bundle's persistent storage area.
This only applies to FilePermission
objects assigned to
a bundle via a condition. Any FilePermission
object with a
relative path name must be ignored by the
AccessControlContext
object returned by the
getAccessControlContext
method.
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:
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.
This specification provides a number of standard conditions. These conditions are explained in the following sections.
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.
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.
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.
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.
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:
-
If the Permission Admin defines the permissions for bundle A (location is set), then these permissions override any Conditional Permission Admin information.
-
If the Conditional Permission Admin has a non-empty table, then this table is used to calculate the permissions for bundle A.
-
If the default permissions are set in Permission Admin, then these are given to bundle A.
-
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.
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.
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.
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
.
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.
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.
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.
The Conditional Permission Admin service should be part of the Framework and therefore has All Permission.
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)"
-
BundleLocationCondition
- Condition to test if the location of a bundle matches or does not match a pattern. -
BundleSignerCondition
- Condition to test if the signer of a bundle matches or does not match a pattern. -
Condition
- The interface implemented by a Condition. -
ConditionalPermissionAdmin
- Framework service to administer Conditional Permissions. -
ConditionalPermissionInfo
- A list of Permissions guarded by a list of conditions with an access decision. -
ConditionalPermissionUpdate
- Update the Conditional Permission Table. -
ConditionInfo
- Condition representation used by the Conditional Permission Admin service.
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
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.
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
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.
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
A Condition object that will always evaluate to false and that is never postponed.
A Condition object that will always evaluate to true and that is never postponed.
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.
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.
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.
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.
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
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.
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.
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.
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.
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
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
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
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.
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
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
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
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.
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
Returns the access decision for this Conditional Permission Info.
One of the following values:
1.1
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.
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
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.
Returns the Permission Infos for the Permissions in this Conditional Permission Info.
The Permission Infos for the Permissions in this Conditional Permission Info.
Returns the hash code value for this object.
A hash code value for this object.
1.1
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
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.
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.
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 aBundle
object and aConditionInfo
object as arguments. That method must return an object that implements theCondition
interface. -
Implement the
Condition
interface and define a public constructor that takes aBundle
object and aConditionInfo
object as arguments.
Immutable
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
.
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.
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.
Returns arguments of this ConditionInfo
.
The arguments of this ConditionInfo
. An empty array is
returned if the ConditionInfo
has no arguments.
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
.
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
.
Returns the hash code value for this object.
A hash code value for this object.