160df10cb426b9d44980b6b8e3ba6edd2649c7e3
[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)
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
192         if (DEBUG)
193           {
194             debug("checking " + clazz + "." + method);
195             // subject to getClassLoader RuntimePermission
196             debug("loader = " + clazz.getClassLoader());
197           }
198
199         // If the previous frame was a call to doPrivileged, then this is
200         // the last frame we look at.
201         if (privileged == 1)
202           privileged = 2;
203
204         if (clazz.equals (AccessController.class)
205             && method.equals ("doPrivileged"))
206           {
207             // If there was a call to doPrivileged with a supplied context,
208             // return that context. If using JAAS doAs*, it should be 
209             // a context with a SubjectDomainCombiner
210             LinkedList l = (LinkedList) contexts.get();
211             if (l != null)
212               context = (AccessControlContext) l.getFirst();
213             privileged = 1;
214           }
215
216         // subject to getProtectionDomain RuntimePermission
217         ProtectionDomain domain = clazz.getProtectionDomain();
218
219         if (domain == null)
220           continue;
221         if (seenDomains.contains(domain))
222           continue;
223         seenDomains.add(domain);
224
225         // Create a static snapshot of this domain, which may change over time
226         // if the current policy changes.
227         domains.add(new ProtectionDomain(domain.getCodeSource(),
228                                          domain.getPermissions()));
229       }
230
231     if (DEBUG)
232       debug("created domains: " + domains);
233
234     ProtectionDomain[] result = (ProtectionDomain[])
235       domains.toArray(new ProtectionDomain[domains.size()]);
236
237     if (context != null)
238       {
239         DomainCombiner dc = context.getDomainCombiner ();
240         // If the supplied context had no explicit DomainCombiner, use
241         // our private version, which computes the intersection of the
242         // context's domains with the derived set.
243         if (dc == null)
244           context = new AccessControlContext
245             (IntersectingDomainCombiner.SINGLETON.combine
246              (result, context.getProtectionDomains ()));
247         // Use the supplied DomainCombiner. This should be secure,
248         // because only trusted code may create an
249         // AccessControlContext with a custom DomainCombiner.
250         else
251           context = new AccessControlContext (result, context, dc);
252       }
253     // No context was supplied. Return the derived one.
254     else
255       context = new AccessControlContext (result);
256
257     inGetContext.set(Boolean.FALSE);
258     return context;
259   }
260
261   /**
262    * Returns a snapshot of the current call stack as a pair of arrays:
263    * the first an array of classes in the call stack, the second an array
264    * of strings containing the method names in the call stack. The two
265    * arrays match up, meaning that method <i>i</i> is declared in class
266    * <i>i</i>. The arrays are clean; it will only contain Java methods,
267    * and no element of the list should be null.
268    *
269    * <p>The default implementation returns an empty stack, which will be
270    * interpreted as having no permissions whatsoever.
271    *
272    * @return A pair of arrays describing the current call stack. The first
273    *    element is an array of Class objects, and the second is an array
274    *    of Strings comprising the method names.
275    */
276 //    private static Object[][] getStack()
277 //    {
278 //      return new Object[][] { new Class[0], new String[0] };
279 //    }
280   private native static Object[][] getStack();
281 }