1 /*
2  * Copyright (c) 2003, 2020, 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 "/"
667             String prefix = lib.substring(0, i);
668             String suffix = lib.substring(i + 5);
669             lib = prefix + suffix;
670         }
671         debug(keyword + ": " + lib);
672 
673         // Check to see if full path is specified to prevent the DLL
674         // preloading attack
675         if (!(new File(lib)).isAbsolute()) {
676             throw new ConfigurationException(
677                 "Absolute path required for library value: " + lib);
678         }
679         return lib;
680     }
681 
parseDescription(String keyword)682     private void parseDescription(String keyword) throws IOException {
683         checkDup(keyword);
684         parseEquals();
685         description = parseLine();
686         debug("description: " + description);
687     }
688 
parseSlotID(String keyword)689     private void parseSlotID(String keyword) throws IOException {
690         if (slotID >= 0) {
691             throw excLine("Duplicate slot definition");
692         }
693         if (slotListIndex >= 0) {
694             throw excLine
695                 ("Only one of slot and slotListIndex must be specified");
696         }
697         parseEquals();
698         String slotString = parseWord();
699         slotID = decodeNumber(slotString);
700         debug("slot: " + slotID);
701     }
702 
parseSlotListIndex(String keyword)703     private void parseSlotListIndex(String keyword) throws IOException {
704         if (slotListIndex >= 0) {
705             throw excLine("Duplicate slotListIndex definition");
706         }
707         if (slotID >= 0) {
708             throw excLine
709                 ("Only one of slot and slotListIndex must be specified");
710         }
711         parseEquals();
712         String slotString = parseWord();
713         slotListIndex = decodeNumber(slotString);
714         debug("slotListIndex: " + slotListIndex);
715     }
716 
parseEnabledMechanisms(String keyword)717     private void parseEnabledMechanisms(String keyword) throws IOException {
718         enabledMechanisms = parseMechanisms(keyword);
719     }
720 
parseDisabledMechanisms(String keyword)721     private void parseDisabledMechanisms(String keyword) throws IOException {
722         disabledMechanisms = parseMechanisms(keyword);
723     }
724 
parseMechanisms(String keyword)725     private Set<Long> parseMechanisms(String keyword) throws IOException {
726         checkDup(keyword);
727         Set<Long> mechs = new HashSet<Long>();
728         parseEquals();
729         parseOpenBraces();
730         while (true) {
731             int token = nextToken();
732             if (isCloseBraces(token)) {
733                 break;
734             }
735             if (token == TT_EOL) {
736                 continue;
737             }
738             if (token != TT_WORD) {
739                 throw excToken("Expected mechanism, read");
740             }
741             long mech = parseMechanism(st.sval);
742             mechs.add(Long.valueOf(mech));
743         }
744         if (DEBUG) {
745             System.out.print("mechanisms: [");
746             for (Long mech : mechs) {
747                 System.out.print(Functions.getMechanismName(mech));
748                 System.out.print(", ");
749             }
750             System.out.println("]");
751         }
752         return mechs;
753     }
754 
parseMechanism(String mech)755     private long parseMechanism(String mech) throws IOException {
756         if (isNumber(mech)) {
757             return decodeNumber(mech);
758         } else {
759             try {
760                 return Functions.getMechanismId(mech);
761             } catch (IllegalArgumentException e) {
762                 throw excLine("Unknown mechanism: " + mech);
763             }
764         }
765     }
766 
parseAttributes(String keyword)767     private void parseAttributes(String keyword) throws IOException {
768         if (templateManager == null) {
769             templateManager = new TemplateManager();
770         }
771         int token = nextToken();
772         if (token == '=') {
773             String s = parseWord();
774             if (s.equals("compatibility") == false) {
775                 throw excLine("Expected 'compatibility', read " + s);
776             }
777             setCompatibilityAttributes();
778             return;
779         }
780         if (token != '(') {
781             throw excToken("Expected '(' or '=', read");
782         }
783         String op = parseOperation();
784         parseComma();
785         long objectClass = parseObjectClass();
786         parseComma();
787         long keyAlg = parseKeyAlgorithm();
788         token = nextToken();
789         if (token != ')') {
790             throw excToken("Expected ')', read");
791         }
792         parseEquals();
793         parseOpenBraces();
794         List<CK_ATTRIBUTE> attributes = new ArrayList<CK_ATTRIBUTE>();
795         while (true) {
796             token = nextToken();
797             if (isCloseBraces(token)) {
798                 break;
799             }
800             if (token == TT_EOL) {
801                 continue;
802             }
803             if (token != TT_WORD) {
804                 throw excToken("Expected mechanism, read");
805             }
806             String attributeName = st.sval;
807             long attributeId = decodeAttributeName(attributeName);
808             parseEquals();
809             String attributeValue = parseWord();
810             attributes.add(decodeAttributeValue(attributeId, attributeValue));
811         }
812         templateManager.addTemplate
813                 (op, objectClass, keyAlg, attributes.toArray(CK_A0));
814     }
815 
setCompatibilityAttributes()816     private void setCompatibilityAttributes() {
817         // all secret keys
818         templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, PCKK_ANY,
819         new CK_ATTRIBUTE[] {
820             TOKEN_FALSE,
821             SENSITIVE_FALSE,
822             EXTRACTABLE_TRUE,
823             ENCRYPT_TRUE,
824             DECRYPT_TRUE,
825             WRAP_TRUE,
826             UNWRAP_TRUE,
827         });
828 
829         // generic secret keys are special
830         // They are used as MAC keys plus for the SSL/TLS (pre)master secrets
831         templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, CKK_GENERIC_SECRET,
832         new CK_ATTRIBUTE[] {
833             SIGN_TRUE,
834             VERIFY_TRUE,
835             ENCRYPT_NULL,
836             DECRYPT_NULL,
837             WRAP_NULL,
838             UNWRAP_NULL,
839             DERIVE_TRUE,
840         });
841 
842         // all private and public keys
843         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, PCKK_ANY,
844         new CK_ATTRIBUTE[] {
845             TOKEN_FALSE,
846             SENSITIVE_FALSE,
847             EXTRACTABLE_TRUE,
848         });
849         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, PCKK_ANY,
850         new CK_ATTRIBUTE[] {
851             TOKEN_FALSE,
852         });
853 
854         // additional attributes for RSA private keys
855         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_RSA,
856         new CK_ATTRIBUTE[] {
857             DECRYPT_TRUE,
858             SIGN_TRUE,
859             SIGN_RECOVER_TRUE,
860             UNWRAP_TRUE,
861         });
862         // additional attributes for RSA public keys
863         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_RSA,
864         new CK_ATTRIBUTE[] {
865             ENCRYPT_TRUE,
866             VERIFY_TRUE,
867             VERIFY_RECOVER_TRUE,
868             WRAP_TRUE,
869         });
870 
871         // additional attributes for DSA private keys
872         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DSA,
873         new CK_ATTRIBUTE[] {
874             SIGN_TRUE,
875         });
876         // additional attributes for DSA public keys
877         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_DSA,
878         new CK_ATTRIBUTE[] {
879             VERIFY_TRUE,
880         });
881 
882         // additional attributes for DH private keys
883         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DH,
884         new CK_ATTRIBUTE[] {
885             DERIVE_TRUE,
886         });
887 
888         // additional attributes for EC private keys
889         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_EC,
890         new CK_ATTRIBUTE[] {
891             SIGN_TRUE,
892             DERIVE_TRUE,
893         });
894         // additional attributes for EC public keys
895         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_EC,
896         new CK_ATTRIBUTE[] {
897             VERIFY_TRUE,
898         });
899     }
900 
901     private final static CK_ATTRIBUTE[] CK_A0 = new CK_ATTRIBUTE[0];
902 
parseOperation()903     private String parseOperation() throws IOException {
904         String op = parseWord();
905         switch (op) {
906             case "*":
907                 return TemplateManager.O_ANY;
908             case "generate":
909                 return TemplateManager.O_GENERATE;
910             case "import":
911                 return TemplateManager.O_IMPORT;
912             default:
913                 throw excLine("Unknown operation " + op);
914         }
915     }
916 
parseObjectClass()917     private long parseObjectClass() throws IOException {
918         String name = parseWord();
919         try {
920             return Functions.getObjectClassId(name);
921         } catch (IllegalArgumentException e) {
922             throw excLine("Unknown object class " + name);
923         }
924     }
925 
parseKeyAlgorithm()926     private long parseKeyAlgorithm() throws IOException {
927         String name = parseWord();
928         if (isNumber(name)) {
929             return decodeNumber(name);
930         } else {
931             try {
932                 return Functions.getKeyId(name);
933             } catch (IllegalArgumentException e) {
934                 throw excLine("Unknown key algorithm " + name);
935             }
936         }
937     }
938 
decodeAttributeName(String name)939     private long decodeAttributeName(String name) throws IOException {
940         if (isNumber(name)) {
941             return decodeNumber(name);
942         } else {
943             try {
944                 return Functions.getAttributeId(name);
945             } catch (IllegalArgumentException e) {
946                 throw excLine("Unknown attribute name " + name);
947             }
948         }
949     }
950 
decodeAttributeValue(long id, String value)951     private CK_ATTRIBUTE decodeAttributeValue(long id, String value)
952             throws IOException {
953         if (value.equals("null")) {
954             return new CK_ATTRIBUTE(id);
955         } else if (value.equals("true")) {
956             return new CK_ATTRIBUTE(id, true);
957         } else if (value.equals("false")) {
958             return new CK_ATTRIBUTE(id, false);
959         } else if (isByteArray(value)) {
960             return new CK_ATTRIBUTE(id, decodeByteArray(value));
961         } else if (isNumber(value)) {
962             return new CK_ATTRIBUTE(id, Integer.valueOf(decodeNumber(value)));
963         } else {
964             throw excLine("Unknown attribute value " + value);
965         }
966     }
967 
parseNSSArgs(String keyword)968     private void parseNSSArgs(String keyword) throws IOException {
969         checkDup(keyword);
970         parseEquals();
971         int token = nextToken();
972         if (token != '"') {
973             throw excToken("Expected quoted string");
974         }
975         nssArgs = expand(st.sval);
976         debug("nssArgs: " + nssArgs);
977     }
978 
parseHandleStartupErrors(String keyword)979     private void parseHandleStartupErrors(String keyword) throws IOException {
980         checkDup(keyword);
981         parseEquals();
982         String val = parseWord();
983         if (val.equals("ignoreAll")) {
984             handleStartupErrors = ERR_IGNORE_ALL;
985         } else if (val.equals("ignoreMissingLibrary")) {
986             handleStartupErrors = ERR_IGNORE_LIB;
987         } else if (val.equals("halt")) {
988             handleStartupErrors = ERR_HALT;
989         } else {
990             throw excToken("Invalid value for handleStartupErrors:");
991         }
992         debug("handleStartupErrors: " + handleStartupErrors);
993     }
994 
995 }
996 
997 class ConfigurationException extends IOException {
998     private static final long serialVersionUID = 254492758807673194L;
ConfigurationException(String msg)999     ConfigurationException(String msg) {
1000         super(msg);
1001     }
1002 }
1003