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