Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 914fa71a9b183f8c3b812fe323e0fac0c2bfe05a (plain) (blame)
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
193
194
195
/*******************************************************************************
 * 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.
 * 
 */
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;
		}

		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;
	}

	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<>();
			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<>();
				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();
	}
}

Back to the top