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., 51 Franklin Street, Fifth Floor, 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
;
40 import gnu
.classpath
.SystemProperties
;
41 import gnu
.classpath
.debug
.Component
;
42 import gnu
.classpath
.debug
.SystemLogger
;
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
;
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
;
72 import java
.util
.StringTokenizer
;
73 import java
.util
.logging
.Logger
;
76 * An implementation of a {@link java.security.Policy} object whose
77 * permissions are specified by a <em>policy file</em>.
79 * <p>The approximate syntax of policy files is:</p>
82 * policyFile ::= keystoreOrGrantEntries ;
84 * keystoreOrGrantEntries ::= keystoreOrGrantEntry |
85 * keystoreOrGrantEntries keystoreOrGrantEntry |
88 * keystoreOrGrantEntry ::= keystoreEntry | grantEntry ;
90 * keystoreEntry ::= "keystore" keystoreUrl ';' |
91 * "keystore" keystoreUrl ',' keystoreAlgorithm ';' ;
93 * keystoreUrl ::= URL ;
94 * keystoreAlgorithm ::= STRING ;
96 * grantEntry ::= "grant" domainParameters '{' permissions '}' ';'
98 * domainParameters ::= domainParameter |
99 * domainParameter ',' domainParameters ;
101 * domainParameter ::= "signedBy" signerNames |
102 * "codeBase" codeBaseUrl |
103 * "principal" principalClassName principalName |
104 * "principal" principalName ;
106 * signerNames ::= quotedString ;
107 * codeBaseUrl ::= URL ;
108 * principalClassName ::= STRING ;
109 * principalName ::= quotedString ;
111 * quotedString ::= quoteChar STRING quoteChar ;
112 * quoteChar ::= '"' | '\'';
114 * permissions ::= permission | permissions permission ;
116 * permission ::= "permission" permissionClassName permissionTarget permissionAction |
117 * "permission" permissionClassName permissionTarget |
118 * "permission" permissionClassName;
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>
128 * <p>This class will load the following files when created or
129 * refreshed, in order:</p>
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>
142 * @author Casey Marshall (csm@gnu.org)
143 * @see java.security.Policy
145 public final class PolicyFile
extends Policy
148 // Constants and fields.
149 // -------------------------------------------------------------------------
151 private static final Logger logger
= SystemLogger
.SYSTEM
;
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";
162 private final Map cs2pc
;
165 // -------------------------------------------------------------------------
169 cs2pc
= new HashMap();
174 // -------------------------------------------------------------------------
176 public PermissionCollection
getPermissions(CodeSource codeSource
)
178 Permissions perms
= new Permissions();
179 for (Iterator it
= cs2pc
.entrySet().iterator(); it
.hasNext(); )
181 Map
.Entry e
= (Map
.Entry
) it
.next();
182 CodeSource cs
= (CodeSource
) e
.getKey();
183 if (cs
.implies(codeSource
))
185 logger
.log (Component
.POLICY
, "{0} -> {1}", new Object
[]
187 PermissionCollection pc
= (PermissionCollection
) e
.getValue();
188 for (Enumeration ee
= pc
.elements(); ee
.hasMoreElements(); )
190 perms
.add((Permission
) ee
.nextElement());
194 logger
.log (Component
.POLICY
, "{0} !-> {1}", new Object
[]
197 logger
.log (Component
.POLICY
, "returning permissions {0} for {1}",
198 new Object
[] { perms
, codeSource
});
202 public void refresh()
205 final List policyFiles
= new LinkedList();
208 policyFiles
.add (new File (DEFAULT_POLICY
).toURL());
209 policyFiles
.add (new File (DEFAULT_USER_POLICY
).toURL ());
211 AccessController
.doPrivileged(
212 new PrivilegedExceptionAction()
214 public Object
run() throws Exception
216 String allow
= Security
.getProperty ("policy.allowSystemProperty");
217 if (allow
== null || Boolean
.getBoolean (allow
))
219 String s
= SystemProperties
.getProperty ("java.security.policy");
220 logger
.log (Component
.POLICY
, "java.security.policy={0}", s
);
223 boolean only
= s
.startsWith ("=");
226 policyFiles
.clear ();
227 policyFiles
.add (new URL (s
));
232 for (int i
= 1; ; i
++)
234 String pname
= "policy.url." + i
;
235 String s
= Security
.getProperty (pname
);
236 logger
.log (Component
.POLICY
, "{0}={1}", new Object
[]
240 policyFiles
.add (new URL (s
));
246 catch (PrivilegedActionException pae
)
248 logger
.log (Component
.POLICY
, "reading policy properties", pae
);
250 catch (MalformedURLException mue
)
252 logger
.log (Component
.POLICY
, "setting default policies", mue
);
255 logger
.log (Component
.POLICY
, "building policy from URLs {0}",
257 for (Iterator it
= policyFiles
.iterator(); it
.hasNext(); )
261 URL url
= (URL
) it
.next();
264 catch (IOException ioe
)
266 logger
.log (Component
.POLICY
, "reading policy", ioe
);
271 public String
toString()
273 return super.toString() + " [ " + cs2pc
.toString() + " ]";
277 // -------------------------------------------------------------------------
279 private static final int STATE_BEGIN
= 0;
280 private static final int STATE_GRANT
= 1;
281 private static final int STATE_PERMS
= 2;
284 * Parse a policy file, incorporating the permission definitions
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
291 private void parse(final URL url
) throws IOException
293 logger
.log (Component
.POLICY
, "reading policy file from {0}", url
);
294 final StreamTokenizer in
= new StreamTokenizer(new InputStreamReader(url
.openStream()));
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');
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
)
323 if (state
!= STATE_GRANT
)
324 error(url
, in
, "spurious '{'");
326 tok
= in
.nextToken();
329 if (state
!= STATE_PERMS
)
330 error(url
, in
, "spurious '}'");
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();
340 tok
= in
.nextToken();
345 if (tok
!= StreamTokenizer
.TT_WORD
)
347 error(url
, in
, "expecting word token");
350 // keystore "<keystore-path>" [',' "<keystore-type>"] ';'
351 if (in
.sval
.equalsIgnoreCase("keystore"))
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();
361 tok
= in
.nextToken();
362 if (tok
!= '"' && tok
!= '\'')
363 error(url
, in
, "expecting key store type");
365 tok
= in
.nextToken();
368 error(url
, in
, "expecting semicolon");
371 KeyStore keystore
= KeyStore
.getInstance(alg
);
372 keystore
.load(new URL(url
, store
).openStream(), null);
373 keystores
.add(keystore
);
377 error(url
, in
, x
.toString());
380 else if (in
.sval
.equalsIgnoreCase("grant"))
382 if (state
!= STATE_BEGIN
)
383 error(url
, in
, "extraneous grant keyword");
386 else if (in
.sval
.equalsIgnoreCase("signedBy"))
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())
398 String alias
= st
.nextToken();
399 for (Iterator it
= keystores
.iterator(); it
.hasNext(); )
401 KeyStore keystore
= (KeyStore
) it
.next();
404 if (keystore
.isCertificateEntry(alias
))
405 currentCerts
.add(keystore
.getCertificate(alias
));
407 catch (KeyStoreException kse
)
409 error(url
, in
, kse
.toString());
413 tok
= in
.nextToken();
416 if (state
!= STATE_GRANT
)
417 error(url
, in
, "spurious ','");
421 else if (in
.sval
.equalsIgnoreCase("codeBase"))
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
, '/');
433 currentBase
= new URL(base
);
435 catch (MalformedURLException mue
)
437 error(url
, in
, mue
.toString());
439 tok
= in
.nextToken();
443 else if (in
.sval
.equalsIgnoreCase("principal"))
445 if (state
!= STATE_GRANT
)
446 error(url
, in
, "spurious 'principal'");
447 tok
= in
.nextToken();
448 if (tok
== StreamTokenizer
.TT_WORD
)
450 tok
= in
.nextToken();
451 if (tok
!= '"' && tok
!= '\'')
452 error(url
, in
, "expecting principal name");
453 String name
= in
.sval
;
457 Class pclass
= Class
.forName(in
.sval
);
459 pclass
.getConstructor(new Class
[] { String
.class });
460 p
= (Principal
) c
.newInstance(new Object
[] { name
});
464 error(url
, in
, x
.toString());
466 for (Iterator it
= keystores
.iterator(); it
.hasNext(); )
468 KeyStore ks
= (KeyStore
) it
.next();
471 for (Enumeration e
= ks
.aliases(); e
.hasMoreElements(); )
473 String alias
= (String
) e
.nextElement();
474 if (ks
.isCertificateEntry(alias
))
476 Certificate cert
= ks
.getCertificate(alias
);
477 if (!(cert
instanceof X509Certificate
))
479 if (p
.equals(((X509Certificate
) cert
).getSubjectDN()) ||
480 p
.equals(((X509Certificate
) cert
).getSubjectX500Principal()))
481 currentCerts
.add(cert
);
485 catch (KeyStoreException kse
)
487 error(url
, in
, kse
.toString());
491 else if (tok
== '"' || tok
== '\'')
493 String alias
= in
.sval
;
494 for (Iterator it
= keystores
.iterator(); it
.hasNext(); )
496 KeyStore ks
= (KeyStore
) it
.next();
499 if (ks
.isCertificateEntry(alias
))
500 currentCerts
.add(ks
.getCertificate(alias
));
502 catch (KeyStoreException kse
)
504 error(url
, in
, kse
.toString());
509 error(url
, in
, "expecting principal");
510 tok
= in
.nextToken();
514 else if (in
.sval
.equalsIgnoreCase("permission"))
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
;
525 clazz
= Class
.forName(className
);
527 catch (ClassNotFoundException cnfe
)
530 tok
= in
.nextToken();
535 currentPerms
.add(new UnresolvedPermission(className
,
536 null, null, (Certificate
[]) currentCerts
.toArray(new Certificate
[currentCerts
.size()])));
541 currentPerms
.add((Permission
) clazz
.newInstance());
545 error(url
, in
, x
.toString());
549 if (tok
!= '"' && tok
!= '\'')
550 error(url
, in
, "expecting permission target");
551 String target
= expand(in
.sval
);
552 tok
= in
.nextToken();
557 currentPerms
.add(new UnresolvedPermission(className
,
558 target
, null, (Certificate
[]) currentCerts
.toArray(new Certificate
[currentCerts
.size()])));
564 clazz
.getConstructor(new Class
[] { String
.class });
565 currentPerms
.add((Permission
) c
.newInstance(
566 new Object
[] { target
}));
570 error(url
, in
, x
.toString());
575 error(url
, in
, "expecting ','");
576 tok
= in
.nextToken();
577 if (tok
== StreamTokenizer
.TT_WORD
)
579 if (!in
.sval
.equalsIgnoreCase("signedBy"))
580 error(url
, in
, "expecting 'signedBy'");
584 clazz
.getConstructor(new Class
[] { String
.class });
585 currentPerms
.add((Permission
) c
.newInstance(
586 new Object
[] { target
}));
590 error(url
, in
, x
.toString());
595 if (tok
!= '"' && tok
!= '\'')
596 error(url
, in
, "expecting permission action");
597 String action
= in
.sval
;
600 currentPerms
.add(new UnresolvedPermission(className
,
601 target
, action
, (Certificate
[]) currentCerts
.toArray(new Certificate
[currentCerts
.size()])));
608 Constructor c
= clazz
.getConstructor(
609 new Class
[] { String
.class, String
.class });
610 currentPerms
.add((Permission
) c
.newInstance(
611 new Object
[] { target
, action
}));
615 error(url
, in
, x
.toString());
618 tok
= in
.nextToken();
619 if (tok
!= ';' && tok
!= ',')
620 error(url
, in
, "expecting ';' or ','");
626 * Expand all instances of <code>"${property-name}"</code> into
627 * <code>System.getProperty("property-name")</code>.
629 private static String
expand(final String s
)
631 final StringBuffer result
= new StringBuffer();
632 final StringBuffer prop
= new StringBuffer();
634 for (int i
= 0; i
< s
.length(); i
++)
639 if (s
.charAt(i
) == '$')
642 result
.append(s
.charAt(i
));
645 if (s
.charAt(i
) == '{')
650 result
.append('$').append(s
.charAt(i
));
654 if (s
.charAt(i
) == '}')
656 String p
= prop
.toString();
658 p
= "file.separator";
659 p
= System
.getProperty(p
);
667 prop
.append(s
.charAt(i
));
672 result
.append('$').append('{').append(prop
);
673 return result
.toString();
679 private static void error(URL base
, StreamTokenizer in
, String msg
)
682 throw new IOException(base
+":"+in
.lineno()+": "+msg
);