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