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.security.CodeSource;
22  import java.security.Permission;
23  import java.security.PermissionCollection;
24  import java.security.Permissions;
25  import java.security.Policy;
26  import java.security.Principal;
27  import java.security.ProtectionDomain;
28  import java.util.Collections;
29  import java.util.Enumeration;
30  import java.util.HashMap;
31  import java.util.Iterator;
32  import java.util.Map;
33  import java.util.Set;
34  
35  import org.eclipse.jetty.policy.loader.DefaultPolicyLoader;
36  
37  /**
38   * Policy implementation that will load a set of policy files and manage the mapping of permissions and protection domains 
39   * 
40   * The reason I created this class and added this mechanism are:
41   * 
42   * 1) I wanted a way to be able to follow the startup mechanic that jetty uses with jetty-start using OPTIONS=policy,default 
43   * to be able to startup a security manager and policy implementation without have to rely on the existing JVM cli options
44   * 2) establish a starting point to add on further functionality to permissions based security with jetty like jmx enabled 
45   * permission tweaking or runtime creation and specification of policies for specific webapps
46   * 3) I wanted to have support for specifying multiple policy files to source permissions from
47   * 
48   * Possible additions are:
49   * - directories of policy file support
50   * - jmx enabled a la #2 above
51   * - proxying of system security policy where we can proxy access to the system policy should the jvm have been started with 
52   *   one, I had support for this but ripped it out to add in again later
53   * - merging of protection domains if process multiple policy files that declare permissions for the same codebase
54   * - an xml policy file parser, had originally added this using modello but tore it out since it would have been
55   *   a nightmare to get its dependencies through IP validation, could do this with jvm xml parser instead sometime
56   * - check performance of the synch'd map I am using for the protection domain mapping
57   */
58  public class JettyPolicy extends Policy
59  {
60      // Policy files that are actively managed by the aggregate policy mechanism
61      private Set<String> _policies;
62  
63      private Map<ProtectionDomain, PolicyBlock> pdMapping =
64          Collections.synchronizedMap( new HashMap<ProtectionDomain, PolicyBlock>() );
65      
66      private PolicyContext _context = new PolicyContext();
67  
68      public JettyPolicy( Set<String> policies, Map<String,String> properties )
69      {
70          _policies = policies;
71          _context.setProperties( properties );
72          
73          // we have the policies we need and an evaluator to reference, lets refresh and save the user a call.
74          refresh();
75      }
76  
77      public PermissionCollection getPermissions( ProtectionDomain domain )
78      {     
79          PermissionCollection perms = new Permissions();
80  
81          for ( Iterator<ProtectionDomain> i = pdMapping.keySet().iterator(); i.hasNext(); )
82          {
83              ProtectionDomain pd = (ProtectionDomain) i.next();
84              
85              if ( pd.getCodeSource() == null || pd.getCodeSource().implies( domain.getCodeSource() ) && pd.getPrincipals() == null || validate( pd.getPrincipals(), domain.getPrincipals() ) )
86              {         
87                  // gather dynamic permissions
88                  if ( pdMapping.get( pd ) != null )
89                  {
90                      for ( Enumeration<Permission> e = pdMapping.get( pd ).getPermissions().elements(); e.hasMoreElements(); )
91                      {
92                          perms.add( e.nextElement() );
93                      }
94                  }
95                  
96                  // gather static permissions
97                  if ( pd.getPermissions() != null )
98                  {
99                      for ( Enumeration<Permission> e = pd.getPermissions().elements(); e.hasMoreElements(); )
100                     {
101                         perms.add( e.nextElement() );
102                     }
103                 }
104             }
105         }
106         
107         return perms;
108     }
109 
110     public PermissionCollection getPermissions( CodeSource codesource )
111     {
112         PermissionCollection perms = new Permissions();
113 
114         for ( Iterator<ProtectionDomain> i = pdMapping.keySet().iterator(); i.hasNext(); )
115         {
116             ProtectionDomain pd = (ProtectionDomain) i.next();
117 
118             if ( pd.getCodeSource() == null || pd.getCodeSource().implies( codesource ) )
119             {
120                 // gather dynamic permissions
121                 if ( pdMapping.get( pd ) != null )
122                 {
123                     for ( Enumeration<Permission> e = pdMapping.get( pd ).getPermissions().elements(); e.hasMoreElements(); )
124                     {
125                         perms.add( e.nextElement() );
126                     }
127                 }
128 
129                 // gather static permissions
130                 if ( pd.getPermissions() != null )
131                 {
132                     for ( Enumeration<Permission> e = pd.getPermissions().elements(); e.hasMoreElements(); )
133                     {
134                         perms.add( e.nextElement() );
135                     }
136                 }
137             }
138         }
139 
140         return perms;
141     }
142 
143     private static boolean validate( Principal[] permCerts, Principal[] classCerts )
144     {
145         if ( classCerts == null )
146         {
147             return false;
148         }
149         
150         for ( int i = 0; i < permCerts.length; ++i )
151         {
152             boolean found = false;           
153             for ( int j = 0; j < classCerts.length; ++j )
154             {
155                 if ( permCerts[i].equals( classCerts[j] ) )
156                 {
157                     found = true;
158                     break;
159                 }
160             }
161             // if we didn't find the permCert in the classCerts then we don't match up
162             if ( found == false )
163             {
164                 return false;
165             }
166         }
167         
168         return true;
169     }
170     
171     public void refresh()
172     {
173         try
174         {
175             pdMapping.clear();
176 
177             for ( Iterator<String> i = _policies.iterator(); i.hasNext(); )
178             {
179                 File policyFile = new File( i.next() );              
180                 pdMapping.putAll( DefaultPolicyLoader.load( new FileInputStream( policyFile ), _context ) );
181             }
182             
183             //for ( Iterator<ProtectionDomain> i = pdMapping.keySet().iterator(); i.hasNext();)
184             //{
185             //    System.out.println(i.next().toString());
186             //}
187         }
188         catch ( Exception e )
189         {
190             e.printStackTrace();
191         }
192     }
193 }