1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
/*******************************************************************************
* Copyright (c) 2008, 2010 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.
List<List<Decision[]>> depthCondSets = new ArrayList<List<Decision[]>>(2);
List<AccessControlContext> accs = new ArrayList<AccessControlContext>(2);
List<Class<?>> CondClassSet;
public int getDepth() {
return depthCondSets.size() - 1;
}
}
static class CheckPermissionAction implements PrivilegedAction<Object> {
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<CheckContext> localCheckContext = new ThreadLocal<CheckContext>();
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<Decision[]>(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 = 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<Class<? extends Condition>, Dictionary<Object, Object>>();
for (Decision[] domainDecisions : conditionSets) {
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, Map<Class<? extends Condition>, Dictionary<Object, Object>> conditionDictionaries, CheckContext cc) {
Condition[] postponed = decision.postponed;
for (int i = 0; i < postponed.length; i++) {
Dictionary<Object, Object> condContext = conditionDictionaries.get(postponed[i].getClass());
if (condContext == null) {
condContext = new Hashtable<Object, Object>();
conditionDictionaries.put(postponed[i].getClass(), condContext);
}
// prevent recursion into Condition
if (cc.CondClassSet == null)
cc.CondClassSet = new ArrayList<Class<?>>(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();
}
}
|