1 /* 2 * Copyright (c) 2005, 2010, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.net.httpserver; 27 28 import java.net.*; 29 import java.nio.*; 30 import java.io.*; 31 import java.nio.channels.*; 32 import java.util.*; 33 import java.util.concurrent.*; 34 import java.util.concurrent.locks.*; 35 import javax.net.ssl.*; 36 import javax.net.ssl.SSLEngineResult.*; 37 import com.sun.net.httpserver.*; 38 import com.sun.net.httpserver.spi.*; 39 40 /** 41 * given a non-blocking SocketChannel, it produces 42 * (blocking) streams which encrypt/decrypt the SSL content 43 * and handle the SSL handshaking automatically. 44 */ 45 46 class SSLStreams { 47 48 SSLContext sslctx; 49 SocketChannel chan; 50 TimeSource time; 51 ServerImpl server; 52 SSLEngine engine; 53 EngineWrapper wrapper; 54 OutputStream os; 55 InputStream is; 56 57 /* held by thread doing the hand-shake on this connection */ 58 Lock handshaking = new ReentrantLock(); 59 60 SSLStreams (ServerImpl server, SSLContext sslctx, SocketChannel chan) throws IOException { 61 this.server = server; 62 this.time= (TimeSource)server; 63 this.sslctx= sslctx; 64 this.chan= chan; 65 InetSocketAddress addr = 66 (InetSocketAddress)chan.socket().getRemoteSocketAddress(); 67 engine = sslctx.createSSLEngine (addr.getHostName(), addr.getPort()); 68 engine.setUseClientMode (false); 69 HttpsConfigurator cfg = server.getHttpsConfigurator(); 70 configureEngine (cfg, addr); 71 wrapper = new EngineWrapper (chan, engine); 72 } 73 74 private void configureEngine(HttpsConfigurator cfg, InetSocketAddress addr){ 75 if (cfg != null) { 76 Parameters params = new Parameters (cfg, addr); 77 //BEGIN_TIGER_EXCLUDE 78 cfg.configure (params); 79 SSLParameters sslParams = params.getSSLParameters(); 80 if (sslParams != null) { 81 engine.setSSLParameters (sslParams); 82 } else 83 //END_TIGER_EXCLUDE 84 { 85 /* tiger compatibility */ 86 if (params.getCipherSuites() != null) { 87 try { 88 engine.setEnabledCipherSuites ( 89 params.getCipherSuites() 90 ); 91 } catch (IllegalArgumentException e) { /* LOG */} 92 } 93 engine.setNeedClientAuth (params.getNeedClientAuth()); 94 engine.setWantClientAuth (params.getWantClientAuth()); 95 if (params.getProtocols() != null) { 96 try { 97 engine.setEnabledProtocols ( 98 params.getProtocols() 99 ); 100 } catch (IllegalArgumentException e) { /* LOG */} 101 } 102 } 103 } 104 } 105 106 class Parameters extends HttpsParameters { 107 InetSocketAddress addr; 108 HttpsConfigurator cfg; 109 110 Parameters (HttpsConfigurator cfg, InetSocketAddress addr) { 111 this.addr = addr; 112 this.cfg = cfg; 113 } 114 public InetSocketAddress getClientAddress () { 115 return addr; 116 } 117 public HttpsConfigurator getHttpsConfigurator() { 118 return cfg; 119 } 120 //BEGIN_TIGER_EXCLUDE 121 SSLParameters params; 122 public void setSSLParameters (SSLParameters p) { 123 params = p; 124 } 125 SSLParameters getSSLParameters () { 126 return params; 127 } 128 //END_TIGER_EXCLUDE 129 } 130 131 /** 132 * cleanup resources allocated inside this object 133 */ 134 void close () throws IOException { 135 wrapper.close(); 136 } 137 138 /** 139 * return the SSL InputStream 140 */ 141 InputStream getInputStream () throws IOException { 142 if (is == null) { 143 is = new InputStream(); 144 } 145 return is; 146 } 147 148 /** 149 * return the SSL OutputStream 150 */ 151 OutputStream getOutputStream () throws IOException { 152 if (os == null) { 153 os = new OutputStream(); 154 } 155 return os; 156 } 157 158 SSLEngine getSSLEngine () { 159 return engine; 160 } 161 162 /** 163 * request the engine to repeat the handshake on this session 164 * the handshake must be driven by reads/writes on the streams 165 * Normally, not necessary to call this. 166 */ 167 void beginHandshake() throws SSLException { 168 engine.beginHandshake(); 169 } 170 171 class WrapperResult { 172 SSLEngineResult result; 173 174 /* if passed in buffer was not big enough then the 175 * a reallocated buffer is returned here 176 */ 177 ByteBuffer buf; 178 } 179 180 int app_buf_size; 181 int packet_buf_size; 182 183 enum BufType { 184 PACKET, APPLICATION 185 }; 186 187 private ByteBuffer allocate (BufType type) { 188 return allocate (type, -1); 189 } 190 191 private ByteBuffer allocate (BufType type, int len) { 192 assert engine != null; 193 synchronized (this) { 194 int size; 195 if (type == BufType.PACKET) { 196 if (packet_buf_size == 0) { 197 SSLSession sess = engine.getSession(); 198 packet_buf_size = sess.getPacketBufferSize(); 199 } 200 if (len > packet_buf_size) { 201 packet_buf_size = len; 202 } 203 size = packet_buf_size; 204 } else { 205 if (app_buf_size == 0) { 206 SSLSession sess = engine.getSession(); 207 app_buf_size = sess.getApplicationBufferSize(); 208 } 209 if (len > app_buf_size) { 210 app_buf_size = len; 211 } 212 size = app_buf_size; 213 } 214 return ByteBuffer.allocate (size); 215 } 216 } 217 218 /* reallocates the buffer by :- 219 * 1. creating a new buffer double the size of the old one 220 * 2. putting the contents of the old buffer into the new one 221 * 3. set xx_buf_size to the new size if it was smaller than new size 222 * 223 * flip is set to true if the old buffer needs to be flipped 224 * before it is copied. 225 */ 226 private ByteBuffer realloc (ByteBuffer b, boolean flip, BufType type) { 227 synchronized (this) { 228 int nsize = 2 * b.capacity(); 229 ByteBuffer n = allocate (type, nsize); 230 if (flip) { 231 b.flip(); 232 } 233 n.put(b); 234 b = n; 235 } 236 return b; 237 } 238 /** 239 * This is a thin wrapper over SSLEngine and the SocketChannel, 240 * which guarantees the ordering of wraps/unwraps with respect to the underlying 241 * channel read/writes. It handles the UNDER/OVERFLOW status codes 242 * It does not handle the handshaking status codes, or the CLOSED status code 243 * though once the engine is closed, any attempt to read/write to it 244 * will get an exception. The overall result is returned. 245 * It functions synchronously/blocking 246 */ 247 class EngineWrapper { 248 249 SocketChannel chan; 250 SSLEngine engine; 251 Object wrapLock, unwrapLock; 252 ByteBuffer unwrap_src, wrap_dst; 253 boolean closed = false; 254 int u_remaining; // the number of bytes left in unwrap_src after an unwrap() 255 256 EngineWrapper (SocketChannel chan, SSLEngine engine) throws IOException { 257 this.chan = chan; 258 this.engine = engine; 259 wrapLock = new Object(); 260 unwrapLock = new Object(); 261 unwrap_src = allocate(BufType.PACKET); 262 wrap_dst = allocate(BufType.PACKET); 263 } 264 265 void close () throws IOException { 266 } 267 268 /* try to wrap and send the data in src. Handles OVERFLOW. 269 * Might block if there is an outbound blockage or if another 270 * thread is calling wrap(). Also, might not send any data 271 * if an unwrap is needed. 272 */ 273 WrapperResult wrapAndSend(ByteBuffer src) throws IOException { 274 return wrapAndSendX(src, false); 275 } 276 277 WrapperResult wrapAndSendX(ByteBuffer src, boolean ignoreClose) throws IOException { 278 if (closed && !ignoreClose) { 279 throw new IOException ("Engine is closed"); 280 } 281 Status status; 282 WrapperResult r = new WrapperResult(); 283 synchronized (wrapLock) { 284 wrap_dst.clear(); 285 do { 286 r.result = engine.wrap (src, wrap_dst); 287 status = r.result.getStatus(); 288 if (status == Status.BUFFER_OVERFLOW) { 289 wrap_dst = realloc (wrap_dst, true, BufType.PACKET); 290 } 291 } while (status == Status.BUFFER_OVERFLOW); 292 if (status == Status.CLOSED && !ignoreClose) { 293 closed = true; 294 return r; 295 } 296 if (r.result.bytesProduced() > 0) { 297 wrap_dst.flip(); 298 int l = wrap_dst.remaining(); 299 assert l == r.result.bytesProduced(); 300 while (l>0) { 301 l -= chan.write (wrap_dst); 302 } 303 } 304 } 305 return r; 306 } 307 308 /* block until a complete message is available and return it 309 * in dst, together with the Result. dst may have been re-allocated 310 * so caller should check the returned value in Result 311 * If handshaking is in progress then, possibly no data is returned 312 */ 313 WrapperResult recvAndUnwrap(ByteBuffer dst) throws IOException { 314 Status status = Status.OK; 315 WrapperResult r = new WrapperResult(); 316 r.buf = dst; 317 if (closed) { 318 throw new IOException ("Engine is closed"); 319 } 320 boolean needData; 321 if (u_remaining > 0) { 322 unwrap_src.compact(); 323 unwrap_src.flip(); 324 needData = false; 325 } else { 326 unwrap_src.clear(); 327 needData = true; 328 } 329 synchronized (unwrapLock) { 330 int x; 331 do { 332 if (needData) { 333 do { 334 x = chan.read (unwrap_src); 335 } while (x == 0); 336 if (x == -1) { 337 throw new IOException ("connection closed for reading"); 338 } 339 unwrap_src.flip(); 340 } 341 r.result = engine.unwrap (unwrap_src, r.buf); 342 status = r.result.getStatus(); 343 if (status == Status.BUFFER_UNDERFLOW) { 344 if (unwrap_src.limit() == unwrap_src.capacity()) { 345 /* buffer not big enough */ 346 unwrap_src = realloc ( 347 unwrap_src, false, BufType.PACKET 348 ); 349 } else { 350 /* Buffer not full, just need to read more 351 * data off the channel. Reset pointers 352 * for reading off SocketChannel 353 */ 354 unwrap_src.position (unwrap_src.limit()); 355 unwrap_src.limit (unwrap_src.capacity()); 356 } 357 needData = true; 358 } else if (status == Status.BUFFER_OVERFLOW) { 359 r.buf = realloc (r.buf, true, BufType.APPLICATION); 360 needData = false; 361 } else if (status == Status.CLOSED) { 362 closed = true; 363 r.buf.flip(); 364 return r; 365 } 366 } while (status != Status.OK); 367 } 368 u_remaining = unwrap_src.remaining(); 369 return r; 370 } 371 } 372 373 /** 374 * send the data in the given ByteBuffer. If a handshake is needed 375 * then this is handled within this method. When this call returns, 376 * all of the given user data has been sent and any handshake has been 377 * completed. Caller should check if engine has been closed. 378 */ 379 public WrapperResult sendData (ByteBuffer src) throws IOException { 380 WrapperResult r=null; 381 while (src.remaining() > 0) { 382 r = wrapper.wrapAndSend(src); 383 Status status = r.result.getStatus(); 384 if (status == Status.CLOSED) { 385 doClosure (); 386 return r; 387 } 388 HandshakeStatus hs_status = r.result.getHandshakeStatus(); 389 if (hs_status != HandshakeStatus.FINISHED && 390 hs_status != HandshakeStatus.NOT_HANDSHAKING) 391 { 392 doHandshake(hs_status); 393 } 394 } 395 return r; 396 } 397 398 /** 399 * read data thru the engine into the given ByteBuffer. If the 400 * given buffer was not large enough, a new one is allocated 401 * and returned. This call handles handshaking automatically. 402 * Caller should check if engine has been closed. 403 */ 404 public WrapperResult recvData (ByteBuffer dst) throws IOException { 405 /* we wait until some user data arrives */ 406 WrapperResult r = null; 407 assert dst.position() == 0; 408 while (dst.position() == 0) { 409 r = wrapper.recvAndUnwrap (dst); 410 dst = (r.buf != dst) ? r.buf: dst; 411 Status status = r.result.getStatus(); 412 if (status == Status.CLOSED) { 413 doClosure (); 414 return r; 415 } 416 417 HandshakeStatus hs_status = r.result.getHandshakeStatus(); 418 if (hs_status != HandshakeStatus.FINISHED && 419 hs_status != HandshakeStatus.NOT_HANDSHAKING) 420 { 421 doHandshake (hs_status); 422 } 423 } 424 dst.flip(); 425 return r; 426 } 427 428 /* we've received a close notify. Need to call wrap to send 429 * the response 430 */ 431 void doClosure () throws IOException { 432 try { 433 handshaking.lock(); 434 ByteBuffer tmp = allocate(BufType.APPLICATION); 435 WrapperResult r; 436 do { 437 tmp.clear(); 438 tmp.flip (); 439 r = wrapper.wrapAndSendX (tmp, true); 440 } while (r.result.getStatus() != Status.CLOSED); 441 } finally { 442 handshaking.unlock(); 443 } 444 } 445 446 /* do the (complete) handshake after acquiring the handshake lock. 447 * If two threads call this at the same time, then we depend 448 * on the wrapper methods being idempotent. eg. if wrapAndSend() 449 * is called with no data to send then there must be no problem 450 */ 451 void doHandshake (HandshakeStatus hs_status) throws IOException { 452 try { 453 handshaking.lock(); 454 ByteBuffer tmp = allocate(BufType.APPLICATION); 455 while (hs_status != HandshakeStatus.FINISHED && 456 hs_status != HandshakeStatus.NOT_HANDSHAKING) 457 { 458 WrapperResult r = null; 459 switch (hs_status) { 460 case NEED_TASK: 461 Runnable task; 462 while ((task = engine.getDelegatedTask()) != null) { 463 /* run in current thread, because we are already 464 * running an external Executor 465 */ 466 task.run(); 467 } 468 /* fall thru - call wrap again */ 469 case NEED_WRAP: 470 tmp.clear(); 471 tmp.flip(); 472 r = wrapper.wrapAndSend(tmp); 473 break; 474 475 case NEED_UNWRAP: 476 tmp.clear(); 477 r = wrapper.recvAndUnwrap (tmp); 478 if (r.buf != tmp) { 479 tmp = r.buf; 480 } 481 assert tmp.position() == 0; 482 break; 483 } 484 hs_status = r.result.getHandshakeStatus(); 485 } 486 } finally { 487 handshaking.unlock(); 488 } 489 } 490 491 /** 492 * represents an SSL input stream. Multiple https requests can 493 * be sent over one stream. closing this stream causes an SSL close 494 * input. 495 */ 496 class InputStream extends java.io.InputStream { 497 498 ByteBuffer bbuf; 499 boolean closed = false; 500 501 /* this stream eof */ 502 boolean eof = false; 503 504 boolean needData = true; 505 506 InputStream () { 507 bbuf = allocate (BufType.APPLICATION); 508 } 509 510 public int read (byte[] buf, int off, int len) throws IOException { 511 if (closed) { 512 throw new IOException ("SSL stream is closed"); 513 } 514 if (eof) { 515 return 0; 516 } 517 int available=0; 518 if (!needData) { 519 available = bbuf.remaining(); 520 needData = (available==0); 521 } 522 if (needData) { 523 bbuf.clear(); 524 WrapperResult r = recvData (bbuf); 525 bbuf = r.buf== bbuf? bbuf: r.buf; 526 if ((available=bbuf.remaining()) == 0) { 527 eof = true; 528 return 0; 529 } else { 530 needData = false; 531 } 532 } 533 /* copy as much as possible from buf into users buf */ 534 if (len > available) { 535 len = available; 536 } 537 bbuf.get (buf, off, len); 538 return len; 539 } 540 541 public int available () throws IOException { 542 return bbuf.remaining(); 543 } 544 545 public boolean markSupported () { 546 return false; /* not possible with SSLEngine */ 547 } 548 549 public void reset () throws IOException { 550 throw new IOException ("mark/reset not supported"); 551 } 552 553 public long skip (long s) throws IOException { 554 int n = (int)s; 555 if (closed) { 556 throw new IOException ("SSL stream is closed"); 557 } 558 if (eof) { 559 return 0; 560 } 561 int ret = n; 562 while (n > 0) { 563 if (bbuf.remaining() >= n) { 564 bbuf.position (bbuf.position()+n); 565 return ret; 566 } else { 567 n -= bbuf.remaining(); 568 bbuf.clear(); 569 WrapperResult r = recvData (bbuf); 570 bbuf = r.buf==bbuf? bbuf: r.buf; 571 } 572 } 573 return ret; /* not reached */ 574 } 575 576 /** 577 * close the SSL connection. All data must have been consumed 578 * before this is called. Otherwise an exception will be thrown. 579 * [Note. May need to revisit this. not quite the normal close() symantics 580 */ 581 public void close () throws IOException { 582 eof = true; 583 engine.closeInbound (); 584 } 585 586 public int read (byte[] buf) throws IOException { 587 return read (buf, 0, buf.length); 588 } 589 590 byte single[] = new byte [1]; 591 592 public int read () throws IOException { 593 int n = read (single, 0, 1); 594 if (n == 0) { 595 return -1; 596 } else { 597 return single[0] & 0xFF; 598 } 599 } 600 } 601 602 /** 603 * represents an SSL output stream. plain text data written to this stream 604 * is encrypted by the stream. Multiple HTTPS responses can be sent on 605 * one stream. closing this stream initiates an SSL closure 606 */ 607 class OutputStream extends java.io.OutputStream { 608 ByteBuffer buf; 609 boolean closed = false; 610 byte single[] = new byte[1]; 611 612 OutputStream() { 613 buf = allocate(BufType.APPLICATION); 614 } 615 616 public void write(int b) throws IOException { 617 single[0] = (byte)b; 618 write (single, 0, 1); 619 } 620 621 public void write(byte b[]) throws IOException { 622 write (b, 0, b.length); 623 } 624 public void write(byte b[], int off, int len) throws IOException { 625 if (closed) { 626 throw new IOException ("output stream is closed"); 627 } 628 while (len > 0) { 629 int l = len > buf.capacity() ? buf.capacity() : len; 630 buf.clear(); 631 buf.put (b, off, l); 632 len -= l; 633 off += l; 634 buf.flip(); 635 WrapperResult r = sendData (buf); 636 if (r.result.getStatus() == Status.CLOSED) { 637 closed = true; 638 if (len > 0) { 639 throw new IOException ("output stream is closed"); 640 } 641 } 642 } 643 } 644 645 public void flush() throws IOException { 646 /* no-op */ 647 } 648 649 public void close() throws IOException { 650 WrapperResult r=null; 651 engine.closeOutbound(); 652 closed = true; 653 HandshakeStatus stat = HandshakeStatus.NEED_WRAP; 654 buf.clear(); 655 while (stat == HandshakeStatus.NEED_WRAP) { 656 r = wrapper.wrapAndSend (buf); 657 stat = r.result.getHandshakeStatus(); 658 } 659 assert r.result.getStatus() == Status.CLOSED; 660 } 661 } 662 }