Crossfire JXClient, Trunk
ChannelIOSecure.java
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * - Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * - Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  *
15  * - Neither the name of Oracle nor the names of its
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * This source code is provided to illustrate the usage of a given feature
34  * or technique and has been deliberately simplified. Additional steps
35  * required for a production-quality application, such as security checks,
36  * input validation and proper error handling, might not be present in
37  * this sample code.
38  */
39 
40 
41 import java.io.*;
42 import java.nio.*;
43 import java.nio.channels.*;
44 import javax.net.ssl.*;
45 import javax.net.ssl.SSLEngineResult.*;
46 
104 class ChannelIOSecure extends ChannelIO {
105 
106  private SSLEngine sslEngine = null;
107 
108  private int appBBSize;
109  private int netBBSize;
110 
111  /*
112  * All I/O goes through these buffers.
113  * <P>
114  * It might be nice to use a cache of ByteBuffers so we're
115  * not alloc/dealloc'ing ByteBuffer's for each new SSLEngine.
116  * <P>
117  * We use our superclass' requestBB for our application input buffer.
118  * Outbound application data is supplied to us by our callers.
119  */
120  private ByteBuffer inNetBB;
121  private ByteBuffer outNetBB;
122 
123  /*
124  * An empty ByteBuffer for use when one isn't available, say
125  * as a source buffer during initial handshake wraps or for close
126  * operations.
127  */
128  private static ByteBuffer hsBB = ByteBuffer.allocate(0);
129 
130  /*
131  * The FileChannel we're currently transferTo'ing (reading).
132  */
133  private ByteBuffer fileChannelBB = null;
134 
135  /*
136  * During our initial handshake, keep track of the next
137  * SSLEngine operation that needs to occur:
138  *
139  * NEED_WRAP/NEED_UNWRAP
140  *
141  * Once the initial handshake has completed, we can short circuit
142  * handshake checks with initialHSComplete.
143  */
144  private HandshakeStatus initialHSStatus;
145  private boolean initialHSComplete;
146 
147  /*
148  * We have received the shutdown request by our caller, and have
149  * closed our outbound side.
150  */
151  private boolean shutdown = false;
152 
153  /*
154  * Constructor for a secure ChannelIO variant.
155  */
156  protected ChannelIOSecure(SocketChannel sc, boolean blocking,
157  SSLContext sslc) throws IOException {
158  super(sc, blocking);
159 
160  /*
161  * We're a server, so no need to use host/port variant.
162  *
163  * The first call for a server is a NEED_UNWRAP.
164  */
165  sslEngine = sslc.createSSLEngine();
166  sslEngine.setUseClientMode(false);
167  initialHSStatus = HandshakeStatus.NEED_UNWRAP;
168  initialHSComplete = false;
169 
170  // Create a buffer using the normal expected packet size we'll
171  // be getting. This may change, depending on the peer's
172  // SSL implementation.
173  netBBSize = sslEngine.getSession().getPacketBufferSize();
174  inNetBB = ByteBuffer.allocate(netBBSize);
175  outNetBB = ByteBuffer.allocate(netBBSize);
176  outNetBB.position(0);
177  outNetBB.limit(0);
178  }
179 
180  /*
181  * Static factory method for creating a secure ChannelIO object.
182  * <P>
183  * We need to allocate different sized application data buffers
184  * based on whether we're secure or not. We can't determine
185  * this until our sslEngine is created.
186  */
187  static ChannelIOSecure getInstance(SocketChannel sc, boolean blocking,
188  SSLContext sslc) throws IOException {
189 
190  ChannelIOSecure cio = new ChannelIOSecure(sc, blocking, sslc);
191 
192  // Create a buffer using the normal expected application size we'll
193  // be getting. This may change, depending on the peer's
194  // SSL implementation.
195  cio.appBBSize = cio.sslEngine.getSession().getApplicationBufferSize();
196  cio.requestBB = ByteBuffer.allocate(cio.appBBSize);
197 
198  return cio;
199  }
200 
201  /*
202  * Calls up to the superclass to adjust the buffer size
203  * by an appropriate increment.
204  */
205  protected void resizeRequestBB() {
206  resizeRequestBB(appBBSize);
207  }
208 
209  /*
210  * Adjust the inbount network buffer to an appropriate size.
211  */
212  private void resizeResponseBB() {
213  ByteBuffer bb = ByteBuffer.allocate(netBBSize);
214  inNetBB.flip();
215  bb.put(inNetBB);
216  inNetBB = bb;
217  }
218 
219  /*
220  * Writes bb to the SocketChannel.
221  * <P>
222  * Returns true when the ByteBuffer has no remaining data.
223  */
224  private boolean tryFlush(ByteBuffer bb) throws IOException {
225  super.write(bb);
226  return !bb.hasRemaining();
227  }
228 
229  /*
230  * Perform any handshaking processing.
231  * <P>
232  * This variant is for Servers without SelectionKeys (e.g.
233  * blocking).
234  */
235  boolean doHandshake() throws IOException {
236  return doHandshake(null);
237  }
238 
239  /*
240  * Perform any handshaking processing.
241  * <P>
242  * If a SelectionKey is passed, register for selectable
243  * operations.
244  * <P>
245  * In the blocking case, our caller will keep calling us until
246  * we finish the handshake. Our reads/writes will block as expected.
247  * <P>
248  * In the non-blocking case, we just received the selection notification
249  * that this channel is ready for whatever the operation is, so give
250  * it a try.
251  * <P>
252  * return:
253  * true when handshake is done.
254  * false while handshake is in progress
255  */
256  boolean doHandshake(SelectionKey sk) throws IOException {
257 
258  SSLEngineResult result;
259 
260  if (initialHSComplete) {
261  return initialHSComplete;
262  }
263 
264  /*
265  * Flush out the outgoing buffer, if there's anything left in
266  * it.
267  */
268  if (outNetBB.hasRemaining()) {
269 
270  if (!tryFlush(outNetBB)) {
271  return false;
272  }
273 
274  // See if we need to switch from write to read mode.
275 
276  switch (initialHSStatus) {
277 
278  /*
279  * Is this the last buffer?
280  */
281  case FINISHED:
282  initialHSComplete = true;
283  // Fall-through to reregister need for a Read.
284 
285  case NEED_UNWRAP:
286  if (sk != null) {
287  sk.interestOps(SelectionKey.OP_READ);
288  }
289  break;
290  }
291 
292  return initialHSComplete;
293  }
294 
295 
296  switch (initialHSStatus) {
297 
298  case NEED_UNWRAP:
299  if (sc.read(inNetBB) == -1) {
300  sslEngine.closeInbound();
301  return initialHSComplete;
302  }
303 
304 needIO:
305  while (initialHSStatus == HandshakeStatus.NEED_UNWRAP) {
306  resizeRequestBB(); // expected room for unwrap
307  inNetBB.flip();
308  result = sslEngine.unwrap(inNetBB, requestBB);
309  inNetBB.compact();
310 
311  initialHSStatus = result.getHandshakeStatus();
312 
313  switch (result.getStatus()) {
314 
315  case OK:
316  switch (initialHSStatus) {
317  case NOT_HANDSHAKING:
318  throw new IOException(
319  "Not handshaking during initial handshake");
320 
321  case NEED_TASK:
322  initialHSStatus = doTasks();
323  break;
324 
325  case FINISHED:
326  initialHSComplete = true;
327  break needIO;
328  }
329 
330  break;
331 
332  case BUFFER_UNDERFLOW:
333  // Resize buffer if needed.
334  netBBSize = sslEngine.getSession().getPacketBufferSize();
335  if (netBBSize > inNetBB.capacity()) {
336  resizeResponseBB();
337  }
338 
339  /*
340  * Need to go reread the Channel for more data.
341  */
342  if (sk != null) {
343  sk.interestOps(SelectionKey.OP_READ);
344  }
345  break needIO;
346 
347  case BUFFER_OVERFLOW:
348  // Reset the application buffer size.
349  appBBSize =
350  sslEngine.getSession().getApplicationBufferSize();
351  break;
352 
353  default: //CLOSED:
354  throw new IOException("Received" + result.getStatus() +
355  "during initial handshaking");
356  }
357  } // "needIO" block.
358 
359  /*
360  * Just transitioned from read to write.
361  */
362  if (initialHSStatus != HandshakeStatus.NEED_WRAP) {
363  break;
364  }
365 
366  // Fall through and fill the write buffers.
367 
368  case NEED_WRAP:
369  /*
370  * The flush above guarantees the out buffer to be empty
371  */
372  outNetBB.clear();
373  result = sslEngine.wrap(hsBB, outNetBB);
374  outNetBB.flip();
375 
376  initialHSStatus = result.getHandshakeStatus();
377 
378  switch (result.getStatus()) {
379  case OK:
380 
381  if (initialHSStatus == HandshakeStatus.NEED_TASK) {
382  initialHSStatus = doTasks();
383  }
384 
385  if (sk != null) {
386  sk.interestOps(SelectionKey.OP_WRITE);
387  }
388 
389  break;
390 
391  default: // BUFFER_OVERFLOW/BUFFER_UNDERFLOW/CLOSED:
392  throw new IOException("Received" + result.getStatus() +
393  "during initial handshaking");
394  }
395  break;
396 
397  default: // NOT_HANDSHAKING/NEED_TASK/FINISHED
398  throw new RuntimeException("Invalid Handshaking State" +
399  initialHSStatus);
400  } // switch
401 
402  return initialHSComplete;
403  }
404 
405  /*
406  * Do all the outstanding handshake tasks in the current Thread.
407  */
408  private SSLEngineResult.HandshakeStatus doTasks() {
409 
410  Runnable runnable;
411 
412  /*
413  * We could run this in a separate thread, but
414  * do in the current for now.
415  */
416  while ((runnable = sslEngine.getDelegatedTask()) != null) {
417  runnable.run();
418  }
419  return sslEngine.getHandshakeStatus();
420  }
421 
422  /*
423  * Read the channel for more information, then unwrap the
424  * (hopefully application) data we get.
425  * <P>
426  * If we run out of data, we'll return to our caller (possibly using
427  * a Selector) to get notification that more is available.
428  * <P>
429  * Each call to this method will perform at most one underlying read().
430  */
431  int read() throws IOException {
432  SSLEngineResult result;
433 
434  if (!initialHSComplete) {
435  throw new IllegalStateException();
436  }
437 
438  int pos = requestBB.position();
439 
440  if (sc.read(inNetBB) == -1) {
441  sslEngine.closeInbound(); // probably throws exception
442  return -1;
443  }
444 
445  do {
446  resizeRequestBB(); // expected room for unwrap
447  inNetBB.flip();
448  result = sslEngine.unwrap(inNetBB, requestBB);
449  inNetBB.compact();
450 
451  /*
452  * Could check here for a renegotation, but we're only
453  * doing a simple read/write, and won't have enough state
454  * transitions to do a complete handshake, so ignore that
455  * possibility.
456  */
457  switch (result.getStatus()) {
458 
459  case BUFFER_OVERFLOW:
460  // Reset the application buffer size.
461  appBBSize = sslEngine.getSession().getApplicationBufferSize();
462  break;
463 
464  case BUFFER_UNDERFLOW:
465  // Resize buffer if needed.
466  netBBSize = sslEngine.getSession().getPacketBufferSize();
467  if (netBBSize > inNetBB.capacity()) {
468  resizeResponseBB();
469 
470  break; // break, next read will support larger buffer.
471  }
472  case OK:
473  if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
474  doTasks();
475  }
476  break;
477 
478  default:
479  throw new IOException("sslEngine error during data read: " +
480  result.getStatus());
481  }
482  } while ((inNetBB.position() != 0) &&
483  result.getStatus() != Status.BUFFER_UNDERFLOW);
484 
485  return (requestBB.position() - pos);
486  }
487 
488  /*
489  * Try to write out as much as possible from the src buffer.
490  */
491  int write(ByteBuffer src) throws IOException {
492 
493  if (!initialHSComplete) {
494  throw new IllegalStateException();
495  }
496 
497  return doWrite(src);
498  }
499 
500  /*
501  * Try to flush out any existing outbound data, then try to wrap
502  * anything new contained in the src buffer.
503  * <P>
504  * Return the number of bytes actually consumed from the buffer,
505  * but the data may actually be still sitting in the output buffer,
506  * waiting to be flushed.
507  */
508  private int doWrite(ByteBuffer src) throws IOException {
509  int retValue = 0;
510 
511  if (outNetBB.hasRemaining() && !tryFlush(outNetBB)) {
512  return retValue;
513  }
514 
515  /*
516  * The data buffer is empty, we can reuse the entire buffer.
517  */
518  outNetBB.clear();
519 
520  SSLEngineResult result = sslEngine.wrap(src, outNetBB);
521  retValue = result.bytesConsumed();
522 
523  outNetBB.flip();
524 
525  switch (result.getStatus()) {
526 
527  case OK:
528  if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
529  doTasks();
530  }
531  break;
532 
533  default:
534  throw new IOException("sslEngine error during data write: " +
535  result.getStatus());
536  }
537 
538  /*
539  * Try to flush the data, regardless of whether or not
540  * it's been selected. Odds of a write buffer being full
541  * is less than a read buffer being empty.
542  */
543  if (outNetBB.hasRemaining()) {
544  tryFlush(outNetBB);
545  }
546 
547  return retValue;
548  }
549 
550  /*
551  * Perform a FileChannel.TransferTo on the socket channel.
552  * <P>
553  * We have to copy the data into an intermediary app ByteBuffer
554  * first, then send it through the SSLEngine.
555  * <P>
556  * We return the number of bytes actually read out of the
557  * filechannel. However, the data may actually be stuck
558  * in the fileChannelBB or the outNetBB. The caller
559  * is responsible for making sure to call dataFlush()
560  * before shutting down.
561  */
562  long transferTo(FileChannel fc, long pos, long len) throws IOException {
563 
564  if (!initialHSComplete) {
565  throw new IllegalStateException();
566  }
567 
568  if (fileChannelBB == null) {
569  fileChannelBB = ByteBuffer.allocate(appBBSize);
570  fileChannelBB.limit(0);
571  }
572 
573  fileChannelBB.compact();
574  int fileRead = fc.read(fileChannelBB);
575  fileChannelBB.flip();
576 
577  /*
578  * We ignore the return value here, we return the
579  * number of bytes actually consumed from the the file.
580  * We'll flush the output buffer before we start shutting down.
581  */
582  doWrite(fileChannelBB);
583 
584  return fileRead;
585  }
586 
587  /*
588  * Flush any remaining data.
589  * <P>
590  * Return true when the fileChannelBB and outNetBB are empty.
591  */
592  boolean dataFlush() throws IOException {
593  boolean fileFlushed = true;
594 
595  if ((fileChannelBB != null) && fileChannelBB.hasRemaining()) {
596  doWrite(fileChannelBB);
597  fileFlushed = !fileChannelBB.hasRemaining();
598  } else if (outNetBB.hasRemaining()) {
599  tryFlush(outNetBB);
600  }
601 
602  return (fileFlushed && !outNetBB.hasRemaining());
603  }
604 
605  /*
606  * Begin the shutdown process.
607  * <P>
608  * Close out the SSLEngine if not already done so, then
609  * wrap our outgoing close_notify message and try to send it on.
610  * <P>
611  * Return true when we're done passing the shutdown messsages.
612  */
613  boolean shutdown() throws IOException {
614 
615  if (!shutdown) {
616  sslEngine.closeOutbound();
617  shutdown = true;
618  }
619 
620  if (outNetBB.hasRemaining() && tryFlush(outNetBB)) {
621  return false;
622  }
623 
624  /*
625  * By RFC 2616, we can "fire and forget" our close_notify
626  * message, so that's what we'll do here.
627  */
628  outNetBB.clear();
629  SSLEngineResult result = sslEngine.wrap(hsBB, outNetBB);
630  if (result.getStatus() != Status.CLOSED) {
631  throw new SSLException("Improper close state");
632  }
633  outNetBB.flip();
634 
635  /*
636  * We won't wait for a select here, but if this doesn't work,
637  * we'll cycle back through on the next select.
638  */
639  if (outNetBB.hasRemaining()) {
640  tryFlush(outNetBB);
641  }
642 
643  return (!outNetBB.hasRemaining() &&
644  (result.getHandshakeStatus() != HandshakeStatus.NEED_WRAP));
645  }
646 
647  /*
648  * close() is not overridden
649  */
650 }