PR152
[cacao.git] / src / classes / gnuclasspath / java / security / VMAccessController.java
1 /* VMAccessController.java -- VM-specific access controller methods.
2    Copyright (C) 2004, 2005  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, null, null)
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 (DEBUG)
107       debug("pushing " + acc);
108     LinkedList stack = (LinkedList) contexts.get();
109     if (stack == null)
110       {
111          if (DEBUG)
112            debug("no stack... creating ");
113         stack = new LinkedList();
114         contexts.set(stack);
115       }
116     stack.addFirst(acc);
117   }
118
119   /**
120    * Removes the relation of a class to an {@link AccessControlContext}.
121    * This method is used by {@link AccessController} when exiting from a
122    * call to {@link
123    * AccessController#doPrivileged(java.security.PrivilegedAction,java.security.AccessControlContext)}.
124    */
125   static void popContext()
126   {
127     if (DEBUG)
128       debug("popping context");
129
130     // Stack should never be null, nor should it be empty, if this method
131     // and its counterpart has been called properly.
132     LinkedList stack = (LinkedList) contexts.get();
133     if (stack != null)
134       {
135         stack.removeFirst();
136         if (stack.isEmpty())
137           contexts.set(null);
138       }
139     else if (DEBUG)
140       {
141         debug("no stack during pop?????");
142       }
143   }
144
145   /**
146    * Examine the method stack of the currently running thread, and create
147    * an {@link AccessControlContext} filled in with the appropriate {@link
148    * ProtectionDomain} objects given this stack.
149    *
150    * @return The context.
151    */
152   static AccessControlContext getContext()
153   {
154     // If we are already in getContext, but called a method that needs
155     // a permission check, return the all-permissive context so methods
156     // called from here succeed.
157     //
158     // XXX is this necessary? We should verify if there are any calls in
159     // the stack below this method that require permission checks.
160     Boolean inCall = (Boolean) inGetContext.get();
161     if (inCall != null && inCall.booleanValue())
162       {
163         if (DEBUG)
164           debug("already in getContext");
165         return DEFAULT_CONTEXT;
166       }
167
168     inGetContext.set(Boolean.TRUE);
169
170     Object[][] stack = getStack();
171     Class[] classes = (Class[]) stack[0];
172     String[] methods = (String[]) stack[1];
173
174     if (DEBUG)
175       debug("got trace of length " + classes.length);
176
177     HashSet domains = new HashSet();
178     HashSet seenDomains = new HashSet();
179     AccessControlContext context = null;
180     int privileged = 0;
181
182     // We walk down the stack, adding each ProtectionDomain for each
183     // class in the call stack. If we reach a call to doPrivileged,
184     // we don't add any more stack frames. We skip the first three stack
185     // frames, since they comprise the calls to getStack, getContext,
186     // and AccessController.getContext.
187     for (int i = 3; i < classes.length && privileged < 2; i++)
188       {
189         Class clazz = classes[i];
190         String method = methods[i];
191         ClassLoader loader = clazz.getClassLoader();
192
193         if (DEBUG)
194           {
195             debug("checking " + clazz + "." + method);
196             // subject to getClassLoader RuntimePermission
197             debug("loader = " + loader);
198           }
199
200         // If the previous frame was a call to doPrivileged, then this is
201         // the last frame we look at.
202         if (privileged == 1)
203           privileged = 2;
204
205         if (clazz.equals (AccessController.class)
206             && method.equals ("doPrivileged"))
207           {
208             // If there was a call to doPrivileged with a supplied context,
209             // return that context. If using JAAS doAs*, it should be 
210             // a context with a SubjectDomainCombiner
211             LinkedList l = (LinkedList) contexts.get();
212             if (l != null)
213               context = (AccessControlContext) l.getFirst();
214             privileged = 1;
215           }
216
217         // subject to getProtectionDomain RuntimePermission
218         ProtectionDomain domain = clazz.getProtectionDomain();
219
220         if (domain == null)
221           continue;
222         if (seenDomains.contains(domain))
223           continue;
224         seenDomains.add(domain);
225
226         // Create a static snapshot of this domain, which may change over time
227         // if the current policy changes.
228         domains.add(new ProtectionDomain(domain.getCodeSource(),
229                                          domain.getPermissions(),
230                                          loader, null));
231       }
232
233     if (DEBUG)
234       debug("created domains: " + domains);
235
236     ProtectionDomain[] result = (ProtectionDomain[])
237       domains.toArray(new ProtectionDomain[domains.size()]);
238
239     if (context != null)
240       {
241         DomainCombiner dc = context.getDomainCombiner ();
242         // If the supplied context had no explicit DomainCombiner, use
243         // our private version, which computes the intersection of the
244         // context's domains with the derived set.
245         if (dc == null)
246           context = new AccessControlContext
247             (IntersectingDomainCombiner.SINGLETON.combine
248              (result, context.getProtectionDomains ()));
249         // Use the supplied DomainCombiner. This should be secure,
250         // because only trusted code may create an
251         // AccessControlContext with a custom DomainCombiner.
252         else
253           context = new AccessControlContext (result, context, dc);
254       }
255     // No context was supplied. Return the derived one.
256     else
257       context = new AccessControlContext (result);
258
259     inGetContext.set(Boolean.FALSE);
260     return context;
261   }
262
263   /**
264    * Returns a snapshot of the current call stack as a pair of arrays:
265    * the first an array of classes in the call stack, the second an array
266    * of strings containing the method names in the call stack. The two
267    * arrays match up, meaning that method <i>i</i> is declared in class
268    * <i>i</i>. The arrays are clean; it will only contain Java methods,
269    * and no element of the list should be null.
270    *
271    * <p>The default implementation returns an empty stack, which will be
272    * interpreted as having no permissions whatsoever.
273    *
274    * @return A pair of arrays describing the current call stack. The first
275    *    element is an array of Class objects, and the second is an array
276    *    of Strings comprising the method names.
277    */
278 //    private static Object[][] getStack()
279 //    {
280 //      return new Object[][] { new Class[0], new String[0] };
281 //    }
282   private native static Object[][] getStack();
283 }