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