View Javadoc

1   package org.eclipse.jetty.policy;
2   
3   //========================================================================
4   //Copyright (c) Webtide LLC
5   //------------------------------------------------------------------------
6   //All rights reserved. This program and the accompanying materials
7   //are made available under the terms of the Eclipse Public License v1.0
8   //and Apache License v2.0 which accompanies this distribution.
9   //
10  //The Eclipse Public License is available at 
11  //http://www.eclipse.org/legal/epl-v10.html
12  //
13  //The Apache License v2.0 is available at
14  //http://www.opensource.org/licenses/apache2.0.php
15  //
16  //You may elect to redistribute this code under either of these licenses. 
17  //========================================================================
18  
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.PrintStream;
22  import java.io.PrintWriter;
23  import java.security.AccessControlException;
24  import java.security.CodeSource;
25  import java.security.Permission;
26  import java.security.PermissionCollection;
27  import java.security.Permissions;
28  import java.security.Policy;
29  import java.security.Principal;
30  import java.security.ProtectionDomain;
31  import java.util.ArrayList;
32  import java.util.Enumeration;
33  import java.util.HashMap;
34  import java.util.HashSet;
35  import java.util.Iterator;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Set;
39  
40  import org.eclipse.jetty.policy.loader.DefaultPolicyLoader;
41  import org.eclipse.jetty.util.Scanner;
42  
43  
44  /**
45   * Policy implementation that will load a set of policy files and manage the mapping of permissions and protection domains
46   * 
47   * The reason I created this class and added this mechanism are:
48   * 
49   * 1) I wanted a way to be able to follow the startup mechanic that jetty uses with jetty-start using OPTIONS=policy,default to be able to startup a security manager and policy implementation without have to rely on the existing JVM cli options 2)
50   * establish a starting point to add on further functionality to permissions based security with jetty like jmx enabled permission tweaking or runtime creation and specification of policies for specific webapps 3) I wanted to have support for specifying
51   * multiple policy files to source permissions from
52   * 
53   * Possible additions are: - directories of policy file support - jmx enabled a la #2 above - proxying of system security policy where we can proxy access to the system policy should the jvm have been started with one, I had support for this but ripped it
54   * out to add in again later - merging of protection domains if process multiple policy files that declare permissions for the same codebase - an xml policy file parser, had originally added this using modello but tore it out since it would have been a
55   * nightmare to get its dependencies through IP validation, could do this with jvm xml parser instead sometime - check performance of the synch'd map I am using for the protection domain mapping
56   */
57  public class JettyPolicy extends Policy
58  {
59      private static boolean __DEBUG = false;
60      private static boolean __RELOAD = false;
61  
62      // Policy files that are actively managed by the aggregate policy mechanism
63      private final Set<String> _policies;
64  
65      private final Set<PolicyBlock> _grants = new HashSet<PolicyBlock>();
66  
67      private final Map<Object, PermissionCollection> _cache = new HashMap<Object, PermissionCollection>();
68  
69      private final PolicyContext _context = new PolicyContext();
70  
71      private Boolean _initialized = false;
72  
73      private Scanner _scanner;
74  
75      public JettyPolicy(Set<String> policies, Map<String, String> properties)
76      {
77          try
78          {
79  
80              __RELOAD = Boolean.getBoolean("org.eclipse.jetty.policy.RELOAD");
81              __DEBUG = Boolean.getBoolean("org.eclipse.jetty.policy.DEBUG");
82          }
83          catch (AccessControlException ace)
84          {
85              __RELOAD = false;
86              __DEBUG = false;
87          }
88  
89          _policies = policies;
90          _context.setProperties(properties);
91      }
92  
93      @Override
94      public PermissionCollection getPermissions(ProtectionDomain domain)
95      {
96  
97          if (!_initialized)
98          {
99              synchronized (_initialized)
100             {
101                 // make sure we haven't been initialized since obtaining lock
102                 if (!_initialized)
103                 {
104                     refresh();
105                 }
106             }
107         }
108 
109         if (_cache.containsKey(domain))
110         {
111             return _cache.get(domain);
112         }
113 
114         synchronized (_cache)
115         {
116             // check if it was added in since we obtained lock
117             if (_cache.containsKey(domain))
118             {
119                 return _cache.get(domain);
120             }
121 
122             PermissionCollection perms = new Permissions();
123 
124             for (Iterator<PolicyBlock> i = _grants.iterator(); i.hasNext();)
125             {
126                 PolicyBlock policyBlock = i.next();
127                 ProtectionDomain grantPD = policyBlock.toProtectionDomain();
128 
129                 if (__DEBUG)
130                 {
131                     System.out.println("----START----");
132                     System.out.println("PDCS: " + policyBlock.getCodeSource());
133                     System.out.println("CS: " + domain.getCodeSource());
134                 }
135 
136                 // 1) if protection domain codesource is null, it is the global permissions (grant {})
137                 // 2) if protection domain codesource implies target codesource and there are no prinicpals
138                 // 2) if protection domain codesource implies target codesource and principals align
139                 if (grantPD.getCodeSource() == null || grantPD.getCodeSource().implies(domain.getCodeSource()) && grantPD.getPrincipals() == null || grantPD.getCodeSource().implies(domain.getCodeSource())
140                         && validate(grantPD.getPrincipals(),domain.getPrincipals()))
141                 {
142 
143                     for (Enumeration<Permission> e = policyBlock.getPermissions().elements(); e.hasMoreElements();)
144                     {
145                         Permission perm = e.nextElement();
146                         if (__DEBUG)
147                         {
148                             System.out.println("D: " + perm);
149                         }
150                         perms.add(perm);
151                     }
152                 }
153                 if (__DEBUG)
154                 {
155                     System.out.println("----STOP----");
156                 }
157             }
158 
159             _cache.put(domain,perms);
160 
161             return perms;
162         }
163     }
164 
165     @Override
166     public PermissionCollection getPermissions(CodeSource codesource)
167     {
168         if (!_initialized)
169         {
170             synchronized (_initialized)
171             {
172                 // make sure we haven't been initialized since obtaining lock
173                 if (!_initialized)
174                 {
175                     refresh();
176                 }
177             }
178         }
179 
180         if (_cache.containsKey(codesource))
181         {
182             return _cache.get(codesource);
183         }
184 
185         synchronized (_cache)
186         {
187             // check if it was added in since we obtained lock
188             if (_cache.containsKey(codesource))
189             {
190                 return _cache.get(codesource);
191             }
192 
193             PermissionCollection perms = new Permissions();
194 
195             for (Iterator<PolicyBlock> i = _grants.iterator(); i.hasNext();)
196             {
197                 PolicyBlock policyBlock = i.next();
198                 ProtectionDomain grantPD = policyBlock.toProtectionDomain();
199 
200                 if (grantPD.getCodeSource() == null || grantPD.getCodeSource().implies(codesource))
201                 {
202                     if (__DEBUG)
203                     {
204                         System.out.println("----START----");
205                         System.out.println("PDCS: " + grantPD.getCodeSource());
206                         System.out.println("CS: " + codesource);
207                     }
208 
209                     for (Enumeration<Permission> e = policyBlock.getPermissions().elements(); e.hasMoreElements();)
210                     {
211                         Permission perm = e.nextElement();
212                         if (__DEBUG)
213                         {
214                             System.out.println("D: " + perm);
215                         }
216                         perms.add(perm);
217                     }
218 
219                     if (__DEBUG)
220                     {
221                         System.out.println("----STOP----");
222                     }
223                 }
224             }
225 
226             _cache.put(codesource,perms);
227 
228             return perms;
229         }
230     }
231 
232     @Override
233     public boolean implies(ProtectionDomain domain, Permission permission) {
234         PermissionCollection pc;
235 
236         if (!_initialized)
237         {
238             synchronized (_initialized)
239             {
240                 // make sure we haven't been initialized since obtaining lock
241                 if (!_initialized)
242                 {
243                     refresh();
244                 }
245             }
246         }
247 
248         synchronized (_cache) {
249             pc = _cache.get(domain);
250         }
251 
252         if (pc != null) {
253             return pc.implies(permission);
254         } 
255         
256         pc = getPermissions(domain);
257         if (pc == null) {
258             return false;
259         }
260 
261         synchronized (_cache) {
262             // cache it 
263             _cache.put(domain, pc);
264         }
265         
266         return pc.implies(permission);
267     }
268     
269 
270     private static boolean validate(Principal[] permCerts, Principal[] classCerts)
271     {
272         if (classCerts == null)
273         {
274             return false;
275         }
276 
277         for (int i = 0; i < permCerts.length; ++i)
278         {
279             boolean found = false;
280             for (int j = 0; j < classCerts.length; ++j)
281             {
282                 if (permCerts[i].equals(classCerts[j]))
283                 {
284                     found = true;
285                     break;
286                 }
287             }
288             // if we didn't find the permCert in the classCerts then we don't match up
289             if (found == false)
290             {
291                 return false;
292             }
293         }
294 
295         return true;
296     }
297 
298     @Override
299     public synchronized void refresh()
300     {
301 
302         try
303         {
304             // initialize the reloading mechanism if enabled
305             if (__RELOAD && _scanner == null)
306             {
307                 initializeReloading();
308             }
309             
310             if (__DEBUG)
311             {
312                 synchronized (_cache)
313                 {
314                     for (Iterator<Object> i = _cache.keySet().iterator(); i.hasNext();)
315                     {
316                         System.out.println(i.next().toString());
317                     }
318                 }
319             }
320 
321             Set<PolicyBlock> clean = new HashSet<PolicyBlock>();
322 
323             for (Iterator<String> i = _policies.iterator(); i.hasNext();)
324             {
325                 File policyFile = new File(i.next());
326 
327                 clean.addAll(DefaultPolicyLoader.load(new FileInputStream(policyFile),_context));
328             }
329 
330             synchronized (_cache)
331             {
332                 _grants.clear();
333                 _grants.addAll(clean);
334                 _cache.clear();
335             }
336             _initialized = true;
337         }
338         catch (Exception e)
339         {
340             e.printStackTrace();
341         }
342     }
343 
344     private void initializeReloading() throws Exception
345     {
346         _scanner = new Scanner();
347 
348         List<File> scanDirs = new ArrayList<File>();
349 
350         for (Iterator<String> i = _policies.iterator(); i.hasNext();)
351         {
352             File policyFile = new File(i.next());
353             scanDirs.add(policyFile.getParentFile());
354         }
355 
356         _scanner.addListener(new Scanner.DiscreteListener()
357         {
358 
359             public void fileRemoved(String filename) throws Exception
360             {
361 
362             }
363 
364             /* will trigger when files are changed, not on load time, just when changed */
365             public void fileChanged(String filename) throws Exception
366             {
367                 if (filename.endsWith("policy")) // TODO match up to existing policies to avoid unnecessary reloads
368                 {
369                     System.out.println("JettyPolicy: refreshing policy files");
370                     refresh();
371                     System.out.println("JettyPolicy: finished refreshing policies");
372                 }
373             }
374 
375             public void fileAdded(String filename) throws Exception
376             {
377 
378             }
379         });
380 
381         _scanner.setScanDirs(scanDirs);
382         _scanner.start();
383         _scanner.setScanInterval(10);
384     }
385 
386     public void dump(PrintStream out)
387     {
388         PrintWriter write = new PrintWriter(out);
389         write.println("dumping policy settings");
390 
391         synchronized (_cache)
392         {
393             for (Iterator<Object> i = _cache.keySet().iterator(); i.hasNext();)
394             {
395                 Object o = i.next();
396                 write.println(o.toString());
397             }
398         }
399         write.flush();
400     }
401 }