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)
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
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
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
;
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
;
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
;
68 import java
.util
.StringTokenizer
;
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>
77 * policyFile ::= keystoreOrGrantEntries ;
79 * keystoreOrGrantEntries ::= keystoreOrGrantEntry |
80 * keystoreOrGrantEntries keystoreOrGrantEntry |
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;
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>
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>
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
;
168 // -------------------------------------------------------------------------
172 cs2pc
= new HashMap();
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());
196 if (DEBUG
) debug(cs
+" !-> "+codeSource
);
198 if (DEBUG
) debug ("returning permissions " + perms
+ " for " + codeSource
);
202 public void refresh()
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
);
224 String s
= System
.getProperty("java.security.policy");
225 if (DEBUG
) debug("java.security.policy="+s
);
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();
247 catch (IOException ioe
)
249 if (DEBUG
) debug(ioe
);
254 public String
toString()
256 return super.toString() + " [ " + cs2pc
.toString() + " ]";
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
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
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()));
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');
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
)
306 if (state
!= STATE_GRANT
)
307 error(url
, in
, "spurious '{'");
309 tok
= in
.nextToken();
312 if (state
!= STATE_PERMS
)
313 error(url
, in
, "spurious '}'");
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();
323 tok
= in
.nextToken();
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();
344 tok
= in
.nextToken();
345 if (tok
!= '"' && tok
!= '\'')
346 error(url
, in
, "expecting key store type");
348 tok
= in
.nextToken();
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
);
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");
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();
399 if (state
!= STATE_GRANT
)
400 error(url
, in
, "spurious ','");
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();
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
;
440 Class pclass
= Class
.forName(in
.sval
);
442 pclass
.getConstructor(new Class
[] { String
.class });
443 p
= (Principal
) c
.newInstance(new Object
[] { name
});
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
))
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());
492 error(url
, in
, "expecting principal");
493 tok
= in
.nextToken();
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
;
508 clazz
= Class
.forName(className
);
510 catch (ClassNotFoundException cnfe
)
513 tok
= in
.nextToken();
518 currentPerms
.add(new UnresolvedPermission(className
,
519 null, null, (Certificate
[]) currentCerts
.toArray(new Certificate
[0])));
524 currentPerms
.add((Permission
) clazz
.newInstance());
528 error(url
, in
, x
.toString());
532 if (tok
!= '"' && tok
!= '\'')
533 error(url
, in
, "expecting permission target");
534 String target
= expand(in
.sval
);
535 tok
= in
.nextToken();
540 currentPerms
.add(new UnresolvedPermission(className
,
541 target
, null, (Certificate
[]) currentCerts
.toArray(new Certificate
[0])));
547 clazz
.getConstructor(new Class
[] { String
.class });
548 currentPerms
.add((Permission
) c
.newInstance(
549 new Object
[] { target
}));
553 error(url
, in
, x
.toString());
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'");
567 clazz
.getConstructor(new Class
[] { String
.class });
568 currentPerms
.add((Permission
) c
.newInstance(
569 new Object
[] { target
}));
573 error(url
, in
, x
.toString());
578 if (tok
!= '"' && tok
!= '\'')
579 error(url
, in
, "expecting permission action");
580 String action
= in
.sval
;
583 currentPerms
.add(new UnresolvedPermission(className
,
584 target
, action
, (Certificate
[]) currentCerts
.toArray(new Certificate
[0])));
591 Constructor c
= clazz
.getConstructor(
592 new Class
[] { String
.class, String
.class });
593 currentPerms
.add((Permission
) c
.newInstance(
594 new Object
[] { target
, action
}));
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();
617 for (int i
= 0; i
< s
.length(); i
++)
622 if (s
.charAt(i
) == '$')
625 result
.append(s
.charAt(i
));
628 if (s
.charAt(i
) == '{')
633 result
.append('$').append(s
.charAt(i
));
637 if (s
.charAt(i
) == '}')
639 String p
= prop
.toString();
641 p
= "file.separator";
642 p
= System
.getProperty(p
);
650 prop
.append(s
.charAt(i
));
655 result
.append('$').append('{').append(prop
);
656 return result
.toString();
662 private static void error(URL base
, StreamTokenizer in
, String msg
)
665 throw new IOException(base
+":"+in
.lineno()+": "+msg
);