1 /* 2 * Copyright (c) 1997, 2018, 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 26 package sun.security.provider; 27 28 import java.io.*; 29 import java.security.GeneralSecurityException; 30 import java.security.Principal; 31 import java.util.*; 32 import javax.security.auth.x500.X500Principal; 33 34 import sun.security.util.Debug; 35 import sun.security.util.PropertyExpander; 36 import sun.security.util.LocalizedMessage; 37 38 /** 39 * The policy for a Java runtime (specifying 40 * which permissions are available for code from various principals) 41 * is represented as a separate 42 * persistent configuration. The configuration may be stored as a 43 * flat ASCII file, as a serialized binary file of 44 * the Policy class, or as a database. 45 * 46 * <p>The Java runtime creates one global Policy object, which is used to 47 * represent the static policy configuration file. It is consulted by 48 * a ProtectionDomain when the protection domain initializes its set of 49 * permissions. 50 * 51 * <p>The Policy <code>init</code> method parses the policy 52 * configuration file, and then 53 * populates the Policy object. The Policy object is agnostic in that 54 * it is not involved in making policy decisions. It is merely the 55 * Java runtime representation of the persistent policy configuration 56 * file. 57 * 58 * <p>When a protection domain needs to initialize its set of 59 * permissions, it executes code such as the following 60 * to ask the global Policy object to populate a 61 * Permissions object with the appropriate permissions: 62 * <pre> 63 * policy = Policy.getPolicy(); 64 * Permissions perms = policy.getPermissions(protectiondomain) 65 * </pre> 66 * 67 * <p>The protection domain contains a CodeSource 68 * object, which encapsulates its codebase (URL) and public key attributes. 69 * It also contains the principals associated with the domain. 70 * The Policy object evaluates the global policy in light of who the 71 * principal is and what the code source is and returns an appropriate 72 * Permissions object. 73 * 74 * @author Roland Schemers 75 * @author Ram Marti 76 * 77 * @since 1.2 78 */ 79 80 public class PolicyParser { 81 82 private Vector<GrantEntry> grantEntries; 83 private Map<String, DomainEntry> domainEntries; 84 85 // Convenience variables for parsing 86 private static final Debug debug = Debug.getInstance("parser", 87 "\t[Policy Parser]"); 88 private StreamTokenizer st; 89 private int lookahead; 90 private boolean expandProp = false; 91 private String keyStoreUrlString = null; // unexpanded 92 private String keyStoreType = null; 93 private String keyStoreProvider = null; 94 private String storePassURL = null; 95 96 private String expand(String value) 97 throws PropertyExpander.ExpandException 98 { 99 return expand(value, false); 100 } 101 102 private String expand(String value, boolean encodeURL) 103 throws PropertyExpander.ExpandException 104 { 105 if (!expandProp) { 106 return value; 107 } else { 108 return PropertyExpander.expand(value, encodeURL); 109 } 110 } 111 112 /** 113 * Creates a PolicyParser object. 114 */ 115 116 public PolicyParser() { 117 grantEntries = new Vector<GrantEntry>(); 118 } 119 120 121 public PolicyParser(boolean expandProp) { 122 this(); 123 this.expandProp = expandProp; 124 } 125 126 /** 127 * Reads a policy configuration into the Policy object using a 128 * Reader object. <p> 129 * 130 * @param policy the policy Reader object. 131 * 132 * @exception ParsingException if the policy configuration contains 133 * a syntax error. 134 * 135 * @exception IOException if an error occurs while reading the policy 136 * configuration. 137 */ 138 139 public void read(Reader policy) 140 throws ParsingException, IOException 141 { 142 if (!(policy instanceof BufferedReader)) { 143 policy = new BufferedReader(policy); 144 } 145 146 /** 147 * Configure the stream tokenizer: 148 * Recognize strings between "..." 149 * Don't convert words to lowercase 150 * Recognize both C-style and C++-style comments 151 * Treat end-of-line as white space, not as a token 152 */ 153 st = new StreamTokenizer(policy); 154 155 st.resetSyntax(); 156 st.wordChars('a', 'z'); 157 st.wordChars('A', 'Z'); 158 st.wordChars('.', '.'); 159 st.wordChars('0', '9'); 160 st.wordChars('_', '_'); 161 st.wordChars('$', '$'); 162 st.wordChars(128 + 32, 255); 163 st.whitespaceChars(0, ' '); 164 st.commentChar('/'); 165 st.quoteChar('\''); 166 st.quoteChar('"'); 167 st.lowerCaseMode(false); 168 st.ordinaryChar('/'); 169 st.slashSlashComments(true); 170 st.slashStarComments(true); 171 172 /** 173 * The main parsing loop. The loop is executed once 174 * for each entry in the config file. The entries 175 * are delimited by semicolons. Once we've read in 176 * the information for an entry, go ahead and try to 177 * add it to the policy vector. 178 * 179 */ 180 181 lookahead = st.nextToken(); 182 GrantEntry ge = null; 183 while (lookahead != StreamTokenizer.TT_EOF) { 184 if (peek("grant")) { 185 ge = parseGrantEntry(); 186 // could be null if we couldn't expand a property 187 if (ge != null) 188 add(ge); 189 } else if (peek("keystore") && keyStoreUrlString==null) { 190 // only one keystore entry per policy file, others will be 191 // ignored 192 parseKeyStoreEntry(); 193 } else if (peek("keystorePasswordURL") && storePassURL==null) { 194 // only one keystore passwordURL per policy file, others will be 195 // ignored 196 parseStorePassURL(); 197 } else if (ge == null && keyStoreUrlString == null && 198 storePassURL == null && peek("domain")) { 199 if (domainEntries == null) { 200 domainEntries = new TreeMap<>(); 201 } 202 DomainEntry de = parseDomainEntry(); 203 if (de != null) { 204 String domainName = de.getName(); 205 if (!domainEntries.containsKey(domainName)) { 206 domainEntries.put(domainName, de); 207 } else { 208 LocalizedMessage localizedMsg = new LocalizedMessage( 209 "duplicate.keystore.domain.name"); 210 Object[] source = {domainName}; 211 String msg = "duplicate keystore domain name: " + 212 domainName; 213 throw new ParsingException(msg, localizedMsg, source); 214 } 215 } 216 } else { 217 // error? 218 } 219 match(";"); 220 } 221 222 if (keyStoreUrlString == null && storePassURL != null) { 223 throw new ParsingException(LocalizedMessage.getNonlocalized 224 ("keystorePasswordURL.can.not.be.specified.without.also.specifying.keystore")); 225 } 226 } 227 228 public void add(GrantEntry ge) 229 { 230 grantEntries.addElement(ge); 231 } 232 233 public void replace(GrantEntry origGe, GrantEntry newGe) 234 { 235 grantEntries.setElementAt(newGe, grantEntries.indexOf(origGe)); 236 } 237 238 public boolean remove(GrantEntry ge) 239 { 240 return grantEntries.removeElement(ge); 241 } 242 243 /** 244 * Returns the (possibly expanded) keystore location, or null if the 245 * expansion fails. 246 */ 247 public String getKeyStoreUrl() { 248 try { 249 if (keyStoreUrlString!=null && keyStoreUrlString.length()!=0) { 250 return expand(keyStoreUrlString, true).replace 251 (File.separatorChar, '/'); 252 } 253 } catch (PropertyExpander.ExpandException peee) { 254 if (debug != null) { 255 debug.println(peee.toString()); 256 } 257 return null; 258 } 259 return null; 260 } 261 262 public void setKeyStoreUrl(String url) { 263 keyStoreUrlString = url; 264 } 265 266 public String getKeyStoreType() { 267 return keyStoreType; 268 } 269 270 public void setKeyStoreType(String type) { 271 keyStoreType = type; 272 } 273 274 public String getKeyStoreProvider() { 275 return keyStoreProvider; 276 } 277 278 public void setKeyStoreProvider(String provider) { 279 keyStoreProvider = provider; 280 } 281 282 public String getStorePassURL() { 283 try { 284 if (storePassURL!=null && storePassURL.length()!=0) { 285 return expand(storePassURL, true).replace 286 (File.separatorChar, '/'); 287 } 288 } catch (PropertyExpander.ExpandException peee) { 289 if (debug != null) { 290 debug.println(peee.toString()); 291 } 292 return null; 293 } 294 return null; 295 } 296 297 public void setStorePassURL(String storePassURL) { 298 this.storePassURL = storePassURL; 299 } 300 301 /** 302 * Enumerate all the entries in the global policy object. 303 * This method is used by policy admin tools. The tools 304 * should use the Enumeration methods on the returned object 305 * to fetch the elements sequentially. 306 */ 307 public Enumeration<GrantEntry> grantElements(){ 308 return grantEntries.elements(); 309 } 310 311 public Collection<DomainEntry> getDomainEntries() { 312 return domainEntries.values(); 313 } 314 315 /** 316 * write out the policy 317 */ 318 319 public void write(Writer policy) 320 { 321 PrintWriter out = new PrintWriter(new BufferedWriter(policy)); 322 323 Enumeration<GrantEntry> enum_ = grantElements(); 324 325 out.println("/* AUTOMATICALLY GENERATED ON "+ 326 (new java.util.Date()) + "*/"); 327 out.println("/* DO NOT EDIT */"); 328 out.println(); 329 330 // write the (unexpanded) keystore entry as the first entry of the 331 // policy file 332 if (keyStoreUrlString != null) { 333 writeKeyStoreEntry(out); 334 } 335 if (storePassURL != null) { 336 writeStorePassURL(out); 337 } 338 339 // write "grant" entries 340 while (enum_.hasMoreElements()) { 341 GrantEntry ge = enum_.nextElement(); 342 ge.write(out); 343 out.println(); 344 } 345 out.flush(); 346 } 347 348 /** 349 * parses a keystore entry 350 */ 351 private void parseKeyStoreEntry() throws ParsingException, IOException { 352 match("keystore"); 353 keyStoreUrlString = match("quoted string"); 354 355 // parse keystore type 356 if (!peek(",")) { 357 return; // default type 358 } 359 match(","); 360 361 if (peek("\"")) { 362 keyStoreType = match("quoted string"); 363 } else { 364 throw new ParsingException(st.lineno(), 365 LocalizedMessage.getNonlocalized("expected.keystore.type")); 366 } 367 368 // parse keystore provider 369 if (!peek(",")) { 370 return; // provider optional 371 } 372 match(","); 373 374 if (peek("\"")) { 375 keyStoreProvider = match("quoted string"); 376 } else { 377 throw new ParsingException(st.lineno(), 378 LocalizedMessage.getNonlocalized("expected.keystore.provider")); 379 } 380 } 381 382 private void parseStorePassURL() throws ParsingException, IOException { 383 match("keyStorePasswordURL"); 384 storePassURL = match("quoted string"); 385 } 386 387 /** 388 * writes the (unexpanded) keystore entry 389 */ 390 private void writeKeyStoreEntry(PrintWriter out) { 391 out.print("keystore \""); 392 out.print(keyStoreUrlString); 393 out.print('"'); 394 if (keyStoreType != null && !keyStoreType.isEmpty()) 395 out.print(", \"" + keyStoreType + "\""); 396 if (keyStoreProvider != null && !keyStoreProvider.isEmpty()) 397 out.print(", \"" + keyStoreProvider + "\""); 398 out.println(";"); 399 out.println(); 400 } 401 402 private void writeStorePassURL(PrintWriter out) { 403 out.print("keystorePasswordURL \""); 404 out.print(storePassURL); 405 out.print('"'); 406 out.println(";"); 407 out.println(); 408 } 409 410 /** 411 * parse a Grant entry 412 */ 413 private GrantEntry parseGrantEntry() 414 throws ParsingException, IOException 415 { 416 GrantEntry e = new GrantEntry(); 417 LinkedList<PrincipalEntry> principals = null; 418 boolean ignoreEntry = false; 419 420 match("grant"); 421 422 while(!peek("{")) { 423 424 if (peekAndMatch("Codebase")) { 425 if (e.codeBase != null) 426 throw new ParsingException( 427 st.lineno(), 428 LocalizedMessage.getNonlocalized 429 ("multiple.Codebase.expressions")); 430 e.codeBase = match("quoted string"); 431 peekAndMatch(","); 432 } else if (peekAndMatch("SignedBy")) { 433 if (e.signedBy != null) 434 throw new ParsingException( 435 st.lineno(), 436 LocalizedMessage.getNonlocalized 437 ("multiple.SignedBy.expressions")); 438 e.signedBy = match("quoted string"); 439 440 // verify syntax of the aliases 441 StringTokenizer aliases = new StringTokenizer(e.signedBy, 442 ",", true); 443 int actr = 0; 444 int cctr = 0; 445 while (aliases.hasMoreTokens()) { 446 String alias = aliases.nextToken().trim(); 447 if (alias.equals(",")) 448 cctr++; 449 else if (!alias.isEmpty()) 450 actr++; 451 } 452 if (actr <= cctr) 453 throw new ParsingException( 454 st.lineno(), 455 LocalizedMessage.getNonlocalized 456 ("SignedBy.has.empty.alias")); 457 458 peekAndMatch(","); 459 } else if (peekAndMatch("Principal")) { 460 if (principals == null) { 461 principals = new LinkedList<>(); 462 } 463 464 String principalClass; 465 String principalName; 466 467 if (peek("\"")) { 468 // both the principalClass and principalName 469 // will be replaced later 470 principalClass = PrincipalEntry.REPLACE_NAME; 471 principalName = match("principal type"); 472 } else { 473 // check for principalClass wildcard 474 if (peek("*")) { 475 match("*"); 476 principalClass = PrincipalEntry.WILDCARD_CLASS; 477 } else { 478 principalClass = match("principal type"); 479 } 480 481 // check for principalName wildcard 482 if (peek("*")) { 483 match("*"); 484 principalName = PrincipalEntry.WILDCARD_NAME; 485 } else { 486 principalName = match("quoted string"); 487 } 488 489 // disallow WILDCARD_CLASS && actual name 490 if (principalClass.equals(PrincipalEntry.WILDCARD_CLASS) && 491 !principalName.equals(PrincipalEntry.WILDCARD_NAME)) { 492 if (debug != null) { 493 debug.println("disallowing principal that " + 494 "has WILDCARD class but no WILDCARD name"); 495 } 496 throw new ParsingException 497 (st.lineno(), 498 LocalizedMessage.getNonlocalized 499 ("can.not.specify.Principal.with.a.wildcard.class.without.a.wildcard.name")); 500 } 501 } 502 503 try { 504 principalName = expand(principalName); 505 506 if (principalClass.equals 507 ("javax.security.auth.x500.X500Principal") && 508 !principalName.equals(PrincipalEntry.WILDCARD_NAME)) { 509 510 // 4702543: X500 names with an EmailAddress 511 // were encoded incorrectly. construct a new 512 // X500Principal with correct encoding. 513 514 X500Principal p = new X500Principal 515 ((new X500Principal(principalName)).toString()); 516 principalName = p.getName(); 517 } 518 519 principals.add 520 (new PrincipalEntry(principalClass, principalName)); 521 } catch (PropertyExpander.ExpandException peee) { 522 // ignore the entire policy entry 523 // but continue parsing all the info 524 // so we can get to the next entry 525 if (debug != null) { 526 debug.println("principal name expansion failed: " + 527 principalName); 528 } 529 ignoreEntry = true; 530 } 531 peekAndMatch(","); 532 533 } else { 534 throw new ParsingException(st.lineno(), 535 LocalizedMessage.getNonlocalized 536 ("expected.codeBase.or.SignedBy.or.Principal")); 537 } 538 } 539 540 if (principals != null) e.principals = principals; 541 match("{"); 542 543 while(!peek("}")) { 544 if (peek("Permission")) { 545 try { 546 PermissionEntry pe = parsePermissionEntry(); 547 e.add(pe); 548 } catch (PropertyExpander.ExpandException peee) { 549 // ignore. The add never happened 550 if (debug != null) { 551 debug.println(peee.toString()); 552 } 553 skipEntry(); // BugId 4219343 554 } 555 match(";"); 556 } else { 557 throw new 558 ParsingException(st.lineno(), 559 LocalizedMessage.getNonlocalized 560 ("expected.permission.entry")); 561 } 562 } 563 match("}"); 564 565 try { 566 if (e.signedBy != null) e.signedBy = expand(e.signedBy); 567 if (e.codeBase != null) { 568 e.codeBase = expand(e.codeBase, true).replace 569 (File.separatorChar, '/'); 570 } 571 } catch (PropertyExpander.ExpandException peee) { 572 if (debug != null) { 573 debug.println(peee.toString()); 574 } 575 return null; 576 } 577 578 return (ignoreEntry == true) ? null : e; 579 } 580 581 /** 582 * parse a Permission entry 583 */ 584 private PermissionEntry parsePermissionEntry() 585 throws ParsingException, IOException, PropertyExpander.ExpandException 586 { 587 PermissionEntry e = new PermissionEntry(); 588 589 // Permission 590 match("Permission"); 591 e.permission = match("permission type"); 592 593 if (peek("\"")) { 594 // Permission name 595 e.name = expand(match("quoted string")); 596 } 597 598 if (!peek(",")) { 599 return e; 600 } 601 match(","); 602 603 if (peek("\"")) { 604 e.action = expand(match("quoted string")); 605 if (!peek(",")) { 606 return e; 607 } 608 match(","); 609 } 610 611 if (peekAndMatch("SignedBy")) { 612 e.signedBy = expand(match("quoted string")); 613 } 614 return e; 615 } 616 617 /** 618 * parse a domain entry 619 */ 620 private DomainEntry parseDomainEntry() 621 throws ParsingException, IOException 622 { 623 boolean ignoreEntry = false; 624 DomainEntry domainEntry; 625 String name = null; 626 Map<String, String> properties = new HashMap<>(); 627 628 match("domain"); 629 name = match("domain name"); 630 631 while(!peek("{")) { 632 // get the domain properties 633 properties = parseProperties("{"); 634 } 635 match("{"); 636 domainEntry = new DomainEntry(name, properties); 637 638 while(!peek("}")) { 639 640 match("keystore"); 641 name = match("keystore name"); 642 // get the keystore properties 643 if (!peek("}")) { 644 properties = parseProperties(";"); 645 } 646 match(";"); 647 domainEntry.add(new KeyStoreEntry(name, properties)); 648 } 649 match("}"); 650 651 return (ignoreEntry == true) ? null : domainEntry; 652 } 653 654 /* 655 * Return a collection of domain properties or keystore properties. 656 */ 657 private Map<String, String> parseProperties(String terminator) 658 throws ParsingException, IOException { 659 660 Map<String, String> properties = new HashMap<>(); 661 String key; 662 String value; 663 while (!peek(terminator)) { 664 key = match("property name"); 665 match("="); 666 667 try { 668 value = expand(match("quoted string")); 669 } catch (PropertyExpander.ExpandException peee) { 670 throw new IOException(peee.getLocalizedMessage()); 671 } 672 properties.put(key.toLowerCase(Locale.ENGLISH), value); 673 } 674 675 return properties; 676 } 677 678 private boolean peekAndMatch(String expect) 679 throws ParsingException, IOException 680 { 681 if (peek(expect)) { 682 match(expect); 683 return true; 684 } else { 685 return false; 686 } 687 } 688 689 private boolean peek(String expect) { 690 boolean found = false; 691 692 switch (lookahead) { 693 694 case StreamTokenizer.TT_WORD: 695 if (expect.equalsIgnoreCase(st.sval)) 696 found = true; 697 break; 698 case ',': 699 if (expect.equalsIgnoreCase(",")) 700 found = true; 701 break; 702 case '{': 703 if (expect.equalsIgnoreCase("{")) 704 found = true; 705 break; 706 case '}': 707 if (expect.equalsIgnoreCase("}")) 708 found = true; 709 break; 710 case '"': 711 if (expect.equalsIgnoreCase("\"")) 712 found = true; 713 break; 714 case '*': 715 if (expect.equalsIgnoreCase("*")) 716 found = true; 717 break; 718 case ';': 719 if (expect.equalsIgnoreCase(";")) 720 found = true; 721 break; 722 default: 723 724 } 725 return found; 726 } 727 728 private String match(String expect) 729 throws ParsingException, IOException 730 { 731 String value = null; 732 733 switch (lookahead) { 734 case StreamTokenizer.TT_NUMBER: 735 throw new ParsingException(st.lineno(), expect, 736 LocalizedMessage.getNonlocalized("number.") + 737 String.valueOf(st.nval)); 738 case StreamTokenizer.TT_EOF: 739 LocalizedMessage localizedMsg = new LocalizedMessage 740 ("expected.expect.read.end.of.file."); 741 Object[] source = {expect}; 742 String msg = "expected [" + expect + "], read [end of file]"; 743 throw new ParsingException(msg, localizedMsg, source); 744 case StreamTokenizer.TT_WORD: 745 if (expect.equalsIgnoreCase(st.sval)) { 746 lookahead = st.nextToken(); 747 } else if (expect.equalsIgnoreCase("permission type")) { 748 value = st.sval; 749 lookahead = st.nextToken(); 750 } else if (expect.equalsIgnoreCase("principal type")) { 751 value = st.sval; 752 lookahead = st.nextToken(); 753 } else if (expect.equalsIgnoreCase("domain name") || 754 expect.equalsIgnoreCase("keystore name") || 755 expect.equalsIgnoreCase("property name")) { 756 value = st.sval; 757 lookahead = st.nextToken(); 758 } else { 759 throw new ParsingException(st.lineno(), expect, 760 st.sval); 761 } 762 break; 763 case '"': 764 if (expect.equalsIgnoreCase("quoted string")) { 765 value = st.sval; 766 lookahead = st.nextToken(); 767 } else if (expect.equalsIgnoreCase("permission type")) { 768 value = st.sval; 769 lookahead = st.nextToken(); 770 } else if (expect.equalsIgnoreCase("principal type")) { 771 value = st.sval; 772 lookahead = st.nextToken(); 773 } else { 774 throw new ParsingException(st.lineno(), expect, st.sval); 775 } 776 break; 777 case ',': 778 if (expect.equalsIgnoreCase(",")) 779 lookahead = st.nextToken(); 780 else 781 throw new ParsingException(st.lineno(), expect, ","); 782 break; 783 case '{': 784 if (expect.equalsIgnoreCase("{")) 785 lookahead = st.nextToken(); 786 else 787 throw new ParsingException(st.lineno(), expect, "{"); 788 break; 789 case '}': 790 if (expect.equalsIgnoreCase("}")) 791 lookahead = st.nextToken(); 792 else 793 throw new ParsingException(st.lineno(), expect, "}"); 794 break; 795 case ';': 796 if (expect.equalsIgnoreCase(";")) 797 lookahead = st.nextToken(); 798 else 799 throw new ParsingException(st.lineno(), expect, ";"); 800 break; 801 case '*': 802 if (expect.equalsIgnoreCase("*")) 803 lookahead = st.nextToken(); 804 else 805 throw new ParsingException(st.lineno(), expect, "*"); 806 break; 807 case '=': 808 if (expect.equalsIgnoreCase("=")) 809 lookahead = st.nextToken(); 810 else 811 throw new ParsingException(st.lineno(), expect, "="); 812 break; 813 default: 814 throw new ParsingException(st.lineno(), expect, 815 new String(new char[] {(char)lookahead})); 816 } 817 return value; 818 } 819 820 /** 821 * skip all tokens for this entry leaving the delimiter ";" 822 * in the stream. 823 */ 824 private void skipEntry() throws ParsingException, IOException { 825 while(lookahead != ';') { 826 switch (lookahead) { 827 case StreamTokenizer.TT_NUMBER: 828 throw new ParsingException(st.lineno(), ";", 829 LocalizedMessage.getNonlocalized("number.") + 830 String.valueOf(st.nval)); 831 case StreamTokenizer.TT_EOF: 832 throw new ParsingException(LocalizedMessage.getNonlocalized 833 ("expected.read.end.of.file.")); 834 default: 835 lookahead = st.nextToken(); 836 } 837 } 838 } 839 840 /** 841 * Each grant entry in the policy configuration file is 842 * represented by a GrantEntry object. 843 * 844 * <p> 845 * For example, the entry 846 * <pre> 847 * grant signedBy "Duke" { 848 * permission java.io.FilePermission "/tmp", "read,write"; 849 * }; 850 * 851 * </pre> 852 * is represented internally 853 * <pre> 854 * 855 * pe = new PermissionEntry("java.io.FilePermission", 856 * "/tmp", "read,write"); 857 * 858 * ge = new GrantEntry("Duke", null); 859 * 860 * ge.add(pe); 861 * 862 * </pre> 863 * 864 * @author Roland Schemers 865 * 866 * version 1.19, 05/21/98 867 */ 868 869 public static class GrantEntry { 870 871 public String signedBy; 872 public String codeBase; 873 public LinkedList<PrincipalEntry> principals; 874 public Vector<PermissionEntry> permissionEntries; 875 876 public GrantEntry() { 877 principals = new LinkedList<PrincipalEntry>(); 878 permissionEntries = new Vector<PermissionEntry>(); 879 } 880 881 public GrantEntry(String signedBy, String codeBase) { 882 this.codeBase = codeBase; 883 this.signedBy = signedBy; 884 principals = new LinkedList<PrincipalEntry>(); 885 permissionEntries = new Vector<PermissionEntry>(); 886 } 887 888 public void add(PermissionEntry pe) 889 { 890 permissionEntries.addElement(pe); 891 } 892 893 public boolean remove(PrincipalEntry pe) 894 { 895 return principals.remove(pe); 896 } 897 898 public boolean remove(PermissionEntry pe) 899 { 900 return permissionEntries.removeElement(pe); 901 } 902 903 public boolean contains(PrincipalEntry pe) 904 { 905 return principals.contains(pe); 906 } 907 908 public boolean contains(PermissionEntry pe) 909 { 910 return permissionEntries.contains(pe); 911 } 912 913 /** 914 * Enumerate all the permission entries in this GrantEntry. 915 */ 916 public Enumeration<PermissionEntry> permissionElements(){ 917 return permissionEntries.elements(); 918 } 919 920 921 public void write(PrintWriter out) { 922 out.print("grant"); 923 if (signedBy != null) { 924 out.print(" signedBy \""); 925 out.print(signedBy); 926 out.print('"'); 927 if (codeBase != null) 928 out.print(", "); 929 } 930 if (codeBase != null) { 931 out.print(" codeBase \""); 932 out.print(codeBase); 933 out.print('"'); 934 if (principals != null && principals.size() > 0) 935 out.print(",\n"); 936 } 937 if (principals != null && principals.size() > 0) { 938 Iterator<PrincipalEntry> pli = principals.iterator(); 939 while (pli.hasNext()) { 940 out.print(" "); 941 PrincipalEntry pe = pli.next(); 942 pe.write(out); 943 if (pli.hasNext()) 944 out.print(",\n"); 945 } 946 } 947 out.println(" {"); 948 Enumeration<PermissionEntry> enum_ = permissionEntries.elements(); 949 while (enum_.hasMoreElements()) { 950 PermissionEntry pe = enum_.nextElement(); 951 out.write(" "); 952 pe.write(out); 953 } 954 out.println("};"); 955 } 956 957 public Object clone() { 958 GrantEntry ge = new GrantEntry(); 959 ge.codeBase = this.codeBase; 960 ge.signedBy = this.signedBy; 961 ge.principals = new LinkedList<PrincipalEntry>(this.principals); 962 ge.permissionEntries = 963 new Vector<PermissionEntry>(this.permissionEntries); 964 return ge; 965 } 966 } 967 968 /** 969 * Principal info (class and name) in a grant entry 970 */ 971 public static class PrincipalEntry implements Principal { 972 973 public static final String WILDCARD_CLASS = "WILDCARD_PRINCIPAL_CLASS"; 974 public static final String WILDCARD_NAME = "WILDCARD_PRINCIPAL_NAME"; 975 public static final String REPLACE_NAME = "PolicyParser.REPLACE_NAME"; 976 977 String principalClass; 978 String principalName; 979 980 /** 981 * A PrincipalEntry consists of the Principal class and Principal name. 982 * 983 * @param principalClass the Principal class 984 * @param principalName the Principal name 985 * @throws NullPointerException if principalClass or principalName 986 * are null 987 */ 988 public PrincipalEntry(String principalClass, String principalName) { 989 if (principalClass == null || principalName == null) 990 throw new NullPointerException(LocalizedMessage.getNonlocalized 991 ("null.principalClass.or.principalName")); 992 this.principalClass = principalClass; 993 this.principalName = principalName; 994 } 995 996 boolean isWildcardName() { 997 return principalName.equals(WILDCARD_NAME); 998 } 999 1000 boolean isWildcardClass() { 1001 return principalClass.equals(WILDCARD_CLASS); 1002 } 1003 1004 boolean isReplaceName() { 1005 return principalClass.equals(REPLACE_NAME); 1006 } 1007 1008 public String getPrincipalClass() { 1009 return principalClass; 1010 } 1011 1012 public String getPrincipalName() { 1013 return principalName; 1014 } 1015 1016 public String getDisplayClass() { 1017 if (isWildcardClass()) { 1018 return "*"; 1019 } else if (isReplaceName()) { 1020 return ""; 1021 } 1022 else return principalClass; 1023 } 1024 1025 public String getDisplayName() { 1026 return getDisplayName(false); 1027 } 1028 1029 public String getDisplayName(boolean addQuote) { 1030 if (isWildcardName()) { 1031 return "*"; 1032 } 1033 else { 1034 if (addQuote) return "\"" + principalName + "\""; 1035 else return principalName; 1036 } 1037 } 1038 1039 @Override 1040 public String getName() { 1041 return principalName; 1042 } 1043 1044 @Override 1045 public String toString() { 1046 if (!isReplaceName()) { 1047 return getDisplayClass() + "/" + getDisplayName(); 1048 } else { 1049 return getDisplayName(); 1050 } 1051 } 1052 1053 /** 1054 * Test for equality between the specified object and this object. 1055 * Two PrincipalEntries are equal if their class and name values 1056 * are equal. 1057 * 1058 * @param obj the object to test for equality with this object 1059 * @return true if the objects are equal, false otherwise 1060 */ 1061 @Override 1062 public boolean equals(Object obj) { 1063 if (this == obj) 1064 return true; 1065 1066 if (!(obj instanceof PrincipalEntry)) 1067 return false; 1068 1069 PrincipalEntry that = (PrincipalEntry)obj; 1070 return (principalClass.equals(that.principalClass) && 1071 principalName.equals(that.principalName)); 1072 } 1073 1074 /** 1075 * Return a hashcode for this PrincipalEntry. 1076 * 1077 * @return a hashcode for this PrincipalEntry 1078 */ 1079 @Override 1080 public int hashCode() { 1081 return principalClass.hashCode(); 1082 } 1083 1084 public void write(PrintWriter out) { 1085 out.print("principal " + getDisplayClass() + " " + 1086 getDisplayName(true)); 1087 } 1088 } 1089 1090 /** 1091 * Each permission entry in the policy configuration file is 1092 * represented by a 1093 * PermissionEntry object. 1094 * 1095 * <p> 1096 * For example, the entry 1097 * <pre> 1098 * permission java.io.FilePermission "/tmp", "read,write"; 1099 * </pre> 1100 * is represented internally 1101 * <pre> 1102 * 1103 * pe = new PermissionEntry("java.io.FilePermission", 1104 * "/tmp", "read,write"); 1105 * </pre> 1106 * 1107 * @author Roland Schemers 1108 * 1109 * version 1.19, 05/21/98 1110 */ 1111 1112 public static class PermissionEntry { 1113 1114 public String permission; 1115 public String name; 1116 public String action; 1117 public String signedBy; 1118 1119 public PermissionEntry() { 1120 } 1121 1122 public PermissionEntry(String permission, 1123 String name, 1124 String action) { 1125 this.permission = permission; 1126 this.name = name; 1127 this.action = action; 1128 } 1129 1130 /** 1131 * Calculates a hash code value for the object. Objects 1132 * which are equal will also have the same hashcode. 1133 */ 1134 @Override 1135 public int hashCode() { 1136 int retval = permission.hashCode(); 1137 if (name != null) retval ^= name.hashCode(); 1138 if (action != null) retval ^= action.hashCode(); 1139 return retval; 1140 } 1141 1142 @Override 1143 public boolean equals(Object obj) { 1144 if (obj == this) 1145 return true; 1146 1147 if (! (obj instanceof PermissionEntry)) 1148 return false; 1149 1150 PermissionEntry that = (PermissionEntry) obj; 1151 1152 if (this.permission == null) { 1153 if (that.permission != null) return false; 1154 } else { 1155 if (!this.permission.equals(that.permission)) return false; 1156 } 1157 1158 if (this.name == null) { 1159 if (that.name != null) return false; 1160 } else { 1161 if (!this.name.equals(that.name)) return false; 1162 } 1163 1164 if (this.action == null) { 1165 if (that.action != null) return false; 1166 } else { 1167 if (!this.action.equals(that.action)) return false; 1168 } 1169 1170 if (this.signedBy == null) { 1171 if (that.signedBy != null) return false; 1172 } else { 1173 if (!this.signedBy.equals(that.signedBy)) return false; 1174 } 1175 1176 // everything matched -- the 2 objects are equal 1177 return true; 1178 } 1179 1180 public void write(PrintWriter out) { 1181 out.print("permission "); 1182 out.print(permission); 1183 if (name != null) { 1184 out.print(" \""); 1185 1186 // ATTENTION: regex with double escaping, 1187 // the normal forms look like: 1188 // $name =~ s/\\/\\\\/g; and 1189 // $name =~ s/\"/\\\"/g; 1190 // and then in a java string, it's escaped again 1191 1192 out.print(name.replaceAll("\\\\", "\\\\\\\\").replaceAll("\\\"", "\\\\\\\"")); 1193 out.print('"'); 1194 } 1195 if (action != null) { 1196 out.print(", \""); 1197 out.print(action); 1198 out.print('"'); 1199 } 1200 if (signedBy != null) { 1201 out.print(", signedBy \""); 1202 out.print(signedBy); 1203 out.print('"'); 1204 } 1205 out.println(";"); 1206 } 1207 } 1208 1209 /** 1210 * Each domain entry in the keystore domain configuration file is 1211 * represented by a DomainEntry object. 1212 */ 1213 static class DomainEntry { 1214 private final String name; 1215 private final Map<String, String> properties; 1216 private final Map<String, KeyStoreEntry> entries; 1217 1218 DomainEntry(String name, Map<String, String> properties) { 1219 this.name = name; 1220 this.properties = properties; 1221 entries = new HashMap<>(); 1222 } 1223 1224 String getName() { 1225 return name; 1226 } 1227 1228 Map<String, String> getProperties() { 1229 return properties; 1230 } 1231 1232 Collection<KeyStoreEntry> getEntries() { 1233 return entries.values(); 1234 } 1235 1236 void add(KeyStoreEntry entry) throws ParsingException { 1237 String keystoreName = entry.getName(); 1238 if (!entries.containsKey(keystoreName)) { 1239 entries.put(keystoreName, entry); 1240 } else { 1241 LocalizedMessage localizedMsg = new LocalizedMessage 1242 ("duplicate.keystore.name"); 1243 Object[] source = {keystoreName}; 1244 String msg = "duplicate keystore name: " + keystoreName; 1245 throw new ParsingException(msg, localizedMsg, source); 1246 } 1247 } 1248 1249 @Override 1250 public String toString() { 1251 StringBuilder s = 1252 new StringBuilder("\ndomain ").append(name); 1253 1254 if (properties != null) { 1255 for (Map.Entry<String, String> property : 1256 properties.entrySet()) { 1257 s.append("\n ").append(property.getKey()).append('=') 1258 .append(property.getValue()); 1259 } 1260 } 1261 s.append(" {\n"); 1262 1263 if (entries != null) { 1264 for (KeyStoreEntry entry : entries.values()) { 1265 s.append(entry).append("\n"); 1266 } 1267 } 1268 s.append("}"); 1269 1270 return s.toString(); 1271 } 1272 } 1273 1274 /** 1275 * Each keystore entry in the keystore domain configuration file is 1276 * represented by a KeyStoreEntry object. 1277 */ 1278 1279 static class KeyStoreEntry { 1280 private final String name; 1281 private final Map<String, String> properties; 1282 1283 KeyStoreEntry(String name, Map<String, String> properties) { 1284 this.name = name; 1285 this.properties = properties; 1286 } 1287 1288 String getName() { 1289 return name; 1290 } 1291 1292 Map<String, String> getProperties() { 1293 return properties; 1294 } 1295 1296 @Override 1297 public String toString() { 1298 StringBuilder s = new StringBuilder("\n keystore ").append(name); 1299 if (properties != null) { 1300 for (Map.Entry<String, String> property : 1301 properties.entrySet()) { 1302 s.append("\n ").append(property.getKey()).append('=') 1303 .append(property.getValue()); 1304 } 1305 } 1306 s.append(";"); 1307 1308 return s.toString(); 1309 } 1310 } 1311 1312 public static class ParsingException extends GeneralSecurityException { 1313 1314 private static final long serialVersionUID = -4330692689482574072L; 1315 1316 private String i18nMessage; 1317 private LocalizedMessage localizedMsg; 1318 private Object[] source; 1319 1320 /** 1321 * Constructs a ParsingException with the specified 1322 * detail message. A detail message is a String that describes 1323 * this particular exception, which may, for example, specify which 1324 * algorithm is not available. 1325 * 1326 * @param msg the detail message. 1327 */ 1328 public ParsingException(String msg) { 1329 super(msg); 1330 i18nMessage = msg; 1331 } 1332 1333 public ParsingException(String msg, LocalizedMessage localizedMsg, 1334 Object[] source) { 1335 super(msg); 1336 this.localizedMsg = localizedMsg; 1337 this.source = source; 1338 } 1339 1340 public ParsingException(int line, String msg) { 1341 super("line " + line + ": " + msg); 1342 localizedMsg = new LocalizedMessage("line.number.msg"); 1343 source = new Object[] {line, msg}; 1344 } 1345 1346 public ParsingException(int line, String expect, String actual) { 1347 super("line " + line + ": expected [" + expect + 1348 "], found [" + actual + "]"); 1349 localizedMsg = new LocalizedMessage 1350 ("line.number.expected.expect.found.actual."); 1351 source = new Object[] {line, expect, actual}; 1352 } 1353 1354 public String getNonlocalizedMessage() { 1355 return i18nMessage != null ? i18nMessage : 1356 localizedMsg.formatNonlocalized(source); 1357 } 1358 } 1359 1360 public static void main(String[] arg) throws Exception { 1361 try (FileReader fr = new FileReader(arg[0]); 1362 FileWriter fw = new FileWriter(arg[1])) { 1363 PolicyParser pp = new PolicyParser(true); 1364 pp.read(fr); 1365 pp.write(fw); 1366 } 1367 } 1368 }