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