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