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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @bug 8058865
27 * @summary Checks various secure ways of connecting from remote jmx client
28 * @author Olivier Lagneau
29 * @modules java.management.rmi
30 * @library /lib/testlibrary
31 * @compile MBS_Light.java ServerDelegate.java TestSampleLoginModule.java
32 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=SQE_username -Dpassword=SQE_password SecurityTest -server -mapType x.password.file -client -mapType credentials
33 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=UNKNOWN_username -Dpassword=SQE_password SecurityTest -server -mapType x.password.file -client -mapType credentials -expectedThrowable java.lang.SecurityException
34 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dusername=SQE_username -Dpassword=WRONG_password SecurityTest -server -mapType x.password.file -client -mapType credentials -expectedThrowable java.lang.SecurityException
35 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dsusername=TestJMXAuthenticatorUsername -Dspassword=TestJMXAuthenticatorPassword -Dusername=TestJMXAuthenticatorUsername -Dpassword=TestJMXAuthenticatorPassword SecurityTest -server -mapType x.authenticator -client -mapType credentials
36 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dsusername=TestJMXAuthenticatorUsername -Dspassword=TestJMXAuthenticatorPassword -Dusername=AnotherTestJMXAuthenticatorUsername -Dpassword=TestJMXAuthenticatorPassword SecurityTest -server -mapType x.authenticator -client -mapType credentials -expectedThrowable java.lang.SecurityException
37 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dpassword.file=password.properties -Dusername=usernameFileLoginModule -Dpassword=passwordFileLoginModule SecurityTest -server -mapType x.login.config.PasswordFileAuthentication -client -mapType credentials
38 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config.UNKNOWN -Dpassword.file=password.properties -Dusername=usernameFileLoginModule -Dpassword=passwordFileLoginModule SecurityTest -server -mapType x.login.config.PasswordFileAuthentication -client -mapType credentialss -expectedThrowable java.lang.SecurityException
39 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dpassword.file=password.properties -Dusername=usernameFileLoginModule -Dpassword=passwordFileLoginModule SecurityTest -server -mapType x.login.config.UnknownAuthentication -client -mapType credentials -expectedThrowable java.lang.SecurityException
40 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dsusername=usernameSampleLoginModule -Dspassword=passwordSampleLoginModule -Dpassword.file=password.properties -Dusername=usernameSampleLoginModule -Dpassword=passwordSampleLoginModule SecurityTest -server -mapType x.login.config.SampleLoginModule -client -mapType credentials
41 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Dlogin.config.file=${test.src}/login.config -Dsusername=usernameSampleLoginModule -Dspassword=passwordSampleLoginModule -Dpassword.file=password.properties -Dusername=AnotherUsernameSampleLoginModule -Dpassword=passwordSampleLoginModule SecurityTest -server -mapType x.login.config.SampleLoginModule -client -mapType credentials -expectedThrowable java.lang.SecurityException
42 * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop
43 * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword WRONG_password -expectedThrowable java.io.IOException
44 * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
45 * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
46 * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl -keystore keystoreAgent -keystorepassword glopglop -client -expectedThrowable java.io.IOException
47 * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -truststore truststoreAgent -truststorepassword glopglop -client  -keystore keystoreClient -keystorepassword glopglop -truststore truststoreClient -truststorepassword glopglop
48 * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -truststore truststoreAgent -truststorepassword glopglop -client -keystore keystoreClient -keystorepassword WRONG_password -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
49 * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -truststore truststoreAgent -truststorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
50 * @run main/othervm/timeout=300 -DDEBUG_STANDARD SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.need.client.authentication -keystore keystoreAgent -keystorepassword glopglop -client -keystore keystoreClient -keystorepassword glopglop -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
51 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_MD5 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.cipher.suites.md5 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop
52 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_SHA SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.cipher.suites.md5 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
53 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledCipherSuites=SSL_RSA_WITH_RC4_128_MD5 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.cipher.suites.sha -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
54 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledProtocols=SSLv3 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.protocols.sslv3 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop
55 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledProtocols=TLSv1 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.protocols.sslv3 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
56 * @run main/othervm/timeout=300 -DDEBUG_STANDARD -Djavax.rmi.ssl.client.enabledProtocols=SSLv3 SecurityTest -server -mapType rmi.client.socket.factory.ssl;rmi.server.socket.factory.ssl.enabled.protocols.tlsv1 -keystore keystoreAgent -keystorepassword glopglop -client -truststore truststoreClient -truststorepassword glopglop -expectedThrowable java.io.IOException
57 */
58
59import java.io.File;
60import java.util.Map ;
61import java.util.HashMap ;
62import java.util.List;
63import java.util.ArrayList;
64import java.util.Arrays;
65
66import javax.management.MBeanServer;
67import javax.management.MBeanServerFactory ;
68import javax.management.MBeanServerConnection;
69import javax.management.remote.JMXConnector;
70import javax.management.remote.JMXConnectorFactory;
71import javax.management.remote.JMXConnectorServer;
72import javax.management.remote.JMXConnectorServerFactory;
73import javax.management.remote.JMXServiceURL;
74
75import javax.management.Attribute ;
76import javax.management.ObjectName ;
77
78import javax.rmi.ssl.SslRMIClientSocketFactory;
79import javax.rmi.ssl.SslRMIServerSocketFactory;
80
81import java.security.Security;
82
83import jdk.testlibrary.ProcessTools;
84import jdk.testlibrary.JDKToolFinder;
85
86public class SecurityTest {
87
88    static final String SERVER_CLASS_NAME = "SecurityTest";
89    static final String CLIENT_CLASS_NAME = "SecurityTest$ClientSide";
90    static final String CLIENT_CLASS_MAIN = CLIENT_CLASS_NAME;
91
92    static final String USERNAME_PROPERTY = "username";
93    static final String PASSWORD_PROPERTY = "password";
94
95    static final String SERVER_DELEGATE_MBEAN_NAME =
96        "defaultDomain:class=ServerDelegate";
97
98    static final String RMI_SERVER_SOCKET_FACTORY_SSL = "rmi.server.socket.factory.ssl";
99    static final String RMI_CLIENT_SOCKET_FACTORY_SSL = "rmi.client.socket.factory.ssl";
100    static final String KEYSTORE_PROPNAME = "javax.net.ssl.keyStore";
101    static final String KEYSTORE_PWD_PROPNAME = "javax.net.ssl.keyStorePassword";
102    static final String TRUSTSTORE_PROPNAME = "javax.net.ssl.trustStore";
103    static final String TRUSTSTORE_PWD_PROPNAME = "javax.net.ssl.trustStorePassword";
104
105    static final String RMI_SSL_CLIENT_ENABLEDCIPHERSUITES =
106        "javax.rmi.ssl.client.enabledCipherSuites";
107    static final String RMI_SSL_CLIENT_ENABLEDPROTOCOLS =
108        "javax.rmi.ssl.client.enabledProtocols";
109
110    private JMXConnectorServer cs;
111
112    // Construct and set keyStore properties from given map
113    static void setKeyStoreProperties(Map<String, Object> map) {
114
115        String keyStore = (String) map.get("-keystore");
116        keyStore = buildSourcePath(keyStore);
117        System.setProperty(KEYSTORE_PROPNAME, keyStore);
118        System.out.println("keyStore location = \"" + keyStore + "\"");
119
120        String password = (String) map.get("-keystorepassword");
121        System.setProperty(KEYSTORE_PWD_PROPNAME, password);
122        System.out.println("keyStore password = " + password);
123
124    }
125
126    // Construct and set trustStore properties from given map
127    static void setTrustStoreProperties(Map<String, Object> map) {
128
129        String trustStore = (String) map.get("-truststore");
130        trustStore = buildSourcePath(trustStore);
131        System.setProperty(TRUSTSTORE_PROPNAME, trustStore);
132        System.out.println("trustStore location = \"" + trustStore + "\"");
133
134        String password = (String) map.get("-truststorepassword");
135        System.setProperty(TRUSTSTORE_PWD_PROPNAME, password);
136        System.out.println("trustStore password = " + password);
137
138    }
139
140    /*
141     * First Debug properties and arguments are collect in expected
142     * map  (argName, value) format, then calls original test's run method.
143     */
144    public static void main(String args[]) throws Exception {
145
146        System.out.println("=================================================");
147
148        // Parses parameters
149        Utils.parseDebugProperties();
150
151        // Supported parameters list format is :
152        // "MainClass [-server <param-spec> ...] [-client <param-spec> ...]
153        // with <param-spec> either "-parami valuei" or "-parami"
154        HashMap<String, Object> serverMap = new HashMap<>() ;
155        int clientArgsIndex =
156            Utils.parseServerParameters(args, SERVER_CLASS_NAME, serverMap);
157
158        // Extract and records client params
159        String[] clientParams = null;
160        if (clientArgsIndex < args.length) {
161            int clientParamsSize = args.length - clientArgsIndex;
162            clientParams = new String[clientParamsSize];
163            System.arraycopy(args, clientArgsIndex, clientParams, 0, clientParamsSize);
164        } else {
165            clientParams = new String[0];
166        }
167
168        // Run test
169        SecurityTest test = new SecurityTest();
170        test.run(serverMap, clientParams);
171
172    }
173
174    // Return full path of filename in the test sopurce directory
175    private static String buildSourcePath(String filename) {
176        return System.getProperty("test.src") + File.separator + filename;
177    }
178
179    /*
180     * Collects security run params for server side.
181     */
182    private HashMap<String, Object> setServerSecurityEnv(Map<String, Object> map)
183    throws Exception {
184
185        // Creates Authentication environment from server side params
186        HashMap<String, Object> env = new HashMap<>();
187
188        // Retrieve and set keystore and truststore config if any
189        if (map.containsKey("-keystore") &&
190            map.get("-keystore") != null) {
191            setKeyStoreProperties(map);
192        }
193        System.out.println("Done keystore properties");
194
195        if (map.containsKey("-truststore") &&
196            map.get("-truststore") != null) {
197            setTrustStoreProperties(map);
198        }
199        System.out.println("Done truststore properties");
200
201        String value = null;
202        if ((value = (String)map.get("-mapType")) != null) {
203
204            // Case of remote password file with all authorized credentials
205            if (value.contains("x.password.file")) {
206                String passwordFileStr = buildSourcePath("password.properties");
207                env.put("jmx.remote.x.password.file", passwordFileStr);
208                System.out.println("Added " + passwordFileStr +
209                    " file as jmx.remote.x.password.file");
210            }
211
212            // Case of dedicated authenticator class : TestJMXAuthenticator
213            if (value.contains("x.authenticator")) {
214                env.put("jmx.remote.authenticator", new TestJMXAuthenticator()) ;
215                System.out.println(
216                    "Added \"jmx.remote.authenticator\" = TestJMXAuthenticator");
217            }
218
219            // Case of security config file with standard Authentication
220            if (value.contains("x.login.config.PasswordFileAuthentication")) {
221                String loginConfig = System.getProperty("login.config.file");
222
223                // Override the default JAAS configuration
224                System.setProperty("java.security.auth.login.config",
225                    "file:" + loginConfig);
226                System.out.println("Overrided default JAAS configuration with " +
227                    "\"java.security.auth.login.config\" = \"" + loginConfig + "\"") ;
228
229                env.put("jmx.remote.x.login.config", "PasswordFileAuthentication") ;
230                System.out.println(
231                    "Added \"jmx.remote.x.login.config\" = " +
232                    "\"PasswordFileAuthentication\"") ;
233
234                // redirects "password.file" property to file in ${test.src}
235                String passwordFileStr =
236                    buildSourcePath(System.getProperty("password.file"));
237                System.setProperty("password.file", passwordFileStr);
238                System.out.println(
239                    "Redirected \"password.file\" property value to = " +
240                    passwordFileStr) ;
241            }
242
243            // Case of security config file with unexisting athentication config
244            if (value.contains("x.login.config.UnknownAuthentication")) {
245                String loginConfig = System.getProperty("login.config.file");
246
247                // Override the default JAAS configuration
248                System.setProperty("java.security.auth.login.config",
249                        "file:" + loginConfig);
250                System.out.println("Overrided default JAAS configuration with " +
251                    "\"java.security.auth.login.config\" = \"" + loginConfig + "\"") ;
252
253                env.put("jmx.remote.x.login.config", "UnknownAuthentication") ;
254                System.out.println(
255                    "Added \"jmx.remote.x.login.config\" = " +
256                    "\"UnknownAuthentication\"") ;
257
258                // redirects "password.file" property to file in ${test.src}
259                 String passwordFileStr =
260                   buildSourcePath(System.getProperty("password.file"));
261                System.setProperty("password.file", passwordFileStr);
262                System.out.println(
263                    "Redirected \"password.file\" property value to = " +
264                    passwordFileStr) ;
265            }
266
267            // Case of security config file with dedicated login module
268            if (value.contains("x.login.config.SampleLoginModule")) {
269                String loginConfig = System.getProperty("login.config.file");
270
271                // Override the default JAAS configuration
272                System.setProperty("java.security.auth.login.config",
273                        "file:" + loginConfig);
274                System.out.println("Overrided default JAAS configuration with " +
275                    "\"java.security.auth.login.config\" = \"" + loginConfig + "\"") ;
276
277                env.put("jmx.remote.x.login.config", "SampleLoginModule") ;
278                System.out.println(
279                    "Added \"jmx.remote.x.login.config\" = " +
280                    "\"SampleLoginModule\"") ;
281            }
282
283            // Simple rmi ssl authentication
284            if (value.contains(RMI_CLIENT_SOCKET_FACTORY_SSL)) {
285                env.put("jmx.remote.rmi.client.socket.factory",
286                    new SslRMIClientSocketFactory()) ;
287                System.out.println(
288                     "Added \"jmx.remote.rmi.client.socket.factory\"" +
289                     " = SslRMIClientSocketFactory") ;
290            }
291
292            if (value.contains(RMI_SERVER_SOCKET_FACTORY_SSL)) {
293                if (value.contains(
294                        "rmi.server.socket.factory.ssl.need.client.authentication")) {
295                   // rmi ssl authentication with client authentication
296                   env.put("jmx.remote.rmi.server.socket.factory",
297                       new SslRMIServerSocketFactory(null, null, true)) ;
298                   System.out.println(
299                       "Added \"jmx.remote.rmi.server.socket.factory\"" +
300                       " = SslRMIServerSocketFactory with client authentication") ;
301
302                } else if (value.contains("rmi.server.socket.factory.ssl.enabled.cipher.suites.md5")) {
303                    // Allows all ciphering and protocols for testing purpose
304                    Security.setProperty("jdk.tls.disabledAlgorithms", "");
305
306                    env.put("jmx.remote.rmi.server.socket.factory",
307                        new SslRMIServerSocketFactory(
308                            new String[] {"SSL_RSA_WITH_RC4_128_MD5"}, null, false));
309                    System.out.println(
310                        "Added \"jmx.remote.rmi.server.socket.factory\"" +
311                        " = SslRMIServerSocketFactory with SSL_RSA_WITH_RC4_128_MD5 cipher suite");
312
313                } else if (value.contains("rmi.server.socket.factory.ssl.enabled.cipher.suites.sha")) {
314                    // Allows all ciphering and protocols for testing purpose
315                    Security.setProperty("jdk.tls.disabledAlgorithms", "");
316
317                    env.put("jmx.remote.rmi.server.socket.factory",
318                        new SslRMIServerSocketFactory(
319                            new String[] { "SSL_RSA_WITH_RC4_128_SHA" }, null, false)) ;
320                    System.out.println(
321                        "Added \"jmx.remote.rmi.server.socket.factory\"" +
322                        " = SslRMIServerSocketFactory with SSL_RSA_WITH_RC4_128_SHA cipher suite") ;
323
324                } else if (value.contains("rmi.server.socket.factory.ssl.enabled.protocols.sslv3")) {
325                    // Allows all ciphering and protocols for testing purpose
326                    Security.setProperty("jdk.tls.disabledAlgorithms", "");
327
328                    env.put("jmx.remote.rmi.server.socket.factory",
329                        new SslRMIServerSocketFactory(null, new String[] {"SSLv3"}, false)) ;
330                    System.out.println(
331                        "Added \"jmx.remote.rmi.server.socket.factory\"" +
332                        " = SslRMIServerSocketFactory with SSLv3 protocol") ;
333
334                } else if (value.contains("rmi.server.socket.factory.ssl.enabled.protocols.tlsv1")) {
335                    // Allows all ciphering and protocols for testing purpose
336                    Security.setProperty("jdk.tls.disabledAlgorithms", "");
337
338                    env.put("jmx.remote.rmi.server.socket.factory",
339                        new SslRMIServerSocketFactory(null, new String[] {"TLSv1"}, false)) ;
340                    System.out.println(
341                        "Added \"jmx.remote.rmi.server.socket.factory\"" +
342                        " = SslRMIServerSocketFactory with TLSv1 protocol") ;
343
344                } else {
345                    env.put("jmx.remote.rmi.server.socket.factory",
346                        new SslRMIServerSocketFactory());
347                    System.out.println(
348                        "Added \"jmx.remote.rmi.server.socket.factory\"" +
349                        " = SslRMIServerSocketFactory");
350                }
351            }
352        }
353
354        return env;
355    }
356
357    /*
358     * Create the MBeansServer side of the test and returns its address
359     */
360    private JMXServiceURL createServerSide(Map<String, Object> serverMap)
361    throws Exception {
362        final int NINETY_SECONDS = 90;
363
364        System.out.println("SecurityTest::createServerSide: Start") ;
365
366        // Prepare server side security env
367        HashMap<String, Object> env = setServerSecurityEnv(serverMap);
368
369        // Create and start mbean server and connector server
370        MBeanServer mbs = MBeanServerFactory.newMBeanServer();
371        JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
372        cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
373        cs.start();
374
375        // Waits availibility of connector server
376        Utils.waitReady(cs, NINETY_SECONDS);
377
378        JMXServiceURL addr = cs.getAddress();
379
380        System.out.println("SecurityTest::createServerSide: Done.") ;
381
382        return addr;
383    }
384
385    /*
386     * Creating command-line for running subprocess JVM:
387     *
388     * JVM command line is like:
389     * {test_jdk}/bin/java {defaultopts} -cp {test.class.path} {testopts} main
390     *
391     * {defaultopts} are the default java options set by the framework.
392     *
393     */
394    private List<String> buildCommandLine(String args[]) {
395
396        System.out.println("SecurityTest::buildCommandLine: Start") ;
397
398        List<String> opts = new ArrayList<>();
399        opts.add(JDKToolFinder.getJDKTool("java"));
400        opts.addAll(Arrays.asList(jdk.testlibrary.Utils.getTestJavaOpts()));
401
402        // We need to forward some properties to the client side
403        opts.add("-Dtest.src=" + System.getProperty("test.src"));
404
405        String usernameValue = System.getProperty(USERNAME_PROPERTY);
406        if (usernameValue != null) {
407            System.out.println("SecurityTest::buildCommandLine: "+
408                " forward username property to client side");
409            opts.add("-D" + USERNAME_PROPERTY + "=" + usernameValue);
410        }
411        String passwordValue = System.getProperty(PASSWORD_PROPERTY);
412        if (passwordValue != null) {
413            System.out.println("SecurityTest::buildCommandLine: "+
414                " forward password property to client side");
415            opts.add("-D" + PASSWORD_PROPERTY + "=" + passwordValue);
416        }
417
418        String enabledCipherSuites =
419            System.getProperty(RMI_SSL_CLIENT_ENABLEDCIPHERSUITES);
420        if (enabledCipherSuites != null) {
421            System.out.println("SecurityTest::buildCommandLine: "+
422                " forward enabledCipherSuites property to client side");
423            opts.add("-D" + RMI_SSL_CLIENT_ENABLEDCIPHERSUITES +
424                "=" + enabledCipherSuites);
425        }
426
427        String enabledProtocols =
428            System.getProperty(RMI_SSL_CLIENT_ENABLEDPROTOCOLS);
429        if (enabledProtocols != null) {
430            System.out.println("SecurityTest::buildCommandLine: "+
431                " forward enabledProtocols property to client side");
432            opts.add("-D" + RMI_SSL_CLIENT_ENABLEDPROTOCOLS +
433                "=" + enabledProtocols);
434        }
435
436        opts.add("-cp");
437        opts.add(System.getProperty("test.class.path", "test.class.path"));
438        opts.add(CLIENT_CLASS_MAIN);
439        opts.addAll(Arrays.asList(args));
440
441        System.out.println("SecurityTest::buildCommandLine: Done.") ;
442
443        return opts;
444    }
445
446    /**
447     * Runs SecurityTest$ClientSide with the passed options and redirects
448     * subprocess standard I/O to the current (parent) process. This provides a
449     * trace of what happens in the subprocess while it is runnning (and before
450     * it terminates).
451     *
452     * @param serviceUrlStr string representing the JMX service Url to connect to.
453     */
454    private int runClientSide(String args[], String serviceUrlStr) throws Exception {
455
456        System.out.println("SecurityTest::runClientSide: Start") ;
457
458        // Building command-line
459        List<String> opts = buildCommandLine(args);
460        opts.add("-serviceUrl");
461        opts.add(serviceUrlStr);
462
463        // Launch separate JVM subprocess
464        int exitCode = 0;
465        String[] optsArray = opts.toArray(new String[0]);
466        ProcessBuilder pb = new ProcessBuilder(optsArray);
467        Process p = ProcessTools.startProcess("SecurityTest$ClientSide", pb);
468
469        // Handling end of subprocess
470        try {
471            exitCode = p.waitFor();
472            if (exitCode != 0) {
473                System.out.println(
474                    "Subprocess unexpected exit value of [" + exitCode +
475                    "]. Expected 0.\n");
476            }
477        } catch (InterruptedException e) {
478            System.out.println("Parent process interrupted with exception : \n " + e + " :" );
479
480            // Parent thread unknown state, killing subprocess.
481            p.destroyForcibly();
482
483            throw new RuntimeException(
484                "Parent process interrupted with exception : \n " + e + " :" );
485
486        } finally {
487            if (p.isAlive()) {
488                p.destroyForcibly();
489            }
490
491            System.out.println("SecurityTest::runClientSide: Done") ;
492
493            return exitCode;
494        }
495
496     }
497
498    public void run(Map<String, Object> serverArgs, String clientArgs[]) {
499
500        System.out.println("SecurityTest::run: Start") ;
501        int errorCount = 0;
502
503        try {
504            // Initialise the server side
505            JMXServiceURL urlToUse = createServerSide(serverArgs);
506
507            // Run client side
508            errorCount = runClientSide(clientArgs, urlToUse.toString());
509
510            if ( errorCount == 0 ) {
511                System.out.println("SecurityTest::run: Done without any error") ;
512            } else {
513                System.out.println(
514                    "SecurityTest::run: Done with " + errorCount + " error(s)");
515                throw new RuntimeException("errorCount = " + errorCount);
516            }
517
518            cs.stop();
519
520        } catch(Exception e) {
521            throw new RuntimeException(e);
522        }
523
524    }
525
526    private static class ClientSide {
527
528        private JMXConnector cc = null;
529        private MBeanServerConnection mbsc = null;
530
531        public static void main(String args[]) throws Exception {
532
533            // Parses parameters
534            Utils.parseDebugProperties();
535
536            // Supported parameters list format is : "MainClass [-client <param-spec> ...]
537            // with <param-spec> either "-parami valuei" or "-parami"
538            HashMap<String, Object> clientMap = new HashMap<>() ;
539            Utils.parseClientParameters(args, CLIENT_CLASS_NAME, clientMap);
540
541            // Run test
542            ClientSide test = new ClientSide();
543            test.run(clientMap);
544        }
545
546        public void run(Map<String, Object> args) {
547
548            System.out.println("ClientSide::run: Start");
549            int errorCount = 0;
550
551            try {
552                // Setup client side parameters
553                HashMap<String, Object> env = new HashMap<>();
554
555                // If needed allows all ciphering and protocols for testing purpose
556                if (System.getProperty(RMI_SSL_CLIENT_ENABLEDCIPHERSUITES) != null) {
557                    Security.setProperty("jdk.tls.disabledAlgorithms", "");
558                }
559
560                // If needed allows all ciphering and protocols for testing purpose
561                if (System.getProperty(RMI_SSL_CLIENT_ENABLEDPROTOCOLS) != null) {
562                    Security.setProperty("jdk.tls.disabledAlgorithms", "");
563                }
564
565                // Retrieve and set keystore and truststore config if any
566                if (args.containsKey("-keystore") &&
567                    args.get("-keystore") != null) {
568                    SecurityTest.setKeyStoreProperties(args);
569                }
570                if (args.containsKey("-truststore") &&
571                    args.get("-truststore") != null) {
572                    SecurityTest.setTrustStoreProperties(args);
573                }
574
575                Object value = args.get("-mapType");
576                if ((value != null) &&
577                    value.equals("credentials")) {
578                    String username = System.getProperty("username");
579                    String password = System.getProperty("password");
580                    Utils.debug(Utils.DEBUG_STANDARD,
581                        "add \"jmx.remote.credentials\" = \"" +
582                        username + "\", \"" + password + "\"");
583                    env.put("jmx.remote.credentials",
584                        new String[] { username , password });
585                }
586
587                String expectedThrowable = (String) args.get("-expectedThrowable");
588
589                String authCallCountName = "-expectedAuthenticatorCallCount";
590                int authCallCountValue = 0;
591                if (args.containsKey(authCallCountName)) {
592                    authCallCountValue =
593                        (new Integer((String) args.get(authCallCountName))).intValue();
594                }
595
596                try {
597                    // Get a connection to remote mbean server
598                    JMXServiceURL addr = new JMXServiceURL((String)args.get("-serviceUrl"));
599                    cc = JMXConnectorFactory.connect(addr,env);
600                    mbsc = cc.getMBeanServerConnection();
601
602                    // In case we should have got an exception
603                    if (expectedThrowable != null) {
604                        System.out.println("ClientSide::run: (ERROR) " +
605                            " Connect did not fail with expected " + expectedThrowable);
606                        errorCount++;
607                    } else {
608                        System.out.println("ClientSide::run: (OK) Connect succeed");
609                    }
610                } catch (Throwable e) {
611                    Utils.printThrowable(e, true);
612                    if (expectedThrowable != null) {
613                        if (Utils.compareThrowable(e, expectedThrowable)) {
614                            System.out.println("ClientSide::run: (OK) " +
615                                "Connect failed with expected " + expectedThrowable);
616                        } else {
617                            System.out.println("ClientSide::run: (ERROR) Connect failed with " +
618                                e.getClass() + " instead of expected " +
619                                expectedThrowable);
620                            errorCount++;
621                        }
622                    } else {
623                        System.out.println("ClientSide::run: (ERROR) " +
624                            "Connect failed with exception");
625                        errorCount++;
626                    }
627                }
628
629                // Depending on the client state,
630                // perform some requests
631                if (mbsc != null && errorCount == 0) {
632                    // Perform some little JMX requests
633                    System.out.println("ClientSide::run: Start sending requests");
634
635                    doRequests();
636
637                    // In case authentication has been used we check how it did.
638                    if (authCallCountValue != 0) {
639                        errorCount += checkAuthenticator(mbsc, authCallCountValue);
640                    }
641                }
642            } catch (Exception e) {
643                Utils.printThrowable(e, true);
644                errorCount++;
645            } finally {
646                // Terminate the JMX Client if any
647                if (cc != null) {
648                    try {
649                        cc.close();
650                    } catch (Exception e) {
651                        Utils.printThrowable(e, true) ;
652                        errorCount++;
653                    }
654                }
655            }
656
657            System.out.println("ClientSide::run: Done");
658
659            // Handle result
660            if (errorCount != 0) {
661                throw new RuntimeException();
662            }
663        }
664
665        private void doRequests() throws Exception {
666
667            // Send  some requests to the remote JMX server
668            ObjectName objName1 =
669                new ObjectName("TestDomain:class=MBS_Light,rank=1");
670            String mbeanClass = "MBS_Light";
671            Exception exception = new Exception("MY TEST EXCEPTION");
672            Attribute attException = new Attribute("AnException", exception);
673            Error error = new Error("MY TEST ERROR");
674            Attribute attError = new Attribute("AnError", error);
675            String opParamString = "TOTORO";
676            RjmxMBeanParameter opParam = new RjmxMBeanParameter(opParamString);
677            Object[] params1 = {opParamString};
678            String[] sig1 = {"java.lang.String"};
679            Object[] params2 = {opParam};
680            String[] sig2 = {"RjmxMBeanParameter"};
681
682            // Create and register the MBean
683            Utils.debug(Utils.DEBUG_STANDARD,
684                "ClientSide::doRequests: Create and register the MBean");
685            mbsc.createMBean(mbeanClass, objName1);
686            if (!mbsc.isRegistered(objName1)) {
687                throw new Exception("Unable to register an MBean");
688            }
689
690            // Set attributes of the MBean
691            Utils.debug(Utils.DEBUG_STANDARD,
692                "ClientSide::doRequests: Set attributes of the MBean");
693            mbsc.setAttribute(objName1, attException);
694            mbsc.setAttribute(objName1, attError);
695
696            // Get attributes of the MBean
697            Utils.debug(Utils.DEBUG_STANDARD,
698                "ClientSide::doRequests: Get attributes of the MBean");
699            Exception retException =
700                (Exception) mbsc.getAttribute(objName1,"AnException");
701            if (!retException.getMessage().equals(exception.getMessage())) {
702                System.out.println("Expected = " + exception);
703                System.out.println("Got = " + retException);
704                throw new Exception("Attribute AnException not as expected");
705            }
706            Error retError = (Error) mbsc.getAttribute(objName1, "AnError");
707            if (!retError.getMessage().equals(error.getMessage())) {
708                System.out.println("Expected = " + error);
709                System.out.println("Got = " + retError);
710                throw new Exception("Attribute AnError not as expected");
711            }
712
713            // Invoke operations on the MBean
714            Utils.debug(Utils.DEBUG_STANDARD,
715                "ClientSide::doRequests: Invoke operations on the MBean");
716            RjmxMBeanParameter res1 =
717                (RjmxMBeanParameter) mbsc.invoke(objName1, "operate1", params1, sig1);
718            if (!res1.equals(opParam)) {
719                System.out.println("Expected = " + opParam);
720                System.out.println("Got = " + res1);
721                throw new Exception("Operation operate1 behaved badly");
722            }
723            String res2 =
724                (String) mbsc.invoke(objName1, "operate2", params2, sig2);
725            if (!res2.equals(opParamString)) {
726                System.out.println("Expected = " + opParamString);
727                System.out.println("Got = " + res2);
728                throw new Exception("Operation operate2 behaved badly");
729            }
730
731            // Unregister the MBean
732            Utils.debug(Utils.DEBUG_STANDARD,
733                "ClientSide::doRequests: Unregister the MBean");
734            mbsc.unregisterMBean(objName1);
735            if (mbsc.isRegistered(objName1)) {
736                throw new Exception("Unable to unregister an MBean");
737            }
738        }
739
740        /**
741         * Make some check about the instance of TestJMXAuthenticator.
742         * The authenticator is supposed to have set some properties on
743         * a ServerDelegate MBean.
744         * We compare the number of times it has been called with the expected value.
745         * We also check the Principal that has been given to the authenticator
746         * was not null.
747         * That method is of use to authentication with the JSR 262.
748         * @param mbs
749         * @param expectedAuthenticatorCallCount
750         * @return The number of errors encountered.
751         * @throws java.lang.Exception
752         */
753        protected int checkAuthenticator(MBeanServerConnection mbs,
754                int expectedAuthenticatorCallCount) throws Exception {
755            int errorCount = 0;
756
757            // Ensure the authenticator has been called the right number
758            // of times.
759            int callCount =
760                    ((Integer) mbs.getAttribute(
761                    new ObjectName(SERVER_DELEGATE_MBEAN_NAME),
762                    "TestJMXAuthenticatorCallCount")).intValue();
763
764            if (callCount == expectedAuthenticatorCallCount) {
765                System.out.println("---- OK Authenticator has been called "
766                        + expectedAuthenticatorCallCount + " time");
767            } else {
768                errorCount++;
769                System.out.println("---- ERROR Authenticator has been called " + callCount
770                        + " times in place of " + expectedAuthenticatorCallCount);
771            }
772
773            // Ensure the provider has been called with
774            // a non null Principal.
775            String principalString =
776                (String) mbs.getAttribute(
777                new ObjectName(SERVER_DELEGATE_MBEAN_NAME),
778                "TestJMXAuthenticatorPrincipalString");
779
780            if (principalString == null) {
781                errorCount++;
782                System.out.println("---- ERROR Authenticator has been called"
783                        + " with a null Principal");
784            } else {
785                if (principalString.length() > 0) {
786                    System.out.println("---- OK Authenticator has been called"
787                            + " with the Principal " + principalString);
788                } else {
789                    errorCount++;
790                    System.out.println("---- ERROR Authenticator has been called"
791                            + " with an empty Principal");
792                }
793            }
794
795            return errorCount;
796        }
797
798    }
799
800}
801