Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/sun/security/provider/PolicyParser.java
41159 views
1
/*
2
* Copyright (c) 1997, 2019, 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
String.valueOf((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
@java.io.Serial
1315
private static final long serialVersionUID = -4330692689482574072L;
1316
1317
private String i18nMessage;
1318
@SuppressWarnings("serial") // Not statically typed as Serializable
1319
private LocalizedMessage localizedMsg;
1320
@SuppressWarnings("serial") // Not statically typed as Serializable
1321
private Object[] source;
1322
1323
/**
1324
* Constructs a ParsingException with the specified
1325
* detail message. A detail message is a String that describes
1326
* this particular exception, which may, for example, specify which
1327
* algorithm is not available.
1328
*
1329
* @param msg the detail message.
1330
*/
1331
public ParsingException(String msg) {
1332
super(msg);
1333
i18nMessage = msg;
1334
}
1335
1336
public ParsingException(String msg, LocalizedMessage localizedMsg,
1337
Object[] source) {
1338
super(msg);
1339
this.localizedMsg = localizedMsg;
1340
this.source = source;
1341
}
1342
1343
public ParsingException(int line, String msg) {
1344
super("line " + line + ": " + msg);
1345
localizedMsg = new LocalizedMessage("line.number.msg");
1346
source = new Object[] {line, msg};
1347
}
1348
1349
public ParsingException(int line, String expect, String actual) {
1350
super("line " + line + ": expected [" + expect +
1351
"], found [" + actual + "]");
1352
localizedMsg = new LocalizedMessage
1353
("line.number.expected.expect.found.actual.");
1354
source = new Object[] {line, expect, actual};
1355
}
1356
1357
public String getNonlocalizedMessage() {
1358
return i18nMessage != null ? i18nMessage :
1359
localizedMsg.formatNonlocalized(source);
1360
}
1361
}
1362
1363
public static void main(String[] arg) throws Exception {
1364
try (FileReader fr = new FileReader(arg[0]);
1365
FileWriter fw = new FileWriter(arg[1])) {
1366
PolicyParser pp = new PolicyParser(true);
1367
pp.read(fr);
1368
pp.write(fw);
1369
}
1370
}
1371
}
1372
1373