1 /*
   2  * Copyright (c) 2003, 2015, 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     private final static boolean DEBUG = false;
  73 
  74     private static void debug(Object o) {
  75         if (DEBUG) {
  76             System.out.println(o);
  77         }
  78     }
  79 
  80     // file name containing this configuration
  81     private String filename;
  82 
  83     // Reader and StringTokenizer used during parsing
  84     private Reader reader;
  85 
  86     private StreamTokenizer st;
  87 
  88     private Set<String> parsedKeywords;
  89 
  90     // name suffix of the provider
  91     private String name;
  92 
  93     // name of the PKCS#11 library
  94     private String library;
  95 
  96     // description to pass to the provider class
  97     private String description;
  98 
  99     // slotID of the slot to use
 100     private int slotID = -1;
 101 
 102     // slot to use, specified as index in the slotlist
 103     private int slotListIndex = -1;
 104 
 105     // set of enabled mechanisms (or null to use default)
 106     private Set<Long> enabledMechanisms;
 107 
 108     // set of disabled mechanisms
 109     private Set<Long> disabledMechanisms;
 110 
 111     // whether to print debug info during startup
 112     private boolean showInfo = false;
 113 
 114     // template manager, initialized from parsed attributes
 115     private TemplateManager templateManager;
 116 
 117     // how to handle error during startup, one of ERR_
 118     private int handleStartupErrors = ERR_HALT;
 119 
 120     // flag indicating whether the P11KeyStore should
 121     // be more tolerant of input parameters
 122     private boolean keyStoreCompatibilityMode = true;
 123 
 124     // flag indicating whether we need to explicitly cancel operations
 125     // see Token
 126     private boolean explicitCancel = true;
 127 
 128     // how often to test for token insertion, if no token is present
 129     private int insertionCheckInterval = 2000;
 130 
 131     // flag inidicating whether to omit the call to C_Initialize()
 132     // should be used only if we are running within a process that
 133     // has already called it (e.g. Plugin inside of Mozilla/NSS)
 134     private boolean omitInitialize = false;
 135 
 136     // whether to allow modules that only support single threaded access.
 137     // they cannot be used safely from multiple PKCS#11 consumers in the
 138     // same process, for example NSS and SunPKCS11
 139     private boolean allowSingleThreadedModules = true;
 140 
 141     // name of the C function that returns the PKCS#11 functionlist
 142     // This option primarily exists for the deprecated
 143     // Secmod.Module.getProvider() method.
 144     private String functionList = "C_GetFunctionList";
 145 
 146     // whether to use NSS secmod mode. Implicitly set if nssLibraryDirectory,
 147     // nssSecmodDirectory, or nssModule is specified.
 148     private boolean nssUseSecmod;
 149 
 150     // location of the NSS library files (libnss3.so, etc.)
 151     private String nssLibraryDirectory;
 152 
 153     // location of secmod.db
 154     private String nssSecmodDirectory;
 155 
 156     // which NSS module to use
 157     private String nssModule;
 158 
 159     private Secmod.DbMode nssDbMode = Secmod.DbMode.READ_WRITE;
 160 
 161     // Whether the P11KeyStore should specify the CKA_NETSCAPE_DB attribute
 162     // when creating private keys. Only valid if nssUseSecmod is true.
 163     private boolean nssNetscapeDbWorkaround = true;
 164 
 165     // Special init argument string for the NSS softtoken.
 166     // This is used when using the NSS softtoken directly without secmod mode.
 167     private String nssArgs;
 168 
 169     // whether to use NSS trust attributes for the KeyStore of this provider
 170     // this option is for internal use by the SunPKCS11 code only and
 171     // works only for NSS providers created via the Secmod API
 172     private boolean nssUseSecmodTrust = false;
 173 
 174     // Flag to indicate whether the X9.63 encoding for EC points shall be used
 175     // (true) or whether that encoding shall be wrapped in an ASN.1 OctetString
 176     // (false).
 177     private boolean useEcX963Encoding = false;
 178 
 179     // Flag to indicate whether NSS should favour performance (false) or
 180     // memory footprint (true).
 181     private boolean nssOptimizeSpace = false;
 182 
 183     Config(String fn) throws IOException {
 184         this.filename = fn;
 185         if (filename.startsWith("--")) {
 186             // inline config
 187             String config = filename.substring(2).replace("\\n", "\n");
 188             reader = new StringReader(config);
 189         } else {
 190             reader = new BufferedReader(new InputStreamReader
 191                 (new FileInputStream(expand(filename))));
 192         }
 193         parsedKeywords = new HashSet<String>();
 194         st = new StreamTokenizer(reader);
 195         setupTokenizer();
 196         parse();
 197     }
 198 
 199     String getFileName() {
 200         return filename;
 201     }
 202 
 203     String getName() {
 204         return name;
 205     }
 206 
 207     String getLibrary() {
 208         return library;
 209     }
 210 
 211     String getDescription() {
 212         if (description != null) {
 213             return description;
 214         }
 215         return "SunPKCS11-" + name + " using library " + library;
 216     }
 217 
 218     int getSlotID() {
 219         return slotID;
 220     }
 221 
 222     int getSlotListIndex() {
 223         if ((slotID == -1) && (slotListIndex == -1)) {
 224             // if neither is set, default to first slot
 225             return 0;
 226         } else {
 227             return slotListIndex;
 228         }
 229     }
 230 
 231     boolean getShowInfo() {
 232         return (SunPKCS11.debug != null) || showInfo;
 233     }
 234 
 235     TemplateManager getTemplateManager() {
 236         if (templateManager == null) {
 237             templateManager = new TemplateManager();
 238         }
 239         return templateManager;
 240     }
 241 
 242     boolean isEnabled(long m) {
 243         if (enabledMechanisms != null) {
 244             return enabledMechanisms.contains(Long.valueOf(m));
 245         }
 246         if (disabledMechanisms != null) {
 247             return !disabledMechanisms.contains(Long.valueOf(m));
 248         }
 249         return true;
 250     }
 251 
 252     int getHandleStartupErrors() {
 253         return handleStartupErrors;
 254     }
 255 
 256     boolean getKeyStoreCompatibilityMode() {
 257         return keyStoreCompatibilityMode;
 258     }
 259 
 260     boolean getExplicitCancel() {
 261         return explicitCancel;
 262     }
 263 
 264     int getInsertionCheckInterval() {
 265         return insertionCheckInterval;
 266     }
 267 
 268     boolean getOmitInitialize() {
 269         return omitInitialize;
 270     }
 271 
 272     boolean getAllowSingleThreadedModules() {
 273         return staticAllowSingleThreadedModules && allowSingleThreadedModules;
 274     }
 275 
 276     String getFunctionList() {
 277         return functionList;
 278     }
 279 
 280     boolean getNssUseSecmod() {
 281         return nssUseSecmod;
 282     }
 283 
 284     String getNssLibraryDirectory() {
 285         return nssLibraryDirectory;
 286     }
 287 
 288     String getNssSecmodDirectory() {
 289         return nssSecmodDirectory;
 290     }
 291 
 292     String getNssModule() {
 293         return nssModule;
 294     }
 295 
 296     Secmod.DbMode getNssDbMode() {
 297         return nssDbMode;
 298     }
 299 
 300     public boolean getNssNetscapeDbWorkaround() {
 301         return nssUseSecmod && nssNetscapeDbWorkaround;
 302     }
 303 
 304     String getNssArgs() {
 305         return nssArgs;
 306     }
 307 
 308     boolean getNssUseSecmodTrust() {
 309         return nssUseSecmodTrust;
 310     }
 311 
 312     boolean getUseEcX963Encoding() {
 313         return useEcX963Encoding;
 314     }
 315 
 316     boolean getNssOptimizeSpace() {
 317         return nssOptimizeSpace;
 318     }
 319 
 320     private static String expand(final String s) throws IOException {
 321         try {
 322             return PropertyExpander.expand(s);
 323         } catch (Exception e) {
 324             throw new RuntimeException(e.getMessage());
 325         }
 326     }
 327 
 328     private void setupTokenizer() {
 329         st.resetSyntax();
 330         st.wordChars('a', 'z');
 331         st.wordChars('A', 'Z');
 332         st.wordChars('0', '9');
 333         st.wordChars(':', ':');
 334         st.wordChars('.', '.');
 335         st.wordChars('_', '_');
 336         st.wordChars('-', '-');
 337         st.wordChars('/', '/');
 338         st.wordChars('\\', '\\');
 339         st.wordChars('$', '$');
 340         st.wordChars('{', '{'); // need {} for property subst
 341         st.wordChars('}', '}');
 342         st.wordChars('*', '*');
 343         st.wordChars('+', '+');
 344         st.wordChars('~', '~');
 345         // XXX check ASCII table and add all other characters except special
 346 
 347         // special: #="(),
 348         st.whitespaceChars(0, ' ');
 349         st.commentChar('#');
 350         st.eolIsSignificant(true);
 351         st.quoteChar('\"');
 352     }
 353 
 354     private ConfigurationException excToken(String msg) {
 355         return new ConfigurationException(msg + " " + st);
 356     }
 357 
 358     private ConfigurationException excLine(String msg) {
 359         return new ConfigurationException(msg + ", line " + st.lineno());
 360     }
 361 
 362     private void parse() throws IOException {
 363         while (true) {
 364             int token = nextToken();
 365             if (token == TT_EOF) {
 366                 break;
 367             }
 368             if (token == TT_EOL) {
 369                 continue;
 370             }
 371             if (token != TT_WORD) {
 372                 throw excToken("Unexpected token:");
 373             }
 374             String word = st.sval;
 375             if (word.equals("name")) {
 376                 name = parseStringEntry(word);
 377             } else if (word.equals("library")) {
 378                 library = parseLibrary(word);
 379             } else if (word.equals("description")) {
 380                 parseDescription(word);
 381             } else if (word.equals("slot")) {
 382                 parseSlotID(word);
 383             } else if (word.equals("slotListIndex")) {
 384                 parseSlotListIndex(word);
 385             } else if (word.equals("enabledMechanisms")) {
 386                 parseEnabledMechanisms(word);
 387             } else if (word.equals("disabledMechanisms")) {
 388                 parseDisabledMechanisms(word);
 389             } else if (word.equals("attributes")) {
 390                 parseAttributes(word);
 391             } else if (word.equals("handleStartupErrors")) {
 392                 parseHandleStartupErrors(word);
 393             } else if (word.endsWith("insertionCheckInterval")) {
 394                 insertionCheckInterval = parseIntegerEntry(word);
 395                 if (insertionCheckInterval < 100) {
 396                     throw excLine(word + " must be at least 100 ms");
 397                 }
 398             } else if (word.equals("showInfo")) {
 399                 showInfo = parseBooleanEntry(word);
 400             } else if (word.equals("keyStoreCompatibilityMode")) {
 401                 keyStoreCompatibilityMode = parseBooleanEntry(word);
 402             } else if (word.equals("explicitCancel")) {
 403                 explicitCancel = parseBooleanEntry(word);
 404             } else if (word.equals("omitInitialize")) {
 405                 omitInitialize = parseBooleanEntry(word);
 406             } else if (word.equals("allowSingleThreadedModules")) {
 407                 allowSingleThreadedModules = parseBooleanEntry(word);
 408             } else if (word.equals("functionList")) {
 409                 functionList = parseStringEntry(word);
 410             } else if (word.equals("nssUseSecmod")) {
 411                 nssUseSecmod = parseBooleanEntry(word);
 412             } else if (word.equals("nssLibraryDirectory")) {
 413                 nssLibraryDirectory = parseLibrary(word);
 414                 nssUseSecmod = true;
 415             } else if (word.equals("nssSecmodDirectory")) {
 416                 nssSecmodDirectory = expand(parseStringEntry(word));
 417                 nssUseSecmod = true;
 418             } else if (word.equals("nssModule")) {
 419                 nssModule = parseStringEntry(word);
 420                 nssUseSecmod = true;
 421             } else if (word.equals("nssDbMode")) {
 422                 String mode = parseStringEntry(word);
 423                 if (mode.equals("readWrite")) {
 424                     nssDbMode = Secmod.DbMode.READ_WRITE;
 425                 } else if (mode.equals("readOnly")) {
 426                     nssDbMode = Secmod.DbMode.READ_ONLY;
 427                 } else if (mode.equals("noDb")) {
 428                     nssDbMode = Secmod.DbMode.NO_DB;
 429                 } else {
 430                     throw excToken("nssDbMode must be one of readWrite, readOnly, and noDb:");
 431                 }
 432                 nssUseSecmod = true;
 433             } else if (word.equals("nssNetscapeDbWorkaround")) {
 434                 nssNetscapeDbWorkaround = parseBooleanEntry(word);
 435                 nssUseSecmod = true;
 436             } else if (word.equals("nssArgs")) {
 437                 parseNSSArgs(word);
 438             } else if (word.equals("nssUseSecmodTrust")) {
 439                 nssUseSecmodTrust = parseBooleanEntry(word);
 440             } else if (word.equals("useEcX963Encoding")) {
 441                 useEcX963Encoding = parseBooleanEntry(word);
 442             } else if (word.equals("nssOptimizeSpace")) {
 443                 nssOptimizeSpace = parseBooleanEntry(word);
 444             } else {
 445                 throw new ConfigurationException
 446                         ("Unknown keyword '" + word + "', line " + st.lineno());
 447             }
 448             parsedKeywords.add(word);
 449         }
 450         reader.close();
 451         reader = null;
 452         st = null;
 453         parsedKeywords = null;
 454         if (name == null) {
 455             throw new ConfigurationException("name must be specified");
 456         }
 457         if (nssUseSecmod == false) {
 458             if (library == null) {
 459                 throw new ConfigurationException("library must be specified");
 460             }
 461         } else {
 462             if (library != null) {
 463                 throw new ConfigurationException
 464                     ("library must not be specified in NSS mode");
 465             }
 466             if ((slotID != -1) || (slotListIndex != -1)) {
 467                 throw new ConfigurationException
 468                     ("slot and slotListIndex must not be specified in NSS mode");
 469             }
 470             if (nssArgs != null) {
 471                 throw new ConfigurationException
 472                     ("nssArgs must not be specified in NSS mode");
 473             }
 474             if (nssUseSecmodTrust != false) {
 475                 throw new ConfigurationException("nssUseSecmodTrust is an "
 476                     + "internal option and must not be specified in NSS mode");
 477             }
 478         }
 479     }
 480 
 481     //
 482     // Parsing helper methods
 483     //
 484 
 485     private int nextToken() throws IOException {
 486         int token = st.nextToken();
 487         debug(st);
 488         return token;
 489     }
 490 
 491     private void parseEquals() throws IOException {
 492         int token = nextToken();
 493         if (token != '=') {
 494             throw excToken("Expected '=', read");
 495         }
 496     }
 497 
 498     private void parseOpenBraces() throws IOException {
 499         while (true) {
 500             int token = nextToken();
 501             if (token == TT_EOL) {
 502                 continue;
 503             }
 504             if ((token == TT_WORD) && st.sval.equals("{")) {
 505                 return;
 506             }
 507             throw excToken("Expected '{', read");
 508         }
 509     }
 510 
 511     private boolean isCloseBraces(int token) {
 512         return (token == TT_WORD) && st.sval.equals("}");
 513     }
 514 
 515     private String parseWord() throws IOException {
 516         int token = nextToken();
 517         if (token != TT_WORD) {
 518             throw excToken("Unexpected value:");
 519         }
 520         return st.sval;
 521     }
 522 
 523     private String parseStringEntry(String keyword) throws IOException {
 524         checkDup(keyword);
 525         parseEquals();
 526 
 527         int token = nextToken();
 528         if (token != TT_WORD && token != '\"') {
 529             // not a word token nor a string enclosed by double quotes
 530             throw excToken("Unexpected value:");
 531         }
 532         String value = st.sval;
 533 
 534         debug(keyword + ": " + value);
 535         return value;
 536     }
 537 
 538     private boolean parseBooleanEntry(String keyword) throws IOException {
 539         checkDup(keyword);
 540         parseEquals();
 541         boolean value = parseBoolean();
 542         debug(keyword + ": " + value);
 543         return value;
 544     }
 545 
 546     private int parseIntegerEntry(String keyword) throws IOException {
 547         checkDup(keyword);
 548         parseEquals();
 549         int value = decodeNumber(parseWord());
 550         debug(keyword + ": " + value);
 551         return value;
 552     }
 553 
 554     private boolean parseBoolean() throws IOException {
 555         String val = parseWord();
 556         switch (val) {
 557             case "true":
 558                 return true;
 559             case "false":
 560                 return false;
 561             default:
 562                 throw excToken("Expected boolean value, read:");
 563         }
 564     }
 565 
 566     private String parseLine() throws IOException {
 567         // allow quoted string as part of line
 568         String s = null;
 569         while (true) {
 570             int token = nextToken();
 571             if ((token == TT_EOL) || (token == TT_EOF)) {
 572                 break;
 573             }
 574             if (token != TT_WORD && token != '\"') {
 575                 throw excToken("Unexpected value");
 576             }
 577             if (s == null) {
 578                 s = st.sval;
 579             } else {
 580                 s = s + " " + st.sval;
 581             }
 582         }
 583         if (s == null) {
 584             throw excToken("Unexpected empty line");
 585         }
 586         return s;
 587     }
 588 
 589     private int decodeNumber(String str) throws IOException {
 590         try {
 591             if (str.startsWith("0x") || str.startsWith("0X")) {
 592                 return Integer.parseInt(str.substring(2), 16);
 593             } else {
 594                 return Integer.parseInt(str);
 595             }
 596         } catch (NumberFormatException e) {
 597             throw excToken("Expected number, read");
 598         }
 599     }
 600 
 601     private static boolean isNumber(String s) {
 602         if (s.length() == 0) {
 603             return false;
 604         }
 605         char ch = s.charAt(0);
 606         return ((ch >= '0') && (ch <= '9'));
 607     }
 608 
 609     private void parseComma() throws IOException {
 610         int token = nextToken();
 611         if (token != ',') {
 612             throw excToken("Expected ',', read");
 613         }
 614     }
 615 
 616     private static boolean isByteArray(String val) {
 617         return val.startsWith("0h");
 618     }
 619 
 620     private byte[] decodeByteArray(String str) throws IOException {
 621         if (str.startsWith("0h") == false) {
 622             throw excToken("Expected byte array value, read");
 623         }
 624         str = str.substring(2);
 625         // XXX proper hex parsing
 626         try {
 627             return new BigInteger(str, 16).toByteArray();
 628         } catch (NumberFormatException e) {
 629             throw excToken("Expected byte array value, read");
 630         }
 631     }
 632 
 633     private void checkDup(String keyword) throws IOException {
 634         if (parsedKeywords.contains(keyword)) {
 635             throw excLine(keyword + " must only be specified once");
 636         }
 637     }
 638 
 639     //
 640     // individual entry parsing methods
 641     //
 642 
 643     private String parseLibrary(String keyword) throws IOException {
 644         checkDup(keyword);
 645         parseEquals();
 646         String lib = parseLine();
 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 }