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