Purpose-built debugger for determining missing OSGi bundle security permissions.
The current implementation will put a breakpoint on line 472 of
java.security.AccessControlContext
(the throws clause), analyze and report possible solutions to the security failure.
It will do so for a single failure and exit unless the -continuous
option is used in which case it will let the VM continue as if no failures had occurred and report for all failures (unless they were already detected and reported).
It also provides an option where it will put a breakpoint on line 1096 of org.eclipse.osgi.internal.serviceregistry.ServiceRegistry
to detect all service permission checks done before the registry dispatches service events to other bundles.
This option is not on by default as it can slow down the system a bit.
To run the debugger tool, open the debugger project in IDE and run the org.codice.acdebuger.Main.main()
method. Options can be set in the corresponding IDE Run/Debug Configuration
.
The maven build can be used to produce an executable jar. The jar can
be run by doing: java -jar acdebugger-debugger-1.0-SNAPSHOT-jar-with-dependencies.jar [options]>
The following debugger options are available:
- -host
<hostname or IP>
- -port
<port number>
- -wait
[<timeout>]
- -continuous
- -admin
- -dump
- -service
- -grant
0002 - Check permission failure for platform-migratable-api: java.io.FilePermission "/projects/ddf-2.14.0-SNAPSHOT/security/configurations.policy", "read" {
Analyze the following 2 solutions and choose the best:
{
Add an AccessController.doPrivileged() block around:
platform-migration(org.codice.ddf.configuration.migration.ExportMigrationContextImpl:145)
}
{
Add the following permission to default.policy:
grant codeBase "file:/platform-migratable-api" {
permission java.io.FilePermission "/projects/ddf-2.14.0-SNAPSHOT/security/configurations.policy", "read";
}
}
}
The above example is reporting the second analysis solutions block with 2 potential solutions.
It lists on the first line the bundle currently responsible (platform-migratable-api
) for the failure and the corresponding permission(s).
You will also see a summary of all possible solutions to the problem.
These are sorted in order of priority but it is still up to the developer to assess which one is better suited in the current context.
As seen in the above example, the analysis gives priority to extending existing privileges to solve a security failure over introducing a permission to a new bundle.
Specifies the host or IP where the VM to attach to is located
Specifies the port number the VM is awaiting debuggers to connect to
Indicates to wait for a connection. The timeout is optional and indicates the maximum number of minutes to wait (defaults to 10 minutes).
Specifies to run in continuous mode where the debugger will tell the VM not to fail on any security failures detected and report on all failures found.
Indicates the tool is being run for an admin. In such cases, the analysis won't be as extensive since an administrator wouldn't be able to modify the code for example.
At the moment, it disables analyzing solutions that involve extending privileges in bundles using doPrivileged()
blocks.
In the above example, only the second solution would have been reported if this option had been provided. As such, this option should not be used by developers.
Additional information about detected security failures such as stack traces and bundle information will be dumped along with solutions.
Here is an example of a dump:
0136 - ACCESS CONTROL PERMISSION FAILURE
========================================
Permission:
java.io.FilePermission "/projects/ddf-2.14.0-SNAPSHOT/security/default.policy", "read"
Context:
bundle-0
org.apache.commons.io
--> *platform-migration
Stack:
at bundle-0(java.security.AccessControlContext:472)
at bundle-0(java.security.AccessController:884)
at bundle-0(java.lang.SecurityManager:549)
at bundle-0(java.lang.SecurityManager:888)
at bundle-0(java.io.File:844)
at org.apache.commons.io(org.apache.commons.io.filefilter.DirectoryFileFilter:71)
at org.apache.commons.io(org.apache.commons.io.filefilter.NotFileFilter:56)
at org.apache.commons.io(org.apache.commons.io.filefilter.AndFileFilter:128)
at org.apache.commons.io(org.apache.commons.io.filefilter.OrFileFilter:123)
at bundle-0(java.io.File:1291)
at org.apache.commons.io(org.apache.commons.io.FileUtils:473)
at org.apache.commons.io(org.apache.commons.io.FileUtils:524)
--> at *platform-migration(org.codice.ddf.configuration.migration.ExportMigrationContextImpl:145)
at *platform-migratable-api(org.codice.ddf.migration.ExportMigrationContext:169)
at *platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl:392)
at *platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl:362)
at *platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl$$Lambda$462.135345875.get()+8)
at *platform-migration(org.codice.ddf.configuration.migration.AccessUtils$1:56)
at bundle-0(java.security.AccessController.doPrivileged(java.security.PrivilegedExceptionAction)+-1)
at *platform-migration(org.codice.ddf.configuration.migration.AccessUtils:52)
----------------------------------------------------------
at *platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl:358)
at *platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl:208)
at *platform-migratable-api(org.codice.ddf.migration.ExportMigrationEntry:207)
at *security-migratable(org.codice.ddf.security.migratable.impl.SecurityMigratable:104)
at *platform-migration(org.codice.ddf.configuration.migration.ExportMigrationContextImpl:195)
at *platform-migration(org.codice.ddf.configuration.migration.ExportMigrationManagerImpl$$Lambda$444.842134888.apply(java.lang.Object)+4)
at bundle-0(java.util.stream.ReferencePipeline$3$1:193)
at bundle-0(java.util.Iterator:116)
at bundle-0(java.util.Spliterators$IteratorSpliterator:1801)
at bundle-0(java.util.stream.AbstractPipeline:481)
at bundle-0(java.util.stream.AbstractPipeline:471)
at bundle-0(java.util.stream.ReduceOps$ReduceOp:708)
at bundle-0(java.util.stream.AbstractPipeline:234)
at bundle-0(java.util.stream.ReferencePipeline:510)
at *platform-migration(org.codice.ddf.configuration.migration.ExportMigrationManagerImpl:166)
at *platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager:226)
at *platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager:276)
at *platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager:160)
at *platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager$$Lambda$408.1094074659.get()+12)
at *platform-migration(org.codice.ddf.configuration.migration.AccessUtils$1:56)
at bundle-0(java.security.AccessController.doPrivileged(java.security.PrivilegedExceptionAction)+-1)
at *platform-migration(org.codice.ddf.configuration.migration.AccessUtils:52)
at *platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager:160)
at *admin-core-migration-commands(org.codice.ddf.migration.commands.ExportCommand:47)
at *admin-core-migration-commands(org.codice.ddf.migration.commands.MigrationCommand$$Lambda$376.1915231195.call()+4)
at *org.apache.shiro.core(org.apache.shiro.subject.support.SubjectCallable:90)
at *org.apache.shiro.core(org.apache.shiro.subject.support.SubjectCallable:83)
at *org.apache.shiro.core(org.apache.shiro.subject.support.DelegatingSubject:383)
at *admin-core-migration-commands(org.codice.ddf.security.common.Security:182)
at *admin-core-migration-commands(org.codice.ddf.migration.commands.MigrationCommand:107)
at *org.apache.karaf.shell.core(org.apache.karaf.shell.impl.action.command.ActionCommand:84)
at *org.apache.karaf.shell.core(org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand:68)
at *org.apache.karaf.shell.core(org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand:86)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Closure:571)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Closure:497)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Closure:386)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Pipe:417)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Pipe:229)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Pipe:59)
at bundle-0(java.util.concurrent.FutureTask:266)
at bundle-0(java.util.concurrent.ThreadPoolExecutor:1142)
at bundle-0(java.util.concurrent.ThreadPoolExecutor$Worker:617)
at bundle-0(java.lang.Thread:748)
OPTION 1
--------
Permission:
java.io.FilePermission "/projects/ddf-2.14.0-SNAPSHOT/security/default.policy", "read"
Granting permission to bundles:
platform-migration
Extending privileges at:
platform-migration(org.codice.ddf.configuration.migration.ExportMigrationContextImpl:145)
Context:
bundle-0
org.apache.commons.io
platform-migration
Stack:
at bundle-0(java.security.AccessControlContext:472)
at bundle-0(java.security.AccessController:884)
at bundle-0(java.lang.SecurityManager:549)
at bundle-0(java.lang.SecurityManager:888)
at bundle-0(java.io.File:844)
at org.apache.commons.io(org.apache.commons.io.filefilter.DirectoryFileFilter:71)
at org.apache.commons.io(org.apache.commons.io.filefilter.NotFileFilter:56)
at org.apache.commons.io(org.apache.commons.io.filefilter.AndFileFilter:128)
at org.apache.commons.io(org.apache.commons.io.filefilter.OrFileFilter:123)
at bundle-0(java.io.File:1291)
at org.apache.commons.io(org.apache.commons.io.FileUtils:473)
at org.apache.commons.io(org.apache.commons.io.FileUtils:524)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationContextImpl:145)
at bundle-0(java.security.AccessController.doPrivileged(java.security.PrivilegedExceptionAction))
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationContextImpl:145)
----------------------------------------------------------
at *platform-migratable-api(org.codice.ddf.migration.ExportMigrationContext:169)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl:392)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl:362)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl$$Lambda$462.135345875.get()+8)
at platform-migration(org.codice.ddf.configuration.migration.AccessUtils$1:56)
at bundle-0(java.security.AccessController.doPrivileged(java.security.PrivilegedExceptionAction)+-1)
at platform-migration(org.codice.ddf.configuration.migration.AccessUtils:52)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl:358)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl:208)
at *platform-migratable-api(org.codice.ddf.migration.ExportMigrationEntry:207)
at *security-migratable(org.codice.ddf.security.migratable.impl.SecurityMigratable:104)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationContextImpl:195)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationManagerImpl$$Lambda$444.842134888.apply(java.lang.Object)+4)
at bundle-0(java.util.stream.ReferencePipeline$3$1:193)
at bundle-0(java.util.Iterator:116)
at bundle-0(java.util.Spliterators$IteratorSpliterator:1801)
at bundle-0(java.util.stream.AbstractPipeline:481)
at bundle-0(java.util.stream.AbstractPipeline:471)
at bundle-0(java.util.stream.ReduceOps$ReduceOp:708)
at bundle-0(java.util.stream.AbstractPipeline:234)
at bundle-0(java.util.stream.ReferencePipeline:510)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationManagerImpl:166)
at platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager:226)
at platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager:276)
at platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager:160)
at platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager$$Lambda$408.1094074659.get()+12)
at platform-migration(org.codice.ddf.configuration.migration.AccessUtils$1:56)
at bundle-0(java.security.AccessController.doPrivileged(java.security.PrivilegedExceptionAction)+-1)
at platform-migration(org.codice.ddf.configuration.migration.AccessUtils:52)
at platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager:160)
at *admin-core-migration-commands(org.codice.ddf.migration.commands.ExportCommand:47)
at *admin-core-migration-commands(org.codice.ddf.migration.commands.MigrationCommand$$Lambda$376.1915231195.call()+4)
at *org.apache.shiro.core(org.apache.shiro.subject.support.SubjectCallable:90)
at *org.apache.shiro.core(org.apache.shiro.subject.support.SubjectCallable:83)
at *org.apache.shiro.core(org.apache.shiro.subject.support.DelegatingSubject:383)
at *admin-core-migration-commands(org.codice.ddf.security.common.Security:182)
at *admin-core-migration-commands(org.codice.ddf.migration.commands.MigrationCommand:107)
at *org.apache.karaf.shell.core(org.apache.karaf.shell.impl.action.command.ActionCommand:84)
at *org.apache.karaf.shell.core(org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand:68)
at *org.apache.karaf.shell.core(org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand:86)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Closure:571)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Closure:497)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Closure:386)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Pipe:417)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Pipe:229)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Pipe:59)
at bundle-0(java.util.concurrent.FutureTask:266)
at bundle-0(java.util.concurrent.ThreadPoolExecutor:1142)
at bundle-0(java.util.concurrent.ThreadPoolExecutor$Worker:617)
at bundle-0(java.lang.Thread:748)
OPTION 2
--------
Permission:
java.io.FilePermission "/projects/ddf-2.14.0-SNAPSHOT/security/default.policy", "read"
Granting permission to bundles:
platform-migration
platform-migratable-api
Context:
bundle-0
org.apache.commons.io
platform-migration
platform-migratable-api
Stack:
at bundle-0(java.security.AccessControlContext:472)
at bundle-0(java.security.AccessController:884)
at bundle-0(java.lang.SecurityManager:549)
at bundle-0(java.lang.SecurityManager:888)
at bundle-0(java.io.File:844)
at org.apache.commons.io(org.apache.commons.io.filefilter.DirectoryFileFilter:71)
at org.apache.commons.io(org.apache.commons.io.filefilter.NotFileFilter:56)
at org.apache.commons.io(org.apache.commons.io.filefilter.AndFileFilter:128)
at org.apache.commons.io(org.apache.commons.io.filefilter.OrFileFilter:123)
at bundle-0(java.io.File:1291)
at org.apache.commons.io(org.apache.commons.io.FileUtils:473)
at org.apache.commons.io(org.apache.commons.io.FileUtils:524)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationContextImpl:145)
at platform-migratable-api(org.codice.ddf.migration.ExportMigrationContext:169)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl:392)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl:362)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl$$Lambda$462.135345875.get()+8)
at platform-migration(org.codice.ddf.configuration.migration.AccessUtils$1:56)
at bundle-0(java.security.AccessController.doPrivileged(java.security.PrivilegedExceptionAction)+-1)
at platform-migration(org.codice.ddf.configuration.migration.AccessUtils:52)
----------------------------------------------------------
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl:358)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationEntryImpl:208)
at platform-migratable-api(org.codice.ddf.migration.ExportMigrationEntry:207)
at *security-migratable(org.codice.ddf.security.migratable.impl.SecurityMigratable:104)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationContextImpl:195)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationManagerImpl$$Lambda$444.842134888.apply(java.lang.Object)+4)
at bundle-0(java.util.stream.ReferencePipeline$3$1:193)
at bundle-0(java.util.Iterator:116)
at bundle-0(java.util.Spliterators$IteratorSpliterator:1801)
at bundle-0(java.util.stream.AbstractPipeline:481)
at bundle-0(java.util.stream.AbstractPipeline:471)
at bundle-0(java.util.stream.ReduceOps$ReduceOp:708)
at bundle-0(java.util.stream.AbstractPipeline:234)
at bundle-0(java.util.stream.ReferencePipeline:510)
at platform-migration(org.codice.ddf.configuration.migration.ExportMigrationManagerImpl:166)
at platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager:226)
at platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager:276)
at platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager:160)
at platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager$$Lambda$408.1094074659.get()+12)
at platform-migration(org.codice.ddf.configuration.migration.AccessUtils$1:56)
at bundle-0(java.security.AccessController.doPrivileged(java.security.PrivilegedExceptionAction)+-1)
at platform-migration(org.codice.ddf.configuration.migration.AccessUtils:52)
at platform-migration(org.codice.ddf.configuration.migration.ConfigurationMigrationManager:160)
at *admin-core-migration-commands(org.codice.ddf.migration.commands.ExportCommand:47)
at *admin-core-migration-commands(org.codice.ddf.migration.commands.MigrationCommand$$Lambda$376.1915231195.call()+4)
at *org.apache.shiro.core(org.apache.shiro.subject.support.SubjectCallable:90)
at *org.apache.shiro.core(org.apache.shiro.subject.support.SubjectCallable:83)
at *org.apache.shiro.core(org.apache.shiro.subject.support.DelegatingSubject:383)
at *admin-core-migration-commands(org.codice.ddf.security.common.Security:182)
at *admin-core-migration-commands(org.codice.ddf.migration.commands.MigrationCommand:107)
at *org.apache.karaf.shell.core(org.apache.karaf.shell.impl.action.command.ActionCommand:84)
at *org.apache.karaf.shell.core(org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand:68)
at *org.apache.karaf.shell.core(org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand:86)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Closure:571)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Closure:497)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Closure:386)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Pipe:417)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Pipe:229)
at *org.apache.karaf.shell.core(org.apache.felix.gogo.runtime.Pipe:59)
at bundle-0(java.util.concurrent.FutureTask:266)
at bundle-0(java.util.concurrent.ThreadPoolExecutor:1142)
at bundle-0(java.util.concurrent.ThreadPoolExecutor$Worker:617)
at bundle-0(java.lang.Thread:748)
SOLUTIONS
---------
{
Add the following permission to the appropriate policy file:
grant codeBase "file:/platform-migration" {
permission java.io.FilePermission "/projects/ddf-2.14.0-SNAPSHOT/security/default.policy", "read";
}
and add an AccessController.doPrivileged() block around:
platform-migration(org.codice.ddf.configuration.migration.ExportMigrationContextImpl:145)
}
{
Add the following permission to the appropriate policy file:
grant codeBase "file:/platform-migratable-api/platform-migration" {
permission java.io.FilePermission "/projects/ddf-2.14.0-SNAPSHOT/security/default.policy", "read";
}
}
The first line 0136 - ACCESS CONTROL PERMISSION FAILURE
indicates this was the 136th failure detected and corresponds to an access control permission check.
Following that, we can see the permission or permissions involved in the failure and the state of the security context.
The security context is a stack of domains which in OSGi corresponds to bundles.
The *
is prefixed in front of any domain that is not currently granted the permission(s).
The -->
is used to identify the domain in the context that is about to generate the failure.
The next section provides information about the current stack. it will show the location in the code in between parenthesis and the name of the bundle the code is located in which will also be prefixed with a *
if it is detected that the bundle is not granted the permission(s).
As in the context above, a line will show -->
next to culprit which is responsible for the imminent failure.
The stack will also show a break line ( ----------------------------------------------------------
) whenever a doPrivileged()
block is detected.
This means that everything after the block is ignored by the security manager.
After having provided information about the current failure, possible options or solutions will be dumped in a similar fashion.
Additional information will be provided to indicate what needs to be done.
For example the Granting permissions to bundles:
section will list a set of bundles that are given the missing permission(s).
The Extending privileges at:
section is used to indicate lines in the code where one can introduce a doPrivileged()
block to solve the issue at hand.
The stack that will follow will show how it would appear if the option were implemented. There should no longer be any failure; thus, no -->
will be seen.
The final section will list a summary of all possible solutions. Usually, these are sorted in order of priority but it is still up to the developer to assess which one is better suited in the current context. In the example above, there were 2 solutions.
This section is the only one that will be printed out on the console when the -dump
option is not specified.
Specifies that a breakpoint should be added in Eclipse's Service Registry to detect internal security checks done for given bundles before dispatching service events. These failures are analyzed and reported as normal security check failures. This option tends to slow down the system a bit as the debugger is invoked for all checks and not just when a failure is about to be reported.
When specified, the debugger will use the backdoor and a registered ServicePermission service to temporarily grant permissions for detected security failures which after analysis yields a single solution. This is only temporary and will not survive a restart of the VM but will prevent any further failures that would otherwise not be if the permission(s) were defined. It also tends to slow down the system since the OSGi permission cache ends up being cleared each time.
The following modules are defined:
- acdebugger-api
- acdebugger-backdoor
- acdebugger-debugger
- acdebugger-common
Defines a bundle that provides an interface for a permission service which is used by the backdoor bundle to temporarily grant missing permissions. This service should be registered by the VM.
Defines a bundle that provides backdoor support to the debugger. It should be installed in the VM in order for the debugger to be more optimal.
Creates an executable jar with the debugger tool.
Defines a library of common classes used by both the backdoor and the debugger. This artifact is embeded inside the backdoor bundle.
Future implementations will:
- Provide a user interface
- Provide a Kotlin implementation
- Enhance the backdoor bundle to actually intercept calls inside the VM rendering the debugging experience much more stable