1 /*
   2  * Copyright (c) 2020, Azul Systems, Inc. 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 8245527
  27  * @library lib/ /test/lib
  28  * @run main/othervm LdapCBPropertiesTest true  true  com.sun.jndi.ldap.tls.cbtype tls-server-end-point
  29  * @run main/othervm LdapCBPropertiesTest false false com.sun.jndi.ldap.tls.cbtype tls-server-end-point
  30  * @run main/othervm LdapCBPropertiesTest true  true  com.sun.jndi.ldap.tls.cbtype tls-server-end-point com.sun.jndi.ldap.connect.timeout 2000
  31  * @run main/othervm LdapCBPropertiesTest false false com.sun.jndi.ldap.tls.cbtype tls-server-end-point com.sun.jndi.ldap.connect.timeout 2000
  32  * @run main/othervm LdapCBPropertiesTest false true  com.sun.jndi.ldap.tls.cbtype tls-unique
  33  * @run main/othervm LdapCBPropertiesTest false true  com.sun.jndi.ldap.tls.cbtype tls-unknown
  34  * @run main/othervm LdapCBPropertiesTest false true  jdk.internal.sasl.tlschannelbinding value
  35  * @summary test new JNDI property to control the Channel Binding data
  36  */
  37 
  38 import javax.naming.AuthenticationException;
  39 import javax.naming.CommunicationException;
  40 import javax.naming.Context;
  41 import javax.naming.NamingException;
  42 import javax.naming.directory.DirContext;
  43 import javax.naming.directory.InitialDirContext;
  44 import java.net.InetAddress;
  45 import java.net.URI;
  46 import java.util.Hashtable;
  47 
  48 import org.ietf.jgss.GSSException;
  49 
  50 import javax.net.ssl.SSLException;
  51 import javax.net.ssl.SSLServerSocket;
  52 import javax.net.ssl.SSLServerSocketFactory;
  53 import javax.security.sasl.SaslException;
  54 
  55 import jdk.test.lib.net.URIBuilder;
  56 
  57 public class LdapCBPropertiesTest {
  58     /*
  59      * Where do we find the keystores?
  60      */
  61     static String pathToStores = "../../../../javax/net/ssl/etc";
  62     static String keyStoreFile = "keystore";
  63     static String trustStoreFile = "truststore";
  64     static String passwd = "passphrase";
  65 
  66     static boolean debug = false;
  67 
  68     public static void main(String[] args) throws Exception {
  69         String keyFilename =
  70                 System.getProperty("test.src", "./") + "/" + pathToStores +
  71                         "/" + keyStoreFile;
  72         String trustFilename =
  73                 System.getProperty("test.src", "./") + "/" + pathToStores +
  74                         "/" + trustStoreFile;
  75 
  76         System.setProperty("javax.net.ssl.keyStore", keyFilename);
  77         System.setProperty("javax.net.ssl.keyStorePassword", passwd);
  78         System.setProperty("javax.net.ssl.trustStore", trustFilename);
  79         System.setProperty("javax.net.ssl.trustStorePassword", passwd);
  80 
  81         if (debug)
  82             System.setProperty("javax.net.debug", "all");
  83 
  84         /*
  85          * Start the tests.
  86          */
  87         new LdapCBPropertiesTest(args);
  88     }
  89 
  90     /*
  91      * Primary constructor, used to drive remainder of the test.
  92      */
  93     LdapCBPropertiesTest(String[] args) throws Exception {
  94         InetAddress loopback = InetAddress.getLoopbackAddress();
  95         SSLServerSocketFactory sslssf =
  96                 (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
  97         SSLServerSocket sslServerSocket =
  98                 (SSLServerSocket) sslssf.createServerSocket(0, 0, loopback);
  99         int serverPort = sslServerSocket.getLocalPort();
 100 
 101         try (var ignore = new BaseLdapServer(sslServerSocket).start()) {
 102             doClientSide(serverPort, args);
 103         }
 104     }
 105 
 106     /*
 107      * Define the client side of the test.
 108      *
 109      * The server should start at this time already
 110      */
 111     void doClientSide(int serverPort, String[] args) throws Exception {
 112         boolean passed = false;
 113         boolean shouldPass = Boolean.parseBoolean(args[0]);
 114         boolean shouldConnect = Boolean.parseBoolean(args[1]);
 115         // set disableEndpointIdentification to disable hostname verification
 116         if (shouldConnect) {
 117             System.setProperty(
 118                     "com.sun.jndi.ldap.object.disableEndpointIdentification", "true");
 119         }
 120 
 121         // Set up the environment for creating the initial context
 122         Hashtable env = new Hashtable();
 123         URI uri = URIBuilder.newBuilder()
 124             .scheme("ldaps")
 125             .loopback()
 126             .port(serverPort)
 127             .build();
 128         env.put(Context.PROVIDER_URL, uri.toString());
 129         env.put(Context.INITIAL_CONTEXT_FACTORY,
 130                 "com.sun.jndi.ldap.LdapCtxFactory");
 131         env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
 132 
 133         // read properties
 134         for (int i = 2; i < args.length; i += 2) {
 135             env.put(args[i], args[i + 1]);
 136             if (debug)
 137                 System.out.println("Env=" + args[i] + "=" + args[i + 1]);
 138         }
 139 
 140         try {
 141             DirContext ctx = new InitialDirContext(env);
 142             passed = shouldPass;
 143             ctx.close();
 144         } catch (NamingException ne) {
 145             // only NamingException is allowed
 146             if (debug)
 147                 System.out.println("Exception=" + ne + " cause=" + ne.getRootCause());
 148             passed = handleNamingException(ne, shouldPass, shouldConnect);
 149         } catch(Exception e) {
 150             System.err.println("Failed: caught an unexpected Exception - " + e);
 151             throw e;
 152         } finally {
 153             // test if internal property accessible to application
 154             if(shouldPass &&
 155                     env.get("jdk.internal.sasl.tlschannelbinding") != null) {
 156                 throw new Exception(
 157                         "Test FAILED: jdk.internal.sasl.tlschannelbinding should not be accessible");
 158             }
 159         }
 160         if (!passed) {
 161             throw new Exception(
 162                     "Test FAILED: NamingException exception should be thrown");
 163         }
 164         System.out.println("Test PASSED");
 165     }
 166 
 167     private static boolean handleNamingException(NamingException ne, boolean shouldPass, boolean shouldConnect)
 168         throws NamingException {
 169         if (ne instanceof AuthenticationException &&
 170             ne.getRootCause() instanceof SaslException) {
 171             SaslException saslEx = (SaslException) ne.getRootCause();
 172             if (shouldConnect && saslEx.getCause() instanceof GSSException) {
 173                 // SSL connection successful, expected exception from SaslClient
 174                 if (shouldPass)
 175                     return true;
 176             }
 177         }
 178         if (!shouldConnect) {
 179             // SSL handshake fails
 180             Exception ex = ne;
 181             while(ex != null && !(ex instanceof CommunicationException)) {
 182                 ex = (Exception)ex.getCause();
 183             }
 184             if (ex != null) {
 185                 if (ex.getCause() instanceof SSLException) {
 186                     if (!shouldPass)
 187                         return true;
 188                 }
 189             }
 190         }
 191         if (!shouldPass && ne.getRootCause() == null) {
 192             // Expected exception caused by Channel Binding parameter inconsistency
 193             return true;
 194         }
 195         throw ne;
 196     }
 197 }