/* * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ import sun.management.jmxremote.ConnectorBootstrap; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.FilenameFilter; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.util.Properties; import java.util.Iterator; import java.util.Set; import java.util.Arrays; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Enumeration; import javax.management.remote.*; import javax.management.*; import sun.management.AgentConfigurationError; import util.TestLogger; /** * <p>This class implements unit test for RMI Bootstrap. * When called with no arguments main() looks in the directory indicated * by the "test.src" system property for files called management*ok.properties * or management*ko.properties. The *ok.properties files are assumed to be * valid Java M&M config files for which the bootstrap should succeed. * The *ko.properties files are assumed to be configurations for which the * bootstrap & connection test will fail.</p> * * <p>The rmi port number can be specified with the "rmi.port" system property. * If not, this test will use 12424</p> * * <p>When called with some argument, the main() will interprete its args to * be Java M&M configuration file names. The filenames are expected to end * with ok.properties or ko.properties - and are interpreted as above.</p> * * <p>Note that a limitation of the RMI registry (bug 4267864) prevent * this test from succeeding if more than 1 configuration is used. * As long as 4267864 isn't fix, this test must be called as many times * as needed but with a single argument (no arguments, or several arguments * will fail).</p> * * <p>Debug traces are logged in "sun.management.test"</p> **/ public class RmiSslNoKeyStoreTest { static TestLogger log = new TestLogger("RmiSslNoKeyStoreTest"); /** * When launching several registries, we increment the port number * to avoid falling into "port number already in use" problems. **/ static int testPort = 0; /** * Default values for RMI configuration properties. **/ public static interface DefaultValues { public static final String PORT="0"; public static final String CONFIG_FILE_NAME="management.properties"; public static final String USE_SSL="true"; public static final String USE_AUTHENTICATION="true"; public static final String PASSWORD_FILE_NAME="jmxremote.password"; public static final String ACCESS_FILE_NAME="jmxremote.access"; public static final String KEYSTORE="keystore"; public static final String KEYSTORE_PASSWD="password"; public static final String TRUSTSTORE="truststore"; public static final String TRUSTSTORE_PASSWD="trustword"; } /** * Names of RMI configuration properties. **/ public static interface PropertyNames { public static final String PORT="com.sun.management.jmxremote.port"; public static final String CONFIG_FILE_NAME= "com.sun.management.config.file"; public static final String USE_SSL="com.sun.management.jmxremote.ssl"; public static final String USE_AUTHENTICATION= "com.sun.management.jmxremote.authenticate"; public static final String PASSWORD_FILE_NAME= "com.sun.management.jmxremote.password.file"; public static final String ACCESS_FILE_NAME= "com.sun.management.jmxremote.access.file"; public static final String INSTRUMENT_ALL= "com.sun.management.instrumentall"; public static final String CREDENTIALS = "jmx.remote.credentials"; public static final String KEYSTORE="javax.net.ssl.keyStore"; public static final String KEYSTORE_PASSWD= "javax.net.ssl.keyStorePassword"; public static final String KEYSTORE_TYPE="javax.net.ssl.keyStoreType"; public static final String TRUSTSTORE="javax.net.ssl.trustStore"; public static final String TRUSTSTORE_PASSWD= "javax.net.ssl.trustStorePassword"; } /** * Compute the full path name for a default file. * @param basename basename (with extension) of the default file. * @return ${JRE}/lib/management/${basename} **/ private static String getDefaultFileName(String basename) { final String fileSeparator = File.separator; final StringBuffer defaultFileName = new StringBuffer(System.getProperty("java.home")). append(fileSeparator).append("lib").append(fileSeparator). append("management").append(fileSeparator). append(basename); return defaultFileName.toString(); } /** * Compute the full path name for a default file. * @param basename basename (with extension) of the default file. * @return ${JRE}/lib/management/${basename} **/ private static String getDefaultStoreName(String basename) { final String fileSeparator = File.separator; final StringBuffer defaultFileName = new StringBuffer(System.getProperty("test.src")). append(fileSeparator).append("ssl").append(fileSeparator). append(basename); return defaultFileName.toString(); } private static void checkKeystore(Properties props) throws IOException, GeneralSecurityException { if (log.isDebugOn()) log.debug("checkKeystore","Checking Keystore configuration"); final String keyStore = System.getProperty(PropertyNames.KEYSTORE); if (keyStore == null) throw new IllegalArgumentException("System property " + PropertyNames.KEYSTORE + " not specified"); final String keyStorePass = System.getProperty(PropertyNames.KEYSTORE_PASSWD); if (keyStorePass == null) { // We don't have the password, we can only check whether the // file exists... // final File ksf = new File(keyStore); if (! ksf.canRead()) throw new IOException(keyStore + ": not readable"); if (log.isDebugOn()) log.debug("checkSSL", "No password."); throw new IllegalArgumentException("System property " + PropertyNames.KEYSTORE_PASSWD + " not specified"); } // Now we're going to load the keyStore - just to check it's // correct. // final String keyStoreType = System.getProperty(PropertyNames.KEYSTORE_TYPE, KeyStore.getDefaultType()); final KeyStore ks = KeyStore.getInstance(keyStoreType); final FileInputStream fin = new FileInputStream(keyStore); final char keypassword[] = keyStorePass.toCharArray(); try { ks.load(fin,keypassword); } finally { Arrays.fill(keypassword,' '); fin.close(); } if (log.isDebugOn()) log.debug("checkSSL","SSL configuration successfully checked"); } private void checkSslConfiguration() throws Exception { final String defaultConf = getDefaultFileName(DefaultValues.CONFIG_FILE_NAME); final String confname = System.getProperty(PropertyNames.CONFIG_FILE_NAME,defaultConf); final Properties props = new Properties(); final File conf = new File(confname); if (conf.exists()) { FileInputStream fin = new FileInputStream(conf); try {props.load(fin);} finally {fin.close();} } // Do we use SSL? final String useSslStr = props.getProperty(PropertyNames.USE_SSL, DefaultValues.USE_SSL); final boolean useSsl = Boolean.valueOf(useSslStr).booleanValue(); log.debug("checkSslConfiguration",PropertyNames.USE_SSL+"="+useSsl); if (useSsl == false) { final String msg = PropertyNames.USE_SSL+"="+useSsl+", can't run test"; throw new IllegalArgumentException(msg); } try { checkKeystore(props); } catch (Exception x) { // Ok! log.debug("checkSslConfiguration","Test configuration OK: " + x); return; } final String msg = "KeyStore properly configured, can't run test"; throw new IllegalArgumentException(msg); } /** * Test the configuration indicated by `file'. * Sets the appropriate System properties for config file and * port and then calls ConnectorBootstrap.initialize(). * eventually cleans up by calling ConnectorBootstrap.terminate(). * @return null if the test succeeds, an error message otherwise. **/ private String testConfiguration(File file,int port) { final String path = (file==null)?null:file.getAbsolutePath(); final String config = (path==null)?"Default config file":path; try { System.out.println("***"); System.out.println("*** Testing configuration (port="+ port + "): "+ path); System.out.println("***"); System.setProperty("com.sun.management.jmxremote.port", Integer.toString(port)); if (path != null) System.setProperty("com.sun.management.config.file", path); else System.getProperties(). remove("com.sun.management.config.file"); log.trace("testConfiguration","com.sun.management.jmxremote.port="+port); if (path != null && log.isDebugOn()) log.trace("testConfiguration", "com.sun.management.config.file="+path); checkSslConfiguration(); final JMXConnectorServer cs; try { cs = ConnectorBootstrap.initialize(); } catch (AgentConfigurationError x) { final String err = "Failed to initialize connector:" + "\n\tcom.sun.management.jmxremote.port=" + port + ((path!=null)?"\n\tcom.sun.management.config.file="+path: "\n\t"+config) + "\n\tError is: " + x; log.trace("testConfiguration","Expected failure: " + err); log.debug("testConfiguration",x); System.out.println("Got expected failure: " + x); return null; } catch (Exception x) { log.debug("testConfiguration",x); return x.toString(); } try { JMXConnector cc = JMXConnectorFactory.connect(cs.getAddress(), null); cc.close(); } catch (IOException x) { final String err = "Failed to initialize connector:" + "\n\tcom.sun.management.jmxremote.port=" + port + ((path!=null)?"\n\tcom.sun.management.config.file="+path: "\n\t"+config) + "\n\tError is: " + x; log.trace("testConfiguration","Expected failure: " + err); log.debug("testConfiguration",x); System.out.println("Got expected failure: " + x); return null; } catch (Exception x) { log.debug("testConfiguration",x); return x.toString(); } try { cs.stop(); } catch (Exception x) { final String err = "Failed to terminate: "+x; log.trace("testConfiguration",err); log.debug("testConfiguration",x); } final String err = "Bootstrap should have failed:" + "\n\tcom.sun.management.jmxremote.port=" + port + ((path!=null)?"\n\tcom.sun.management.config.file="+path: "\n\t"+config); log.trace("testConfiguration",err); return err; } catch (Exception x) { final String err = "Failed to test bootstrap for:" + "\n\tcom.sun.management.jmxremote.port=" + port + ((path!=null)?"\n\tcom.sun.management.config.file="+path: "\n\t"+config)+ "\n\tError is: " + x; log.trace("testConfiguration",err); log.debug("testConfiguration",x); return err; } } /** * Test a configuration file. Determines whether the bootstrap * should succeed or fail depending on the file name: * *ok.properties: bootstrap should succeed. * *ko.properties: bootstrap or connection should fail. * @return null if the test succeeds, an error message otherwise. **/ private String testConfigurationFile(String fileName) { File file = new File(fileName); final String portStr = System.getProperty("rmi.port","12424"); final int port = Integer.parseInt(portStr); return testConfiguration(file,port+testPort++); } /** * Tests the specified configuration files. * If args[] is not empty, each element in args[] is expected to be * a filename ending either by ok.properties or ko.properties. * Otherwise, the configuration files will be automatically determined * by looking at all *.properties files located in the directory * indicated by the System property "test.src". * @throws RuntimeException if the test fails. **/ public void run(String args[]) { final String defaultKeyStore = getDefaultStoreName(DefaultValues.KEYSTORE); final String keyStore = System.getProperty(PropertyNames.KEYSTORE, defaultKeyStore); for (int i=0; i<args.length; i++) { String errStr =testConfigurationFile(args[i]); if (errStr != null) { throw new RuntimeException(errStr); } if ((System.getProperty(PropertyNames.KEYSTORE) == null) && (System.getProperty(PropertyNames.KEYSTORE_PASSWD) == null)) { try { // Specify the keystore, but don't specify the // password. // System.setProperty(PropertyNames.KEYSTORE,keyStore); log.trace("run",PropertyNames.KEYSTORE+"="+keyStore); errStr =testConfigurationFile(args[i]); if (errStr != null) { throw new RuntimeException(errStr); } } finally { System.getProperties().remove(PropertyNames.KEYSTORE); } } } } /** * Calls run(args[]). * exit(1) if the test fails. **/ public static void main(String args[]) { RmiSslNoKeyStoreTest manager = new RmiSslNoKeyStoreTest(); try { manager.run(args); } catch (RuntimeException r) { System.err.println("Test Failed: "+ r.getMessage()); System.exit(1); } catch (Throwable t) { System.err.println("Test Failed: "+ t); t.printStackTrace(); System.exit(2); } System.out.println("**** Test RmiSslNoKeyStoreTest Passed ****"); } }