Copyright (c) 2008, 2016 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2008, 2016 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * 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.
/** * * 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. List<List<Decision[]>> depthCondSets = new ArrayList<>(2); List<AccessControlContext> accs = new ArrayList<>(2); List<Class<?>> CondClassSet; public int getDepth() { return depthCondSets.size() - 1; } } static class CheckPermissionAction implements PrivilegedAction<Void> { Permission perm; Object context; EquinoxSecurityManager fsm; CheckPermissionAction(EquinoxSecurityManager fsm, Permission perm, Object context) { this.fsm = fsm; this.perm = perm; this.context = context; } @Override public Void run() { fsm.internalCheckPermission(perm, context); return null; } } private final ThreadLocal<CheckContext> localCheckContext = new ThreadLocal<>(); boolean addConditionsForDomain(Decision[] results) { CheckContext cc = localCheckContext.get(); if (cc == null) { // We are being invoked in a weird way. Perhaps the ProtectionDomain is // getting invoked directly. return false; } List<Decision[]> condSets = 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; } @Override public void checkPermission(Permission perm, Object context) { AccessController.doPrivileged(new CheckPermissionAction(this, perm, context)); }
Gets the AccessControlContext currently being evaluated by the SecurityManager.
Returns: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.
/** * 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 = localCheckContext.get(); if (cc != null && cc.accs != null && !cc.accs.isEmpty()) return cc.accs.get(cc.accs.size() - 1); return null; } void internalCheckPermission(Permission perm, Object context) { AccessControlContext acc = (AccessControlContext) context; CheckContext cc = 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 List<Decision[]> conditionSets = cc.depthCondSets.get(cc.getDepth()); if (conditionSets == null) return; // TODO the spec seems impossible to implement just doing the simple thing for now Map<Class<? extends Condition>, Dictionary<Object, Object>> conditionDictionaries = new HashMap<>(); for (Decision[] domainDecisions : conditionSets) { boolean grant = false; for (Decision domainDecision : domainDecisions) { if (domainDecision == null) { break; } if ((domainDecision.decision & SecurityTable.ABSTAIN) != 0) { continue; } if ((domainDecision.decision & SecurityTable.POSTPONED) == 0) { // hit an immediate decision; use it if ((domainDecision.decision & SecurityTable.GRANTED) != 0) { grant = true; } break; } int decision = getPostponedDecision(domainDecision, 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, Map<Class<? extends Condition>, Dictionary<Object, Object>> conditionDictionaries, CheckContext cc) { Condition[] postponed = decision.postponed; for (Condition postponedCond : postponed) { Dictionary<Object, Object> condContext = conditionDictionaries.get(postponedCond.getClass()); if (condContext == null) { condContext = new Hashtable<>(); conditionDictionaries.put(postponedCond.getClass(), condContext); } // prevent recursion into Condition if (cc.CondClassSet == null) cc.CondClassSet = new ArrayList<>(2); if (cc.CondClassSet.contains(postponedCond.getClass())) { return SecurityTable.ABSTAIN; } cc.CondClassSet.add(postponedCond.getClass()); try { // must call isMutable before calling isSatisfied according to the specification boolean mutable = postponedCond.isMutable(); boolean isSatisfied = postponedCond.isSatisfied(new Condition[]{postponedCond}, condContext); decision.handleImmutable(postponedCond, isSatisfied, mutable); if (!isSatisfied) return SecurityTable.ABSTAIN; } finally { cc.CondClassSet.remove(postponedCond.getClass()); } } // call postponed conditions are satisfied return the decision return decision.decision; } @Override public void checkPermission(Permission perm) { checkPermission(perm, getSecurityContext()); } @Override public Object getSecurityContext() { return AccessController.getContext(); } }