PageRenderTime 65ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/jdk/src/share/classes/sun/security/tools/PolicyTool.java

https://bitbucket.org/nkabir/jdk-6
Java | 4251 lines | 3528 code | 304 blank | 419 comment | 169 complexity | 0a6ee0120998d09229f408df9771c2d3 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0, BSD-3-Clause-No-Nuclear-License-2014, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
  3. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4. *
  5. * This code is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License version 2 only, as
  7. * published by the Free Software Foundation. Oracle designates this
  8. * particular file as subject to the "Classpath" exception as provided
  9. * by Oracle in the LICENSE file that accompanied this code.
  10. *
  11. * This code is distributed in the hope that it will be useful, but WITHOUT
  12. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14. * version 2 for more details (a copy is included in the LICENSE file that
  15. * accompanied this code).
  16. *
  17. * You should have received a copy of the GNU General Public License version
  18. * 2 along with this work; if not, write to the Free Software Foundation,
  19. * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20. *
  21. * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22. * or visit www.oracle.com if you need additional information or have any
  23. * questions.
  24. */
  25. package sun.security.tools;
  26. import java.io.*;
  27. import java.util.LinkedList;
  28. import java.util.ListIterator;
  29. import java.util.Vector;
  30. import java.util.Enumeration;
  31. import java.net.URL;
  32. import java.net.MalformedURLException;
  33. import java.lang.reflect.*;
  34. import java.text.Collator;
  35. import java.text.MessageFormat;
  36. import sun.misc.BASE64Decoder;
  37. import sun.security.provider.PolicyParser.PermissionEntry;
  38. import sun.security.util.PropertyExpander;
  39. import sun.security.util.PropertyExpander.ExpandException;
  40. import java.awt.*;
  41. import java.awt.event.*;
  42. import java.security.cert.Certificate;
  43. import java.security.cert.CertificateFactory;
  44. import java.security.cert.X509Certificate;
  45. import java.security.cert.CertificateException;
  46. import java.security.*;
  47. import sun.security.provider.*;
  48. import sun.security.util.PolicyUtil;
  49. import javax.security.auth.x500.X500Principal;
  50. import java.util.HashSet;
  51. /**
  52. * PolicyTool may be used by users and administrators to configure the
  53. * overall java security policy (currently stored in the policy file).
  54. * Using PolicyTool administators may add and remove policies from
  55. * the policy file. <p>
  56. *
  57. * @see java.security.Policy
  58. * @since 1.2
  59. */
  60. public class PolicyTool {
  61. // for i18n
  62. static final java.util.ResourceBundle rb =
  63. java.util.ResourceBundle.getBundle("sun.security.util.Resources");
  64. static final Collator collator = Collator.getInstance();
  65. static {
  66. // this is for case insensitive string comparisons
  67. collator.setStrength(Collator.PRIMARY);
  68. };
  69. // anyone can add warnings
  70. Vector<String> warnings;
  71. boolean newWarning = false;
  72. // set to true if policy modified.
  73. // this way upon exit we know if to ask the user to save changes
  74. boolean modified = false;
  75. private static final boolean testing = false;
  76. private static final Class[] TWOPARAMS = { String.class, String.class };
  77. private static final Class[] ONEPARAMS = { String.class };
  78. private static final Class[] NOPARAMS = {};
  79. /*
  80. * All of the policy entries are read in from the
  81. * policy file and stored here. Updates to the policy entries
  82. * using addEntry() and removeEntry() are made here. To ultimately save
  83. * the policy entries back to the policy file, the SavePolicy button
  84. * must be clicked.
  85. **/
  86. private static String policyFileName = null;
  87. private Vector<PolicyEntry> policyEntries = null;
  88. private PolicyParser parser = null;
  89. /* The public key alias information is stored here. */
  90. private KeyStore keyStore = null;
  91. private String keyStoreName = " ";
  92. private String keyStoreType = " ";
  93. private String keyStoreProvider = " ";
  94. private String keyStorePwdURL = " ";
  95. /* standard PKCS11 KeyStore type */
  96. private static final String P11KEYSTORE = "PKCS11";
  97. /* reserved word for PKCS11 KeyStores */
  98. private static final String NONE = "NONE";
  99. /**
  100. * default constructor
  101. */
  102. private PolicyTool() {
  103. policyEntries = new Vector<PolicyEntry>();
  104. parser = new PolicyParser();
  105. warnings = new Vector<String>();
  106. }
  107. /**
  108. * get the PolicyFileName
  109. */
  110. String getPolicyFileName() {
  111. return policyFileName;
  112. }
  113. /**
  114. * set the PolicyFileName
  115. */
  116. void setPolicyFileName(String policyFileName) {
  117. this.policyFileName = policyFileName;
  118. }
  119. /**
  120. * clear keyStore info
  121. */
  122. void clearKeyStoreInfo() {
  123. this.keyStoreName = null;
  124. this.keyStoreType = null;
  125. this.keyStoreProvider = null;
  126. this.keyStorePwdURL = null;
  127. this.keyStore = null;
  128. }
  129. /**
  130. * get the keyStore URL name
  131. */
  132. String getKeyStoreName() {
  133. return keyStoreName;
  134. }
  135. /**
  136. * get the keyStore Type
  137. */
  138. String getKeyStoreType() {
  139. return keyStoreType;
  140. }
  141. /**
  142. * get the keyStore Provider
  143. */
  144. String getKeyStoreProvider() {
  145. return keyStoreProvider;
  146. }
  147. /**
  148. * get the keyStore password URL
  149. */
  150. String getKeyStorePwdURL() {
  151. return keyStorePwdURL;
  152. }
  153. /**
  154. * Open and read a policy file
  155. */
  156. void openPolicy(String filename) throws FileNotFoundException,
  157. PolicyParser.ParsingException,
  158. KeyStoreException,
  159. CertificateException,
  160. InstantiationException,
  161. MalformedURLException,
  162. IOException,
  163. NoSuchAlgorithmException,
  164. IllegalAccessException,
  165. NoSuchMethodException,
  166. UnrecoverableKeyException,
  167. NoSuchProviderException,
  168. ClassNotFoundException,
  169. PropertyExpander.ExpandException,
  170. InvocationTargetException {
  171. newWarning = false;
  172. // start fresh - blow away the current state
  173. policyEntries = new Vector<PolicyEntry>();
  174. parser = new PolicyParser();
  175. warnings = new Vector<String>();
  176. setPolicyFileName(null);
  177. clearKeyStoreInfo();
  178. // see if user is opening a NEW policy file
  179. if (filename == null) {
  180. modified = false;
  181. return;
  182. }
  183. // Read in the policy entries from the file and
  184. // populate the parser vector table. The parser vector
  185. // table only holds the entries as strings, so it only
  186. // guarantees that the policies are syntactically
  187. // correct.
  188. setPolicyFileName(filename);
  189. parser.read(new FileReader(filename));
  190. // open the keystore
  191. openKeyStore(parser.getKeyStoreUrl(), parser.getKeyStoreType(),
  192. parser.getKeyStoreProvider(), parser.getStorePassURL());
  193. // Update the local vector with the same policy entries.
  194. // This guarantees that the policy entries are not only
  195. // syntactically correct, but semantically valid as well.
  196. Enumeration<PolicyParser.GrantEntry> enum_ = parser.grantElements();
  197. while (enum_.hasMoreElements()) {
  198. PolicyParser.GrantEntry ge = enum_.nextElement();
  199. // see if all the signers have public keys
  200. if (ge.signedBy != null) {
  201. String signers[] = parseSigners(ge.signedBy);
  202. for (int i = 0; i < signers.length; i++) {
  203. PublicKey pubKey = getPublicKeyAlias(signers[i]);
  204. if (pubKey == null) {
  205. newWarning = true;
  206. MessageFormat form = new MessageFormat(rb.getString
  207. ("Warning: A public key for alias " +
  208. "'signers[i]' does not exist. " +
  209. "Make sure a KeyStore is properly configured."));
  210. Object[] source = {signers[i]};
  211. warnings.addElement(form.format(source));
  212. }
  213. }
  214. }
  215. // check to see if the Principals are valid
  216. ListIterator<PolicyParser.PrincipalEntry> prinList =
  217. ge.principals.listIterator(0);
  218. while (prinList.hasNext()) {
  219. PolicyParser.PrincipalEntry pe = prinList.next();
  220. try {
  221. verifyPrincipal(pe.getPrincipalClass(),
  222. pe.getPrincipalName());
  223. } catch (ClassNotFoundException fnfe) {
  224. newWarning = true;
  225. MessageFormat form = new MessageFormat(rb.getString
  226. ("Warning: Class not found: class"));
  227. Object[] source = {pe.getPrincipalClass()};
  228. warnings.addElement(form.format(source));
  229. }
  230. }
  231. // check to see if the Permissions are valid
  232. Enumeration<PolicyParser.PermissionEntry> perms =
  233. ge.permissionElements();
  234. while (perms.hasMoreElements()) {
  235. PolicyParser.PermissionEntry pe = perms.nextElement();
  236. try {
  237. verifyPermission(pe.permission, pe.name, pe.action);
  238. } catch (ClassNotFoundException fnfe) {
  239. newWarning = true;
  240. MessageFormat form = new MessageFormat(rb.getString
  241. ("Warning: Class not found: class"));
  242. Object[] source = {pe.permission};
  243. warnings.addElement(form.format(source));
  244. } catch (InvocationTargetException ite) {
  245. newWarning = true;
  246. MessageFormat form = new MessageFormat(rb.getString
  247. ("Warning: Invalid argument(s) for constructor: arg"));
  248. Object[] source = {pe.permission};
  249. warnings.addElement(form.format(source));
  250. }
  251. // see if all the permission signers have public keys
  252. if (pe.signedBy != null) {
  253. String signers[] = parseSigners(pe.signedBy);
  254. for (int i = 0; i < signers.length; i++) {
  255. PublicKey pubKey = getPublicKeyAlias(signers[i]);
  256. if (pubKey == null) {
  257. newWarning = true;
  258. MessageFormat form = new MessageFormat(rb.getString
  259. ("Warning: A public key for alias " +
  260. "'signers[i]' does not exist. " +
  261. "Make sure a KeyStore is properly configured."));
  262. Object[] source = {signers[i]};
  263. warnings.addElement(form.format(source));
  264. }
  265. }
  266. }
  267. }
  268. PolicyEntry pEntry = new PolicyEntry(this, ge);
  269. policyEntries.addElement(pEntry);
  270. }
  271. // just read in the policy -- nothing has been modified yet
  272. modified = false;
  273. }
  274. /**
  275. * Save a policy to a file
  276. */
  277. void savePolicy(String filename)
  278. throws FileNotFoundException, IOException {
  279. // save the policy entries to a file
  280. parser.setKeyStoreUrl(keyStoreName);
  281. parser.setKeyStoreType(keyStoreType);
  282. parser.setKeyStoreProvider(keyStoreProvider);
  283. parser.setStorePassURL(keyStorePwdURL);
  284. parser.write(new FileWriter(filename));
  285. modified = false;
  286. }
  287. /**
  288. * Open the KeyStore
  289. */
  290. void openKeyStore(String name,
  291. String type,
  292. String provider,
  293. String pwdURL) throws KeyStoreException,
  294. NoSuchAlgorithmException,
  295. UnrecoverableKeyException,
  296. IOException,
  297. CertificateException,
  298. NoSuchProviderException,
  299. ExpandException {
  300. if (name == null && type == null &&
  301. provider == null && pwdURL == null) {
  302. // policy did not specify a keystore during open
  303. // or use wants to reset keystore values
  304. this.keyStoreName = null;
  305. this.keyStoreType = null;
  306. this.keyStoreProvider = null;
  307. this.keyStorePwdURL = null;
  308. // caller will set (tool.modified = true) if appropriate
  309. return;
  310. }
  311. URL policyURL = null;
  312. if (policyFileName != null) {
  313. File pfile = new File(policyFileName);
  314. policyURL = new URL("file:" + pfile.getCanonicalPath());
  315. }
  316. // although PolicyUtil.getKeyStore may properly handle
  317. // defaults and property expansion, we do it here so that
  318. // if the call is successful, we can set the proper values
  319. // (PolicyUtil.getKeyStore does not return expanded values)
  320. if (name != null && name.length() > 0) {
  321. name = PropertyExpander.expand(name).replace
  322. (File.separatorChar, '/');
  323. }
  324. if (type == null || type.length() == 0) {
  325. type = KeyStore.getDefaultType();
  326. }
  327. if (pwdURL != null && pwdURL.length() > 0) {
  328. pwdURL = PropertyExpander.expand(pwdURL).replace
  329. (File.separatorChar, '/');
  330. }
  331. try {
  332. this.keyStore = PolicyUtil.getKeyStore(policyURL,
  333. name,
  334. type,
  335. provider,
  336. pwdURL,
  337. null);
  338. } catch (IOException ioe) {
  339. // copied from sun.security.pkcs11.SunPKCS11
  340. String MSG = "no password provided, and no callback handler " +
  341. "available for retrieving password";
  342. Throwable cause = ioe.getCause();
  343. if (cause != null &&
  344. cause instanceof javax.security.auth.login.LoginException &&
  345. MSG.equals(cause.getMessage())) {
  346. // throw a more friendly exception message
  347. throw new IOException(MSG);
  348. } else {
  349. throw ioe;
  350. }
  351. }
  352. this.keyStoreName = name;
  353. this.keyStoreType = type;
  354. this.keyStoreProvider = provider;
  355. this.keyStorePwdURL = pwdURL;
  356. // caller will set (tool.modified = true)
  357. }
  358. /**
  359. * Add a Grant entry to the overall policy at the specified index.
  360. * A policy entry consists of a CodeSource.
  361. */
  362. boolean addEntry(PolicyEntry pe, int index) {
  363. if (index < 0) {
  364. // new entry -- just add it to the end
  365. policyEntries.addElement(pe);
  366. parser.add(pe.getGrantEntry());
  367. } else {
  368. // existing entry -- replace old one
  369. PolicyEntry origPe = policyEntries.elementAt(index);
  370. parser.replace(origPe.getGrantEntry(), pe.getGrantEntry());
  371. policyEntries.setElementAt(pe, index);
  372. }
  373. return true;
  374. }
  375. /**
  376. * Add a Principal entry to an existing PolicyEntry at the specified index.
  377. * A Principal entry consists of a class, and name.
  378. *
  379. * If the principal already exists, it is not added again.
  380. */
  381. boolean addPrinEntry(PolicyEntry pe,
  382. PolicyParser.PrincipalEntry newPrin,
  383. int index) {
  384. // first add the principal to the Policy Parser entry
  385. PolicyParser.GrantEntry grantEntry = pe.getGrantEntry();
  386. if (grantEntry.contains(newPrin) == true)
  387. return false;
  388. LinkedList<PolicyParser.PrincipalEntry> prinList =
  389. grantEntry.principals;
  390. if (index != -1)
  391. prinList.set(index, newPrin);
  392. else
  393. prinList.add(newPrin);
  394. modified = true;
  395. return true;
  396. }
  397. /**
  398. * Add a Permission entry to an existing PolicyEntry at the specified index.
  399. * A Permission entry consists of a permission, name, and actions.
  400. *
  401. * If the permission already exists, it is not added again.
  402. */
  403. boolean addPermEntry(PolicyEntry pe,
  404. PolicyParser.PermissionEntry newPerm,
  405. int index) {
  406. // first add the permission to the Policy Parser Vector
  407. PolicyParser.GrantEntry grantEntry = pe.getGrantEntry();
  408. if (grantEntry.contains(newPerm) == true)
  409. return false;
  410. Vector<PolicyParser.PermissionEntry> permList =
  411. grantEntry.permissionEntries;
  412. if (index != -1)
  413. permList.setElementAt(newPerm, index);
  414. else
  415. permList.addElement(newPerm);
  416. modified = true;
  417. return true;
  418. }
  419. /**
  420. * Remove a Permission entry from an existing PolicyEntry.
  421. */
  422. boolean removePermEntry(PolicyEntry pe,
  423. PolicyParser.PermissionEntry perm) {
  424. // remove the Permission from the GrantEntry
  425. PolicyParser.GrantEntry ppge = pe.getGrantEntry();
  426. modified = ppge.remove(perm);
  427. return modified;
  428. }
  429. /**
  430. * remove an entry from the overall policy
  431. */
  432. boolean removeEntry(PolicyEntry pe) {
  433. parser.remove(pe.getGrantEntry());
  434. modified = true;
  435. return (policyEntries.removeElement(pe));
  436. }
  437. /**
  438. * retrieve all Policy Entries
  439. */
  440. PolicyEntry[] getEntry() {
  441. if (policyEntries.size() > 0) {
  442. PolicyEntry entries[] = new PolicyEntry[policyEntries.size()];
  443. for (int i = 0; i < policyEntries.size(); i++)
  444. entries[i] = policyEntries.elementAt(i);
  445. return entries;
  446. }
  447. return null;
  448. }
  449. /**
  450. * Retrieve the public key mapped to a particular name.
  451. * If the key has expired, a KeyException is thrown.
  452. */
  453. PublicKey getPublicKeyAlias(String name) throws KeyStoreException {
  454. if (keyStore == null) {
  455. return null;
  456. }
  457. Certificate cert = keyStore.getCertificate(name);
  458. if (cert == null) {
  459. return null;
  460. }
  461. PublicKey pubKey = cert.getPublicKey();
  462. return pubKey;
  463. }
  464. /**
  465. * Retrieve all the alias names stored in the certificate database
  466. */
  467. String[] getPublicKeyAlias() throws KeyStoreException {
  468. int numAliases = 0;
  469. String aliases[] = null;
  470. if (keyStore == null) {
  471. return null;
  472. }
  473. Enumeration<String> enum_ = keyStore.aliases();
  474. // first count the number of elements
  475. while (enum_.hasMoreElements()) {
  476. enum_.nextElement();
  477. numAliases++;
  478. }
  479. if (numAliases > 0) {
  480. // now copy them into an array
  481. aliases = new String[numAliases];
  482. numAliases = 0;
  483. enum_ = keyStore.aliases();
  484. while (enum_.hasMoreElements()) {
  485. aliases[numAliases] = new String(enum_.nextElement());
  486. numAliases++;
  487. }
  488. }
  489. return aliases;
  490. }
  491. /**
  492. * This method parses a single string of signers separated by commas
  493. * ("jordan, duke, pippen") into an array of individual strings.
  494. */
  495. String[] parseSigners(String signedBy) {
  496. String signers[] = null;
  497. int numSigners = 1;
  498. int signedByIndex = 0;
  499. int commaIndex = 0;
  500. int signerNum = 0;
  501. // first pass thru "signedBy" counts the number of signers
  502. while (commaIndex >= 0) {
  503. commaIndex = signedBy.indexOf(',', signedByIndex);
  504. if (commaIndex >= 0) {
  505. numSigners++;
  506. signedByIndex = commaIndex + 1;
  507. }
  508. }
  509. signers = new String[numSigners];
  510. // second pass thru "signedBy" transfers signers to array
  511. commaIndex = 0;
  512. signedByIndex = 0;
  513. while (commaIndex >= 0) {
  514. if ((commaIndex = signedBy.indexOf(',', signedByIndex)) >= 0) {
  515. // transfer signer and ignore trailing part of the string
  516. signers[signerNum] =
  517. signedBy.substring(signedByIndex, commaIndex).trim();
  518. signerNum++;
  519. signedByIndex = commaIndex + 1;
  520. } else {
  521. // we are at the end of the string -- transfer signer
  522. signers[signerNum] = signedBy.substring(signedByIndex).trim();
  523. }
  524. }
  525. return signers;
  526. }
  527. /**
  528. * Check to see if the Principal contents are OK
  529. */
  530. void verifyPrincipal(String type, String name)
  531. throws ClassNotFoundException,
  532. InstantiationException
  533. {
  534. if (type.equals(PolicyParser.PrincipalEntry.WILDCARD_CLASS) ||
  535. type.equals(PolicyParser.REPLACE_NAME)) {
  536. return;
  537. };
  538. Class<?> PRIN = Class.forName("java.security.Principal");
  539. Class<?> pc = Class.forName(type, true,
  540. Thread.currentThread().getContextClassLoader());
  541. if (!PRIN.isAssignableFrom(pc)) {
  542. MessageFormat form = new MessageFormat(rb.getString
  543. ("Illegal Principal Type: type"));
  544. Object[] source = {type};
  545. throw new InstantiationException(form.format(source));
  546. }
  547. if (ToolDialog.X500_PRIN_CLASS.equals(pc.getName())) {
  548. // PolicyParser checks validity of X500Principal name
  549. // - PolicyTool needs to as well so that it doesn't store
  550. // an invalid name that can't be read in later
  551. //
  552. // this can throw an IllegalArgumentException
  553. X500Principal newP = new X500Principal(name);
  554. }
  555. }
  556. /**
  557. * Check to see if the Permission contents are OK
  558. */
  559. void verifyPermission(String type,
  560. String name,
  561. String actions)
  562. throws ClassNotFoundException,
  563. InstantiationException,
  564. IllegalAccessException,
  565. NoSuchMethodException,
  566. InvocationTargetException
  567. {
  568. //XXX we might want to keep a hash of created factories...
  569. Class<?> pc = Class.forName(type, true,
  570. Thread.currentThread().getContextClassLoader());
  571. Constructor<?> c = null;
  572. Vector<String> objects = new Vector<String>(2);
  573. if (name != null) objects.add(name);
  574. if (actions != null) objects.add(actions);
  575. switch (objects.size()) {
  576. case 0:
  577. try {
  578. c = pc.getConstructor(NOPARAMS);
  579. break;
  580. } catch (NoSuchMethodException ex) {
  581. // proceed to the one-param constructor
  582. objects.add(null);
  583. }
  584. case 1:
  585. try {
  586. c = pc.getConstructor(ONEPARAMS);
  587. break;
  588. } catch (NoSuchMethodException ex) {
  589. // proceed to the two-param constructor
  590. objects.add(null);
  591. }
  592. case 2:
  593. c = pc.getConstructor(TWOPARAMS);
  594. break;
  595. }
  596. Object parameters[] = objects.toArray();
  597. Permission p = (Permission)c.newInstance(parameters);
  598. }
  599. /*
  600. * Parse command line arguments.
  601. */
  602. static void parseArgs(String args[]) {
  603. /* parse flags */
  604. int n = 0;
  605. for (n=0; (n < args.length) && args[n].startsWith("-"); n++) {
  606. String flags = args[n];
  607. if (collator.compare(flags, "-file") == 0) {
  608. if (++n == args.length) usage();
  609. policyFileName = args[n];
  610. } else {
  611. MessageFormat form = new MessageFormat(rb.getString
  612. ("Illegal option: option"));
  613. Object[] source = { flags };
  614. System.err.println(form.format(source));
  615. usage();
  616. }
  617. }
  618. }
  619. static void usage() {
  620. System.out.println(rb.getString("Usage: policytool [options]"));
  621. System.out.println();
  622. System.out.println(rb.getString
  623. (" [-file <file>] policy file location"));
  624. System.out.println();
  625. System.exit(1);
  626. }
  627. /**
  628. * run the PolicyTool
  629. */
  630. public static void main(String args[]) {
  631. parseArgs(args);
  632. ToolWindow tw = new ToolWindow(new PolicyTool());
  633. tw.displayToolWindow(args);
  634. }
  635. // split instr to words according to capitalization,
  636. // like, AWTControl -> A W T Control
  637. // this method is for easy pronounciation
  638. static String splitToWords(String instr) {
  639. return instr.replaceAll("([A-Z])", " $1");
  640. }
  641. }
  642. /**
  643. * Each entry in the policy configuration file is represented by a
  644. * PolicyEntry object.
  645. *
  646. * A PolicyEntry is a (CodeSource,Permission) pair. The
  647. * CodeSource contains the (URL, PublicKey) that together identify
  648. * where the Java bytecodes come from and who (if anyone) signed
  649. * them. The URL could refer to localhost. The URL could also be
  650. * null, meaning that this policy entry is given to all comers, as
  651. * long as they match the signer field. The signer could be null,
  652. * meaning the code is not signed.
  653. *
  654. * The Permission contains the (Type, Name, Action) triplet.
  655. *
  656. */
  657. class PolicyEntry {
  658. private CodeSource codesource;
  659. private PolicyTool tool;
  660. private PolicyParser.GrantEntry grantEntry;
  661. private boolean testing = false;
  662. /**
  663. * Create a PolicyEntry object from the information read in
  664. * from a policy file.
  665. */
  666. PolicyEntry(PolicyTool tool, PolicyParser.GrantEntry ge)
  667. throws MalformedURLException, NoSuchMethodException,
  668. ClassNotFoundException, InstantiationException, IllegalAccessException,
  669. InvocationTargetException, CertificateException,
  670. IOException, NoSuchAlgorithmException, UnrecoverableKeyException {
  671. this.tool = tool;
  672. URL location = null;
  673. // construct the CodeSource
  674. if (ge.codeBase != null)
  675. location = new URL(ge.codeBase);
  676. this.codesource = new CodeSource(location,
  677. (java.security.cert.Certificate[]) null);
  678. if (testing) {
  679. System.out.println("Adding Policy Entry:");
  680. System.out.println(" CodeBase = " + location);
  681. System.out.println(" Signers = " + ge.signedBy);
  682. System.out.println(" with " + ge.principals.size() +
  683. " Principals");
  684. }
  685. this.grantEntry = ge;
  686. }
  687. /**
  688. * get the codesource associated with this PolicyEntry
  689. */
  690. CodeSource getCodeSource() {
  691. return codesource;
  692. }
  693. /**
  694. * get the GrantEntry associated with this PolicyEntry
  695. */
  696. PolicyParser.GrantEntry getGrantEntry() {
  697. return grantEntry;
  698. }
  699. /**
  700. * convert the header portion, i.e. codebase, signer, principals, of
  701. * this policy entry into a string
  702. */
  703. String headerToString() {
  704. String pString = principalsToString();
  705. if (pString.length() == 0) {
  706. return codebaseToString();
  707. } else {
  708. return codebaseToString() + ", " + pString;
  709. }
  710. }
  711. /**
  712. * convert the Codebase/signer portion of this policy entry into a string
  713. */
  714. String codebaseToString() {
  715. String stringEntry = new String();
  716. if (grantEntry.codeBase != null &&
  717. grantEntry.codeBase.equals("") == false)
  718. stringEntry = stringEntry.concat
  719. ("CodeBase \"" +
  720. grantEntry.codeBase +
  721. "\"");
  722. if (grantEntry.signedBy != null &&
  723. grantEntry.signedBy.equals("") == false)
  724. stringEntry = ((stringEntry.length() > 0) ?
  725. stringEntry.concat(", SignedBy \"" +
  726. grantEntry.signedBy +
  727. "\"") :
  728. stringEntry.concat("SignedBy \"" +
  729. grantEntry.signedBy +
  730. "\""));
  731. if (stringEntry.length() == 0)
  732. return new String("CodeBase <ALL>");
  733. return stringEntry;
  734. }
  735. /**
  736. * convert the Principals portion of this policy entry into a string
  737. */
  738. String principalsToString() {
  739. String result = "";
  740. if ((grantEntry.principals != null) &&
  741. (!grantEntry.principals.isEmpty())) {
  742. StringBuffer buffer = new StringBuffer(200);
  743. ListIterator<PolicyParser.PrincipalEntry> list =
  744. grantEntry.principals.listIterator();
  745. while (list.hasNext()) {
  746. PolicyParser.PrincipalEntry pppe = list.next();
  747. buffer.append(" Principal " + pppe.getDisplayClass() + " " +
  748. pppe.getDisplayName(true));
  749. if (list.hasNext()) buffer.append(", ");
  750. }
  751. result = buffer.toString();
  752. }
  753. return result;
  754. }
  755. /**
  756. * convert this policy entry into a PolicyParser.PermissionEntry
  757. */
  758. PolicyParser.PermissionEntry toPermissionEntry(Permission perm) {
  759. String actions = null;
  760. // get the actions
  761. if (perm.getActions() != null &&
  762. perm.getActions().trim() != "")
  763. actions = perm.getActions();
  764. PolicyParser.PermissionEntry pe = new PolicyParser.PermissionEntry
  765. (perm.getClass().getName(),
  766. perm.getName(),
  767. actions);
  768. return pe;
  769. }
  770. }
  771. /**
  772. * The main window for the PolicyTool
  773. */
  774. class ToolWindow extends Frame {
  775. // use serialVersionUID from JDK 1.2.2 for interoperability
  776. private static final long serialVersionUID = 5682568601210376777L;
  777. /* external paddings */
  778. public static final Insets TOP_PADDING = new Insets(25,0,0,0);
  779. public static final Insets BOTTOM_PADDING = new Insets(0,0,25,0);
  780. public static final Insets LITE_BOTTOM_PADDING = new Insets(0,0,10,0);
  781. public static final Insets LR_PADDING = new Insets(0,10,0,10);
  782. public static final Insets TOP_BOTTOM_PADDING = new Insets(15, 0, 15, 0);
  783. public static final Insets L_TOP_BOTTOM_PADDING = new Insets(5,10,15,0);
  784. public static final Insets LR_BOTTOM_PADDING = new Insets(0,10,5,10);
  785. public static final Insets L_BOTTOM_PADDING = new Insets(0,10,5,0);
  786. public static final Insets R_BOTTOM_PADDING = new Insets(0,0,5,10);
  787. /* buttons and menus */
  788. public static final String NEW_POLICY_FILE =
  789. PolicyTool.rb.getString("New");
  790. public static final String OPEN_POLICY_FILE =
  791. PolicyTool.rb.getString("Open");
  792. public static final String SAVE_POLICY_FILE =
  793. PolicyTool.rb.getString("Save");
  794. public static final String SAVE_AS_POLICY_FILE =
  795. PolicyTool.rb.getString("Save As");
  796. public static final String VIEW_WARNINGS =
  797. PolicyTool.rb.getString("View Warning Log");
  798. public static final String QUIT =
  799. PolicyTool.rb.getString("Exit");
  800. public static final String ADD_POLICY_ENTRY =
  801. PolicyTool.rb.getString("Add Policy Entry");
  802. public static final String EDIT_POLICY_ENTRY =
  803. PolicyTool.rb.getString("Edit Policy Entry");
  804. public static final String REMOVE_POLICY_ENTRY =
  805. PolicyTool.rb.getString("Remove Policy Entry");
  806. public static final String EDIT_KEYSTORE =
  807. PolicyTool.rb.getString("Edit");
  808. public static final String ADD_PUBKEY_ALIAS =
  809. PolicyTool.rb.getString("Add Public Key Alias");
  810. public static final String REMOVE_PUBKEY_ALIAS =
  811. PolicyTool.rb.getString("Remove Public Key Alias");
  812. /* gridbag index for components in the main window (MW) */
  813. public static final int MW_FILENAME_LABEL = 0;
  814. public static final int MW_FILENAME_TEXTFIELD = 1;
  815. public static final int MW_PANEL = 2;
  816. public static final int MW_ADD_BUTTON = 0;
  817. public static final int MW_EDIT_BUTTON = 1;
  818. public static final int MW_REMOVE_BUTTON = 2;
  819. public static final int MW_POLICY_LIST = 3; // follows MW_PANEL
  820. private PolicyTool tool;
  821. /**
  822. * Constructor
  823. */
  824. ToolWindow(PolicyTool tool) {
  825. this.tool = tool;
  826. }
  827. /**
  828. * Initialize the PolicyTool window with the necessary components
  829. */
  830. private void initWindow() {
  831. // create the top menu bar
  832. MenuBar menuBar = new MenuBar();
  833. // create a File menu
  834. Menu menu = new Menu(PolicyTool.rb.getString("File"));
  835. menu.add(NEW_POLICY_FILE);
  836. menu.add(OPEN_POLICY_FILE);
  837. menu.add(SAVE_POLICY_FILE);
  838. menu.add(SAVE_AS_POLICY_FILE);
  839. menu.add(VIEW_WARNINGS);
  840. menu.add(QUIT);
  841. menu.addActionListener(new FileMenuListener(tool, this));
  842. menuBar.add(menu);
  843. setMenuBar(menuBar);
  844. // create a KeyStore menu
  845. menu = new Menu(PolicyTool.rb.getString("KeyStore"));
  846. menu.add(EDIT_KEYSTORE);
  847. menu.addActionListener(new MainWindowListener(tool, this));
  848. menuBar.add(menu);
  849. setMenuBar(menuBar);
  850. // policy entry listing
  851. Label label = new Label(PolicyTool.rb.getString("Policy File:"));
  852. addNewComponent(this, label, MW_FILENAME_LABEL,
  853. 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH,
  854. TOP_BOTTOM_PADDING);
  855. TextField tf = new TextField(50);
  856. tf.getAccessibleContext().setAccessibleName(
  857. PolicyTool.rb.getString("Policy File:"));
  858. tf.setEditable(false);
  859. addNewComponent(this, tf, MW_FILENAME_TEXTFIELD,
  860. 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH,
  861. TOP_BOTTOM_PADDING);
  862. // add ADD/REMOVE/EDIT buttons in a new panel
  863. Panel panel = new Panel();
  864. panel.setLayout(new GridBagLayout());
  865. Button button = new Button(ADD_POLICY_ENTRY);
  866. button.addActionListener(new MainWindowListener(tool, this));
  867. addNewComponent(panel, button, MW_ADD_BUTTON,
  868. 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH,
  869. LR_PADDING);
  870. button = new Button(EDIT_POLICY_ENTRY);
  871. button.addActionListener(new MainWindowListener(tool, this));
  872. addNewComponent(panel, button, MW_EDIT_BUTTON,
  873. 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH,
  874. LR_PADDING);
  875. button = new Button(REMOVE_POLICY_ENTRY);
  876. button.addActionListener(new MainWindowListener(tool, this));
  877. addNewComponent(panel, button, MW_REMOVE_BUTTON,
  878. 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH,
  879. LR_PADDING);
  880. addNewComponent(this, panel, MW_PANEL,
  881. 0, 2, 2, 1, 0.0, 0.0, GridBagConstraints.BOTH,
  882. BOTTOM_PADDING);
  883. String policyFile = tool.getPolicyFileName();
  884. if (policyFile == null) {
  885. String userHome;
  886. userHome = java.security.AccessController.doPrivileged(
  887. new sun.security.action.GetPropertyAction("user.home"));
  888. policyFile = userHome + File.separatorChar + ".java.policy";
  889. }
  890. try {
  891. // open the policy file
  892. tool.openPolicy(policyFile);
  893. // display the policy entries via the policy list textarea
  894. List list = new List(40, false);
  895. list.addActionListener(new PolicyListListener(tool, this));
  896. PolicyEntry entries[] = tool.getEntry();
  897. if (entries != null) {
  898. for (int i = 0; i < entries.length; i++)
  899. list.add(entries[i].headerToString());
  900. }
  901. TextField newFilename = (TextField)
  902. getComponent(MW_FILENAME_TEXTFIELD);
  903. newFilename.setText(policyFile);
  904. initPolicyList(list);
  905. } catch (FileNotFoundException fnfe) {
  906. // add blank policy listing
  907. List list = new List(40, false);
  908. list.addActionListener(new PolicyListListener(tool, this));
  909. initPolicyList(list);
  910. tool.setPolicyFileName(null);
  911. tool.modified = false;
  912. setVisible(true);
  913. // just add warning
  914. tool.warnings.addElement(fnfe.toString());
  915. } catch (Exception e) {
  916. // add blank policy listing
  917. List list = new List(40, false);
  918. list.addActionListener(new PolicyListListener(tool, this));
  919. initPolicyList(list);
  920. tool.setPolicyFileName(null);
  921. tool.modified = false;
  922. setVisible(true);
  923. // display the error
  924. MessageFormat form = new MessageFormat(PolicyTool.rb.getString
  925. ("Could not open policy file: policyFile: e.toString()"));
  926. Object[] source = {policyFile, e.toString()};
  927. displayErrorDialog(null, form.format(source));
  928. }
  929. }
  930. /**
  931. * Add a component to the PolicyTool window
  932. */
  933. void addNewComponent(Container container, Component component,
  934. int index, int gridx, int gridy, int gridwidth, int gridheight,
  935. double weightx, double weighty, int fill, Insets is) {
  936. // add the component at the specified gridbag index
  937. container.add(component, index);
  938. // set the constraints
  939. GridBagLayout gbl = (GridBagLayout)container.getLayout();
  940. GridBagConstraints gbc = new GridBagConstraints();
  941. gbc.gridx = gridx;
  942. gbc.gridy = gridy;
  943. gbc.gridwidth = gridwidth;
  944. gbc.gridheight = gridheight;
  945. gbc.weightx = weightx;
  946. gbc.weighty = weighty;
  947. gbc.fill = fill;
  948. if (is != null) gbc.insets = is;
  949. gbl.setConstraints(component, gbc);
  950. }
  951. /**
  952. * Add a component to the PolicyTool window without external padding
  953. */
  954. void addNewComponent(Container container, Component component,
  955. int index, int gridx, int gridy, int gridwidth, int gridheight,
  956. double weightx, double weighty, int fill) {
  957. // delegate with "null" external padding
  958. addNewComponent(container, component, index, gridx, gridy,
  959. gridwidth, gridheight, weightx, weighty,
  960. fill, null);
  961. }
  962. /**
  963. * Init the policy_entry_list TEXTAREA component in the
  964. * PolicyTool window
  965. */
  966. void initPolicyList(List policyList) {
  967. // add the policy list to the window
  968. addNewComponent(this, policyList, MW_POLICY_LIST,
  969. 0, 3, 2, 1, 1.0, 1.0, GridBagConstraints.BOTH);
  970. }
  971. /**
  972. * Replace the policy_entry_list TEXTAREA component in the
  973. * PolicyTool window with an updated one.
  974. */
  975. void replacePolicyList(List policyList) {
  976. // remove the original list of Policy Entries
  977. // and add the new list of entries
  978. List list = (List)getComponent(MW_POLICY_LIST);
  979. list.removeAll();
  980. String newItems[] = policyList.getItems();
  981. for (int i = 0; i < newItems.length; i++)
  982. list.add(newItems[i]);
  983. }
  984. /**
  985. * display the main PolicyTool window
  986. */
  987. void displayToolWindow(String args[]) {
  988. setTitle(PolicyTool.rb.getString("Policy Tool"));
  989. setResizable(true);
  990. addWindowListener(new ToolWindowListener(this));
  991. setBounds(135, 80, 500, 500);
  992. setLayout(new GridBagLayout());
  993. initWindow();
  994. // display it
  995. setVisible(true);
  996. if (tool.newWarning == true) {
  997. displayStatusDialog(this, PolicyTool.rb.getString
  998. ("Errors have occurred while opening the " +
  999. "policy configuration. View the Warning Log " +
  1000. "for more information."));
  1001. }
  1002. }
  1003. /**
  1004. * displays a dialog box describing an error which occurred.
  1005. */
  1006. void displayErrorDialog(Window w, String error) {
  1007. ToolDialog ed = new ToolDialog
  1008. (PolicyTool.rb.getString("Error"), tool, this, true);
  1009. // find where the PolicyTool gui is
  1010. Point location = ((w == null) ?
  1011. getLocationOnScreen() : w.getLocationOnScreen());
  1012. ed.setBounds(location.x + 50, location.y + 50, 600, 100);
  1013. ed.setLayout(new GridBagLayout());
  1014. Label label = new Label(error);
  1015. addNewComponent(ed, label, 0,
  1016. 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH);
  1017. Button okButton = new Button(PolicyTool.rb.getString("OK"));
  1018. okButton.addActionListener(new ErrorOKButtonListener(ed));
  1019. addNewComponent(ed, okButton, 1,
  1020. 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL);
  1021. ed.pack();
  1022. ed.setVisible(true);
  1023. }
  1024. /**
  1025. * displays a dialog box describing an error which occurred.
  1026. */
  1027. void displayErrorDialog(Window w, Throwable t) {
  1028. if (t instanceof NoDisplayException) {
  1029. return;
  1030. }
  1031. displayErrorDialog(w, t.toString());
  1032. }
  1033. /**
  1034. * displays a dialog box describing the status of an event
  1035. */
  1036. void displayStatusDialog(Window w, String status) {
  1037. ToolDialog sd = new ToolDialog
  1038. (PolicyTool.rb.getString("Status"), tool, this, true);
  1039. // find the location of the PolicyTool gui
  1040. Point location = ((w == null) ?
  1041. getLocationOnScreen() : w.getLocationOnScreen());
  1042. sd.setBounds(location.x + 50, location.y + 50, 500, 100);
  1043. sd.setLayout(new GridBagLayout());
  1044. Label label = new Label(status);
  1045. addNewComponent(sd, label, 0,
  1046. 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH);
  1047. Button okButton = new Button(PolicyTool.rb.getString("OK"));
  1048. okButton.addActionListener(new StatusOKButtonListener(sd));
  1049. addNewComponent(sd, okButton, 1,
  1050. 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL);
  1051. sd.pack();
  1052. sd.setVisible(true);
  1053. }
  1054. /**
  1055. * display the warning log
  1056. */
  1057. void displayWarningLog(Window w) {
  1058. ToolDialog wd = new ToolDialog
  1059. (PolicyTool.rb.getString("Warning"), tool, this, true);
  1060. // find the location of the PolicyTool gui
  1061. Point location = ((w == null) ?
  1062. getLocationOnScreen() : w.getLocationOnScreen());
  1063. wd.setBounds(location.x + 50, location.y + 50, 500, 100);
  1064. wd.setLayout(new GridBagLayout());
  1065. TextArea ta = new TextArea();
  1066. ta.setEditable(false);
  1067. for (int i = 0; i < tool.warnings.size(); i++) {
  1068. ta.append(tool.warnings.elementAt(i));
  1069. ta.append(PolicyTool.rb.getString("\n"));
  1070. }
  1071. addNewComponent(wd, ta, 0,
  1072. 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH,
  1073. BOTTOM_PADDING);
  1074. ta.setFocusable(false);
  1075. Button okButton = new Button(PolicyTool.rb.getString("OK"));
  1076. okButton.addActionListener(new CancelButtonListener(wd));
  1077. addNewComponent(wd, okButton, 1,
  1078. 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL,
  1079. LR_PADDING);
  1080. wd.pack();
  1081. wd.setVisible(true);
  1082. }
  1083. char displayYesNoDialog(Window w, String title, String prompt, String yes, String no) {
  1084. final ToolDialog tw = new ToolDialog
  1085. (title, tool, this, true);
  1086. Point location = ((w == null) ?
  1087. getLocationOnScreen() : w.getLocationOnScreen());
  1088. tw.setBounds(location.x + 75, location.y + 100, 400, 150);
  1089. tw.setLayout(new GridBagLayout());
  1090. TextArea ta = new TextArea(prompt, 10, 50, TextArea.SCROLLBARS_VERTICAL_ONLY);
  1091. ta.setEditable(false);
  1092. addNewComponent(tw, ta, 0,
  1093. 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.BOTH);
  1094. ta.setFocusable(false);
  1095. Panel panel = new Panel();
  1096. panel.setLayout(new GridBagLayout());
  1097. // StringBuffer to store button press. Must be final.
  1098. final StringBuffer chooseResult = new StringBuffer();
  1099. Button button = new Button(yes);
  1100. button.addActionListener(new ActionListener() {
  1101. public void actionPerformed(ActionEvent e) {
  1102. chooseResult.append('Y');
  1103. tw.setVisible(false);
  1104. tw.dispose();
  1105. }
  1106. });
  1107. addNewComponent(panel, button, 0,
  1108. 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL,
  1109. LR_PADDING);
  1110. button = new Button(no);
  1111. button.addActionListener(new ActionListener() {
  1112. public void actionPerformed(ActionEvent e) {
  1113. chooseResult.append('N');
  1114. tw.setVisible(false);
  1115. tw.dispose();
  1116. }
  1117. });
  1118. addNewComponent(panel, button, 1,
  1119. 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL,
  1120. LR_PADDING);
  1121. addNewComponent(tw, panel, 1,
  1122. 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.VERTICAL);
  1123. tw.pack();
  1124. tw.setVisible(true);
  1125. if (chooseResult.length() > 0) {
  1126. return chooseResult.charAt(0);
  1127. } else {
  1128. // I did encounter this once, don't why.
  1129. return 'N';
  1130. }
  1131. }
  1132. }
  1133. /**
  1134. * General dialog window
  1135. */
  1136. class ToolDialog extends Dialog {
  1137. // use serialVersionUID from JDK 1.2.2 for interoperability
  1138. private static final long serialVersionUID = -372244357011301190L;
  1139. /* necessary constants */
  1140. public static final int NOACTION = 0;
  1141. public static final int QUIT = 1;
  1142. public static final int NEW = 2;
  1143. public static final int OPEN = 3;
  1144. public static final String ALL_PERM_CLASS =
  1145. "java.security.AllPermission";
  1146. public static final String FILE_PERM_CLASS =
  1147. "java.io.FilePermission";
  1148. public static final String X500_PRIN_CLASS =
  1149. "javax.security.auth.x500.X500Principal";
  1150. /* popup menus */
  1151. public static final String PERM =
  1152. PolicyTool.rb.getString
  1153. ("Permission: ");
  1154. public static final String PRIN_TYPE =
  1155. PolicyTool.rb.getString("Principal Type:");
  1156. public static final String PRIN_NAME =
  1157. PolicyTool.rb.getString("Principal Name:");
  1158. /* more popu menus */
  1159. public static final String PERM_NAME =
  1160. PolicyTool.rb.getString
  1161. ("Target Name: ");
  1162. /* and more popup menus */
  1163. public static final String PERM_ACTIONS =
  1164. PolicyTool.rb.getString
  1165. ("Actions: ");
  1166. /* gridbag index for display OverWriteFile (OW) components */
  1167. public static final int OW_LABEL = 0;
  1168. public static final int OW_OK_BUTTON = 1;
  1169. public static final int OW_CANCEL_BUTTON = 2;
  1170. /* gridbag index for display PolicyEntry (PE) components */
  1171. public static final int PE_CODEBASE_LABEL = 0;
  1172. public static final int PE_CODEBASE_TEXTFIELD = 1;
  1173. public static final int PE_SIGNEDBY_LABEL = 2;
  1174. public static final int PE_SIGNEDBY_TEXTFIELD = 3;
  1175. public static final int PE_PANEL0 = 4;
  1176. public static final int PE_ADD_PRIN_BUTTON = 0;
  1177. public static final int PE_EDIT_PRIN_BUTTON = 1;
  1178. public static final int PE_REMOVE_PRIN_BUTTON = 2;
  1179. public static final int PE_PRIN_LABEL = 5;
  1180. public static final int PE_PRIN_LIST = 6;
  1181. public static final int PE_PANEL1 = 7;
  1182. public static final int PE_ADD_PERM_BUTTON = 0;
  1183. public static final int PE_EDIT_PERM_BUTTON = 1;
  1184. public static final int PE_REMOVE_PERM_BUTTON = 2;
  1185. public static final int PE_PERM_LIST = 8;
  1186. public static final int PE_PANEL2 = 9;
  1187. public static final int PE_CANCEL_BUTTON = 1;
  1188. public static final int PE_DONE_BUTTON = 0;
  1189. /* the gridbag index for components in the Principal Dialog (PRD) */
  1190. public static final int PRD_DESC_LABEL = 0;
  1191. public static final int PRD_PRIN_CHOICE = 1;
  1192. public static final int PRD_PRIN_TEXTFIELD = 2;
  1193. public static final int PRD_NAME_LABEL = 3;
  1194. public static fi

Large files files are truncated, but you can click here to view the full file