1 /*
2  * Copyright (c) 2003, 2017, 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.pkcs11;
27 
28 import java.io.*;
29 import static java.io.StreamTokenizer.*;
30 import java.math.BigInteger;
31 import java.nio.charset.StandardCharsets;
32 import java.util.*;
33 
34 import java.security.*;
35 
36 import sun.security.action.GetPropertyAction;
37 import sun.security.util.PropertyExpander;
38 
39 import sun.security.pkcs11.wrapper.*;
40 import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
41 import static sun.security.pkcs11.wrapper.CK_ATTRIBUTE.*;
42 
43 import static sun.security.pkcs11.TemplateManager.*;
44 
45 /**
46  * Configuration container and file parsing.
47  *
48  * @author  Andreas Sterbenz
49  * @since   1.5
50  */
51 final class Config {
52 
53     static final int ERR_HALT       = 1;
54     static final int ERR_IGNORE_ALL = 2;
55     static final int ERR_IGNORE_LIB = 3;
56 
57     // same as allowSingleThreadedModules but controlled via a system property
58     // and applied to all providers. if set to false, no SunPKCS11 instances
59     // will accept single threaded modules regardless of the setting in their
60     // config files.
61     private static final boolean staticAllowSingleThreadedModules;
62 
63     static {
64         String p = "sun.security.pkcs11.allowSingleThreadedModules";
65         String s = AccessController.doPrivileged(new GetPropertyAction(p));
66         if ("false".equalsIgnoreCase(s)) {
67             staticAllowSingleThreadedModules = false;
68         } else {
69             staticAllowSingleThreadedModules = true;
70         }
71     }
72 
73     // temporary storage for configurations
74     // needed because the SunPKCS11 needs to call the superclass constructor
75     // in provider before accessing any instance variables
76     private final static Map<String,Config> configMap =
77                                         new HashMap<String,Config>();
78 
getConfig(final String name, final InputStream stream)79     static Config getConfig(final String name, final InputStream stream) {
80         Config config = configMap.get(name);
81         if (config != null) {
82             return config;
83         }
84         try {
85             config = new Config(name, stream);
86             configMap.put(name, config);
87             return config;
88         } catch (Exception e) {
89             throw new ProviderException("Error parsing configuration", e);
90         }
91     }
92 
removeConfig(String name)93     static Config removeConfig(String name) {
94         return configMap.remove(name);
95     }
96 
97     private final static boolean DEBUG = false;
98 
debug(Object o)99     private static void debug(Object o) {
100         if (DEBUG) {
101             System.out.println(o);
102         }
103     }
104 
105     // Reader and StringTokenizer used during parsing
106     private Reader reader;
107 
108     private StreamTokenizer st;
109 
110     private Set<String> parsedKeywords;
111 
112     // name suffix of the provider
113     private String name;
114 
115     // name of the PKCS#11 library
116     private String library;
117 
118     // description to pass to the provider class
119     private String description;
120 
121     // slotID of the slot to use
122     private int slotID = -1;
123 
124     // slot to use, specified as index in the slotlist
125     private int slotListIndex = -1;
126 
127     // set of enabled mechanisms (or null to use default)
128     private Set<Long> enabledMechanisms;
129 
130     // set of disabled mechanisms
131     private Set<Long> disabledMechanisms;
132 
133     // whether to print debug info during startup
134     private boolean showInfo = false;
135 
136     // template manager, initialized from parsed attributes
137     private TemplateManager templateManager;
138 
139     // how to handle error during startup, one of ERR_
140     private int handleStartupErrors = ERR_HALT;
141 
142     // flag indicating whether the P11KeyStore should
143     // be more tolerant of input parameters
144     private boolean keyStoreCompatibilityMode = true;
145 
146     // flag indicating whether we need to explicitly cancel operations
147     // see Token
148     private boolean explicitCancel = true;
149 
150     // how often to test for token insertion, if no token is present
151     private int insertionCheckInterval = 2000;
152 
153     // flag inidicating whether to omit the call to C_Initialize()
154     // should be used only if we are running within a process that
155     // has already called it (e.g. Plugin inside of Mozilla/NSS)
156     private boolean omitInitialize = false;
157 
158     // whether to allow modules that only support single threaded access.
159     // they cannot be used safely from multiple PKCS#11 consumers in the
160     // same process, for example NSS and SunPKCS11
161     private boolean allowSingleThreadedModules = true;
162 
163     // name of the C function that returns the PKCS#11 functionlist
164     // This option primarily exists for the deprecated
165     // Secmod.Module.getProvider() method.
166     private String functionList = "C_GetFunctionList";
167 
168     // whether to use NSS secmod mode. Implicitly set if nssLibraryDirectory,
169     // nssSecmodDirectory, or nssModule is specified.
170     private boolean nssUseSecmod;
171 
172     // location of the NSS library files (libnss3.so, etc.)
173     private String nssLibraryDirectory;
174 
175     // location of secmod.db
176     private String nssSecmodDirectory;
177 
178     // which NSS module to use
179     private String nssModule;
180 
181     private Secmod.DbMode nssDbMode = Secmod.DbMode.READ_WRITE;
182 
183     // Whether the P11KeyStore should specify the CKA_NETSCAPE_DB attribute
184     // when creating private keys. Only valid if nssUseSecmod is true.
185     private boolean nssNetscapeDbWorkaround = true;
186 
187     // Special init argument string for the NSS softtoken.
188     // This is used when using the NSS softtoken directly without secmod mode.
189     private String nssArgs;
190 
191     // whether to use NSS trust attributes for the KeyStore of this provider
192     // this option is for internal use by the SunPKCS11 code only and
193     // works only for NSS providers created via the Secmod API
194     private boolean nssUseSecmodTrust = false;
195 
196     // Flag to indicate whether the X9.63 encoding for EC points shall be used
197     // (true) or whether that encoding shall be wrapped in an ASN.1 OctetString
198     // (false).
199     private boolean useEcX963Encoding = false;
200 
201     // Flag to indicate whether NSS should favour performance (false) or
202     // memory footprint (true).
203     private boolean nssOptimizeSpace = false;
204 
Config(String filename, InputStream in)205     private Config(String filename, InputStream in) throws IOException {
206         if (in == null) {
207             if (filename.startsWith("--")) {
208                 // inline config
209                 String config = filename.substring(2).replace("\\n", "\n");
210                 reader = new StringReader(config);
211             } else {
212                 in = new FileInputStream(expand(filename));
213             }
214         }
215         if (reader == null) {
216             reader = new BufferedReader(new InputStreamReader(in,
217                     StandardCharsets.ISO_8859_1));
218         }
219         parsedKeywords = new HashSet<String>();
220         st = new StreamTokenizer(reader);
221         setupTokenizer();
222         parse();
223     }
224 
getName()225     String getName() {
226         return name;
227     }
228 
getLibrary()229     String getLibrary() {
230         return library;
231     }
232 
getDescription()233     String getDescription() {
234         if (description != null) {
235             return description;
236         }
237         return "SunPKCS11-" + name + " using library " + library;
238     }
239 
getSlotID()240     int getSlotID() {
241         return slotID;
242     }
243 
getSlotListIndex()244     int getSlotListIndex() {
245         if ((slotID == -1) && (slotListIndex == -1)) {
246             // if neither is set, default to first slot
247             return 0;
248         } else {
249             return slotListIndex;
250         }
251     }
252 
getShowInfo()253     boolean getShowInfo() {
254         return (SunPKCS11.debug != null) || showInfo;
255     }
256 
getTemplateManager()257     TemplateManager getTemplateManager() {
258         if (templateManager == null) {
259             templateManager = new TemplateManager();
260         }
261         return templateManager;
262     }
263 
isEnabled(long m)264     boolean isEnabled(long m) {
265         if (enabledMechanisms != null) {
266             return enabledMechanisms.contains(Long.valueOf(m));
267         }
268         if (disabledMechanisms != null) {
269             return !disabledMechanisms.contains(Long.valueOf(m));
270         }
271         return true;
272     }
273 
getHandleStartupErrors()274     int getHandleStartupErrors() {
275         return handleStartupErrors;
276     }
277 
getKeyStoreCompatibilityMode()278     boolean getKeyStoreCompatibilityMode() {
279         return keyStoreCompatibilityMode;
280     }
281 
getExplicitCancel()282     boolean getExplicitCancel() {
283         return explicitCancel;
284     }
285 
getInsertionCheckInterval()286     int getInsertionCheckInterval() {
287         return insertionCheckInterval;
288     }
289 
getOmitInitialize()290     boolean getOmitInitialize() {
291         return omitInitialize;
292     }
293 
getAllowSingleThreadedModules()294     boolean getAllowSingleThreadedModules() {
295         return staticAllowSingleThreadedModules && allowSingleThreadedModules;
296     }
297 
getFunctionList()298     String getFunctionList() {
299         return functionList;
300     }
301 
getNssUseSecmod()302     boolean getNssUseSecmod() {
303         return nssUseSecmod;
304     }
305 
getNssLibraryDirectory()306     String getNssLibraryDirectory() {
307         return nssLibraryDirectory;
308     }
309 
getNssSecmodDirectory()310     String getNssSecmodDirectory() {
311         return nssSecmodDirectory;
312     }
313 
getNssModule()314     String getNssModule() {
315         return nssModule;
316     }
317 
getNssDbMode()318     Secmod.DbMode getNssDbMode() {
319         return nssDbMode;
320     }
321 
getNssNetscapeDbWorkaround()322     public boolean getNssNetscapeDbWorkaround() {
323         return nssUseSecmod && nssNetscapeDbWorkaround;
324     }
325 
getNssArgs()326     String getNssArgs() {
327         return nssArgs;
328     }
329 
getNssUseSecmodTrust()330     boolean getNssUseSecmodTrust() {
331         return nssUseSecmodTrust;
332     }
333 
getUseEcX963Encoding()334     boolean getUseEcX963Encoding() {
335         return useEcX963Encoding;
336     }
337 
getNssOptimizeSpace()338     boolean getNssOptimizeSpace() {
339         return nssOptimizeSpace;
340     }
341 
expand(final String s)342     private static String expand(final String s) throws IOException {
343         try {
344             return PropertyExpander.expand(s);
345         } catch (Exception e) {
346             throw new RuntimeException(e.getMessage());
347         }
348     }
349 
setupTokenizer()350     private void setupTokenizer() {
351         st.resetSyntax();
352         st.wordChars('a', 'z');
353         st.wordChars('A', 'Z');
354         st.wordChars('0', '9');
355         st.wordChars(':', ':');
356         st.wordChars('.', '.');
357         st.wordChars('_', '_');
358         st.wordChars('-', '-');
359         st.wordChars('/', '/');
360         st.wordChars('\\', '\\');
361         st.wordChars('$', '$');
362         st.wordChars('{', '{'); // need {} for property subst
363         st.wordChars('}', '}');
364         st.wordChars('*', '*');
365         st.wordChars('+', '+');
366         st.wordChars('~', '~');
367         // XXX check ASCII table and add all other characters except special
368 
369         // special: #="(),
370         st.whitespaceChars(0, ' ');
371         st.commentChar('#');
372         st.eolIsSignificant(true);
373         st.quoteChar('\"');
374     }
375 
excToken(String msg)376     private ConfigurationException excToken(String msg) {
377         return new ConfigurationException(msg + " " + st);
378     }
379 
excLine(String msg)380     private ConfigurationException excLine(String msg) {
381         return new ConfigurationException(msg + ", line " + st.lineno());
382     }
383 
parse()384     private void parse() throws IOException {
385         while (true) {
386             int token = nextToken();
387             if (token == TT_EOF) {
388                 break;
389             }
390             if (token == TT_EOL) {
391                 continue;
392             }
393             if (token != TT_WORD) {
394                 throw excToken("Unexpected token:");
395             }
396             String word = st.sval;
397             if (word.equals("name")) {
398                 name = parseStringEntry(word);
399             } else if (word.equals("library")) {
400                 library = parseLibrary(word);
401             } else if (word.equals("description")) {
402                 parseDescription(word);
403             } else if (word.equals("slot")) {
404                 parseSlotID(word);
405             } else if (word.equals("slotListIndex")) {
406                 parseSlotListIndex(word);
407             } else if (word.equals("enabledMechanisms")) {
408                 parseEnabledMechanisms(word);
409             } else if (word.equals("disabledMechanisms")) {
410                 parseDisabledMechanisms(word);
411             } else if (word.equals("attributes")) {
412                 parseAttributes(word);
413             } else if (word.equals("handleStartupErrors")) {
414                 parseHandleStartupErrors(word);
415             } else if (word.endsWith("insertionCheckInterval")) {
416                 insertionCheckInterval = parseIntegerEntry(word);
417                 if (insertionCheckInterval < 100) {
418                     throw excLine(word + " must be at least 100 ms");
419                 }
420             } else if (word.equals("showInfo")) {
421                 showInfo = parseBooleanEntry(word);
422             } else if (word.equals("keyStoreCompatibilityMode")) {
423                 keyStoreCompatibilityMode = parseBooleanEntry(word);
424             } else if (word.equals("explicitCancel")) {
425                 explicitCancel = parseBooleanEntry(word);
426             } else if (word.equals("omitInitialize")) {
427                 omitInitialize = parseBooleanEntry(word);
428             } else if (word.equals("allowSingleThreadedModules")) {
429                 allowSingleThreadedModules = parseBooleanEntry(word);
430             } else if (word.equals("functionList")) {
431                 functionList = parseStringEntry(word);
432             } else if (word.equals("nssUseSecmod")) {
433                 nssUseSecmod = parseBooleanEntry(word);
434             } else if (word.equals("nssLibraryDirectory")) {
435                 nssLibraryDirectory = parseLibrary(word);
436                 nssUseSecmod = true;
437             } else if (word.equals("nssSecmodDirectory")) {
438                 nssSecmodDirectory = expand(parseStringEntry(word));
439                 nssUseSecmod = true;
440             } else if (word.equals("nssModule")) {
441                 nssModule = parseStringEntry(word);
442                 nssUseSecmod = true;
443             } else if (word.equals("nssDbMode")) {
444                 String mode = parseStringEntry(word);
445                 if (mode.equals("readWrite")) {
446                     nssDbMode = Secmod.DbMode.READ_WRITE;
447                 } else if (mode.equals("readOnly")) {
448                     nssDbMode = Secmod.DbMode.READ_ONLY;
449                 } else if (mode.equals("noDb")) {
450                     nssDbMode = Secmod.DbMode.NO_DB;
451                 } else {
452                     throw excToken("nssDbMode must be one of readWrite, readOnly, and noDb:");
453                 }
454                 nssUseSecmod = true;
455             } else if (word.equals("nssNetscapeDbWorkaround")) {
456                 nssNetscapeDbWorkaround = parseBooleanEntry(word);
457                 nssUseSecmod = true;
458             } else if (word.equals("nssArgs")) {
459                 parseNSSArgs(word);
460             } else if (word.equals("nssUseSecmodTrust")) {
461                 nssUseSecmodTrust = parseBooleanEntry(word);
462             } else if (word.equals("useEcX963Encoding")) {
463                 useEcX963Encoding = parseBooleanEntry(word);
464             } else if (word.equals("nssOptimizeSpace")) {
465                 nssOptimizeSpace = parseBooleanEntry(word);
466             } else {
467                 throw new ConfigurationException
468                         ("Unknown keyword '" + word + "', line " + st.lineno());
469             }
470             parsedKeywords.add(word);
471         }
472         reader.close();
473         reader = null;
474         st = null;
475         parsedKeywords = null;
476         if (name == null) {
477             throw new ConfigurationException("name must be specified");
478         }
479         if (nssUseSecmod == false) {
480             if (library == null) {
481                 throw new ConfigurationException("library must be specified");
482             }
483         } else {
484             if (library != null) {
485                 throw new ConfigurationException
486                     ("library must not be specified in NSS mode");
487             }
488             if ((slotID != -1) || (slotListIndex != -1)) {
489                 throw new ConfigurationException
490                     ("slot and slotListIndex must not be specified in NSS mode");
491             }
492             if (nssArgs != null) {
493                 throw new ConfigurationException
494                     ("nssArgs must not be specified in NSS mode");
495             }
496             if (nssUseSecmodTrust != false) {
497                 throw new ConfigurationException("nssUseSecmodTrust is an "
498                     + "internal option and must not be specified in NSS mode");
499             }
500         }
501     }
502 
503     //
504     // Parsing helper methods
505     //
506 
nextToken()507     private int nextToken() throws IOException {
508         int token = st.nextToken();
509         debug(st);
510         return token;
511     }
512 
parseEquals()513     private void parseEquals() throws IOException {
514         int token = nextToken();
515         if (token != '=') {
516             throw excToken("Expected '=', read");
517         }
518     }
519 
parseOpenBraces()520     private void parseOpenBraces() throws IOException {
521         while (true) {
522             int token = nextToken();
523             if (token == TT_EOL) {
524                 continue;
525             }
526             if ((token == TT_WORD) && st.sval.equals("{")) {
527                 return;
528             }
529             throw excToken("Expected '{', read");
530         }
531     }
532 
isCloseBraces(int token)533     private boolean isCloseBraces(int token) {
534         return (token == TT_WORD) && st.sval.equals("}");
535     }
536 
parseWord()537     private String parseWord() throws IOException {
538         int token = nextToken();
539         if (token != TT_WORD) {
540             throw excToken("Unexpected value:");
541         }
542         return st.sval;
543     }
544 
parseStringEntry(String keyword)545     private String parseStringEntry(String keyword) throws IOException {
546         checkDup(keyword);
547         parseEquals();
548 
549         int token = nextToken();
550         if (token != TT_WORD && token != '\"') {
551             // not a word token nor a string enclosed by double quotes
552             throw excToken("Unexpected value:");
553         }
554         String value = st.sval;
555 
556         debug(keyword + ": " + value);
557         return value;
558     }
559 
parseBooleanEntry(String keyword)560     private boolean parseBooleanEntry(String keyword) throws IOException {
561         checkDup(keyword);
562         parseEquals();
563         boolean value = parseBoolean();
564         debug(keyword + ": " + value);
565         return value;
566     }
567 
parseIntegerEntry(String keyword)568     private int parseIntegerEntry(String keyword) throws IOException {
569         checkDup(keyword);
570         parseEquals();
571         int value = decodeNumber(parseWord());
572         debug(keyword + ": " + value);
573         return value;
574     }
575 
parseBoolean()576     private boolean parseBoolean() throws IOException {
577         String val = parseWord();
578         switch (val) {
579             case "true":
580                 return true;
581             case "false":
582                 return false;
583             default:
584                 throw excToken("Expected boolean value, read:");
585         }
586     }
587 
parseLine()588     private String parseLine() throws IOException {
589         // allow quoted string as part of line
590         String s = null;
591         while (true) {
592             int token = nextToken();
593             if ((token == TT_EOL) || (token == TT_EOF)) {
594                 break;
595             }
596             if (token != TT_WORD && token != '\"') {
597                 throw excToken("Unexpected value");
598             }
599             if (s == null) {
600                 s = st.sval;
601             } else {
602                 s = s + " " + st.sval;
603             }
604         }
605         if (s == null) {
606             throw excToken("Unexpected empty line");
607         }
608         return s;
609     }
610 
decodeNumber(String str)611     private int decodeNumber(String str) throws IOException {
612         try {
613             if (str.startsWith("0x") || str.startsWith("0X")) {
614                 return Integer.parseInt(str.substring(2), 16);
615             } else {
616                 return Integer.parseInt(str);
617             }
618         } catch (NumberFormatException e) {
619             throw excToken("Expected number, read");
620         }
621     }
622 
isNumber(String s)623     private static boolean isNumber(String s) {
624         if (s.length() == 0) {
625             return false;
626         }
627         char ch = s.charAt(0);
628         return ((ch >= '0') && (ch <= '9'));
629     }
630 
parseComma()631     private void parseComma() throws IOException {
632         int token = nextToken();
633         if (token != ',') {
634             throw excToken("Expected ',', read");
635         }
636     }
637 
isByteArray(String val)638     private static boolean isByteArray(String val) {
639         return val.startsWith("0h");
640     }
641 
decodeByteArray(String str)642     private byte[] decodeByteArray(String str) throws IOException {
643         if (str.startsWith("0h") == false) {
644             throw excToken("Expected byte array value, read");
645         }
646         str = str.substring(2);
647         // XXX proper hex parsing
648         try {
649             return new BigInteger(str, 16).toByteArray();
650         } catch (NumberFormatException e) {
651             throw excToken("Expected byte array value, read");
652         }
653     }
654 
checkDup(String keyword)655     private void checkDup(String keyword) throws IOException {
656         if (parsedKeywords.contains(keyword)) {
657             throw excLine(keyword + " must only be specified once");
658         }
659     }
660 
661     //
662     // individual entry parsing methods
663     //
664 
parseLibrary(String keyword)665     private String parseLibrary(String keyword) throws IOException {
666         checkDup(keyword);
667         parseEquals();
668         String lib = parseLine();
669         lib = expand(lib);
670         int i = lib.indexOf("/$ISA/");
671         if (i != -1) {
672             // replace "/$ISA/" with "/sparcv9/" on 64-bit Solaris SPARC
673             // and with "/amd64/" on Solaris AMD64.
674             // On all other platforms, just turn it into a "/"
675             String osName = System.getProperty("os.name", "");
676             String osArch = System.getProperty("os.arch", "");
677             String prefix = lib.substring(0, i);
678             String suffix = lib.substring(i + 5);
679             if (osName.equals("SunOS") && osArch.equals("sparcv9")) {
680                 lib = prefix + "/sparcv9" + suffix;
681             } else if (osName.equals("SunOS") && osArch.equals("amd64")) {
682                 lib = prefix + "/amd64" + suffix;
683             } else {
684                 lib = prefix + suffix;
685             }
686         }
687         debug(keyword + ": " + lib);
688 
689         // Check to see if full path is specified to prevent the DLL
690         // preloading attack
691         if (!(new File(lib)).isAbsolute()) {
692             throw new ConfigurationException(
693                 "Absolute path required for library value: " + lib);
694         }
695         return lib;
696     }
697 
parseDescription(String keyword)698     private void parseDescription(String keyword) throws IOException {
699         checkDup(keyword);
700         parseEquals();
701         description = parseLine();
702         debug("description: " + description);
703     }
704 
parseSlotID(String keyword)705     private void parseSlotID(String keyword) throws IOException {
706         if (slotID >= 0) {
707             throw excLine("Duplicate slot definition");
708         }
709         if (slotListIndex >= 0) {
710             throw excLine
711                 ("Only one of slot and slotListIndex must be specified");
712         }
713         parseEquals();
714         String slotString = parseWord();
715         slotID = decodeNumber(slotString);
716         debug("slot: " + slotID);
717     }
718 
parseSlotListIndex(String keyword)719     private void parseSlotListIndex(String keyword) throws IOException {
720         if (slotListIndex >= 0) {
721             throw excLine("Duplicate slotListIndex definition");
722         }
723         if (slotID >= 0) {
724             throw excLine
725                 ("Only one of slot and slotListIndex must be specified");
726         }
727         parseEquals();
728         String slotString = parseWord();
729         slotListIndex = decodeNumber(slotString);
730         debug("slotListIndex: " + slotListIndex);
731     }
732 
parseEnabledMechanisms(String keyword)733     private void parseEnabledMechanisms(String keyword) throws IOException {
734         enabledMechanisms = parseMechanisms(keyword);
735     }
736 
parseDisabledMechanisms(String keyword)737     private void parseDisabledMechanisms(String keyword) throws IOException {
738         disabledMechanisms = parseMechanisms(keyword);
739     }
740 
parseMechanisms(String keyword)741     private Set<Long> parseMechanisms(String keyword) throws IOException {
742         checkDup(keyword);
743         Set<Long> mechs = new HashSet<Long>();
744         parseEquals();
745         parseOpenBraces();
746         while (true) {
747             int token = nextToken();
748             if (isCloseBraces(token)) {
749                 break;
750             }
751             if (token == TT_EOL) {
752                 continue;
753             }
754             if (token != TT_WORD) {
755                 throw excToken("Expected mechanism, read");
756             }
757             long mech = parseMechanism(st.sval);
758             mechs.add(Long.valueOf(mech));
759         }
760         if (DEBUG) {
761             System.out.print("mechanisms: [");
762             for (Long mech : mechs) {
763                 System.out.print(Functions.getMechanismName(mech));
764                 System.out.print(", ");
765             }
766             System.out.println("]");
767         }
768         return mechs;
769     }
770 
parseMechanism(String mech)771     private long parseMechanism(String mech) throws IOException {
772         if (isNumber(mech)) {
773             return decodeNumber(mech);
774         } else {
775             try {
776                 return Functions.getMechanismId(mech);
777             } catch (IllegalArgumentException e) {
778                 throw excLine("Unknown mechanism: " + mech);
779             }
780         }
781     }
782 
parseAttributes(String keyword)783     private void parseAttributes(String keyword) throws IOException {
784         if (templateManager == null) {
785             templateManager = new TemplateManager();
786         }
787         int token = nextToken();
788         if (token == '=') {
789             String s = parseWord();
790             if (s.equals("compatibility") == false) {
791                 throw excLine("Expected 'compatibility', read " + s);
792             }
793             setCompatibilityAttributes();
794             return;
795         }
796         if (token != '(') {
797             throw excToken("Expected '(' or '=', read");
798         }
799         String op = parseOperation();
800         parseComma();
801         long objectClass = parseObjectClass();
802         parseComma();
803         long keyAlg = parseKeyAlgorithm();
804         token = nextToken();
805         if (token != ')') {
806             throw excToken("Expected ')', read");
807         }
808         parseEquals();
809         parseOpenBraces();
810         List<CK_ATTRIBUTE> attributes = new ArrayList<CK_ATTRIBUTE>();
811         while (true) {
812             token = nextToken();
813             if (isCloseBraces(token)) {
814                 break;
815             }
816             if (token == TT_EOL) {
817                 continue;
818             }
819             if (token != TT_WORD) {
820                 throw excToken("Expected mechanism, read");
821             }
822             String attributeName = st.sval;
823             long attributeId = decodeAttributeName(attributeName);
824             parseEquals();
825             String attributeValue = parseWord();
826             attributes.add(decodeAttributeValue(attributeId, attributeValue));
827         }
828         templateManager.addTemplate
829                 (op, objectClass, keyAlg, attributes.toArray(CK_A0));
830     }
831 
setCompatibilityAttributes()832     private void setCompatibilityAttributes() {
833         // all secret keys
834         templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, PCKK_ANY,
835         new CK_ATTRIBUTE[] {
836             TOKEN_FALSE,
837             SENSITIVE_FALSE,
838             EXTRACTABLE_TRUE,
839             ENCRYPT_TRUE,
840             DECRYPT_TRUE,
841             WRAP_TRUE,
842             UNWRAP_TRUE,
843         });
844 
845         // generic secret keys are special
846         // They are used as MAC keys plus for the SSL/TLS (pre)master secrets
847         templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, CKK_GENERIC_SECRET,
848         new CK_ATTRIBUTE[] {
849             SIGN_TRUE,
850             VERIFY_TRUE,
851             ENCRYPT_NULL,
852             DECRYPT_NULL,
853             WRAP_NULL,
854             UNWRAP_NULL,
855             DERIVE_TRUE,
856         });
857 
858         // all private and public keys
859         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, PCKK_ANY,
860         new CK_ATTRIBUTE[] {
861             TOKEN_FALSE,
862             SENSITIVE_FALSE,
863             EXTRACTABLE_TRUE,
864         });
865         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, PCKK_ANY,
866         new CK_ATTRIBUTE[] {
867             TOKEN_FALSE,
868         });
869 
870         // additional attributes for RSA private keys
871         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_RSA,
872         new CK_ATTRIBUTE[] {
873             DECRYPT_TRUE,
874             SIGN_TRUE,
875             SIGN_RECOVER_TRUE,
876             UNWRAP_TRUE,
877         });
878         // additional attributes for RSA public keys
879         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_RSA,
880         new CK_ATTRIBUTE[] {
881             ENCRYPT_TRUE,
882             VERIFY_TRUE,
883             VERIFY_RECOVER_TRUE,
884             WRAP_TRUE,
885         });
886 
887         // additional attributes for DSA private keys
888         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DSA,
889         new CK_ATTRIBUTE[] {
890             SIGN_TRUE,
891         });
892         // additional attributes for DSA public keys
893         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_DSA,
894         new CK_ATTRIBUTE[] {
895             VERIFY_TRUE,
896         });
897 
898         // additional attributes for DH private keys
899         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DH,
900         new CK_ATTRIBUTE[] {
901             DERIVE_TRUE,
902         });
903 
904         // additional attributes for EC private keys
905         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_EC,
906         new CK_ATTRIBUTE[] {
907             SIGN_TRUE,
908             DERIVE_TRUE,
909         });
910         // additional attributes for EC public keys
911         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_EC,
912         new CK_ATTRIBUTE[] {
913             VERIFY_TRUE,
914         });
915     }
916 
917     private final static CK_ATTRIBUTE[] CK_A0 = new CK_ATTRIBUTE[0];
918 
parseOperation()919     private String parseOperation() throws IOException {
920         String op = parseWord();
921         switch (op) {
922             case "*":
923                 return TemplateManager.O_ANY;
924             case "generate":
925                 return TemplateManager.O_GENERATE;
926             case "import":
927                 return TemplateManager.O_IMPORT;
928             default:
929                 throw excLine("Unknown operation " + op);
930         }
931     }
932 
parseObjectClass()933     private long parseObjectClass() throws IOException {
934         String name = parseWord();
935         try {
936             return Functions.getObjectClassId(name);
937         } catch (IllegalArgumentException e) {
938             throw excLine("Unknown object class " + name);
939         }
940     }
941 
parseKeyAlgorithm()942     private long parseKeyAlgorithm() throws IOException {
943         String name = parseWord();
944         if (isNumber(name)) {
945             return decodeNumber(name);
946         } else {
947             try {
948                 return Functions.getKeyId(name);
949             } catch (IllegalArgumentException e) {
950                 throw excLine("Unknown key algorithm " + name);
951             }
952         }
953     }
954 
decodeAttributeName(String name)955     private long decodeAttributeName(String name) throws IOException {
956         if (isNumber(name)) {
957             return decodeNumber(name);
958         } else {
959             try {
960                 return Functions.getAttributeId(name);
961             } catch (IllegalArgumentException e) {
962                 throw excLine("Unknown attribute name " + name);
963             }
964         }
965     }
966 
decodeAttributeValue(long id, String value)967     private CK_ATTRIBUTE decodeAttributeValue(long id, String value)
968             throws IOException {
969         if (value.equals("null")) {
970             return new CK_ATTRIBUTE(id);
971         } else if (value.equals("true")) {
972             return new CK_ATTRIBUTE(id, true);
973         } else if (value.equals("false")) {
974             return new CK_ATTRIBUTE(id, false);
975         } else if (isByteArray(value)) {
976             return new CK_ATTRIBUTE(id, decodeByteArray(value));
977         } else if (isNumber(value)) {
978             return new CK_ATTRIBUTE(id, Integer.valueOf(decodeNumber(value)));
979         } else {
980             throw excLine("Unknown attribute value " + value);
981         }
982     }
983 
parseNSSArgs(String keyword)984     private void parseNSSArgs(String keyword) throws IOException {
985         checkDup(keyword);
986         parseEquals();
987         int token = nextToken();
988         if (token != '"') {
989             throw excToken("Expected quoted string");
990         }
991         nssArgs = expand(st.sval);
992         debug("nssArgs: " + nssArgs);
993     }
994 
parseHandleStartupErrors(String keyword)995     private void parseHandleStartupErrors(String keyword) throws IOException {
996         checkDup(keyword);
997         parseEquals();
998         String val = parseWord();
999         if (val.equals("ignoreAll")) {
1000             handleStartupErrors = ERR_IGNORE_ALL;
1001         } else if (val.equals("ignoreMissingLibrary")) {
1002             handleStartupErrors = ERR_IGNORE_LIB;
1003         } else if (val.equals("halt")) {
1004             handleStartupErrors = ERR_HALT;
1005         } else {
1006             throw excToken("Invalid value for handleStartupErrors:");
1007         }
1008         debug("handleStartupErrors: " + handleStartupErrors);
1009     }
1010 
1011 }
1012 
1013 class ConfigurationException extends IOException {
1014     private static final long serialVersionUID = 254492758807673194L;
ConfigurationException(String msg)1015     ConfigurationException(String msg) {
1016         super(msg);
1017     }
1018 }
1019