Daily bump.
[gcc.git] / libjava / java / security / VMAccessController.java
1 /* VMAccessController.java -- VM-specific access controller methods.
2 Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; see the file COPYING. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301 USA.
18
19 Linking this library statically or dynamically with other modules is
20 making a combined work based on this library. Thus, the terms and
21 conditions of the GNU General Public License cover the whole
22 combination.
23
24 As a special exception, the copyright holders of this library give you
25 permission to link this library with independent modules to produce an
26 executable, regardless of the license terms of these independent
27 modules, and to copy and distribute the resulting executable under
28 terms of your choice, provided that you also meet, for each linked
29 independent module, the terms and conditions of the license of that
30 module. An independent module is a module which is not derived from
31 or based on this library. If you modify this library, you may extend
32 this exception to your version of the library, but you are not
33 obligated to do so. If you do not wish to do so, delete this
34 exception statement from your version. */
35
36
37 package java.security;
38
39 import java.util.HashSet;
40 import java.util.LinkedList;
41
42 final class VMAccessController
43 {
44
45 // Fields.
46 // -------------------------------------------------------------------------
47
48 /**
49 * This is a per-thread stack of AccessControlContext objects (which can
50 * be null) for each call to AccessController.doPrivileged in each thread's
51 * call stack. We use this to remember which context object corresponds to
52 * which call.
53 */
54 private static final ThreadLocal contexts = new ThreadLocal();
55
56 /**
57 * This is a Boolean that, if set, tells getContext that it has already
58 * been called once, allowing us to handle recursive permission checks
59 * caused by methods getContext calls.
60 */
61 private static final ThreadLocal inGetContext = new ThreadLocal();
62
63 /**
64 * And we return this all-permissive context to ensure that privileged
65 * methods called from getContext succeed.
66 */
67 private static final AccessControlContext DEFAULT_CONTEXT;
68 static
69 {
70 CodeSource source = new CodeSource(null, null);
71 Permissions permissions = new Permissions();
72 permissions.add(new AllPermission());
73 ProtectionDomain[] domain = new ProtectionDomain[] {
74 new ProtectionDomain(source, permissions)
75 };
76 DEFAULT_CONTEXT = new AccessControlContext(domain);
77 }
78
79 private static final boolean DEBUG = gnu.classpath.Configuration.DEBUG;
80 private static void debug(String msg)
81 {
82 System.err.print(">>> VMAccessController: ");
83 System.err.println(msg);
84 }
85
86 // Constructors.
87 // -------------------------------------------------------------------------
88
89 private VMAccessController() { }
90
91 // Class methods.
92 // -------------------------------------------------------------------------
93
94 /**
95 * Relate a class (which should be an instance of {@link PrivilegedAction}
96 * with an access control context. This method is used by {@link
97 * AccessController#doPrivileged(java.security.PrivilegedAction,java.security.AccessControlContext)}
98 * to set up the context that will be returned by {@link #getContext()}.
99 * This method relates the class to the current thread, so contexts
100 * pushed from one thread will not be available to another.
101 *
102 * @param acc The access control context.
103 */
104 static void pushContext (AccessControlContext acc)
105 {
106 if (Thread.currentThread() == null)
107 return;
108
109 if (DEBUG)
110 debug("pushing " + acc);
111 LinkedList stack = (LinkedList) contexts.get();
112 if (stack == null)
113 {
114 if (DEBUG)
115 debug("no stack... creating ");
116 stack = new LinkedList();
117 contexts.set(stack);
118 }
119 stack.addFirst(acc);
120 }
121
122 /**
123 * Removes the relation of a class to an {@link AccessControlContext}.
124 * This method is used by {@link AccessController} when exiting from a
125 * call to {@link
126 * AccessController#doPrivileged(java.security.PrivilegedAction,java.security.AccessControlContext)}.
127 */
128 static void popContext()
129 {
130 if (Thread.currentThread() == null)
131 return;
132
133 if (DEBUG)
134 debug("popping context");
135
136 // Stack should never be null, nor should it be empty, if this method
137 // and its counterpart has been called properly.
138 LinkedList stack = (LinkedList) contexts.get();
139 if (stack != null)
140 {
141 stack.removeFirst();
142 if (stack.isEmpty())
143 contexts.set(null);
144 }
145 else if (DEBUG)
146 {
147 debug("no stack during pop?????");
148 }
149 }
150
151 /**
152 * Examine the method stack of the currently running thread, and create
153 * an {@link AccessControlContext} filled in with the appropriate {@link
154 * ProtectionDomain} objects given this stack.
155 *
156 * @return The context.
157 */
158 static AccessControlContext getContext()
159 {
160 // If the VM is initializing return the all-permissive context
161 // so that any security checks succeed.
162 if (Thread.currentThread() == null)
163 return DEFAULT_CONTEXT;
164
165 // If we are already in getContext, but called a method that needs
166 // a permission check, return the all-permissive context so methods
167 // called from here succeed.
168 //
169 // XXX is this necessary? We should verify if there are any calls in
170 // the stack below this method that require permission checks.
171 Boolean inCall = (Boolean) inGetContext.get();
172 if (inCall != null && inCall.booleanValue())
173 {
174 if (DEBUG)
175 debug("already in getContext");
176 return DEFAULT_CONTEXT;
177 }
178
179 inGetContext.set(Boolean.TRUE);
180
181 Object[][] stack = getStack();
182 Class[] classes = (Class[]) stack[0];
183 String[] methods = (String[]) stack[1];
184
185 if (DEBUG)
186 debug("got trace of length " + classes.length);
187
188 HashSet domains = new HashSet();
189 HashSet seenDomains = new HashSet();
190 AccessControlContext context = null;
191 int privileged = 0;
192
193 // We walk down the stack, adding each ProtectionDomain for each
194 // class in the call stack. If we reach a call to doPrivileged,
195 // we don't add any more stack frames. We skip the first three stack
196 // frames, since they comprise the calls to getStack, getContext,
197 // and AccessController.getContext.
198 for (int i = 3; i < classes.length && privileged < 2; i++)
199 {
200 Class clazz = classes[i];
201 String method = methods[i];
202
203 if (DEBUG)
204 {
205 debug("checking " + clazz + "." + method);
206 // subject to getClassLoader RuntimePermission
207 debug("loader = " + clazz.getClassLoader());
208 }
209
210 // If the previous frame was a call to doPrivileged, then this is
211 // the last frame we look at.
212 if (privileged == 1)
213 privileged = 2;
214
215 if (clazz.equals (AccessController.class)
216 && method.equals ("doPrivileged"))
217 {
218 // If there was a call to doPrivileged with a supplied context,
219 // return that context. If using JAAS doAs*, it should be
220 // a context with a SubjectDomainCombiner
221 LinkedList l = (LinkedList) contexts.get();
222 if (l != null)
223 context = (AccessControlContext) l.getFirst();
224 privileged = 1;
225 }
226
227 // subject to getProtectionDomain RuntimePermission
228 ProtectionDomain domain = clazz.getProtectionDomain();
229
230 if (domain == null)
231 continue;
232 if (seenDomains.contains(domain))
233 continue;
234 seenDomains.add(domain);
235
236 // Create a static snapshot of this domain, which may change over time
237 // if the current policy changes.
238 domains.add(new ProtectionDomain(domain.getCodeSource(),
239 domain.getPermissions()));
240 }
241
242 if (DEBUG)
243 debug("created domains: " + domains);
244
245 ProtectionDomain[] result = (ProtectionDomain[])
246 domains.toArray(new ProtectionDomain[domains.size()]);
247
248 if (context != null)
249 {
250 DomainCombiner dc = context.getDomainCombiner ();
251 // If the supplied context had no explicit DomainCombiner, use
252 // our private version, which computes the intersection of the
253 // context's domains with the derived set.
254 if (dc == null)
255 context = new AccessControlContext
256 (IntersectingDomainCombiner.SINGLETON.combine
257 (result, context.getProtectionDomains ()));
258 // Use the supplied DomainCombiner. This should be secure,
259 // because only trusted code may create an
260 // AccessControlContext with a custom DomainCombiner.
261 else
262 context = new AccessControlContext (result, context, dc);
263 }
264 // No context was supplied. Return the derived one.
265 else
266 context = new AccessControlContext (result);
267
268 inGetContext.set(Boolean.FALSE);
269 return context;
270 }
271
272 /**
273 * Returns a snapshot of the current call stack as a pair of arrays:
274 * the first an array of classes in the call stack, the second an array
275 * of strings containing the method names in the call stack. The two
276 * arrays match up, meaning that method <i>i</i> is declared in class
277 * <i>i</i>. The arrays are clean; it will only contain Java methods,
278 * and no element of the list should be null.
279 *
280 * @return A pair of arrays describing the current call stack. The first
281 * element is an array of Class objects, and the second is an array
282 * of Strings comprising the method names.
283 */
284 private static native Object[][] getStack();
285 }