1 /* 2 * Copyright (c) 2009, 2018, 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 6894643 6913636 8005523 8025123 8194486 27 * @summary Test JSSE Kerberos ciphersuite 28 * @library /test/lib 29 * @run main jdk.test.lib.FileInstaller TestHosts TestHosts 30 * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL 31 * TLS_KRB5_WITH_RC4_128_SHA 32 * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL 33 * TLS_KRB5_WITH_RC4_128_SHA unbound 34 * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL 35 * TLS_KRB5_WITH_RC4_128_SHA unbound sni 36 * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL 37 * TLS_KRB5_WITH_3DES_EDE_CBC_SHA 38 * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL 39 * TLS_KRB5_WITH_3DES_EDE_CBC_MD5 40 * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL 41 * TLS_KRB5_WITH_DES_CBC_SHA 42 * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL 43 * TLS_KRB5_WITH_DES_CBC_MD5 44 * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL 45 * TLS_KRB5_EXPORT_WITH_RC4_40_SHA 46 * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL 47 * TLS_KRB5_EXPORT_WITH_RC4_40_MD5 48 * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL 49 * TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA 50 * @run main/othervm -Djdk.net.hosts.file=TestHosts SSL 51 * TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 52 */ 53 import java.io.*; 54 import java.security.Permission; 55 import javax.net.ssl.*; 56 import java.security.Principal; 57 import java.security.Security; 58 import java.util.Date; 59 import java.util.List; 60 import java.util.ArrayList; 61 import java.util.Locale; 62 import javax.security.auth.kerberos.ServicePermission; 63 import sun.security.jgss.GSSUtil; 64 import sun.security.krb5.PrincipalName; 65 import sun.security.krb5.internal.ktab.KeyTab; 66 67 public class SSL extends SecurityManager { 68 69 private static String krb5Cipher; 70 private static final int LOOP_LIMIT = 3; 71 private static int loopCount = 0; 72 private static volatile String server; 73 private static volatile int port; 74 private static String sniHostname = null; 75 private static String sniMatcherPattern = null; 76 77 private static String permChecks = ""; 78 79 // 0-Not started, 1-Start OK, 2-Failure 80 private static volatile int serverState = 0; 81 82 @Override 83 public void checkPermission(Permission perm, Object context) { 84 checkPermission(perm); 85 } 86 87 public void checkPermission(Permission perm) { 88 if (!(perm instanceof ServicePermission)) { 89 return; 90 } 91 ServicePermission p = (ServicePermission)perm; 92 // ServicePermissions required to create GSSName are ignored 93 if (!p.getActions().isEmpty()) { 94 permChecks = permChecks 95 + p.getActions().toUpperCase(Locale.US).charAt(0); 96 } 97 } 98 99 public static void main(String[] args) throws Exception { 100 // reset the security property to make sure that the algorithms 101 // and keys used in this test are not disabled. 102 Security.setProperty("jdk.tls.disabledAlgorithms", ""); 103 104 krb5Cipher = args[0]; 105 106 boolean unbound = args.length > 1; 107 108 // Workaround for JDK-8161101, reference the class before 109 // SecurityManager is set. 110 System.out.println("Touching " + ServicePermission.class); 111 112 System.setSecurityManager(new SSL()); 113 114 KDC kdc = KDC.create(OneKDC.REALM); 115 server = "host." + OneKDC.REALM_LOWER_CASE; 116 117 if (args.length > 2) { 118 sniHostname = "test." + server; 119 sniMatcherPattern = ".*"; 120 } 121 122 kdc.addPrincipal(OneKDC.USER, OneKDC.PASS); 123 kdc.addPrincipalRandKey("krbtgt/" + OneKDC.REALM); 124 KDC.saveConfig(OneKDC.KRB5_CONF, kdc); 125 System.setProperty("java.security.krb5.conf", OneKDC.KRB5_CONF); 126 127 // Add 3 versions of keys into keytab 128 KeyTab ktab = KeyTab.create(OneKDC.KTAB); 129 String serviceName = null; 130 if (sniHostname != null) { 131 serviceName = "host/" + sniHostname; 132 } else { 133 serviceName = "host/" + server; 134 } 135 PrincipalName service = new PrincipalName( 136 serviceName, PrincipalName.KRB_NT_SRV_HST); 137 ktab.addEntry(service, "pass1".toCharArray(), 1, true); 138 ktab.addEntry(service, "pass2".toCharArray(), 2, true); 139 ktab.addEntry(service, "pass3".toCharArray(), 3, true); 140 ktab.save(); 141 142 // and use the middle one as the real key 143 kdc.addPrincipal(serviceName, "pass2".toCharArray()); 144 145 146 // JAAS config entry name ssl 147 System.setProperty("java.security.auth.login.config", OneKDC.JAAS_CONF); 148 File f = new File(OneKDC.JAAS_CONF); 149 FileOutputStream fos = new FileOutputStream(f); 150 fos.write(( 151 "ssl {\n" + 152 " com.sun.security.auth.module.Krb5LoginModule required\n" + 153 (unbound ? 154 " principal=*\n" : 155 " principal=\"" + serviceName + "\"\n") + 156 " useKeyTab=true\n" + 157 " keyTab=" + OneKDC.KTAB + "\n" + 158 " isInitiator=false\n" + 159 " storeKey=true;\n};\n" 160 ).getBytes()); 161 fos.close(); 162 163 Context c; 164 final Context s = Context.fromJAAS("ssl"); 165 166 s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); 167 168 Thread server = new Thread(new Runnable() { 169 public void run() { 170 try { 171 s.doAs(new JsseServerAction(), null); 172 } catch (Exception e) { 173 e.printStackTrace(); 174 serverState = 2; 175 } 176 } 177 }); 178 server.setDaemon(true); 179 server.start(); 180 181 while (serverState == 0) { 182 Thread.sleep(50); 183 } 184 185 if (serverState == 2) { 186 throw new Exception("Server already failed"); 187 } 188 189 c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); 190 c.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); 191 c.doAs(new JsseClientAction(), null); 192 193 // Add another version of key, make sure it can be loaded 194 Thread.sleep(2000); 195 ktab = KeyTab.getInstance(OneKDC.KTAB); 196 ktab.addEntry(service, "pass4".toCharArray(), 4, true); 197 ktab.save(); 198 kdc.addPrincipal(serviceName, "pass4".toCharArray()); 199 200 c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); 201 c.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); 202 c.doAs(new JsseClientAction(), null); 203 204 // Permission checking check. Please note this is highly 205 // implementation related. 206 if (unbound) { 207 // For unbound, server does not know what name to check. 208 // Client checks "initiate", then server gets the name 209 // and checks "accept". Second connection resume. 210 if (!permChecks.equals("IA")) { 211 throw new Exception(permChecks); 212 } 213 } else { 214 // For bound, JAAS checks "accept" once. Server checks again, 215 // client then checks "initiate". Second connection resume. 216 if (!permChecks.equals("AAI")) { 217 throw new Exception(permChecks); 218 } 219 } 220 } 221 222 // Following codes copied from 223 // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/lab/part2.html#JSSE 224 private static class JsseClientAction implements Action { 225 public byte[] run(Context s, byte[] input) throws Exception { 226 SSLSocketFactory sslsf = 227 (SSLSocketFactory) SSLSocketFactory.getDefault(); 228 System.out.println("Connecting " + server + ":" + port); 229 SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(server, port); 230 231 // Enable only a KRB5 cipher suite. 232 String enabledSuites[] = {krb5Cipher}; 233 sslSocket.setEnabledCipherSuites(enabledSuites); 234 // Should check for exception if enabledSuites is not supported 235 236 if (sniHostname != null) { 237 List<SNIServerName> serverNames = new ArrayList<>(); 238 serverNames.add(new SNIHostName(sniHostname)); 239 SSLParameters params = sslSocket.getSSLParameters(); 240 params.setServerNames(serverNames); 241 sslSocket.setSSLParameters(params); 242 } 243 244 BufferedReader in = new BufferedReader(new InputStreamReader( 245 sslSocket.getInputStream())); 246 BufferedWriter out = new BufferedWriter(new OutputStreamWriter( 247 sslSocket.getOutputStream())); 248 249 String outStr = "Hello There!\n"; 250 out.write(outStr); 251 out.flush(); 252 System.out.print("Sending " + outStr); 253 254 String inStr = in.readLine(); 255 System.out.println("Received " + inStr); 256 257 String cipherSuiteChosen = sslSocket.getSession().getCipherSuite(); 258 System.out.println("Cipher suite in use: " + cipherSuiteChosen); 259 Principal self = sslSocket.getSession().getLocalPrincipal(); 260 System.out.println("I am: " + self.toString()); 261 Principal peer = sslSocket.getSession().getPeerPrincipal(); 262 System.out.println("Server is: " + peer.toString()); 263 264 sslSocket.close(); 265 // This line should not be needed. It's the server's duty to 266 // forget the old key 267 //sslSocket.getSession().invalidate(); 268 return null; 269 } 270 } 271 272 private static class JsseServerAction implements Action { 273 public byte[] run(Context s, byte[] input) throws Exception { 274 SSLServerSocketFactory sslssf = 275 (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); 276 SSLServerSocket sslServerSocket = 277 (SSLServerSocket) sslssf.createServerSocket(0); // any port 278 port = sslServerSocket.getLocalPort(); 279 System.out.println("Listening on " + port); 280 serverState = 1; 281 282 // Enable only a KRB5 cipher suite. 283 String enabledSuites[] = {krb5Cipher}; 284 sslServerSocket.setEnabledCipherSuites(enabledSuites); 285 // Should check for exception if enabledSuites is not supported 286 287 if (sniMatcherPattern != null) { 288 List<SNIMatcher> matchers = new ArrayList<>(); 289 matchers.add(SNIHostName.createSNIMatcher(sniMatcherPattern)); 290 SSLParameters params = sslServerSocket.getSSLParameters(); 291 params.setSNIMatchers(matchers); 292 sslServerSocket.setSSLParameters(params); 293 } 294 295 while (loopCount++ < LOOP_LIMIT) { 296 System.out.println("Waiting for incoming connection..."); 297 298 SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); 299 300 System.out.println("Got connection from client " 301 + sslSocket.getInetAddress()); 302 303 BufferedReader in = new BufferedReader(new InputStreamReader( 304 sslSocket.getInputStream())); 305 BufferedWriter out = new BufferedWriter(new OutputStreamWriter( 306 sslSocket.getOutputStream())); 307 308 String inStr = in.readLine(); 309 System.out.println("Received " + inStr); 310 311 String outStr = inStr + " " + new Date().toString() + "\n"; 312 out.write(outStr); 313 System.out.println("Sending " + outStr); 314 out.flush(); 315 316 String cipherSuiteChosen = 317 sslSocket.getSession().getCipherSuite(); 318 System.out.println("Cipher suite in use: " + cipherSuiteChosen); 319 Principal self = sslSocket.getSession().getLocalPrincipal(); 320 System.out.println("I am: " + self.toString()); 321 Principal peer = sslSocket.getSession().getPeerPrincipal(); 322 System.out.println("Client is: " + peer.toString()); 323 324 sslSocket.close(); 325 } 326 return null; 327 } 328 } 329 }