Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: gnu/java/security/PolicyFile.java


1   /* PolicyFile.java -- policy file reader
2      Copyright (C) 2004, 2005  Free Software Foundation, Inc.
3   
4   This file is part of GNU Classpath.
5   
6   GNU Classpath is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10  
11  GNU Classpath is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  General Public License for more details.
15  
16  You should have received a copy of the GNU General Public License
17  along with GNU Classpath; see the file COPYING.  If not, write to the
18  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  02110-1301 USA.
20  
21  Linking this library statically or dynamically with other modules is
22  making a combined work based on this library.  Thus, the terms and
23  conditions of the GNU General Public License cover the whole
24  combination.
25  
26  As a special exception, the copyright holders of this library give you
27  permission to link this library with independent modules to produce an
28  executable, regardless of the license terms of these independent
29  modules, and to copy and distribute the resulting executable under
30  terms of your choice, provided that you also meet, for each linked
31  independent module, the terms and conditions of the license of that
32  module.  An independent module is a module which is not derived from
33  or based on this library.  If you modify this library, you may extend
34  this exception to your version of the library, but you are not
35  obligated to do so.  If you do not wish to do so, delete this
36  exception statement from your version. */
37  
38  package gnu.java.security;
39  
40  import gnu.classpath.SystemProperties;
41  import gnu.classpath.debug.Component;
42  import gnu.classpath.debug.SystemLogger;
43  
44  import java.io.File;
45  import java.io.IOException;
46  import java.io.InputStreamReader;
47  import java.io.StreamTokenizer;
48  import java.lang.reflect.Constructor;
49  import java.net.MalformedURLException;
50  import java.net.URL;
51  import java.security.AccessController;
52  import java.security.CodeSource;
53  import java.security.KeyStore;
54  import java.security.KeyStoreException;
55  import java.security.Permission;
56  import java.security.PermissionCollection;
57  import java.security.Permissions;
58  import java.security.Policy;
59  import java.security.Principal;
60  import java.security.PrivilegedActionException;
61  import java.security.PrivilegedExceptionAction;
62  import java.security.Security;
63  import java.security.UnresolvedPermission;
64  import java.security.cert.Certificate;
65  import java.security.cert.X509Certificate;
66  import java.util.Enumeration;
67  import java.util.HashMap;
68  import java.util.Iterator;
69  import java.util.LinkedList;
70  import java.util.List;
71  import java.util.Map;
72  import java.util.StringTokenizer;
73  import java.util.logging.Logger;
74  
75  /**
76   * An implementation of a {@link java.security.Policy} object whose
77   * permissions are specified by a <em>policy file</em>.
78   *
79   * <p>The approximate syntax of policy files is:</p>
80   *
81   * <pre>
82   * policyFile ::= keystoreOrGrantEntries ;
83   *
84   * keystoreOrGrantEntries ::= keystoreOrGrantEntry |
85   *                            keystoreOrGrantEntries keystoreOrGrantEntry |
86   *                            EMPTY ;
87   *
88   * keystoreOrGrantEntry ::= keystoreEntry | grantEntry ;
89   *
90   * keystoreEntry ::= "keystore" keystoreUrl ';' |
91   *                   "keystore" keystoreUrl ',' keystoreAlgorithm ';' ;
92   *
93   * keystoreUrl ::= URL ;
94   * keystoreAlgorithm ::= STRING ;
95   *
96   * grantEntry ::= "grant" domainParameters '{' permissions '}' ';'
97   *
98   * domainParameters ::= domainParameter |
99   *                      domainParameter ',' domainParameters ;
100  *
101  * domainParameter ::= "signedBy" signerNames |
102  *                     "codeBase" codeBaseUrl |
103  *                     "principal" principalClassName principalName |
104  *                     "principal" principalName ;
105  *
106  * signerNames ::= quotedString ;
107  * codeBaseUrl ::= URL ;
108  * principalClassName ::= STRING ;
109  * principalName ::= quotedString ;
110  *
111  * quotedString ::= quoteChar STRING quoteChar ;
112  * quoteChar ::= '"' | '\'';
113  *
114  * permissions ::= permission | permissions permission ;
115  *
116  * permission ::= "permission" permissionClassName permissionTarget permissionAction |
117  *                "permission" permissionClassName permissionTarget |
118  *                "permission" permissionClassName;
119  * </pre>
120  *
121  * <p>Comments are either form of Java comments. Keystore entries only
122  * affect subsequent grant entries, so if a grant entry preceeds a
123  * keystore entry, that grant entry is not affected by that keystore
124  * entry. Certian instances of <code>${property-name}</code> will be
125  * replaced with <code>System.getProperty("property-name")</code> in
126  * quoted strings.</p>
127  *
128  * <p>This class will load the following files when created or
129  * refreshed, in order:</p>
130  *
131  * <ol>
132  * <li>The file <code>${java.home}/lib/security/java.policy</code>.</li>
133  * <li>All URLs specified by security properties
134  * <code>"policy.file.<i>n</i>"</code>, for increasing <i>n</i>
135  * starting from 1. The sequence stops at the first undefined
136  * property, so you must set <code>"policy.file.1"</code> if you also
137  * set <code>"policy.file.2"</code>, and so on.</li>
138  * <li>The URL specified by the property
139  * <code>"java.security.policy"</code>.</li>
140  * </ol>
141  *
142  * @author Casey Marshall (csm@gnu.org)
143  * @see java.security.Policy
144  */
145 public final class PolicyFile extends Policy
146 {
147 
148   // Constants and fields.
149   // -------------------------------------------------------------------------
150 
151   private static final Logger logger = SystemLogger.SYSTEM;
152 
153   private static final String DEFAULT_POLICY =
154     SystemProperties.getProperty("java.home")
155     + SystemProperties.getProperty("file.separator") + "lib"
156     + SystemProperties.getProperty("file.separator") + "security"
157     + SystemProperties.getProperty("file.separator") + "java.policy";
158   private static final String DEFAULT_USER_POLICY =
159     SystemProperties.getProperty ("user.home") +
160     SystemProperties.getProperty ("file.separator") + ".java.policy";
161 
162   private final Map cs2pc;
163 
164   // Constructors.
165   // -------------------------------------------------------------------------
166 
167   public PolicyFile()
168   {
169     cs2pc = new HashMap();
170     refresh();
171   }
172 
173   // Instance methods.
174   // -------------------------------------------------------------------------
175 
176   public PermissionCollection getPermissions(CodeSource codeSource)
177   {
178     Permissions perms = new Permissions();
179     for (Iterator it = cs2pc.entrySet().iterator(); it.hasNext(); )
180       {
181         Map.Entry e = (Map.Entry) it.next();
182         CodeSource cs = (CodeSource) e.getKey();
183         if (cs.implies(codeSource))
184           {
185             logger.log (Component.POLICY, "{0} -> {1}", new Object[]
186               { cs, codeSource });
187             PermissionCollection pc = (PermissionCollection) e.getValue();
188             for (Enumeration ee = pc.elements(); ee.hasMoreElements(); )
189               {
190                 perms.add((Permission) ee.nextElement());
191               }
192           }
193         else
194           logger.log (Component.POLICY, "{0} !-> {1}", new Object[]
195             { cs, codeSource });
196       }
197     logger.log (Component.POLICY, "returning permissions {0} for {1}",
198                 new Object[] { perms, codeSource });
199     return perms;
200   }
201 
202   public void refresh()
203   {
204     cs2pc.clear();
205     final List policyFiles = new LinkedList();
206     try
207       {
208         policyFiles.add (new File (DEFAULT_POLICY).toURL());
209         policyFiles.add (new File (DEFAULT_USER_POLICY).toURL ());
210 
211         AccessController.doPrivileged(
212           new PrivilegedExceptionAction()
213           {
214             public Object run() throws Exception
215             {
216               String allow = Security.getProperty ("policy.allowSystemProperty");
217               if (allow == null || Boolean.getBoolean (allow))
218                 {
219                   String s = SystemProperties.getProperty ("java.security.policy");
220                   logger.log (Component.POLICY, "java.security.policy={0}", s);
221                   if (s != null)
222                     {
223                       boolean only = s.startsWith ("=");
224                       if (only)
225                         s = s.substring (1);
226                       policyFiles.clear ();
227                       policyFiles.add (new URL (s));
228                       if (only)
229                         return null;
230                     }
231                 }
232               for (int i = 1; ; i++)
233                 {
234                   String pname = "policy.url." + i;
235                   String s = Security.getProperty (pname);
236                   logger.log (Component.POLICY, "{0}={1}", new Object []
237                     { pname, s });
238                   if (s == null)
239                     break;
240                   policyFiles.add (new URL (s));
241                 }
242               return null;
243             }
244           });
245       }
246     catch (PrivilegedActionException pae)
247       {
248         logger.log (Component.POLICY, "reading policy properties", pae);
249       }
250     catch (MalformedURLException mue)
251       {
252         logger.log (Component.POLICY, "setting default policies", mue);
253       }
254 
255     logger.log (Component.POLICY, "building policy from URLs {0}",
256                 policyFiles);
257     for (Iterator it = policyFiles.iterator(); it.hasNext(); )
258       {
259         try
260           {
261             URL url = (URL) it.next();
262             parse(url);
263           }
264         catch (IOException ioe)
265           {
266             logger.log (Component.POLICY, "reading policy", ioe);
267           }
268       }
269   }
270 
271   public String toString()
272   {
273     return super.toString() + " [ " + cs2pc.toString() + " ]";
274   }
275 
276   // Own methods.
277   // -------------------------------------------------------------------------
278 
279   private static final int STATE_BEGIN = 0;
280   private static final int STATE_GRANT = 1;
281   private static final int STATE_PERMS = 2;
282 
283   /**
284    * Parse a policy file, incorporating the permission definitions
285    * described therein.
286    *
287    * @param url The URL of the policy file to read.
288    * @throws IOException if an I/O error occurs, or if the policy file
289    * cannot be parsed.
290    */
291   private void parse(final URL url) throws IOException
292   {
293     logger.log (Component.POLICY, "reading policy file from {0}", url);
294     final StreamTokenizer in = new StreamTokenizer(new InputStreamReader(url.openStream()));
295     in.resetSyntax();
296     in.slashSlashComments(true);
297     in.slashStarComments(true);
298     in.wordChars('A', 'Z');
299     in.wordChars('a', 'z');
300     in.wordChars('0', '9');
301     in.wordChars('.', '.');
302     in.wordChars('_', '_');
303     in.wordChars('$', '$');
304     in.whitespaceChars(' ', ' ');
305     in.whitespaceChars('\t', '\t');
306     in.whitespaceChars('\f', '\f');
307     in.whitespaceChars('\n', '\n');
308     in.whitespaceChars('\r', '\r');
309     in.quoteChar('\'');
310     in.quoteChar('"');
311 
312     int tok;
313     int state = STATE_BEGIN;
314     List keystores = new LinkedList();
315     URL currentBase = null;
316     List currentCerts = new LinkedList();
317     Permissions currentPerms = new Permissions();
318     while ((tok = in.nextToken()) != StreamTokenizer.TT_EOF)
319       {
320         switch (tok)
321           {
322           case '{':
323             if (state != STATE_GRANT)
324               error(url, in, "spurious '{'");
325             state = STATE_PERMS;
326             tok = in.nextToken();
327             break;
328           case '}':
329             if (state != STATE_PERMS)
330               error(url, in, "spurious '}'");
331             state = STATE_BEGIN;
332             currentPerms.setReadOnly();
333             Certificate[] c = null;
334             if (!currentCerts.isEmpty())
335               c = (Certificate[]) currentCerts.toArray(new Certificate[currentCerts.size()]);
336             cs2pc.put(new CodeSource(currentBase, c), currentPerms);
337             currentCerts.clear();
338             currentPerms = new Permissions();
339             currentBase = null;
340             tok = in.nextToken();
341             if (tok != ';')
342               in.pushBack();
343             continue;
344           }
345         if (tok != StreamTokenizer.TT_WORD)
346           {
347             error(url, in, "expecting word token");
348           }
349 
350         // keystore "<keystore-path>" [',' "<keystore-type>"] ';'
351         if (in.sval.equalsIgnoreCase("keystore"))
352           {
353             String alg = KeyStore.getDefaultType();
354             tok = in.nextToken();
355             if (tok != '"' && tok != '\'')
356               error(url, in, "expecting key store URL");
357             String store = in.sval;
358             tok = in.nextToken();
359             if (tok == ',')
360               {
361                 tok = in.nextToken();
362                 if (tok != '"' && tok != '\'')
363                   error(url, in, "expecting key store type");
364                 alg = in.sval;
365                 tok = in.nextToken();
366               }
367             if (tok != ';')
368               error(url, in, "expecting semicolon");
369             try
370               {
371                 KeyStore keystore = KeyStore.getInstance(alg);
372                 keystore.load(new URL(url, store).openStream(), null);
373                 keystores.add(keystore);
374               }
375             catch (Exception x)
376               {
377                 error(url, in, x.toString());
378               }
379           }
380         else if (in.sval.equalsIgnoreCase("grant"))
381           {
382             if (state != STATE_BEGIN)
383               error(url, in, "extraneous grant keyword");
384             state = STATE_GRANT;
385           }
386         else if (in.sval.equalsIgnoreCase("signedBy"))
387           {
388             if (state != STATE_GRANT && state != STATE_PERMS)
389               error(url, in, "spurious 'signedBy'");
390             if (keystores.isEmpty())
391               error(url, in, "'signedBy' with no keystores");
392             tok = in.nextToken();
393             if (tok != '"' && tok != '\'')
394               error(url, in, "expecting signedBy name");
395             StringTokenizer st = new StringTokenizer(in.sval, ",");
396             while (st.hasMoreTokens())
397               {
398                 String alias = st.nextToken();
399                 for (Iterator it = keystores.iterator(); it.hasNext(); )
400                   {
401                     KeyStore keystore = (KeyStore) it.next();
402                     try
403                       {
404                         if (keystore.isCertificateEntry(alias))
405                           currentCerts.add(keystore.getCertificate(alias));
406                       }
407                     catch (KeyStoreException kse)
408                       {
409                         error(url, in, kse.toString());
410                       }
411                   }
412               }
413             tok = in.nextToken();
414             if (tok != ',')
415               {
416                 if (state != STATE_GRANT)
417                   error(url, in, "spurious ','");
418                 in.pushBack();
419               }
420           }
421         else if (in.sval.equalsIgnoreCase("codeBase"))
422           {
423             if (state != STATE_GRANT)
424               error(url, in, "spurious 'codeBase'");
425             tok = in.nextToken();
426             if (tok != '"' && tok != '\'')
427               error(url, in, "expecting code base URL");
428             String base = expand(in.sval);
429             if (File.separatorChar != '/')
430               base = base.replace(File.separatorChar, '/');
431             try
432               {
433                 currentBase = new URL(base);
434               }
435             catch (MalformedURLException mue)
436               {
437                 error(url, in, mue.toString());
438               }
439             tok = in.nextToken();
440             if (tok != ',')
441               in.pushBack();
442           }
443         else if (in.sval.equalsIgnoreCase("principal"))
444           {
445             if (state != STATE_GRANT)
446               error(url, in, "spurious 'principal'");
447             tok = in.nextToken();
448             if (tok == StreamTokenizer.TT_WORD)
449               {
450                 tok = in.nextToken();
451                 if (tok != '"' && tok != '\'')
452                   error(url, in, "expecting principal name");
453                 String name = in.sval;
454                 Principal p = null;
455                 try
456                   {
457                     Class pclass = Class.forName(in.sval);
458                     Constructor c =
459                       pclass.getConstructor(new Class[] { String.class });
460                     p = (Principal) c.newInstance(new Object[] { name });
461                   }
462                 catch (Exception x)
463                   {
464                     error(url, in, x.toString());
465                   }
466                 for (Iterator it = keystores.iterator(); it.hasNext(); )
467                   {
468                     KeyStore ks = (KeyStore) it.next();
469                     try
470                       {
471                         for (Enumeration e = ks.aliases(); e.hasMoreElements(); )
472                           {
473                             String alias = (String) e.nextElement();
474                             if (ks.isCertificateEntry(alias))
475                               {
476                                 Certificate cert = ks.getCertificate(alias);
477                                 if (!(cert instanceof X509Certificate))
478                                   continue;
479                                 if (p.equals(((X509Certificate) cert).getSubjectDN()) ||
480                                     p.equals(((X509Certificate) cert).getSubjectX500Principal()))
481                                   currentCerts.add(cert);
482                               }
483                           }
484                       }
485                     catch (KeyStoreException kse)
486                       {
487                         error(url, in, kse.toString());
488                       }
489                   }
490               }
491             else if (tok == '"' || tok == '\'')
492               {
493                 String alias = in.sval;
494                 for (Iterator it = keystores.iterator(); it.hasNext(); )
495                   {
496                     KeyStore ks = (KeyStore) it.next();
497                     try
498                       {
499                         if (ks.isCertificateEntry(alias))
500                           currentCerts.add(ks.getCertificate(alias));
501                       }
502                     catch (KeyStoreException kse)
503                       {
504                         error(url, in, kse.toString());
505                       }
506                   }
507               }
508             else
509               error(url, in, "expecting principal");
510             tok = in.nextToken();
511             if (tok != ',')
512               in.pushBack();
513           }
514         else if (in.sval.equalsIgnoreCase("permission"))
515           {
516             if (state != STATE_PERMS)
517               error(url, in, "spurious 'permission'");
518             tok = in.nextToken();
519             if (tok != StreamTokenizer.TT_WORD)
520               error(url, in, "expecting permission class name");
521             String className = in.sval;
522             Class clazz = null;
523             try
524               {
525                 clazz = Class.forName(className);
526               }
527             catch (ClassNotFoundException cnfe)
528               {
529               }
530             tok = in.nextToken();
531             if (tok == ';')
532               {
533                 if (clazz == null)
534                   {
535                     currentPerms.add(new UnresolvedPermission(className,
536                       null, null, (Certificate[]) currentCerts.toArray(new Certificate[0])));
537                     continue;
538                   }
539                 try
540                   {
541                     currentPerms.add((Permission) clazz.newInstance());
542                   }
543                 catch (Exception x)
544                   {
545                     error(url, in, x.toString());
546                   }
547                 continue;
548               }
549             if (tok != '"' && tok != '\'')
550               error(url, in, "expecting permission target");
551             String target = expand(in.sval);
552             tok = in.nextToken();
553             if (tok == ';')
554               {
555                 if (clazz == null)
556                   {
557                     currentPerms.add(new UnresolvedPermission(className,
558                       target, null, (Certificate[]) currentCerts.toArray(new Certificate[0])));
559                     continue;
560                   }
561                 try
562                   {
563                     Constructor c =
564                       clazz.getConstructor(new Class[] { String.class });
565                     currentPerms.add((Permission) c.newInstance(
566                       new Object[] { target }));
567                   }
568                 catch (Exception x)
569                   {
570                     error(url, in, x.toString());
571                   }
572                 continue;
573               }
574             if (tok != ',')
575               error(url, in, "expecting ','");
576             tok = in.nextToken();
577             if (tok == StreamTokenizer.TT_WORD)
578               {
579                 if (!in.sval.equalsIgnoreCase("signedBy"))
580                   error(url, in, "expecting 'signedBy'");
581                 try
582                   {
583                     Constructor c =
584                       clazz.getConstructor(new Class[] { String.class });
585                     currentPerms.add((Permission) c.newInstance(
586                       new Object[] { target }));
587                   }
588                 catch (Exception x)
589                   {
590                     error(url, in, x.toString());
591                   }
592                 in.pushBack();
593                 continue;
594               }
595             if (tok != '"' && tok != '\'')
596               error(url, in, "expecting permission action");
597             String action = in.sval;
598             if (clazz == null)
599               {
600                 currentPerms.add(new UnresolvedPermission(className,
601                   target, action, (Certificate[]) currentCerts.toArray(new Certificate[0])));
602                 continue;
603               }
604             else
605               {
606                 try
607                   {
608                     Constructor c = clazz.getConstructor(
609                       new Class[] { String.class, String.class });
610                     currentPerms.add((Permission) c.newInstance(
611                       new Object[] { target, action }));
612                   }
613                 catch (Exception x)
614                   {
615                     error(url, in, x.toString());
616                   }
617               }
618             tok = in.nextToken();
619             if (tok != ';' && tok != ',')
620               error(url, in, "expecting ';' or ','");
621           }
622       }
623   }
624 
625   /**
626    * Expand all instances of <code>"${property-name}"</code> into
627    * <code>System.getProperty("property-name")</code>.
628    */
629   private static String expand(final String s)
630   {
631     final StringBuffer result = new StringBuffer();
632     final StringBuffer prop = new StringBuffer();
633     int state = 0;
634     for (int i = 0; i < s.length(); i++)
635       {
636         switch (state)
637           {
638           case 0:
639             if (s.charAt(i) == '$')
640               state = 1;
641             else
642               result.append(s.charAt(i));
643             break;
644           case 1:
645             if (s.charAt(i) == '{')
646               state = 2;
647             else
648               {
649                 state = 0;
650                 result.append('$').append(s.charAt(i));
651               }
652             break;
653           case 2:
654             if (s.charAt(i) == '}')
655               {
656                 String p = prop.toString();
657                 if (p.equals("/"))
658                   p = "file.separator";
659                 p = System.getProperty(p);
660                 if (p == null)
661                   p = "";
662                 result.append(p);
663                 prop.setLength(0);
664                 state = 0;
665               }
666             else
667               prop.append(s.charAt(i));
668             break;
669           }
670       }
671     if (state != 0)
672       result.append('$').append('{').append(prop);
673     return result.toString();
674   }
675 
676   /**
677    * I miss macros.
678    */
679   private static void error(URL base, StreamTokenizer in, String msg)
680     throws IOException
681   {
682     throw new IOException(base+":"+in.lineno()+": "+msg);
683   }
684 }