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 nssUseOptimizeSpace = 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 getNssUseOptimizeSpace() {
 337         return nssUseOptimizeSpace;
 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("nssUseOptimizeSpace")) {
 463                 nssUseOptimizeSpace = 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         String s = parseWord();
 588         while (true) {
 589             int token = nextToken();
 590             if ((token == TT_EOL) || (token == TT_EOF)) {
 591                 break;
 592             }
 593             if (token != TT_WORD) {
 594                 throw excToken("Unexpected value");
 595             }
 596             s = s + " " + st.sval;
 597         }
 598         return s;
 599     }
 600 
 601     private int decodeNumber(String str) throws IOException {
 602         try {
 603             if (str.startsWith("0x") || str.startsWith("0X")) {
 604                 return Integer.parseInt(str.substring(2), 16);
 605             } else {
 606                 return Integer.parseInt(str);
 607             }
 608         } catch (NumberFormatException e) {
 609             throw excToken("Expected number, read");
 610         }
 611     }
 612 
 613     private static boolean isNumber(String s) {
 614         if (s.length() == 0) {
 615             return false;
 616         }
 617         char ch = s.charAt(0);
 618         return ((ch >= '0') && (ch <= '9'));
 619     }
 620 
 621     private void parseComma() throws IOException {
 622         int token = nextToken();
 623         if (token != ',') {
 624             throw excToken("Expected ',', read");
 625         }
 626     }
 627 
 628     private static boolean isByteArray(String val) {
 629         return val.startsWith("0h");
 630     }
 631 
 632     private byte[] decodeByteArray(String str) throws IOException {
 633         if (str.startsWith("0h") == false) {
 634             throw excToken("Expected byte array value, read");
 635         }
 636         str = str.substring(2);
 637         // XXX proper hex parsing
 638         try {
 639             return new BigInteger(str, 16).toByteArray();
 640         } catch (NumberFormatException e) {
 641             throw excToken("Expected byte array value, read");
 642         }
 643     }
 644 
 645     private void checkDup(String keyword) throws IOException {
 646         if (parsedKeywords.contains(keyword)) {
 647             throw excLine(keyword + " must only be specified once");
 648         }
 649     }
 650 
 651     //
 652     // individual entry parsing methods
 653     //
 654 
 655     private String parseLibrary(String keyword) throws IOException {
 656         String lib = parseStringEntry(keyword);
 657         lib = expand(lib);
 658         int i = lib.indexOf("/$ISA/");
 659         if (i != -1) {
 660             // replace "/$ISA/" with "/sparcv9/" on 64-bit Solaris SPARC
 661             // and with "/amd64/" on Solaris AMD64.
 662             // On all other platforms, just turn it into a "/"
 663             String osName = System.getProperty("os.name", "");
 664             String osArch = System.getProperty("os.arch", "");
 665             String prefix = lib.substring(0, i);
 666             String suffix = lib.substring(i + 5);
 667             if (osName.equals("SunOS") && osArch.equals("sparcv9")) {
 668                 lib = prefix + "/sparcv9" + suffix;
 669             } else if (osName.equals("SunOS") && osArch.equals("amd64")) {
 670                 lib = prefix + "/amd64" + suffix;
 671             } else {
 672                 lib = prefix + suffix;
 673             }
 674         }
 675         debug(keyword + ": " + lib);
 676 
 677         // Check to see if full path is specified to prevent the DLL
 678         // preloading attack
 679         if (!(new File(lib)).isAbsolute()) {
 680             throw new ConfigurationException(
 681                 "Absolute path required for library value: " + lib);
 682         }
 683         return lib;
 684     }
 685 
 686     private void parseDescription(String keyword) throws IOException {
 687         checkDup(keyword);
 688         parseEquals();
 689         description = parseLine();
 690         debug("description: " + description);
 691     }
 692 
 693     private void parseSlotID(String keyword) throws IOException {
 694         if (slotID >= 0) {
 695             throw excLine("Duplicate slot definition");
 696         }
 697         if (slotListIndex >= 0) {
 698             throw excLine
 699                 ("Only one of slot and slotListIndex must be specified");
 700         }
 701         parseEquals();
 702         String slotString = parseWord();
 703         slotID = decodeNumber(slotString);
 704         debug("slot: " + slotID);
 705     }
 706 
 707     private void parseSlotListIndex(String keyword) throws IOException {
 708         if (slotListIndex >= 0) {
 709             throw excLine("Duplicate slotListIndex definition");
 710         }
 711         if (slotID >= 0) {
 712             throw excLine
 713                 ("Only one of slot and slotListIndex must be specified");
 714         }
 715         parseEquals();
 716         String slotString = parseWord();
 717         slotListIndex = decodeNumber(slotString);
 718         debug("slotListIndex: " + slotListIndex);
 719     }
 720 
 721     private void parseEnabledMechanisms(String keyword) throws IOException {
 722         enabledMechanisms = parseMechanisms(keyword);
 723     }
 724 
 725     private void parseDisabledMechanisms(String keyword) throws IOException {
 726         disabledMechanisms = parseMechanisms(keyword);
 727     }
 728 
 729     private Set<Long> parseMechanisms(String keyword) throws IOException {
 730         checkDup(keyword);
 731         Set<Long> mechs = new HashSet<Long>();
 732         parseEquals();
 733         parseOpenBraces();
 734         while (true) {
 735             int token = nextToken();
 736             if (isCloseBraces(token)) {
 737                 break;
 738             }
 739             if (token == TT_EOL) {
 740                 continue;
 741             }
 742             if (token != TT_WORD) {
 743                 throw excToken("Expected mechanism, read");
 744             }
 745             long mech = parseMechanism(st.sval);
 746             mechs.add(Long.valueOf(mech));
 747         }
 748         if (DEBUG) {
 749             System.out.print("mechanisms: [");
 750             for (Long mech : mechs) {
 751                 System.out.print(Functions.getMechanismName(mech));
 752                 System.out.print(", ");
 753             }
 754             System.out.println("]");
 755         }
 756         return mechs;
 757     }
 758 
 759     private long parseMechanism(String mech) throws IOException {
 760         if (isNumber(mech)) {
 761             return decodeNumber(mech);
 762         } else {
 763             try {
 764                 return Functions.getMechanismId(mech);
 765             } catch (IllegalArgumentException e) {
 766                 throw excLine("Unknown mechanism: " + mech);
 767             }
 768         }
 769     }
 770 
 771     private void parseAttributes(String keyword) throws IOException {
 772         if (templateManager == null) {
 773             templateManager = new TemplateManager();
 774         }
 775         int token = nextToken();
 776         if (token == '=') {
 777             String s = parseWord();
 778             if (s.equals("compatibility") == false) {
 779                 throw excLine("Expected 'compatibility', read " + s);
 780             }
 781             setCompatibilityAttributes();
 782             return;
 783         }
 784         if (token != '(') {
 785             throw excToken("Expected '(' or '=', read");
 786         }
 787         String op = parseOperation();
 788         parseComma();
 789         long objectClass = parseObjectClass();
 790         parseComma();
 791         long keyAlg = parseKeyAlgorithm();
 792         token = nextToken();
 793         if (token != ')') {
 794             throw excToken("Expected ')', read");
 795         }
 796         parseEquals();
 797         parseOpenBraces();
 798         List<CK_ATTRIBUTE> attributes = new ArrayList<CK_ATTRIBUTE>();
 799         while (true) {
 800             token = nextToken();
 801             if (isCloseBraces(token)) {
 802                 break;
 803             }
 804             if (token == TT_EOL) {
 805                 continue;
 806             }
 807             if (token != TT_WORD) {
 808                 throw excToken("Expected mechanism, read");
 809             }
 810             String attributeName = st.sval;
 811             long attributeId = decodeAttributeName(attributeName);
 812             parseEquals();
 813             String attributeValue = parseWord();
 814             attributes.add(decodeAttributeValue(attributeId, attributeValue));
 815         }
 816         templateManager.addTemplate
 817                 (op, objectClass, keyAlg, attributes.toArray(CK_A0));
 818     }
 819 
 820     private void setCompatibilityAttributes() {
 821         // all secret keys
 822         templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, PCKK_ANY,
 823         new CK_ATTRIBUTE[] {
 824             TOKEN_FALSE,
 825             SENSITIVE_FALSE,
 826             EXTRACTABLE_TRUE,
 827             ENCRYPT_TRUE,
 828             DECRYPT_TRUE,
 829             WRAP_TRUE,
 830             UNWRAP_TRUE,
 831         });
 832 
 833         // generic secret keys are special
 834         // They are used as MAC keys plus for the SSL/TLS (pre)master secrets
 835         templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, CKK_GENERIC_SECRET,
 836         new CK_ATTRIBUTE[] {
 837             SIGN_TRUE,
 838             VERIFY_TRUE,
 839             ENCRYPT_NULL,
 840             DECRYPT_NULL,
 841             WRAP_NULL,
 842             UNWRAP_NULL,
 843             DERIVE_TRUE,
 844         });
 845 
 846         // all private and public keys
 847         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, PCKK_ANY,
 848         new CK_ATTRIBUTE[] {
 849             TOKEN_FALSE,
 850             SENSITIVE_FALSE,
 851             EXTRACTABLE_TRUE,
 852         });
 853         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, PCKK_ANY,
 854         new CK_ATTRIBUTE[] {
 855             TOKEN_FALSE,
 856         });
 857 
 858         // additional attributes for RSA private keys
 859         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_RSA,
 860         new CK_ATTRIBUTE[] {
 861             DECRYPT_TRUE,
 862             SIGN_TRUE,
 863             SIGN_RECOVER_TRUE,
 864             UNWRAP_TRUE,
 865         });
 866         // additional attributes for RSA public keys
 867         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_RSA,
 868         new CK_ATTRIBUTE[] {
 869             ENCRYPT_TRUE,
 870             VERIFY_TRUE,
 871             VERIFY_RECOVER_TRUE,
 872             WRAP_TRUE,
 873         });
 874 
 875         // additional attributes for DSA private keys
 876         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DSA,
 877         new CK_ATTRIBUTE[] {
 878             SIGN_TRUE,
 879         });
 880         // additional attributes for DSA public keys
 881         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_DSA,
 882         new CK_ATTRIBUTE[] {
 883             VERIFY_TRUE,
 884         });
 885 
 886         // additional attributes for DH private keys
 887         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DH,
 888         new CK_ATTRIBUTE[] {
 889             DERIVE_TRUE,
 890         });
 891 
 892         // additional attributes for EC private keys
 893         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_EC,
 894         new CK_ATTRIBUTE[] {
 895             SIGN_TRUE,
 896             DERIVE_TRUE,
 897         });
 898         // additional attributes for EC public keys
 899         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_EC,
 900         new CK_ATTRIBUTE[] {
 901             VERIFY_TRUE,
 902         });
 903     }
 904 
 905     private final static CK_ATTRIBUTE[] CK_A0 = new CK_ATTRIBUTE[0];
 906 
 907     private String parseOperation() throws IOException {
 908         String op = parseWord();
 909         switch (op) {
 910             case "*":
 911                 return TemplateManager.O_ANY;
 912             case "generate":
 913                 return TemplateManager.O_GENERATE;
 914             case "import":
 915                 return TemplateManager.O_IMPORT;
 916             default:
 917                 throw excLine("Unknown operation " + op);
 918         }
 919     }
 920 
 921     private long parseObjectClass() throws IOException {
 922         String name = parseWord();
 923         try {
 924             return Functions.getObjectClassId(name);
 925         } catch (IllegalArgumentException e) {
 926             throw excLine("Unknown object class " + name);
 927         }
 928     }
 929 
 930     private long parseKeyAlgorithm() throws IOException {
 931         String name = parseWord();
 932         if (isNumber(name)) {
 933             return decodeNumber(name);
 934         } else {
 935             try {
 936                 return Functions.getKeyId(name);
 937             } catch (IllegalArgumentException e) {
 938                 throw excLine("Unknown key algorithm " + name);
 939             }
 940         }
 941     }
 942 
 943     private long decodeAttributeName(String name) throws IOException {
 944         if (isNumber(name)) {
 945             return decodeNumber(name);
 946         } else {
 947             try {
 948                 return Functions.getAttributeId(name);
 949             } catch (IllegalArgumentException e) {
 950                 throw excLine("Unknown attribute name " + name);
 951             }
 952         }
 953     }
 954 
 955     private CK_ATTRIBUTE decodeAttributeValue(long id, String value)
 956             throws IOException {
 957         if (value.equals("null")) {
 958             return new CK_ATTRIBUTE(id);
 959         } else if (value.equals("true")) {
 960             return new CK_ATTRIBUTE(id, true);
 961         } else if (value.equals("false")) {
 962             return new CK_ATTRIBUTE(id, false);
 963         } else if (isByteArray(value)) {
 964             return new CK_ATTRIBUTE(id, decodeByteArray(value));
 965         } else if (isNumber(value)) {
 966             return new CK_ATTRIBUTE(id, Integer.valueOf(decodeNumber(value)));
 967         } else {
 968             throw excLine("Unknown attribute value " + value);
 969         }
 970     }
 971 
 972     private void parseNSSArgs(String keyword) throws IOException {
 973         checkDup(keyword);
 974         parseEquals();
 975         int token = nextToken();
 976         if (token != '"') {
 977             throw excToken("Expected quoted string");
 978         }
 979         nssArgs = expand(st.sval);
 980         debug("nssArgs: " + nssArgs);
 981     }
 982 
 983     private void parseHandleStartupErrors(String keyword) throws IOException {
 984         checkDup(keyword);
 985         parseEquals();
 986         String val = parseWord();
 987         if (val.equals("ignoreAll")) {
 988             handleStartupErrors = ERR_IGNORE_ALL;
 989         } else if (val.equals("ignoreMissingLibrary")) {
 990             handleStartupErrors = ERR_IGNORE_LIB;
 991         } else if (val.equals("halt")) {
 992             handleStartupErrors = ERR_HALT;
 993         } else {
 994             throw excToken("Invalid value for handleStartupErrors:");
 995         }
 996         debug("handleStartupErrors: " + handleStartupErrors);
 997     }
 998 
 999 }
1000 
1001 class ConfigurationException extends IOException {
1002     private static final long serialVersionUID = 254492758807673194L;
1003     ConfigurationException(String msg) {
1004         super(msg);
1005     }
1006 }