1 /*
   2  * Copyright (c) 2003, 2020, 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 "/"
 667             String prefix = lib.substring(0, i);
 668             String suffix = lib.substring(i + 5);
 669             lib = prefix + suffix;
 670         }
 671         debug(keyword + ": " + lib);
 672 
 673         // Check to see if full path is specified to prevent the DLL
 674         // preloading attack
 675         if (!(new File(lib)).isAbsolute()) {
 676             throw new ConfigurationException(
 677                 "Absolute path required for library value: " + lib);
 678         }
 679         return lib;
 680     }
 681 
 682     private void parseDescription(String keyword) throws IOException {
 683         checkDup(keyword);
 684         parseEquals();
 685         description = parseLine();
 686         debug("description: " + description);
 687     }
 688 
 689     private void parseSlotID(String keyword) throws IOException {
 690         if (slotID >= 0) {
 691             throw excLine("Duplicate slot definition");
 692         }
 693         if (slotListIndex >= 0) {
 694             throw excLine
 695                 ("Only one of slot and slotListIndex must be specified");
 696         }
 697         parseEquals();
 698         String slotString = parseWord();
 699         slotID = decodeNumber(slotString);
 700         debug("slot: " + slotID);
 701     }
 702 
 703     private void parseSlotListIndex(String keyword) throws IOException {
 704         if (slotListIndex >= 0) {
 705             throw excLine("Duplicate slotListIndex definition");
 706         }
 707         if (slotID >= 0) {
 708             throw excLine
 709                 ("Only one of slot and slotListIndex must be specified");
 710         }
 711         parseEquals();
 712         String slotString = parseWord();
 713         slotListIndex = decodeNumber(slotString);
 714         debug("slotListIndex: " + slotListIndex);
 715     }
 716 
 717     private void parseEnabledMechanisms(String keyword) throws IOException {
 718         enabledMechanisms = parseMechanisms(keyword);
 719     }
 720 
 721     private void parseDisabledMechanisms(String keyword) throws IOException {
 722         disabledMechanisms = parseMechanisms(keyword);
 723     }
 724 
 725     private Set<Long> parseMechanisms(String keyword) throws IOException {
 726         checkDup(keyword);
 727         Set<Long> mechs = new HashSet<Long>();
 728         parseEquals();
 729         parseOpenBraces();
 730         while (true) {
 731             int token = nextToken();
 732             if (isCloseBraces(token)) {
 733                 break;
 734             }
 735             if (token == TT_EOL) {
 736                 continue;
 737             }
 738             if (token != TT_WORD) {
 739                 throw excToken("Expected mechanism, read");
 740             }
 741             long mech = parseMechanism(st.sval);
 742             mechs.add(Long.valueOf(mech));
 743         }
 744         if (DEBUG) {
 745             System.out.print("mechanisms: [");
 746             for (Long mech : mechs) {
 747                 System.out.print(Functions.getMechanismName(mech));
 748                 System.out.print(", ");
 749             }
 750             System.out.println("]");
 751         }
 752         return mechs;
 753     }
 754 
 755     private long parseMechanism(String mech) throws IOException {
 756         if (isNumber(mech)) {
 757             return decodeNumber(mech);
 758         } else {
 759             try {
 760                 return Functions.getMechanismId(mech);
 761             } catch (IllegalArgumentException e) {
 762                 throw excLine("Unknown mechanism: " + mech);
 763             }
 764         }
 765     }
 766 
 767     private void parseAttributes(String keyword) throws IOException {
 768         if (templateManager == null) {
 769             templateManager = new TemplateManager();
 770         }
 771         int token = nextToken();
 772         if (token == '=') {
 773             String s = parseWord();
 774             if (s.equals("compatibility") == false) {
 775                 throw excLine("Expected 'compatibility', read " + s);
 776             }
 777             setCompatibilityAttributes();
 778             return;
 779         }
 780         if (token != '(') {
 781             throw excToken("Expected '(' or '=', read");
 782         }
 783         String op = parseOperation();
 784         parseComma();
 785         long objectClass = parseObjectClass();
 786         parseComma();
 787         long keyAlg = parseKeyAlgorithm();
 788         token = nextToken();
 789         if (token != ')') {
 790             throw excToken("Expected ')', read");
 791         }
 792         parseEquals();
 793         parseOpenBraces();
 794         List<CK_ATTRIBUTE> attributes = new ArrayList<CK_ATTRIBUTE>();
 795         while (true) {
 796             token = nextToken();
 797             if (isCloseBraces(token)) {
 798                 break;
 799             }
 800             if (token == TT_EOL) {
 801                 continue;
 802             }
 803             if (token != TT_WORD) {
 804                 throw excToken("Expected mechanism, read");
 805             }
 806             String attributeName = st.sval;
 807             long attributeId = decodeAttributeName(attributeName);
 808             parseEquals();
 809             String attributeValue = parseWord();
 810             attributes.add(decodeAttributeValue(attributeId, attributeValue));
 811         }
 812         templateManager.addTemplate
 813                 (op, objectClass, keyAlg, attributes.toArray(CK_A0));
 814     }
 815 
 816     private void setCompatibilityAttributes() {
 817         // all secret keys
 818         templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, PCKK_ANY,
 819         new CK_ATTRIBUTE[] {
 820             TOKEN_FALSE,
 821             SENSITIVE_FALSE,
 822             EXTRACTABLE_TRUE,
 823             ENCRYPT_TRUE,
 824             DECRYPT_TRUE,
 825             WRAP_TRUE,
 826             UNWRAP_TRUE,
 827         });
 828 
 829         // generic secret keys are special
 830         // They are used as MAC keys plus for the SSL/TLS (pre)master secrets
 831         templateManager.addTemplate(O_ANY, CKO_SECRET_KEY, CKK_GENERIC_SECRET,
 832         new CK_ATTRIBUTE[] {
 833             SIGN_TRUE,
 834             VERIFY_TRUE,
 835             ENCRYPT_NULL,
 836             DECRYPT_NULL,
 837             WRAP_NULL,
 838             UNWRAP_NULL,
 839             DERIVE_TRUE,
 840         });
 841 
 842         // all private and public keys
 843         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, PCKK_ANY,
 844         new CK_ATTRIBUTE[] {
 845             TOKEN_FALSE,
 846             SENSITIVE_FALSE,
 847             EXTRACTABLE_TRUE,
 848         });
 849         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, PCKK_ANY,
 850         new CK_ATTRIBUTE[] {
 851             TOKEN_FALSE,
 852         });
 853 
 854         // additional attributes for RSA private keys
 855         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_RSA,
 856         new CK_ATTRIBUTE[] {
 857             DECRYPT_TRUE,
 858             SIGN_TRUE,
 859             SIGN_RECOVER_TRUE,
 860             UNWRAP_TRUE,
 861         });
 862         // additional attributes for RSA public keys
 863         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_RSA,
 864         new CK_ATTRIBUTE[] {
 865             ENCRYPT_TRUE,
 866             VERIFY_TRUE,
 867             VERIFY_RECOVER_TRUE,
 868             WRAP_TRUE,
 869         });
 870 
 871         // additional attributes for DSA private keys
 872         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DSA,
 873         new CK_ATTRIBUTE[] {
 874             SIGN_TRUE,
 875         });
 876         // additional attributes for DSA public keys
 877         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_DSA,
 878         new CK_ATTRIBUTE[] {
 879             VERIFY_TRUE,
 880         });
 881 
 882         // additional attributes for DH private keys
 883         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_DH,
 884         new CK_ATTRIBUTE[] {
 885             DERIVE_TRUE,
 886         });
 887 
 888         // additional attributes for EC private keys
 889         templateManager.addTemplate(O_ANY, CKO_PRIVATE_KEY, CKK_EC,
 890         new CK_ATTRIBUTE[] {
 891             SIGN_TRUE,
 892             DERIVE_TRUE,
 893         });
 894         // additional attributes for EC public keys
 895         templateManager.addTemplate(O_ANY, CKO_PUBLIC_KEY, CKK_EC,
 896         new CK_ATTRIBUTE[] {
 897             VERIFY_TRUE,
 898         });
 899     }
 900 
 901     private final static CK_ATTRIBUTE[] CK_A0 = new CK_ATTRIBUTE[0];
 902 
 903     private String parseOperation() throws IOException {
 904         String op = parseWord();
 905         switch (op) {
 906             case "*":
 907                 return TemplateManager.O_ANY;
 908             case "generate":
 909                 return TemplateManager.O_GENERATE;
 910             case "import":
 911                 return TemplateManager.O_IMPORT;
 912             default:
 913                 throw excLine("Unknown operation " + op);
 914         }
 915     }
 916 
 917     private long parseObjectClass() throws IOException {
 918         String name = parseWord();
 919         try {
 920             return Functions.getObjectClassId(name);
 921         } catch (IllegalArgumentException e) {
 922             throw excLine("Unknown object class " + name);
 923         }
 924     }
 925 
 926     private long parseKeyAlgorithm() throws IOException {
 927         String name = parseWord();
 928         if (isNumber(name)) {
 929             return decodeNumber(name);
 930         } else {
 931             try {
 932                 return Functions.getKeyId(name);
 933             } catch (IllegalArgumentException e) {
 934                 throw excLine("Unknown key algorithm " + name);
 935             }
 936         }
 937     }
 938 
 939     private long decodeAttributeName(String name) throws IOException {
 940         if (isNumber(name)) {
 941             return decodeNumber(name);
 942         } else {
 943             try {
 944                 return Functions.getAttributeId(name);
 945             } catch (IllegalArgumentException e) {
 946                 throw excLine("Unknown attribute name " + name);
 947             }
 948         }
 949     }
 950 
 951     private CK_ATTRIBUTE decodeAttributeValue(long id, String value)
 952             throws IOException {
 953         if (value.equals("null")) {
 954             return new CK_ATTRIBUTE(id);
 955         } else if (value.equals("true")) {
 956             return new CK_ATTRIBUTE(id, true);
 957         } else if (value.equals("false")) {
 958             return new CK_ATTRIBUTE(id, false);
 959         } else if (isByteArray(value)) {
 960             return new CK_ATTRIBUTE(id, decodeByteArray(value));
 961         } else if (isNumber(value)) {
 962             return new CK_ATTRIBUTE(id, Integer.valueOf(decodeNumber(value)));
 963         } else {
 964             throw excLine("Unknown attribute value " + value);
 965         }
 966     }
 967 
 968     private void parseNSSArgs(String keyword) throws IOException {
 969         checkDup(keyword);
 970         parseEquals();
 971         int token = nextToken();
 972         if (token != '"') {
 973             throw excToken("Expected quoted string");
 974         }
 975         nssArgs = expand(st.sval);
 976         debug("nssArgs: " + nssArgs);
 977     }
 978 
 979     private void parseHandleStartupErrors(String keyword) throws IOException {
 980         checkDup(keyword);
 981         parseEquals();
 982         String val = parseWord();
 983         if (val.equals("ignoreAll")) {
 984             handleStartupErrors = ERR_IGNORE_ALL;
 985         } else if (val.equals("ignoreMissingLibrary")) {
 986             handleStartupErrors = ERR_IGNORE_LIB;
 987         } else if (val.equals("halt")) {
 988             handleStartupErrors = ERR_HALT;
 989         } else {
 990             throw excToken("Invalid value for handleStartupErrors:");
 991         }
 992         debug("handleStartupErrors: " + handleStartupErrors);
 993     }
 994 
 995 }
 996 
 997 class ConfigurationException extends IOException {
 998     private static final long serialVersionUID = 254492758807673194L;
 999     ConfigurationException(String msg) {
1000         super(msg);
1001     }
1002 }