1 /* SSLSocketImpl.java -- implementation of an SSL client socket. 2 Copyright (C) 2006 Free Software Foundation, Inc. 3 4 This file is a part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or (at 9 your option) any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 19 USA 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package gnu.javax.net.ssl.provider; 40 41 import gnu.classpath.debug.Component; 42 import gnu.classpath.debug.SystemLogger; 43 44 import java.io.DataInputStream; 45 import java.io.EOFException; 46 import java.io.IOException; 47 import java.io.InputStream; 48 import java.io.OutputStream; 49 import java.net.InetAddress; 50 import java.net.Socket; 51 import java.net.SocketAddress; 52 import java.net.SocketException; 53 import java.nio.ByteBuffer; 54 import java.nio.channels.SocketChannel; 55 import java.util.HashSet; 56 import java.util.Set; 57 58 import javax.net.ssl.HandshakeCompletedEvent; 59 import javax.net.ssl.HandshakeCompletedListener; 60 import javax.net.ssl.SSLEngineResult; 61 import javax.net.ssl.SSLException; 62 import javax.net.ssl.SSLSession; 63 import javax.net.ssl.SSLSocket; 64 import javax.net.ssl.SSLEngineResult.HandshakeStatus; 65 import javax.net.ssl.SSLEngineResult.Status; 66 67 /** 68 * @author Casey Marshall (csm@gnu.org) 69 */ 70 public class SSLSocketImpl extends SSLSocket 71 { 72 private class SocketOutputStream extends OutputStream 73 { 74 private final ByteBuffer buffer; 75 private final OutputStream out; 76 SocketOutputStream()77 SocketOutputStream() throws IOException 78 { 79 buffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]); 80 if (underlyingSocket != null) 81 out = underlyingSocket.getOutputStream(); 82 else 83 out = SSLSocketImpl.super.getOutputStream(); 84 } 85 write(byte[] buf, int off, int len)86 @Override public void write(byte[] buf, int off, int len) throws IOException 87 { 88 if (!initialHandshakeDone 89 || engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) 90 { 91 doHandshake(); 92 if (handshakeException != null) 93 throw handshakeException; 94 } 95 96 int k = 0; 97 while (k < len) 98 { 99 synchronized (engine) 100 { 101 int l = Math.min(len-k, getSession().getApplicationBufferSize()); 102 ByteBuffer in = ByteBuffer.wrap(buf, off+k, l); 103 SSLEngineResult result = engine.wrap(in, buffer); 104 if (result.getStatus() == Status.CLOSED) 105 return; 106 if (result.getStatus() != Status.OK) 107 throw new SSLException("unexpected SSL state " + result.getStatus()); 108 buffer.flip(); 109 out.write(buffer.array(), 0, buffer.limit()); 110 k += result.bytesConsumed(); 111 buffer.clear(); 112 } 113 } 114 } 115 write(int b)116 @Override public void write(int b) throws IOException 117 { 118 write(new byte[] { (byte) b }); 119 } 120 close()121 @Override public void close() throws IOException 122 { 123 SSLSocketImpl.this.close(); 124 } 125 } 126 127 private class SocketInputStream extends InputStream 128 { 129 private final ByteBuffer inBuffer; 130 private final ByteBuffer appBuffer; 131 private final DataInputStream in; 132 SocketInputStream()133 SocketInputStream() throws IOException 134 { 135 inBuffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]); 136 inBuffer.limit(0); 137 appBuffer = ByteBuffer.allocate(getSession().getApplicationBufferSize()); 138 appBuffer.flip(); 139 if (underlyingSocket != null) 140 in = new DataInputStream(underlyingSocket.getInputStream()); 141 else 142 in = new DataInputStream(SSLSocketImpl.super.getInputStream()); 143 } 144 read(byte[] buf, int off, int len)145 @Override public int read(byte[] buf, int off, int len) throws IOException 146 { 147 if (!initialHandshakeDone || 148 engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING) 149 { 150 doHandshake(); 151 if (handshakeException != null) 152 throw handshakeException; 153 } 154 155 if (!appBuffer.hasRemaining()) 156 { 157 int x = in.read(); 158 if (x == -1) 159 return -1; 160 inBuffer.clear(); 161 inBuffer.put((byte) x); 162 inBuffer.putInt(in.readInt()); 163 int reclen = inBuffer.getShort(3) & 0xFFFF; 164 in.readFully(inBuffer.array(), 5, reclen); 165 inBuffer.position(0).limit(reclen + 5); 166 synchronized (engine) 167 { 168 appBuffer.clear(); 169 SSLEngineResult result = engine.unwrap(inBuffer, appBuffer); 170 Status status = result.getStatus(); 171 if (status == Status.CLOSED && result.bytesProduced() == 0) 172 return -1; 173 } 174 inBuffer.compact(); 175 appBuffer.flip(); 176 } 177 int l = Math.min(len, appBuffer.remaining()); 178 appBuffer.get(buf, off, l); 179 return l; 180 } 181 read()182 @Override public int read() throws IOException 183 { 184 byte[] b = new byte[1]; 185 if (read(b) == -1) 186 return -1; 187 return b[0] & 0xFF; 188 } 189 } 190 191 private static final SystemLogger logger = SystemLogger.getSystemLogger(); 192 193 private SSLEngineImpl engine; 194 private Set<HandshakeCompletedListener> listeners; 195 private Socket underlyingSocket; 196 private boolean isHandshaking; 197 private IOException handshakeException; 198 private boolean initialHandshakeDone = false; 199 private final boolean autoClose; 200 SSLSocketImpl(SSLContextImpl contextImpl, String host, int port)201 public SSLSocketImpl(SSLContextImpl contextImpl, String host, int port) 202 { 203 this(contextImpl, host, port, new Socket(), true); 204 } 205 SSLSocketImpl(SSLContextImpl contextImpl, String host, int port, Socket underlyingSocket, boolean autoClose)206 public SSLSocketImpl(SSLContextImpl contextImpl, String host, int port, 207 Socket underlyingSocket, boolean autoClose) 208 { 209 engine = new SSLEngineImpl(contextImpl, host, port); 210 engine.setUseClientMode(true); // default to client mode 211 listeners = new HashSet<HandshakeCompletedListener>(); 212 this.underlyingSocket = underlyingSocket; 213 this.autoClose = autoClose; 214 } 215 216 /* (non-Javadoc) 217 * @see javax.net.ssl.SSLSocket#addHandshakeCompletedListener(javax.net.ssl.HandshakeCompletedListener) 218 */ 219 @Override addHandshakeCompletedListener(HandshakeCompletedListener listener)220 public void addHandshakeCompletedListener(HandshakeCompletedListener listener) 221 { 222 listeners.add(listener); 223 } 224 225 /* (non-Javadoc) 226 * @see javax.net.ssl.SSLSocket#getEnableSessionCreation() 227 */ getEnableSessionCreation()228 @Override public boolean getEnableSessionCreation() 229 { 230 return engine.getEnableSessionCreation(); 231 } 232 233 /* (non-Javadoc) 234 * @see javax.net.ssl.SSLSocket#getEnabledCipherSuites() 235 */ getEnabledCipherSuites()236 @Override public String[] getEnabledCipherSuites() 237 { 238 return engine.getEnabledCipherSuites(); 239 } 240 241 /* (non-Javadoc) 242 * @see javax.net.ssl.SSLSocket#getEnabledProtocols() 243 */ getEnabledProtocols()244 @Override public String[] getEnabledProtocols() 245 { 246 return engine.getEnabledProtocols(); 247 } 248 249 /* (non-Javadoc) 250 * @see javax.net.ssl.SSLSocket#getNeedClientAuth() 251 */ getNeedClientAuth()252 @Override public boolean getNeedClientAuth() 253 { 254 return engine.getNeedClientAuth(); 255 } 256 257 /* (non-Javadoc) 258 * @see javax.net.ssl.SSLSocket#getSession() 259 */ getSession()260 @Override public SSLSession getSession() 261 { 262 return engine.getSession(); 263 } 264 265 /* (non-Javadoc) 266 * @see javax.net.ssl.SSLSocket#getSupportedCipherSuites() 267 */ getSupportedCipherSuites()268 @Override public String[] getSupportedCipherSuites() 269 { 270 return engine.getSupportedCipherSuites(); 271 } 272 273 /* (non-Javadoc) 274 * @see javax.net.ssl.SSLSocket#getSupportedProtocols() 275 */ getSupportedProtocols()276 @Override public String[] getSupportedProtocols() 277 { 278 return engine.getSupportedProtocols(); 279 } 280 281 /* (non-Javadoc) 282 * @see javax.net.ssl.SSLSocket#getUseClientMode() 283 */ getUseClientMode()284 @Override public boolean getUseClientMode() 285 { 286 return engine.getUseClientMode(); 287 } 288 289 /* (non-Javadoc) 290 * @see javax.net.ssl.SSLSocket#getWantClientAuth() 291 */ getWantClientAuth()292 @Override public boolean getWantClientAuth() 293 { 294 return engine.getWantClientAuth(); 295 } 296 297 /* (non-Javadoc) 298 * @see javax.net.ssl.SSLSocket#removeHandshakeCompletedListener(javax.net.ssl.HandshakeCompletedListener) 299 */ 300 @Override removeHandshakeCompletedListener(HandshakeCompletedListener listener)301 public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) 302 { 303 listeners.remove(listener); 304 } 305 306 /* (non-Javadoc) 307 * @see javax.net.ssl.SSLSocket#setEnableSessionCreation(boolean) 308 */ setEnableSessionCreation(boolean enable)309 @Override public void setEnableSessionCreation(boolean enable) 310 { 311 engine.setEnableSessionCreation(enable); 312 } 313 314 /* (non-Javadoc) 315 * @see javax.net.ssl.SSLSocket#setEnabledCipherSuites(java.lang.String[]) 316 */ setEnabledCipherSuites(String[] suites)317 @Override public void setEnabledCipherSuites(String[] suites) 318 { 319 engine.setEnabledCipherSuites(suites); 320 } 321 322 /* (non-Javadoc) 323 * @see javax.net.ssl.SSLSocket#setEnabledProtocols(java.lang.String[]) 324 */ setEnabledProtocols(String[] protocols)325 @Override public void setEnabledProtocols(String[] protocols) 326 { 327 engine.setEnabledProtocols(protocols); 328 } 329 330 /* (non-Javadoc) 331 * @see javax.net.ssl.SSLSocket#setNeedClientAuth(boolean) 332 */ setNeedClientAuth(boolean needAuth)333 @Override public void setNeedClientAuth(boolean needAuth) 334 { 335 engine.setNeedClientAuth(needAuth); 336 } 337 338 /* (non-Javadoc) 339 * @see javax.net.ssl.SSLSocket#setUseClientMode(boolean) 340 */ setUseClientMode(boolean clientMode)341 @Override public void setUseClientMode(boolean clientMode) 342 { 343 engine.setUseClientMode(clientMode); 344 } 345 346 /* (non-Javadoc) 347 * @see javax.net.ssl.SSLSocket#setWantClientAuth(boolean) 348 */ setWantClientAuth(boolean wantAuth)349 @Override public void setWantClientAuth(boolean wantAuth) 350 { 351 engine.setWantClientAuth(wantAuth); 352 } 353 354 /* (non-Javadoc) 355 * @see javax.net.ssl.SSLSocket#startHandshake() 356 */ startHandshake()357 @Override public void startHandshake() throws IOException 358 { 359 if (isHandshaking) 360 return; 361 362 if (handshakeException != null) 363 throw handshakeException; 364 365 Thread t = new Thread(new Runnable() 366 { 367 public void run() 368 { 369 try 370 { 371 doHandshake(); 372 } 373 catch (IOException ioe) 374 { 375 handshakeException = ioe; 376 } 377 } 378 }, "HandshakeThread@" + System.identityHashCode(this)); 379 t.start(); 380 } 381 doHandshake()382 void doHandshake() throws IOException 383 { 384 synchronized (engine) 385 { 386 if (isHandshaking) 387 { 388 try 389 { 390 engine.wait(); 391 } 392 catch (InterruptedException ie) 393 { 394 } 395 return; 396 } 397 isHandshaking = true; 398 } 399 400 if (initialHandshakeDone) 401 throw new SSLException("rehandshaking not yet implemented"); 402 403 long now = -System.currentTimeMillis(); 404 engine.beginHandshake(); 405 406 HandshakeStatus status = engine.getHandshakeStatus(); 407 assert(status != HandshakeStatus.NOT_HANDSHAKING); 408 409 ByteBuffer inBuffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]); 410 inBuffer.position(inBuffer.limit()); 411 ByteBuffer outBuffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]); 412 ByteBuffer emptyBuffer = ByteBuffer.allocate(0); 413 SSLEngineResult result = null; 414 415 DataInputStream sockIn = new DataInputStream(underlyingSocket.getInputStream()); 416 OutputStream sockOut = underlyingSocket.getOutputStream(); 417 418 try 419 { 420 while (status != HandshakeStatus.NOT_HANDSHAKING 421 && status != HandshakeStatus.FINISHED) 422 { 423 logger.logv(Component.SSL_HANDSHAKE, "socket processing state {0}", 424 status); 425 426 if (inBuffer.capacity() != getSession().getPacketBufferSize()) 427 { 428 ByteBuffer b 429 = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]); 430 if (inBuffer.hasRemaining()) 431 b.put(inBuffer).flip(); 432 inBuffer = b; 433 } 434 if (outBuffer.capacity() != getSession().getPacketBufferSize()) 435 outBuffer 436 = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]); 437 438 switch (status) 439 { 440 case NEED_UNWRAP: 441 // Read in a single SSL record. 442 inBuffer.clear(); 443 int i = sockIn.read(); 444 if (i == -1) 445 throw new EOFException(); 446 if ((i & 0x80) == 0x80) // SSLv2 client hello. 447 { 448 inBuffer.put((byte) i); 449 int v2len = (i & 0x7f) << 8; 450 i = sockIn.read(); 451 v2len = v2len | (i & 0xff); 452 inBuffer.put((byte) i); 453 sockIn.readFully(inBuffer.array(), 2, v2len); 454 inBuffer.position(0).limit(v2len + 2); 455 } 456 else 457 { 458 inBuffer.put((byte) i); 459 inBuffer.putInt(sockIn.readInt()); 460 int reclen = inBuffer.getShort(3) & 0xFFFF; 461 sockIn.readFully(inBuffer.array(), 5, reclen); 462 inBuffer.position(0).limit(reclen + 5); 463 } 464 result = engine.unwrap(inBuffer, emptyBuffer); 465 status = result.getHandshakeStatus(); 466 if (result.getStatus() != Status.OK) 467 throw new SSLException("unexpected SSL status " 468 + result.getStatus()); 469 break; 470 471 case NEED_WRAP: 472 { 473 outBuffer.clear(); 474 result = engine.wrap(emptyBuffer, outBuffer); 475 status = result.getHandshakeStatus(); 476 if (result.getStatus() != Status.OK) 477 throw new SSLException("unexpected SSL status " 478 + result.getStatus()); 479 outBuffer.flip(); 480 sockOut.write(outBuffer.array(), outBuffer.position(), 481 outBuffer.limit()); 482 } 483 break; 484 485 case NEED_TASK: 486 { 487 Runnable task; 488 while ((task = engine.getDelegatedTask()) != null) 489 task.run(); 490 status = engine.getHandshakeStatus(); 491 } 492 break; 493 494 case FINISHED: 495 break; 496 } 497 } 498 499 initialHandshakeDone = true; 500 501 HandshakeCompletedEvent hce = new HandshakeCompletedEvent(this, getSession()); 502 for (HandshakeCompletedListener l : listeners) 503 { 504 try 505 { 506 l.handshakeCompleted(hce); 507 } 508 catch (ThreadDeath td) 509 { 510 throw td; 511 } 512 catch (Throwable x) 513 { 514 logger.log(Component.WARNING, 515 "HandshakeCompletedListener threw exception", x); 516 } 517 } 518 519 now += System.currentTimeMillis(); 520 if (Debug.DEBUG) 521 logger.logv(Component.SSL_HANDSHAKE, 522 "handshake completed in {0}ms in thread {1}", now, 523 Thread.currentThread().getName()); 524 } 525 catch (SSLException ssle) 526 { 527 handshakeException = ssle; 528 throw ssle; 529 } 530 finally 531 { 532 synchronized (engine) 533 { 534 isHandshaking = false; 535 engine.notifyAll(); 536 } 537 } 538 } 539 540 // Methods overriding Socket. 541 bind(SocketAddress bindpoint)542 @Override public void bind(SocketAddress bindpoint) throws IOException 543 { 544 underlyingSocket.bind(bindpoint); 545 } 546 connect(SocketAddress endpoint)547 @Override public void connect(SocketAddress endpoint) throws IOException 548 { 549 underlyingSocket.connect(endpoint); 550 } 551 connect(SocketAddress endpoint, int timeout)552 @Override public void connect(SocketAddress endpoint, int timeout) 553 throws IOException 554 { 555 underlyingSocket.connect(endpoint, timeout); 556 } 557 getInetAddress()558 @Override public InetAddress getInetAddress() 559 { 560 return underlyingSocket.getInetAddress(); 561 } 562 getLocalAddress()563 @Override public InetAddress getLocalAddress() 564 { 565 return underlyingSocket.getLocalAddress(); 566 } 567 getPort()568 @Override public int getPort() 569 { 570 return underlyingSocket.getPort(); 571 } 572 getLocalPort()573 @Override public int getLocalPort() 574 { 575 return underlyingSocket.getLocalPort(); 576 } 577 getRemoteSocketAddress()578 @Override public SocketAddress getRemoteSocketAddress() 579 { 580 return underlyingSocket.getRemoteSocketAddress(); 581 } 582 getLocalSocketAddress()583 public SocketAddress getLocalSocketAddress() 584 { 585 return underlyingSocket.getLocalSocketAddress(); 586 } 587 getChannel()588 @Override public SocketChannel getChannel() 589 { 590 throw new UnsupportedOperationException("use javax.net.ssl.SSLEngine for NIO"); 591 } 592 getInputStream()593 @Override public InputStream getInputStream() throws IOException 594 { 595 return new SocketInputStream(); 596 } 597 getOutputStream()598 @Override public OutputStream getOutputStream() throws IOException 599 { 600 return new SocketOutputStream(); 601 } 602 setTcpNoDelay(boolean on)603 @Override public void setTcpNoDelay(boolean on) throws SocketException 604 { 605 underlyingSocket.setTcpNoDelay(on); 606 } 607 getTcpNoDelay()608 @Override public boolean getTcpNoDelay() throws SocketException 609 { 610 return underlyingSocket.getTcpNoDelay(); 611 } 612 setSoLinger(boolean on, int linger)613 @Override public void setSoLinger(boolean on, int linger) throws SocketException 614 { 615 underlyingSocket.setSoLinger(on, linger); 616 } 617 getSoLinger()618 public int getSoLinger() throws SocketException 619 { 620 return underlyingSocket.getSoLinger(); 621 } 622 sendUrgentData(int x)623 @Override public void sendUrgentData(int x) throws IOException 624 { 625 throw new UnsupportedOperationException("not supported"); 626 } 627 setOOBInline(boolean on)628 @Override public void setOOBInline(boolean on) throws SocketException 629 { 630 underlyingSocket.setOOBInline(on); 631 } 632 getOOBInline()633 @Override public boolean getOOBInline() throws SocketException 634 { 635 return underlyingSocket.getOOBInline(); 636 } 637 setSoTimeout(int timeout)638 @Override public void setSoTimeout(int timeout) throws SocketException 639 { 640 underlyingSocket.setSoTimeout(timeout); 641 } 642 getSoTimeout()643 @Override public int getSoTimeout() throws SocketException 644 { 645 return underlyingSocket.getSoTimeout(); 646 } 647 setSendBufferSize(int size)648 @Override public void setSendBufferSize(int size) throws SocketException 649 { 650 underlyingSocket.setSendBufferSize(size); 651 } 652 getSendBufferSize()653 @Override public int getSendBufferSize() throws SocketException 654 { 655 return underlyingSocket.getSendBufferSize(); 656 } 657 setReceiveBufferSize(int size)658 @Override public void setReceiveBufferSize(int size) throws SocketException 659 { 660 underlyingSocket.setReceiveBufferSize(size); 661 } 662 getReceiveBufferSize()663 @Override public int getReceiveBufferSize() throws SocketException 664 { 665 return underlyingSocket.getReceiveBufferSize(); 666 } 667 setKeepAlive(boolean on)668 @Override public void setKeepAlive(boolean on) throws SocketException 669 { 670 underlyingSocket.setKeepAlive(on); 671 } 672 getKeepAlive()673 @Override public boolean getKeepAlive() throws SocketException 674 { 675 return underlyingSocket.getKeepAlive(); 676 } 677 setTrafficClass(int tc)678 @Override public void setTrafficClass(int tc) throws SocketException 679 { 680 underlyingSocket.setTrafficClass(tc); 681 } 682 getTrafficClass()683 @Override public int getTrafficClass() throws SocketException 684 { 685 return underlyingSocket.getTrafficClass(); 686 } 687 setReuseAddress(boolean reuseAddress)688 @Override public void setReuseAddress(boolean reuseAddress) 689 throws SocketException 690 { 691 underlyingSocket.setReuseAddress(reuseAddress); 692 } 693 getReuseAddress()694 @Override public boolean getReuseAddress() throws SocketException 695 { 696 return underlyingSocket.getReuseAddress(); 697 } 698 close()699 @Override public void close() throws IOException 700 { 701 // XXX closure alerts. 702 if (autoClose) 703 underlyingSocket.close(); 704 } 705 shutdownInput()706 @Override public void shutdownInput() throws IOException 707 { 708 underlyingSocket.shutdownInput(); 709 } 710 shutdownOutput()711 @Override public void shutdownOutput() throws IOException 712 { 713 underlyingSocket.shutdownOutput(); 714 } 715 isConnected()716 @Override public boolean isConnected() 717 { 718 return underlyingSocket.isConnected(); 719 } 720 isBound()721 @Override public boolean isBound() 722 { 723 return underlyingSocket.isBound(); 724 } 725 isClosed()726 @Override public boolean isClosed() 727 { 728 return underlyingSocket.isClosed(); 729 } 730 isInputShutdown()731 @Override public boolean isInputShutdown() 732 { 733 return underlyingSocket.isInputShutdown(); 734 } 735 isOutputShutdown()736 @Override public boolean isOutputShutdown() 737 { 738 return underlyingSocket.isOutputShutdown(); 739 } 740 } 741