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