1/* 2 * Copyright (c) 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 24import java.io.BufferedInputStream; 25import java.io.BufferedOutputStream; 26import java.io.File; 27import java.io.IOException; 28import java.security.NoSuchAlgorithmException; 29import java.security.PrivilegedActionException; 30import java.security.PrivilegedExceptionAction; 31import java.util.ArrayList; 32import java.util.Arrays; 33import java.util.List; 34import java.util.Map; 35import javax.net.ssl.SNIHostName; 36import javax.net.ssl.SNIMatcher; 37import javax.net.ssl.SNIServerName; 38import javax.net.ssl.SSLContext; 39import javax.net.ssl.SSLParameters; 40import javax.net.ssl.SSLServerSocket; 41import javax.net.ssl.SSLServerSocketFactory; 42import javax.net.ssl.SSLSocket; 43import javax.net.ssl.SSLSocketFactory; 44import javax.security.auth.Subject; 45import javax.security.auth.login.LoginContext; 46import javax.security.auth.login.LoginException; 47 48/* 49 * Helper class for unbound krb5 tests. 50 */ 51class UnboundSSLUtils { 52 53 static final String KTAB_FILENAME = "krb5.keytab.data"; 54 static final String HOST = "localhost"; 55 static final String REALM = "TEST.REALM"; 56 static final String KRBTGT_PRINCIPAL = "krbtgt/" + REALM; 57 static final String TEST_SRC = System.getProperty("test.src", "."); 58 static final String TLS_KRB5_FILTER = "TLS_KRB5"; 59 static final String USER = "USER"; 60 static final String USER_PASSWORD = "password"; 61 static final String FS = System.getProperty("file.separator"); 62 static final String SNI_PATTERN = ".*"; 63 static final String USER_PRINCIPAL = USER + "@" + REALM; 64 static final String KRB5_CONF_FILENAME = "krb5.conf"; 65 static final int DELAY = 1000; 66 67 static String[] filterStringArray(String[] src, String filter) { 68 return Arrays.stream(src).filter((item) -> item.startsWith(filter)) 69 .toArray(size -> new String[size]); 70 } 71 72 /* 73 * The method does JAAS login, 74 * and runs an SSL server in the JAAS context. 75 */ 76 static void startServerWithJaas(final SSLEchoServer server, 77 String config) throws LoginException, PrivilegedActionException { 78 LoginContext context = new LoginContext(config); 79 context.login(); 80 System.out.println("Server: successful authentication"); 81 Subject.doAs(context.getSubject(), 82 (PrivilegedExceptionAction<Object>) () -> { 83 SSLEchoServer.startServer(server); 84 return null; 85 }); 86 } 87 88} 89 90class SSLClient { 91 92 private final static byte[][] arrays = { 93 new byte[] {-1, 0, 2}, 94 new byte[] {} 95 }; 96 97 private final SSLSocket socket; 98 99 private SSLClient(SSLSocket socket) { 100 this.socket = socket; 101 } 102 103 void connect() throws IOException { 104 System.out.println("Client: connect to server"); 105 try (BufferedInputStream bis = new BufferedInputStream( 106 socket.getInputStream()); 107 BufferedOutputStream bos = new BufferedOutputStream( 108 socket.getOutputStream())) { 109 110 for (byte[] bytes : arrays) { 111 System.out.println("Client: send byte array: " 112 + Arrays.toString(bytes)); 113 114 bos.write(bytes); 115 bos.flush(); 116 117 byte[] recieved = new byte[bytes.length]; 118 int read = bis.read(recieved, 0, bytes.length); 119 if (read < 0) { 120 throw new IOException("Client: couldn't read a response"); 121 } 122 123 System.out.println("Client: recieved byte array: " 124 + Arrays.toString(recieved)); 125 126 if (!Arrays.equals(bytes, recieved)) { 127 throw new IOException("Client: sent byte array " 128 + "is not equal with recieved byte array"); 129 } 130 } 131 socket.getSession().invalidate(); 132 } finally { 133 if (!socket.isClosed()) { 134 socket.close(); 135 } 136 } 137 } 138 139 static SSLClient init(String host, int port, String cipherSuiteFilter, 140 String sniHostName) throws NoSuchAlgorithmException, IOException { 141 SSLContext sslContext = SSLContext.getDefault(); 142 SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); 143 SSLSocket socket = (SSLSocket) ssf.createSocket(host, port); 144 SSLParameters params = new SSLParameters(); 145 146 if (cipherSuiteFilter != null) { 147 String[] cipherSuites = UnboundSSLUtils.filterStringArray( 148 ssf.getSupportedCipherSuites(), cipherSuiteFilter); 149 System.out.println("Client: enabled cipher suites: " 150 + Arrays.toString(cipherSuites)); 151 params.setCipherSuites(cipherSuites); 152 } 153 154 if (sniHostName != null) { 155 System.out.println("Client: set SNI hostname: " + sniHostName); 156 SNIHostName serverName = new SNIHostName(sniHostName); 157 List<SNIServerName> serverNames = new ArrayList<>(); 158 serverNames.add(serverName); 159 params.setServerNames(serverNames); 160 } 161 162 socket.setSSLParameters(params); 163 164 return new SSLClient(socket); 165 } 166 167} 168 169class SSLEchoServer implements Runnable, AutoCloseable { 170 171 private final SSLServerSocket ssocket; 172 private volatile boolean stopped = false; 173 private volatile boolean ready = false; 174 175 /* 176 * Starts the server in a separate thread. 177 */ 178 static void startServer(SSLEchoServer server) { 179 Thread serverThread = new Thread(server, "SSL echo server thread"); 180 serverThread.setDaemon(true); 181 serverThread.start(); 182 } 183 184 private SSLEchoServer(SSLServerSocket ssocket) { 185 this.ssocket = ssocket; 186 } 187 188 /* 189 * Main server loop. 190 */ 191 @Override 192 public void run() { 193 System.out.println("Server: started"); 194 while (!stopped) { 195 ready = true; 196 try (SSLSocket socket = (SSLSocket) ssocket.accept()) { 197 System.out.println("Server: client connection accepted"); 198 try ( 199 BufferedInputStream bis = new BufferedInputStream( 200 socket.getInputStream()); 201 BufferedOutputStream bos = new BufferedOutputStream( 202 socket.getOutputStream()) 203 ) { 204 byte[] buffer = new byte[1024]; 205 int read; 206 while ((read = bis.read(buffer)) > 0) { 207 bos.write(buffer, 0, read); 208 System.out.println("Server: recieved " + read 209 + " bytes: " 210 + Arrays.toString(Arrays.copyOf(buffer, read))); 211 bos.flush(); 212 } 213 } 214 } catch (IOException e) { 215 if (stopped) { 216 // stopped == true means that stop() method was called, 217 // so just ignore the exception, and finish the loop 218 break; 219 } 220 System.out.println("Server: couldn't accept client connection: " 221 + e); 222 } 223 } 224 System.out.println("Server: finished"); 225 } 226 227 boolean isReady() { 228 return ready; 229 } 230 231 void stop() { 232 stopped = true; 233 ready = false; 234 235 // close the server socket to interupt accept() method 236 try { 237 if (!ssocket.isClosed()) { 238 ssocket.close(); 239 } 240 } catch (IOException e) { 241 throw new RuntimeException("Unexpected exception: " + e); 242 } 243 } 244 245 @Override 246 public void close() { 247 stop(); 248 } 249 250 int getPort() { 251 return ssocket.getLocalPort(); 252 } 253 254 /* 255 * Creates server instance. 256 * 257 * @param cipherSuiteFilter Filter for enabled cipher suites 258 * @param sniMatcherPattern Pattern for SNI server hame 259 */ 260 static SSLEchoServer init(String cipherSuiteFilter, 261 String sniPattern) throws NoSuchAlgorithmException, IOException { 262 SSLContext context = SSLContext.getDefault(); 263 SSLServerSocketFactory ssf = 264 (SSLServerSocketFactory) context.getServerSocketFactory(); 265 SSLServerSocket ssocket = 266 (SSLServerSocket) ssf.createServerSocket(0); 267 268 // specify enabled cipher suites 269 if (cipherSuiteFilter != null) { 270 String[] ciphersuites = UnboundSSLUtils.filterStringArray( 271 ssf.getSupportedCipherSuites(), cipherSuiteFilter); 272 System.out.println("Server: enabled cipher suites: " 273 + Arrays.toString(ciphersuites)); 274 ssocket.setEnabledCipherSuites(ciphersuites); 275 } 276 277 // specify SNI matcher pattern 278 if (sniPattern != null) { 279 System.out.println("Server: set SNI matcher: " + sniPattern); 280 SNIMatcher matcher = SNIHostName.createSNIMatcher(sniPattern); 281 List<SNIMatcher> matchers = new ArrayList<>(); 282 matchers.add(matcher); 283 SSLParameters params = ssocket.getSSLParameters(); 284 params.setSNIMatchers(matchers); 285 ssocket.setSSLParameters(params); 286 } 287 288 return new SSLEchoServer(ssocket); 289 } 290 291} 292 293