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