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