blob: 707a0284f853321dfd61091d579814cc189eb002 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.framework.internal.core;
import java.security.*;
import java.util.*;
import org.osgi.service.condpermadmin.Condition;
/**
*
* This security manager implements the ConditionalPermission processing for
* OSGi. It is to be used with ConditionalPermissionAdmin.
*
*/
public class FrameworkSecurityManager extends SecurityManager {
/*
* This is super goofy, but we need to make sure that the CheckContext and
* CheckPermissionAction classes load early. Otherwise, we run into problems later.
*/
static {
Class c;
c = CheckPermissionAction.class;
c = CheckContext.class;
c.getName(); // to prevent compiler warnings
}
static class CheckContext {
// A non zero depth indicates that we are doing a recursive permission check.
ArrayList depthCondSets = new ArrayList(2);
ArrayList accs = new ArrayList(2);
ArrayList CondClassSet;
public int getDepth() {
return depthCondSets.size() - 1;
}
}
/**
* Adds an array of Condition[] for a ProtectionDomain.
*
* @param condSet the array of Condition[] for the ProtectionDomain.
* @return true if the condSet was added. false if this is a recursive check
* and ConditionalPermissions cannot be used.
*/
boolean addConditionsForDomain(Condition condSet[][]) {
CheckContext cc = (CheckContext) localCheckContext.get();
if (cc == null) {
// We are being invoked in a weird way. Perhaps the ProtectionDomain is
// getting invoked directly.
return false;
}
Vector condSets = (Vector) cc.depthCondSets.get(cc.getDepth());
if (condSets == null) {
condSets = new Vector(2);
cc.depthCondSets.set(cc.getDepth(), condSets);
}
condSets.add(condSet);
return true;
}
ThreadLocal localCheckContext = new ThreadLocal();
static class CheckPermissionAction implements PrivilegedAction {
Permission perm;
Object context;
FrameworkSecurityManager fsm;
CheckPermissionAction(FrameworkSecurityManager fsm, Permission perm, Object context) {
this.fsm = fsm;
this.perm = perm;
this.context = context;
}
public Object run() {
fsm.internalCheckPermission(perm, context);
return null;
}
}
public void checkPermission(Permission perm, Object context) {
AccessController.doPrivileged(new CheckPermissionAction(this, perm, context));
}
/**
* Gets the AccessControlContext currently being evaluated by
* the SecurityManager.
*
* @return the AccessControlContext currently being evaluated by the SecurityManager, or
* null if no AccessControlContext is being evaluted. Note: this method will
* return null if the permission check is being done directly on the AccessControlContext
* rather than the SecurityManager.
*/
public AccessControlContext getContextToBeChecked() {
CheckContext cc = (CheckContext) localCheckContext.get();
if (cc != null && cc.accs != null && !cc.accs.isEmpty())
return (AccessControlContext) cc.accs.get(cc.accs.size()-1);
return null;
}
public void internalCheckPermission(Permission perm, Object context) {
AccessControlContext acc = (AccessControlContext) context;
CheckContext cc = (CheckContext) localCheckContext.get();
if (cc == null) {
cc = new CheckContext();
localCheckContext.set(cc);
}
cc.depthCondSets.add(null); // initialize postponed condition set to null
cc.accs.add(acc);
try {
acc.checkPermission(perm);
// We want to pop the first set of postponed conditions and process them
Vector remainingSets = (Vector) cc.depthCondSets.get(cc.getDepth());
if (remainingSets != null) {
/*
* In this bit of code we have to try every possible combination
* of conditional permissions that still need to be evaluated. We
* do this using recursion to keep track of the different
* combinations we have tried. The top call will setup the different
* combinations for the first protection domain with unevaluated
* conditions. The final call will actually evaluate the condition.
* If a good combination is found, it will immediately bubble
* up.
*/
Hashtable condContextDict = new Hashtable(2);
// The remainder we will process recursively.
Condition conds[][] = (Condition[][]) remainingSets.remove(0);
for (int i = 0; i < conds.length; i++)
if (recursiveCheck(remainingSets, conds[i], null, condContextDict, cc))
return; // found a pass return without SecurityException
throw new SecurityException("Conditions not satisfied"); //$NON-NLS-1$
}
} finally {
cc.depthCondSets.remove(cc.getDepth());
cc.accs.remove(cc.accs.size()-1);
}
}
/**
* Checks that at least one combination of the Condition[] in the Vector can
* be satisfied along with the passed array of Conditions.
*
* @param remainingSets the Vector of Condition[][] to check.
* @param conditions the conditions that must be satisfied.
* @param condDict a dictionary that will be filled with the Conditions to
* check.
* @param condContextDict a Dictionary of Dictionaries that will be passed
* to Condition.isSatisfied when performing repeated check.
* @param cc the CheckContext
* @return true if a successful combination was found.
*/
private boolean recursiveCheck(Vector remainingSets, Condition[] conditions, Hashtable condDict, Hashtable condContextDict, CheckContext cc) {
// clone condDict and clone each Vector in the condDict
if (condDict == null) {
condDict = new Hashtable(2);
} else {
Hashtable copyCondDict = new Hashtable(2);
for (Enumeration keys = condDict.keys(); keys.hasMoreElements();) {
Object key = keys.nextElement();
copyCondDict.put(key, ((Vector) condDict.get(key)).clone());
}
condDict = copyCondDict;
}
for (int i = 0; i < conditions.length; i++) {
if (conditions[i] == null)
continue;
Vector condList = (Vector) condDict.get(conditions[i].getClass());
if (condList == null) {
condList = new Vector();
condDict.put(conditions[i].getClass(), condList);
}
condList.add(conditions[i]);
}
if (remainingSets.size() > 0) {
Condition conds[][] = (Condition[][]) remainingSets.get(0);
Vector newSets = (Vector) remainingSets.clone();
newSets.remove(0);
for (int i = 0; i < conds.length; i++)
if (recursiveCheck(newSets, conds[i], condDict, condContextDict, cc))
return true;
return false;
}
Enumeration keys = condDict.keys();
while (keys.hasMoreElements()) {
Class key = (Class) keys.nextElement();
Vector conds = (Vector) condDict.get(key);
if (conds.size() == 0)
continue; // This should never happen since we only add to the condDict if there is a condition
Condition condArray[] = (Condition[]) conds.toArray(new Condition[conds.size()]);
Dictionary context = (Dictionary) condContextDict.get(key);
if (context == null) {
context = new Hashtable(2);
condContextDict.put(key, context);
}
if (cc.CondClassSet == null)
cc.CondClassSet = new ArrayList(2);
if (cc.CondClassSet.contains(condArray[0].getClass()))
return false; // doing recursion into same condition class
cc.CondClassSet.add(condArray[0].getClass());
try {
if (!condArray[0].isSatisfied(condArray, context))
return false;
} finally {
cc.CondClassSet.remove(condArray[0].getClass());
}
}
return true;
}
public void checkPermission(Permission perm) {
checkPermission(perm, getSecurityContext());
}
public Object getSecurityContext() {
return AccessController.getContext();
}
}