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