Merge from the pain train
[official-gcc.git] / libjava / gnu / java / security / PolicyFile.java
blobc939dda47c48850807644d899da36382e27898be
1 /* PolicyFile.java -- policy file reader
2 Copyright (C) 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
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.
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. */
38 package gnu.java.security;
40 import java.io.File;
41 import java.io.IOException;
42 import java.io.InputStreamReader;
43 import java.io.StreamTokenizer;
44 import java.lang.reflect.Constructor;
45 import java.net.MalformedURLException;
46 import java.net.URL;
47 import java.security.AccessController;
48 import java.security.CodeSource;
49 import java.security.KeyStore;
50 import java.security.KeyStoreException;
51 import java.security.Permission;
52 import java.security.PermissionCollection;
53 import java.security.Permissions;
54 import java.security.Policy;
55 import java.security.Principal;
56 import java.security.PrivilegedActionException;
57 import java.security.PrivilegedExceptionAction;
58 import java.security.Security;
59 import java.security.UnresolvedPermission;
60 import java.security.cert.Certificate;
61 import java.security.cert.X509Certificate;
62 import java.util.Enumeration;
63 import java.util.HashMap;
64 import java.util.Iterator;
65 import java.util.LinkedList;
66 import java.util.List;
67 import java.util.Map;
68 import java.util.StringTokenizer;
70 /**
71 * An implementation of a {@link java.security.Policy} object whose
72 * permissions are specified by a <em>policy file</em>.
74 * <p>The approximate syntax of policy files is:</p>
76 * <pre>
77 * policyFile ::= keystoreOrGrantEntries ;
79 * keystoreOrGrantEntries ::= keystoreOrGrantEntry |
80 * keystoreOrGrantEntries keystoreOrGrantEntry |
81 * EMPTY ;
83 * keystoreOrGrantEntry ::= keystoreEntry | grantEntry ;
85 * keystoreEntry ::= "keystore" keystoreUrl ';' |
86 * "keystore" keystoreUrl ',' keystoreAlgorithm ';' ;
88 * keystoreUrl ::= URL ;
89 * keystoreAlgorithm ::= STRING ;
91 * grantEntry ::= "grant" domainParameters '{' permissions '}' ';'
93 * domainParameters ::= domainParameter |
94 * domainParameter ',' domainParameters ;
96 * domainParameter ::= "signedBy" signerNames |
97 * "codeBase" codeBaseUrl |
98 * "principal" principalClassName principalName |
99 * "principal" principalName ;
101 * signerNames ::= quotedString ;
102 * codeBaseUrl ::= URL ;
103 * principalClassName ::= STRING ;
104 * principalName ::= quotedString ;
106 * quotedString ::= quoteChar STRING quoteChar ;
107 * quoteChar ::= '"' | '\'';
109 * permissions ::= permission | permissions permission ;
111 * permission ::= "permission" permissionClassName permissionTarget permissionAction |
112 * "permission" permissionClassName permissionTarget |
113 * "permission" permissionClassName;
114 * </pre>
116 * <p>Comments are either form of Java comments. Keystore entries only
117 * affect subsequent grant entries, so if a grant entry preceeds a
118 * keystore entry, that grant entry is not affected by that keystore
119 * entry. Certian instances of <code>${property-name}</code> will be
120 * replaced with <code>System.getProperty("property-name")</code> in
121 * quoted strings.</p>
123 * <p>This class will load the following files when created or
124 * refreshed, in order:</p>
126 * <ol>
127 * <li>The file <code>${java.home}/lib/security/java.policy</code>.</li>
128 * <li>All URLs specified by security properties
129 * <code>"policy.file.<i>n</i>"</code>, for increasing <i>n</i>
130 * starting from 1. The sequence stops at the first undefined
131 * property, so you must set <code>"policy.file.1"</code> if you also
132 * set <code>"policy.file.2"</code>, and so on.</li>
133 * <li>The URL specified by the property
134 * <code>"java.security.policy"</code>.</li>
135 * </ol>
137 * @author Casey Marshall (csm@gnu.org)
138 * @see java.security.Policy
140 public final class PolicyFile extends Policy
143 // Constants and fields.
144 // -------------------------------------------------------------------------
146 private static final boolean DEBUG = true;
147 // Package-private to avoid a trampoline.
148 static void debug(String msg)
150 System.err.print(">> PolicyFile: ");
151 System.err.println(msg);
154 private static void debug(Throwable t)
156 System.err.println(">> PolicyFile");
157 t.printStackTrace(System.err);
160 private static final String DEFAULT_POLICY = System.getProperty("java.home")
161 + System.getProperty("file.separator") + "lib"
162 + System.getProperty("file.separator") + "security"
163 + System.getProperty("file.separator") + "java.policy";
165 private final Map cs2pc;
167 // Constructors.
168 // -------------------------------------------------------------------------
170 public PolicyFile()
172 cs2pc = new HashMap();
173 refresh();
176 // Instance methods.
177 // -------------------------------------------------------------------------
179 public PermissionCollection getPermissions(CodeSource codeSource)
181 Permissions perms = new Permissions();
182 for (Iterator it = cs2pc.entrySet().iterator(); it.hasNext(); )
184 Map.Entry e = (Map.Entry) it.next();
185 CodeSource cs = (CodeSource) e.getKey();
186 if (cs.implies(codeSource))
188 if (DEBUG) debug(cs+" -> "+codeSource);
189 PermissionCollection pc = (PermissionCollection) e.getValue();
190 for (Enumeration ee = pc.elements(); ee.hasMoreElements(); )
192 perms.add((Permission) ee.nextElement());
195 else
196 if (DEBUG) debug(cs+" !-> "+codeSource);
198 if (DEBUG) debug ("returning permissions " + perms + " for " + codeSource);
199 return perms;
202 public void refresh()
204 cs2pc.clear();
205 List policyFiles = new LinkedList();
208 policyFiles.add(new File(DEFAULT_POLICY).toURL());
209 if (DEBUG) debug ("defualt policy is " + DEFAULT_POLICY);
210 policyFiles.addAll((List) AccessController.doPrivileged(
211 new PrivilegedExceptionAction()
213 public Object run() throws Exception
215 LinkedList l = new LinkedList();
216 for (int i = 1; ; i++)
218 String s = Security.getProperty("policy.file."+i);
219 if (DEBUG) debug("policy.file."+i+"="+s);
220 if (s == null)
221 break;
222 l.add(new URL(s));
224 String s = System.getProperty("java.security.policy");
225 if (DEBUG) debug("java.security.policy="+s);
226 if (s != null)
227 l.add(new URL(s));
228 return l;
230 }));
232 catch (PrivilegedActionException pae)
234 if (DEBUG) debug(pae);
236 catch (MalformedURLException mue)
238 if (DEBUG) debug(mue);
240 for (Iterator it = policyFiles.iterator(); it.hasNext(); )
244 URL url = (URL) it.next();
245 parse(url);
247 catch (IOException ioe)
249 if (DEBUG) debug(ioe);
254 public String toString()
256 return super.toString() + " [ " + cs2pc.toString() + " ]";
259 // Own methods.
260 // -------------------------------------------------------------------------
262 private static final int STATE_BEGIN = 0;
263 private static final int STATE_GRANT = 1;
264 private static final int STATE_PERMS = 2;
267 * Parse a policy file, incorporating the permission definitions
268 * described therein.
270 * @param url The URL of the policy file to read.
271 * @throws IOException if an I/O error occurs, or if the policy file
272 * cannot be parsed.
274 private void parse(final URL url) throws IOException
276 if (DEBUG) debug ("reading policy file from " + url);
277 final StreamTokenizer in = new StreamTokenizer(new InputStreamReader(url.openStream()));
278 in.resetSyntax();
279 in.slashSlashComments(true);
280 in.slashStarComments(true);
281 in.wordChars('A', 'Z');
282 in.wordChars('a', 'z');
283 in.wordChars('0', '9');
284 in.wordChars('.', '.');
285 in.wordChars('_', '_');
286 in.wordChars('$', '$');
287 in.whitespaceChars(' ', ' ');
288 in.whitespaceChars('\t', '\t');
289 in.whitespaceChars('\f', '\f');
290 in.whitespaceChars('\n', '\n');
291 in.whitespaceChars('\r', '\r');
292 in.quoteChar('\'');
293 in.quoteChar('"');
295 int tok;
296 int state = STATE_BEGIN;
297 List keystores = new LinkedList();
298 URL currentBase = null;
299 List currentCerts = new LinkedList();
300 Permissions currentPerms = new Permissions();
301 while ((tok = in.nextToken()) != StreamTokenizer.TT_EOF)
303 switch (tok)
305 case '{':
306 if (state != STATE_GRANT)
307 error(url, in, "spurious '{'");
308 state = STATE_PERMS;
309 tok = in.nextToken();
310 break;
311 case '}':
312 if (state != STATE_PERMS)
313 error(url, in, "spurious '}'");
314 state = STATE_BEGIN;
315 currentPerms.setReadOnly();
316 Certificate[] c = null;
317 if (!currentCerts.isEmpty())
318 c = (Certificate[]) currentCerts.toArray(new Certificate[currentCerts.size()]);
319 cs2pc.put(new CodeSource(currentBase, c), currentPerms);
320 currentCerts.clear();
321 currentPerms = new Permissions();
322 currentBase = null;
323 tok = in.nextToken();
324 if (tok != ';')
325 in.pushBack();
326 continue;
328 if (tok != StreamTokenizer.TT_WORD)
330 error(url, in, "expecting word token");
333 // keystore "<keystore-path>" [',' "<keystore-type>"] ';'
334 if (in.sval.equalsIgnoreCase("keystore"))
336 String alg = KeyStore.getDefaultType();
337 tok = in.nextToken();
338 if (tok != '"' && tok != '\'')
339 error(url, in, "expecting key store URL");
340 String store = in.sval;
341 tok = in.nextToken();
342 if (tok == ',')
344 tok = in.nextToken();
345 if (tok != '"' && tok != '\'')
346 error(url, in, "expecting key store type");
347 alg = in.sval;
348 tok = in.nextToken();
350 if (tok != ';')
351 error(url, in, "expecting semicolon");
354 KeyStore keystore = KeyStore.getInstance(alg);
355 keystore.load(new URL(url, store).openStream(), null);
356 keystores.add(keystore);
358 catch (Exception x)
360 error(url, in, x.toString());
363 else if (in.sval.equalsIgnoreCase("grant"))
365 if (state != STATE_BEGIN)
366 error(url, in, "extraneous grant keyword");
367 state = STATE_GRANT;
369 else if (in.sval.equalsIgnoreCase("signedBy"))
371 if (state != STATE_GRANT && state != STATE_PERMS)
372 error(url, in, "spurious 'signedBy'");
373 if (keystores.isEmpty())
374 error(url, in, "'signedBy' with no keystores");
375 tok = in.nextToken();
376 if (tok != '"' && tok != '\'')
377 error(url, in, "expecting signedBy name");
378 StringTokenizer st = new StringTokenizer(in.sval, ",");
379 while (st.hasMoreTokens())
381 String alias = st.nextToken();
382 for (Iterator it = keystores.iterator(); it.hasNext(); )
384 KeyStore keystore = (KeyStore) it.next();
387 if (keystore.isCertificateEntry(alias))
388 currentCerts.add(keystore.getCertificate(alias));
390 catch (KeyStoreException kse)
392 error(url, in, kse.toString());
396 tok = in.nextToken();
397 if (tok != ',')
399 if (state != STATE_GRANT)
400 error(url, in, "spurious ','");
401 in.pushBack();
404 else if (in.sval.equalsIgnoreCase("codeBase"))
406 if (state != STATE_GRANT)
407 error(url, in, "spurious 'codeBase'");
408 tok = in.nextToken();
409 if (tok != '"' && tok != '\'')
410 error(url, in, "expecting code base URL");
411 String base = expand(in.sval);
412 if (File.separatorChar != '/')
413 base = base.replace(File.separatorChar, '/');
416 currentBase = new URL(base);
418 catch (MalformedURLException mue)
420 error(url, in, mue.toString());
422 tok = in.nextToken();
423 if (tok != ',')
424 in.pushBack();
426 else if (in.sval.equalsIgnoreCase("principal"))
428 if (state != STATE_GRANT)
429 error(url, in, "spurious 'principal'");
430 tok = in.nextToken();
431 if (tok == StreamTokenizer.TT_WORD)
433 tok = in.nextToken();
434 if (tok != '"' && tok != '\'')
435 error(url, in, "expecting principal name");
436 String name = in.sval;
437 Principal p = null;
440 Class pclass = Class.forName(in.sval);
441 Constructor c =
442 pclass.getConstructor(new Class[] { String.class });
443 p = (Principal) c.newInstance(new Object[] { name });
445 catch (Exception x)
447 error(url, in, x.toString());
449 for (Iterator it = keystores.iterator(); it.hasNext(); )
451 KeyStore ks = (KeyStore) it.next();
454 for (Enumeration e = ks.aliases(); e.hasMoreElements(); )
456 String alias = (String) e.nextElement();
457 if (ks.isCertificateEntry(alias))
459 Certificate cert = ks.getCertificate(alias);
460 if (!(cert instanceof X509Certificate))
461 continue;
462 if (p.equals(((X509Certificate) cert).getSubjectDN()) ||
463 p.equals(((X509Certificate) cert).getSubjectX500Principal()))
464 currentCerts.add(cert);
468 catch (KeyStoreException kse)
470 error(url, in, kse.toString());
474 else if (tok == '"' || tok == '\'')
476 String alias = in.sval;
477 for (Iterator it = keystores.iterator(); it.hasNext(); )
479 KeyStore ks = (KeyStore) it.next();
482 if (ks.isCertificateEntry(alias))
483 currentCerts.add(ks.getCertificate(alias));
485 catch (KeyStoreException kse)
487 error(url, in, kse.toString());
491 else
492 error(url, in, "expecting principal");
493 tok = in.nextToken();
494 if (tok != ',')
495 in.pushBack();
497 else if (in.sval.equalsIgnoreCase("permission"))
499 if (state != STATE_PERMS)
500 error(url, in, "spurious 'permission'");
501 tok = in.nextToken();
502 if (tok != StreamTokenizer.TT_WORD)
503 error(url, in, "expecting permission class name");
504 String className = in.sval;
505 Class clazz = null;
508 clazz = Class.forName(className);
510 catch (ClassNotFoundException cnfe)
513 tok = in.nextToken();
514 if (tok == ';')
516 if (clazz == null)
518 currentPerms.add(new UnresolvedPermission(className,
519 null, null, (Certificate[]) currentCerts.toArray(new Certificate[0])));
520 continue;
524 currentPerms.add((Permission) clazz.newInstance());
526 catch (Exception x)
528 error(url, in, x.toString());
530 continue;
532 if (tok != '"' && tok != '\'')
533 error(url, in, "expecting permission target");
534 String target = expand(in.sval);
535 tok = in.nextToken();
536 if (tok == ';')
538 if (clazz == null)
540 currentPerms.add(new UnresolvedPermission(className,
541 target, null, (Certificate[]) currentCerts.toArray(new Certificate[0])));
542 continue;
546 Constructor c =
547 clazz.getConstructor(new Class[] { String.class });
548 currentPerms.add((Permission) c.newInstance(
549 new Object[] { target }));
551 catch (Exception x)
553 error(url, in, x.toString());
555 continue;
557 if (tok != ',')
558 error(url, in, "expecting ','");
559 tok = in.nextToken();
560 if (tok == StreamTokenizer.TT_WORD)
562 if (!in.sval.equalsIgnoreCase("signedBy"))
563 error(url, in, "expecting 'signedBy'");
566 Constructor c =
567 clazz.getConstructor(new Class[] { String.class });
568 currentPerms.add((Permission) c.newInstance(
569 new Object[] { target }));
571 catch (Exception x)
573 error(url, in, x.toString());
575 in.pushBack();
576 continue;
578 if (tok != '"' && tok != '\'')
579 error(url, in, "expecting permission action");
580 String action = in.sval;
581 if (clazz == null)
583 currentPerms.add(new UnresolvedPermission(className,
584 target, action, (Certificate[]) currentCerts.toArray(new Certificate[0])));
585 continue;
587 else
591 Constructor c = clazz.getConstructor(
592 new Class[] { String.class, String.class });
593 currentPerms.add((Permission) c.newInstance(
594 new Object[] { target, action }));
596 catch (Exception x)
598 error(url, in, x.toString());
601 tok = in.nextToken();
602 if (tok != ';' && tok != ',')
603 error(url, in, "expecting ';' or ','");
609 * Expand all instances of <code>"${property-name}"</code> into
610 * <code>System.getProperty("property-name")</code>.
612 private static String expand(final String s)
614 final StringBuffer result = new StringBuffer();
615 final StringBuffer prop = new StringBuffer();
616 int state = 0;
617 for (int i = 0; i < s.length(); i++)
619 switch (state)
621 case 0:
622 if (s.charAt(i) == '$')
623 state = 1;
624 else
625 result.append(s.charAt(i));
626 break;
627 case 1:
628 if (s.charAt(i) == '{')
629 state = 2;
630 else
632 state = 0;
633 result.append('$').append(s.charAt(i));
635 break;
636 case 2:
637 if (s.charAt(i) == '}')
639 String p = prop.toString();
640 if (p.equals("/"))
641 p = "file.separator";
642 p = System.getProperty(p);
643 if (p == null)
644 p = "";
645 result.append(p);
646 prop.setLength(0);
647 state = 0;
649 else
650 prop.append(s.charAt(i));
651 break;
654 if (state != 0)
655 result.append('$').append('{').append(prop);
656 return result.toString();
660 * I miss macros.
662 private static void error(URL base, StreamTokenizer in, String msg)
663 throws IOException
665 throw new IOException(base+":"+in.lineno()+": "+msg);