1 /*
2  * Copyright (c) 2014, 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 com.oracle.security.ucrypto;
27 
28 import java.io.*;
29 import static java.io.StreamTokenizer.*;
30 import java.math.BigInteger;
31 import java.util.*;
32 
33 import java.security.*;
34 
35 import sun.security.action.GetPropertyAction;
36 import sun.security.util.PropertyExpander;
37 
38 /**
39  * Configuration container and file parsing.
40  *
41  * Currently, there is only one supported entry "disabledServices"
42  * for disabling crypto services. Its syntax is as follows:
43  *
44  * disabledServices = {
45  * <ServiceType>.<Algorithm>
46  * ...
47  * }
48  *
49  * where <Service> can be "MessageDigest", "Cipher", etc. and <Algorithm>
50  * reprepresents the value that's passed into the various getInstance() calls.
51  *
52  * @since   9
53  */
54 final class Config {
55 
56     // Reader and StringTokenizer used during parsing
57     private Reader reader;
58 
59     private StreamTokenizer st;
60 
61     private Set<String> parsedKeywords;
62 
63     // set of disabled crypto services, e.g. MessageDigest.SHA1, or
64     // Cipher.AES/ECB/PKCS5Padding
65     private Set<String> disabledServices;
66 
Config(String filename)67     Config(String filename) throws IOException {
68         FileInputStream in = new FileInputStream(expand(filename));
69         reader = new BufferedReader(new InputStreamReader(in, "ISO-8859-1"));
70         parsedKeywords = new HashSet<String>();
71         st = new StreamTokenizer(reader);
72         setupTokenizer();
73         parse();
74     }
75 
getDisabledServices()76     String[] getDisabledServices() {
77         if (disabledServices != null) {
78             return disabledServices.toArray(new String[disabledServices.size()]);
79         } else {
80             return new String[0];
81         }
82     }
83 
expand(final String s)84     private static String expand(final String s) throws IOException {
85         try {
86             return PropertyExpander.expand(s);
87         } catch (Exception e) {
88             throw new RuntimeException(e.getMessage());
89         }
90     }
91 
setupTokenizer()92     private void setupTokenizer() {
93         st.resetSyntax();
94         st.wordChars('a', 'z');
95         st.wordChars('A', 'Z');
96         st.wordChars('0', '9');
97         st.wordChars(':', ':');
98         st.wordChars('.', '.');
99         st.wordChars('_', '_');
100         st.wordChars('-', '-');
101         st.wordChars('/', '/');
102         st.wordChars('\\', '\\');
103         st.wordChars('$', '$');
104         st.wordChars('{', '{'); // need {} for property subst
105         st.wordChars('}', '}');
106         st.wordChars('*', '*');
107         st.wordChars('+', '+');
108         st.wordChars('~', '~');
109         // XXX check ASCII table and add all other characters except special
110 
111         // special: #="(),
112         st.whitespaceChars(0, ' ');
113         st.commentChar('#');
114         st.eolIsSignificant(true);
115         st.quoteChar('\"');
116     }
117 
excToken(String msg)118     private ConfigException excToken(String msg) {
119         return new ConfigException(msg + " " + st);
120     }
121 
excLine(String msg)122     private ConfigException excLine(String msg) {
123         return new ConfigException(msg + ", line " + st.lineno());
124     }
125 
parse()126     private void parse() throws IOException {
127         while (true) {
128             int token = nextToken();
129             if (token == TT_EOF) {
130                 break;
131             }
132             if (token == TT_EOL) {
133                 continue;
134             }
135             if (token != TT_WORD) {
136                 throw excToken("Unexpected token:");
137             }
138             String word = st.sval;
139             if (word.equals("disabledServices")) {
140                 parseDisabledServices(word);
141             } else {
142                 throw new ConfigException
143                         ("Unknown keyword '" + word + "', line " + st.lineno());
144             }
145             parsedKeywords.add(word);
146         }
147         reader.close();
148         reader = null;
149         st = null;
150         parsedKeywords = null;
151     }
152 
153     //
154     // Parsing helper methods
155     //
nextToken()156     private int nextToken() throws IOException {
157         int token = st.nextToken();
158         return token;
159     }
160 
parseEquals()161     private void parseEquals() throws IOException {
162         int token = nextToken();
163         if (token != '=') {
164             throw excToken("Expected '=', read");
165         }
166     }
167 
parseOpenBraces()168     private void parseOpenBraces() throws IOException {
169         while (true) {
170             int token = nextToken();
171             if (token == TT_EOL) {
172                 continue;
173             }
174             if ((token == TT_WORD) && st.sval.equals("{")) {
175                 return;
176             }
177             throw excToken("Expected '{', read");
178         }
179     }
180 
isCloseBraces(int token)181     private boolean isCloseBraces(int token) {
182         return (token == TT_WORD) && st.sval.equals("}");
183     }
184 
checkDup(String keyword)185     private void checkDup(String keyword) throws IOException {
186         if (parsedKeywords.contains(keyword)) {
187             throw excLine(keyword + " must only be specified once");
188         }
189     }
190 
parseDisabledServices(String keyword)191     private void parseDisabledServices(String keyword) throws IOException {
192         checkDup(keyword);
193         disabledServices = new HashSet<String>();
194         parseEquals();
195         parseOpenBraces();
196         while (true) {
197             int token = nextToken();
198             if (isCloseBraces(token)) {
199                 break;
200             }
201             if (token == TT_EOL) {
202                 continue;
203             }
204             if (token != TT_WORD) {
205                 throw excToken("Expected mechanism, read");
206             }
207             disabledServices.add(st.sval);
208         }
209     }
210 }
211 
212 class ConfigException extends IOException {
213     private static final long serialVersionUID = 254492758127673194L;
ConfigException(String msg)214     ConfigException(String msg) {
215         super(msg);
216     }
217 }
218