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
26package com.oracle.security.ucrypto;
27
28import java.io.*;
29import static java.io.StreamTokenizer.*;
30import java.math.BigInteger;
31import java.util.*;
32
33import java.security.*;
34
35import sun.security.action.GetPropertyAction;
36import 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 */
54final 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
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
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
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
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
118    private ConfigException excToken(String msg) {
119        return new ConfigException(msg + " " + st);
120    }
121
122    private ConfigException excLine(String msg) {
123        return new ConfigException(msg + ", line " + st.lineno());
124    }
125
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    //
156    private int nextToken() throws IOException {
157        int token = st.nextToken();
158        return token;
159    }
160
161    private void parseEquals() throws IOException {
162        int token = nextToken();
163        if (token != '=') {
164            throw excToken("Expected '=', read");
165        }
166    }
167
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
181    private boolean isCloseBraces(int token) {
182        return (token == TT_WORD) && st.sval.equals("}");
183    }
184
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
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
212class ConfigException extends IOException {
213    private static final long serialVersionUID = 254492758127673194L;
214    ConfigException(String msg) {
215        super(msg);
216    }
217}
218