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          synchronized (_initialized)
98          {
99              if (!_initialized)
100             {
101                 refresh();
102             }
103         }
104 
105         synchronized (_cache)
106         {
107             if (_cache.containsKey(domain))
108             {
109                 return copyOf(_cache.get(domain));
110             }
111 
112             PermissionCollection perms = new Permissions();
113 
114             for (Iterator<PolicyBlock> i = _grants.iterator(); i.hasNext();)
115             {
116                 PolicyBlock policyBlock = i.next();
117                 ProtectionDomain grantPD = policyBlock.toProtectionDomain();
118 
119                 if (__DEBUG)
120                 {
121                     System.out.println("----START----");
122                     System.out.println("PDCS: " + policyBlock.getCodeSource());
123                     System.out.println("CS: " + domain.getCodeSource());
124                 }
125 
126                 // 1) if protection domain codesource is null, it is the global permissions (grant {})
127                 // 2) if protection domain codesource implies target codesource and there are no prinicpals
128                 // 2) if protection domain codesource implies target codesource and principals align
129                 if (grantPD.getCodeSource() == null || grantPD.getCodeSource().implies(domain.getCodeSource()) && grantPD.getPrincipals() == null || grantPD.getCodeSource().implies(domain.getCodeSource())
130                         && validate(grantPD.getPrincipals(),domain.getPrincipals()))
131                 {
132 
133                     for (Enumeration<Permission> e = policyBlock.getPermissions().elements(); e.hasMoreElements();)
134                     {
135                         Permission perm = e.nextElement();
136                         if (__DEBUG)
137                         {
138                             System.out.println("D: " + perm);
139                         }
140                         perms.add(perm);
141                     }
142                 }
143                 if (__DEBUG)
144                 {
145                     System.out.println("----STOP----");
146                 }
147             }
148 
149             _cache.put(domain,perms);
150 
151             return copyOf(perms);
152         }
153     }
154 
155     @Override
156     public PermissionCollection getPermissions(CodeSource codesource)
157     {
158         synchronized (_initialized)
159         {
160             if (!_initialized)
161             {
162                 refresh();
163             }
164         }
165 
166         synchronized (_cache)
167         {
168             if (_cache.containsKey(codesource))
169             {
170                 return copyOf(_cache.get(codesource));
171             }
172 
173             PermissionCollection perms = new Permissions();
174 
175             for (Iterator<PolicyBlock> i = _grants.iterator(); i.hasNext();)
176             {
177                 PolicyBlock policyBlock = i.next();
178                 ProtectionDomain grantPD = policyBlock.toProtectionDomain();
179 
180                 if (grantPD.getCodeSource() == null || grantPD.getCodeSource().implies(codesource))
181                 {
182                     if (__DEBUG)
183                     {
184                         System.out.println("----START----");
185                         System.out.println("PDCS: " + grantPD.getCodeSource());
186                         System.out.println("CS: " + codesource);
187                     }
188 
189                     for (Enumeration<Permission> e = policyBlock.getPermissions().elements(); e.hasMoreElements();)
190                     {
191                         Permission perm = e.nextElement();
192                         if (__DEBUG)
193                         {
194                             System.out.println("D: " + perm);
195                         }
196                         perms.add(perm);
197                     }
198 
199                     if (__DEBUG)
200                     {
201                         System.out.println("----STOP----");
202                     }
203                 }
204             }
205 
206             _cache.put(codesource,perms);
207 
208             return copyOf(perms);
209         }
210     }
211 
212     @Override
213     public boolean implies(ProtectionDomain domain, Permission permission)
214     {
215         PermissionCollection pc = getPermissions(domain);
216         
217         return (pc == null ? false : pc.implies(permission));
218     }
219     
220 
221     private static boolean validate(Principal[] permCerts, Principal[] classCerts)
222     {
223         if (classCerts == null)
224         {
225             return false;
226         }
227 
228         for (int i = 0; i < permCerts.length; ++i)
229         {
230             boolean found = false;
231             for (int j = 0; j < classCerts.length; ++j)
232             {
233                 if (permCerts[i].equals(classCerts[j]))
234                 {
235                     found = true;
236                     break;
237                 }
238             }
239             // if we didn't find the permCert in the classCerts then we don't match up
240             if (found == false)
241             {
242                 return false;
243             }
244         }
245 
246         return true;
247     }
248 
249     @Override
250     public synchronized void refresh()
251     {
252 
253         try
254         {
255             // initialize the reloading mechanism if enabled
256             if (__RELOAD && _scanner == null)
257             {
258                 initializeReloading();
259             }
260             
261             if (__DEBUG)
262             {
263                 synchronized (_cache)
264                 {
265                     for (Iterator<Object> i = _cache.keySet().iterator(); i.hasNext();)
266                     {
267                         System.out.println(i.next().toString());
268                     }
269                 }
270             }
271 
272             Set<PolicyBlock> clean = new HashSet<PolicyBlock>();
273 
274             for (Iterator<String> i = _policies.iterator(); i.hasNext();)
275             {
276                 File policyFile = new File(i.next());
277 
278                 clean.addAll(DefaultPolicyLoader.load(new FileInputStream(policyFile),_context));
279             }
280 
281             synchronized (_cache)
282             {
283                 _grants.clear();
284                 _grants.addAll(clean);
285                 _cache.clear();
286             }
287             _initialized = true;
288         }
289         catch (Exception e)
290         {
291             e.printStackTrace();
292         }
293     }
294 
295     private void initializeReloading() throws Exception
296     {
297         _scanner = new Scanner();
298 
299         List<File> scanDirs = new ArrayList<File>();
300 
301         for (Iterator<String> i = _policies.iterator(); i.hasNext();)
302         {
303             File policyFile = new File(i.next());
304             scanDirs.add(policyFile.getParentFile());
305         }
306 
307         _scanner.addListener(new Scanner.DiscreteListener()
308         {
309 
310             public void fileRemoved(String filename) throws Exception
311             {
312 
313             }
314 
315             /* will trigger when files are changed, not on load time, just when changed */
316             public void fileChanged(String filename) throws Exception
317             {
318                 if (filename.endsWith("policy")) // TODO match up to existing policies to avoid unnecessary reloads
319                 {
320                     System.out.println("JettyPolicy: refreshing policy files");
321                     refresh();
322                     System.out.println("JettyPolicy: finished refreshing policies");
323                 }
324             }
325 
326             public void fileAdded(String filename) throws Exception
327             {
328 
329             }
330         });
331 
332         _scanner.setScanDirs(scanDirs);
333         _scanner.start();
334         _scanner.setScanInterval(10);
335     }
336 
337     public void dump(PrintStream out)
338     {
339         PrintWriter write = new PrintWriter(out);
340         write.println("dumping policy settings");
341 
342         synchronized (_cache)
343         {
344             for (Iterator<Object> i = _cache.keySet().iterator(); i.hasNext();)
345             {
346                 Object o = i.next();
347                 write.println(o.toString());
348             }
349         }
350         write.flush();
351     }
352     
353     public PermissionCollection copyOf(final PermissionCollection in)
354     {
355         PermissionCollection out  = new Permissions();
356         synchronized (in)
357         {
358             for (Enumeration el = in.elements() ; el.hasMoreElements() ;)
359             {
360                 out.add((Permission)el.nextElement());
361             }
362         }
363         return out;
364     }
365 }