blob: dd94b59cd32c04855be459c05615c8983e34f1e8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2009 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.internal.permadmin;
import java.security.*;
import java.util.*;
import org.eclipse.osgi.internal.permadmin.SecurityRow.Decision;
import org.osgi.service.condpermadmin.Condition;
/**
*
* This security manager implements the ConditionalPermission processing for
* OSGi. It is to be used with ConditionalPermissionAdmin.
*
*/
public class EquinoxSecurityManager 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;
}
}
static class CheckPermissionAction implements PrivilegedAction {
Permission perm;
Object context;
EquinoxSecurityManager fsm;
CheckPermissionAction(EquinoxSecurityManager fsm, Permission perm, Object context) {
this.fsm = fsm;
this.perm = perm;
this.context = context;
}
public Object run() {
fsm.internalCheckPermission(perm, context);
return null;
}
}
private final ThreadLocal localCheckContext = new ThreadLocal();
boolean addConditionsForDomain(Decision results[]) {
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;
}
ArrayList condSets = (ArrayList) cc.depthCondSets.get(cc.getDepth());
if (condSets == null) {
condSets = new ArrayList(1);
cc.depthCondSets.set(cc.getDepth(), condSets);
}
condSets.add(results);
return true;
}
boolean inCheckPermission() {
return localCheckContext.get() != 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 evaluated. 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;
}
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
ArrayList conditionSets = (ArrayList) cc.depthCondSets.get(cc.getDepth());
if (conditionSets == null)
return;
// TODO the spec seems impossible to implement just doing the simple thing for now
HashMap conditionDictionaries = new HashMap();
for (Iterator iConditionSets = conditionSets.iterator(); iConditionSets.hasNext();) {
Decision[] domainDecisions = (Decision[]) iConditionSets.next();
boolean grant = false;
for (int i = 0; i < domainDecisions.length; i++) {
if (domainDecisions[i] == null)
break;
if ((domainDecisions[i].decision & SecurityTable.ABSTAIN) != 0)
continue;
if ((domainDecisions[i].decision & SecurityTable.POSTPONED) == 0) {
// hit an immediate decision; use it
if ((domainDecisions[i].decision & SecurityTable.GRANTED) != 0)
grant = true;
break;
}
int decision = getPostponedDecision(domainDecisions[i], conditionDictionaries, cc);
if ((decision & SecurityTable.ABSTAIN) != 0)
continue;
if ((decision & SecurityTable.GRANTED) != 0)
grant = true;
break;
}
if (!grant)
// did not find a condition to grant the permission for this domain
throw new SecurityException("Conditions not satisfied"); //$NON-NLS-1$
// continue to next domain
}
} finally {
cc.depthCondSets.remove(cc.getDepth());
cc.accs.remove(cc.accs.size() - 1);
}
}
private int getPostponedDecision(Decision decision, HashMap conditionDictionaries, CheckContext cc) {
Condition[] postponed = decision.postponed;
for (int i = 0; i < postponed.length; i++) {
Dictionary condContext = (Dictionary) conditionDictionaries.get(postponed[i].getClass());
if (condContext == null) {
condContext = new Hashtable();
conditionDictionaries.put(postponed[i].getClass(), condContext);
}
// prevent recursion into Condition
if (cc.CondClassSet == null)
cc.CondClassSet = new ArrayList(2);
if (cc.CondClassSet.contains(postponed[i].getClass()))
return SecurityTable.ABSTAIN;
cc.CondClassSet.add(postponed[i].getClass());
try {
// must call isMutable before calling isSatisfied according to the specification
boolean mutable = postponed[i].isMutable();
boolean isSatisfied = postponed[i].isSatisfied(new Condition[] {postponed[i]}, condContext);
decision.handleImmutable(postponed[i], isSatisfied, mutable);
if (!isSatisfied)
return SecurityTable.ABSTAIN;
} finally {
cc.CondClassSet.remove(postponed[i].getClass());
}
}
// call postponed conditions are satisfied return the decision
return decision.decision;
}
public void checkPermission(Permission perm) {
checkPermission(perm, getSecurityContext());
}
public Object getSecurityContext() {
return AccessController.getContext();
}
}