1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.mina.io.filter;
20
21 import java.nio.ByteBuffer;
22 import java.util.logging.Level;
23 import java.util.logging.Logger;
24
25 import javax.net.ssl.SSLContext;
26 import javax.net.ssl.SSLEngine;
27 import javax.net.ssl.SSLEngineResult;
28 import javax.net.ssl.SSLException;
29 import javax.net.ssl.SSLSession;
30
31 import org.apache.mina.io.IoSession;
32 import org.apache.mina.io.IoFilter.NextFilter;
33 import org.apache.mina.util.Queue;
34
35 /***
36 * A helper class using the SSLEngine API to decrypt/encrypt data.
37 * <p>
38 * Each connection has a SSLEngine that is used through the lifetime of the connection.
39 * We allocate byte buffers for use as the outbound and inbound network buffers.
40 * These buffers handle all of the intermediary data for the SSL connection. To make things easy,
41 * we'll require outNetBuffer be completely flushed before trying to wrap any more data.
42 *
43 * @author Jan Andersson (janne@minq.se)
44 * @version $Rev: 169321 $, $Date: 2005-05-10 00:00:25 +0900 (?, 10 5? 2005) $
45 */
46 class SSLHandler
47 {
48 private static final Logger log = Logger.getLogger( SSLFilter.class.getName() );
49
50 private final SSLFilter parent;
51
52 private final IoSession session;
53
54 private final Queue nextFilterQueue = new Queue();
55
56 private final Queue writeBufferQueue = new Queue();
57
58 private final Queue writeMarkerQueue = new Queue();
59
60 private SSLEngine sslEngine;
61
62 /***
63 * Encrypted data from the net
64 */
65 private ByteBuffer inNetBuffer;
66
67 /***
68 * Encrypted data to be written to the net
69 */
70 private ByteBuffer outNetBuffer;
71
72 /***
73 * Applicaton cleartext data to be read by application
74 */
75 private ByteBuffer appBuffer;
76
77 /***
78 * Empty buffer used during initial handshake and close operations
79 */
80 private ByteBuffer hsBB = ByteBuffer.allocate( 0 );
81
82 /***
83 * Handshake status
84 */
85 private SSLEngineResult.HandshakeStatus initialHandshakeStatus;
86
87 /***
88 * Initial handshake complete?
89 */
90 private boolean initialHandshakeComplete;
91
92 /***
93 * We have received the shutdown request by our caller, and have
94 * closed our outbound side.
95 */
96 private boolean shutdown = false;
97
98 private boolean closed = false;
99
100 private boolean isWritingEncryptedData = false;
101
102 /***
103 * Constuctor.
104 *
105 * @param sslc
106 * @throws SSLException
107 */
108 SSLHandler( SSLFilter parent, SSLContext sslc, IoSession session ) throws SSLException
109 {
110 this.parent = parent;
111 this.session = session;
112 sslEngine = sslc.createSSLEngine();
113 sslEngine.setUseClientMode( parent.isUseClientMode() );
114 sslEngine.setNeedClientAuth( parent.isNeedClientAuth() );
115 sslEngine.setWantClientAuth( parent.isWantClientAuth() );
116
117 if( parent.getEnabledCipherSuites() != null )
118 {
119 sslEngine.setEnabledCipherSuites( parent.getEnabledCipherSuites() );
120 }
121
122 if( parent.getEnabledProtocols() != null )
123 {
124 sslEngine.setEnabledProtocols( parent.getEnabledProtocols() );
125 }
126
127 sslEngine.beginHandshake();
128 initialHandshakeStatus = sslEngine.getHandshakeStatus();
129 initialHandshakeComplete = false;
130
131
132 SSLByteBufferPool.initiate( sslEngine );
133
134 appBuffer = SSLByteBufferPool.getApplicationBuffer();
135
136 inNetBuffer = SSLByteBufferPool.getPacketBuffer();
137 outNetBuffer = SSLByteBufferPool.getPacketBuffer();
138 outNetBuffer.position( 0 );
139 outNetBuffer.limit( 0 );
140 }
141
142 /***
143 * Indicate that we are writing encrypted data.
144 * Only used as a flag by IoSSLFiler
145 */
146 public void setWritingEncryptedData( boolean flag )
147 {
148 isWritingEncryptedData = flag;
149 }
150
151 /***
152 * Check we are writing encrypted data.
153 */
154 public boolean isWritingEncryptedData()
155 {
156 return isWritingEncryptedData;
157 }
158
159 /***
160 * Check if initial handshake is completed.
161 */
162 public boolean isInitialHandshakeComplete()
163 {
164 return initialHandshakeComplete;
165 }
166
167 /***
168 * Check if SSL sesssion closed
169 */
170 public boolean isClosed()
171 {
172 return closed;
173 }
174
175 /***
176 * Check if there is any need to complete initial handshake.
177 */
178 public boolean needToCompleteInitialHandshake()
179 {
180 return ( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !closed );
181 }
182
183 public synchronized void scheduleWrite( NextFilter nextFilter, org.apache.mina.common.ByteBuffer buf, Object marker )
184 {
185 nextFilterQueue.push( nextFilter );
186 writeBufferQueue.push( buf );
187 writeMarkerQueue.push( marker );
188 }
189
190 public synchronized void flushScheduledWrites() throws SSLException
191 {
192 NextFilter nextFilter;
193 org.apache.mina.common.ByteBuffer scheduledBuf;
194 Object scheduledMarker;
195
196 while( ( scheduledBuf = ( org.apache.mina.common.ByteBuffer ) writeBufferQueue.pop() ) != null )
197 {
198 if( log.isLoggable( Level.FINEST ) )
199 {
200 log.log( Level.FINEST, session + " Flushing buffered write request: " + scheduledBuf );
201 }
202 nextFilter = ( NextFilter ) nextFilterQueue.pop();
203 scheduledMarker = writeMarkerQueue.pop();
204 parent.filterWrite( nextFilter, session, scheduledBuf, scheduledMarker );
205 }
206 }
207
208 /***
209 * Call when data read from net. Will perform inial hanshake or decrypt provided
210 * Buffer.
211 * Decrytpted data reurned by getAppBuffer(), if any.
212 *
213 * @param buf buffer to decrypt
214 * @throws SSLException on errors
215 */
216 public void dataRead( NextFilter nextFilter, ByteBuffer buf ) throws SSLException
217 {
218 if ( buf.limit() > inNetBuffer.remaining() ) {
219
220 inNetBuffer = SSLByteBufferPool.expandBuffer( inNetBuffer,
221 inNetBuffer.capacity() + ( buf.limit() * 2 ) );
222
223 appBuffer = SSLByteBufferPool.expandBuffer( appBuffer, inNetBuffer.capacity() * 2);
224 appBuffer.position( 0 );
225 appBuffer.limit( 0 );
226 if( log.isLoggable( Level.FINEST ) )
227 {
228 log.log( Level.FINEST, session +
229 " expanded inNetBuffer:" + inNetBuffer );
230 log.log( Level.FINEST, session +
231 " expanded appBuffer:" + appBuffer );
232 }
233 }
234
235
236 inNetBuffer.put( buf );
237 if( !initialHandshakeComplete )
238 {
239 doHandshake( nextFilter );
240 }
241 else
242 {
243 doDecrypt();
244 }
245 }
246
247 /***
248 * Continue initial SSL handshake.
249 *
250 * @throws SSLException on errors
251 */
252 public void continueHandshake( NextFilter nextFilter ) throws SSLException
253 {
254 if( log.isLoggable( Level.FINEST ) )
255 {
256 log.log( Level.FINEST, session + " continueHandshake()" );
257 }
258 doHandshake( nextFilter );
259 }
260
261 /***
262 * Get decrypted application data.
263 *
264 * @return buffer with data
265 */
266 public ByteBuffer getAppBuffer()
267 {
268 return appBuffer;
269 }
270
271 /***
272 * Get encrypted data to be sent.
273 *
274 * @return buffer with data
275 */
276 public ByteBuffer getOutNetBuffer()
277 {
278 return outNetBuffer;
279 }
280
281 /***
282 * Encrypt provided buffer. Encytpted data reurned by getOutNetBuffer().
283 *
284 * @param buf data to encrypt
285 * @throws SSLException on errors
286 */
287 public void encrypt( ByteBuffer buf ) throws SSLException
288 {
289 doEncrypt( buf );
290 }
291
292 /***
293 * Start SSL shutdown process
294 *
295 * @throws SSLException on errors
296 */
297 public void shutdown() throws SSLException
298 {
299 if( !shutdown )
300 {
301 doShutdown();
302 }
303 }
304
305 /***
306 * Release allocated ByteBuffers.
307 */
308 public void release()
309 {
310 SSLByteBufferPool.release( appBuffer );
311 SSLByteBufferPool.release( inNetBuffer );
312 SSLByteBufferPool.release( outNetBuffer );
313 }
314
315 /***
316 * Decrypt in net buffer. Result is stored in app buffer.
317 *
318 * @throws SSLException
319 */
320 private void doDecrypt() throws SSLException
321 {
322
323 if( !initialHandshakeComplete )
324 {
325 throw new IllegalStateException();
326 }
327
328 if( appBuffer.hasRemaining() )
329 {
330 if ( log.isLoggable( Level.FINEST ) ) {
331 log.log( Level.FINEST, session + " Error: appBuffer not empty!" );
332 }
333
334 throw new IllegalStateException();
335 }
336
337 unwrap();
338 }
339
340 /***
341 * @param status
342 * @throws SSLException
343 */
344 private SSLEngineResult.Status checkStatus( SSLEngineResult.Status status ) throws SSLException
345 {
346 if( status != SSLEngineResult.Status.OK &&
347 status != SSLEngineResult.Status.CLOSED &&
348 status != SSLEngineResult.Status.BUFFER_UNDERFLOW )
349 {
350 throw new SSLException( "SSLEngine error during decrypt: " +
351 status +
352 " inNetBuffer: " + inNetBuffer + "appBuffer: " + appBuffer);
353 }
354
355 return status;
356 }
357
358 private void doEncrypt( ByteBuffer src ) throws SSLException
359 {
360 if( !initialHandshakeComplete )
361 {
362 throw new IllegalStateException();
363 }
364
365
366
367 outNetBuffer.clear();
368
369 SSLEngineResult result;
370
371
372 while ( src.hasRemaining() ) {
373
374 if ( src.remaining() > ( ( outNetBuffer.capacity() - outNetBuffer.position() ) / 2 ) ) {
375
376
377
378 outNetBuffer = SSLByteBufferPool.expandBuffer( outNetBuffer, src.capacity() * 2 );
379 if ( log.isLoggable( Level.FINEST ) ) {
380 log.log( Level.FINEST, session + " expanded outNetBuffer:" + outNetBuffer );
381 }
382 }
383
384 result = sslEngine.wrap( src, outNetBuffer );
385 if ( log.isLoggable( Level.FINEST ) ) {
386 log.log( Level.FINEST, session + " Wrap res:" + result );
387 }
388
389 if ( result.getStatus() == SSLEngineResult.Status.OK ) {
390 if ( result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK ) {
391 doTasks();
392 }
393 } else {
394 throw new SSLException( "SSLEngine error during encrypt: "
395 + result.getStatus() +
396 " src: " + src + "outNetBuffer: " + outNetBuffer);
397 }
398 }
399
400 outNetBuffer.flip();
401 }
402
403 /***
404 * Perform any handshaking processing.
405 */
406 synchronized void doHandshake( NextFilter nextFilter ) throws SSLException
407 {
408
409 if( log.isLoggable( Level.FINEST ) )
410 {
411 log.log( Level.FINEST, session + " doHandshake()" );
412 }
413
414 while( !initialHandshakeComplete )
415 {
416 if( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED )
417 {
418 if( log.isLoggable( Level.FINEST ) )
419 {
420 SSLSession sslSession = sslEngine.getSession();
421 log.log( Level.FINEST, session + " initialHandshakeStatus=FINISHED" );
422 log.log( Level.FINEST, session + " sslSession CipherSuite used " + sslSession.getCipherSuite());
423 }
424 initialHandshakeComplete = true;
425 return;
426 }
427 else if( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_TASK )
428 {
429 if( log.isLoggable( Level.FINEST ) )
430 {
431 log.log( Level.FINEST, session + " initialHandshakeStatus=NEED_TASK" );
432 }
433 initialHandshakeStatus = doTasks();
434 }
435 else if( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP )
436 {
437
438 if( log.isLoggable( Level.FINEST ) )
439 {
440 log.log( Level.FINEST, session +
441 " initialHandshakeStatus=NEED_UNWRAP" );
442 }
443 SSLEngineResult.Status status = unwrapHandshake();
444 if( ( initialHandshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED
445 && status == SSLEngineResult.Status.BUFFER_UNDERFLOW )
446 || closed )
447 {
448
449 return;
450 }
451 }
452 else if( initialHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP )
453 {
454 if( log.isLoggable( Level.FINEST ) )
455 {
456 log.log( Level.FINEST, session + " initialHandshakeStatus=NEED_WRAP" );
457 }
458
459
460 if( outNetBuffer.hasRemaining() )
461 {
462 if( log.isLoggable( Level.FINEST ) )
463 {
464 log.log( Level.FINEST, session + " Still data in out buffer!" );
465 }
466 return;
467 }
468 outNetBuffer.clear();
469 SSLEngineResult result = sslEngine.wrap( hsBB, outNetBuffer );
470 if( log.isLoggable( Level.FINEST ) )
471 {
472 log.log( Level.FINEST, session + " Wrap res:" + result );
473 }
474
475 outNetBuffer.flip();
476 initialHandshakeStatus = result.getHandshakeStatus();
477 parent.writeNetBuffer( nextFilter, session, this );
478
479
480 }
481 else
482 {
483 throw new IllegalStateException( "Invalid Handshaking State"
484 + initialHandshakeStatus );
485 }
486 }
487 }
488
489 SSLEngineResult.Status unwrap() throws SSLException
490 {
491 if( log.isLoggable( Level.FINEST ) )
492 {
493 log.log( Level.FINEST, session + " unwrap()" );
494 }
495
496 appBuffer.clear();
497
498
499 inNetBuffer.flip();
500
501 SSLEngineResult res;
502 do
503 {
504 if( log.isLoggable( Level.FINEST ) )
505 {
506 log.log( Level.FINEST, session + " inNetBuffer: " + inNetBuffer );
507 log.log( Level.FINEST, session + " appBuffer: " + appBuffer );
508 }
509 res = sslEngine.unwrap( inNetBuffer, appBuffer );
510 if( log.isLoggable( Level.FINEST ) )
511 {
512 log.log( Level.FINEST, session + " Unwrap res:" + res );
513 }
514 }
515 while( res.getStatus() == SSLEngineResult.Status.OK );
516
517
518 if( res.getStatus() == SSLEngineResult.Status.CLOSED )
519 {
520 closed = true;
521 }
522
523
524 inNetBuffer.compact();
525
526 appBuffer.flip();
527
528
529
530
531
532
533
534
535
536 return checkStatus( res.getStatus() );
537 }
538
539 private SSLEngineResult.Status unwrapHandshake() throws SSLException
540 {
541 if( log.isLoggable( Level.FINEST ) )
542 {
543 log.log( Level.FINEST, session + " unwrapHandshake()" );
544 }
545
546 appBuffer.clear();
547
548
549 inNetBuffer.flip();
550
551 SSLEngineResult res;
552 do
553 {
554 if( log.isLoggable( Level.FINEST ) )
555 {
556 log.log( Level.FINEST, session + " inNetBuffer: " + inNetBuffer );
557 log.log( Level.FINEST, session + " appBuffer: " + appBuffer );
558 }
559 res = sslEngine.unwrap( inNetBuffer, appBuffer );
560 if( log.isLoggable( Level.FINEST ) )
561 {
562 log.log( Level.FINEST, session + " Unwrap res:" + res );
563 }
564
565 }
566 while( res.getStatus() == SSLEngineResult.Status.OK &&
567 res.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP );
568
569 initialHandshakeStatus = res.getHandshakeStatus();
570
571
572
573 if (initialHandshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED
574 && appBuffer.position() == 0
575 && res.getStatus() == SSLEngineResult.Status.OK
576 && inNetBuffer.hasRemaining()) {
577 do {
578 if (log.isLoggable( Level.FINEST )) {
579 log.log( Level.FINEST, session + " extra handshake unwrap" );
580 log.log( Level.FINEST, session + " inNetBuffer: " + inNetBuffer );
581 log.log( Level.FINEST, session + " appBuffer: " + appBuffer );
582 }
583 res = sslEngine.unwrap(inNetBuffer, appBuffer);
584 if (log.isLoggable( Level.FINEST )) {
585 log.log( Level.FINEST, session + " Unwrap res:" + res );
586 }
587 } while (res.getStatus() == SSLEngineResult.Status.OK);
588 }
589
590
591 if( res.getStatus() == SSLEngineResult.Status.CLOSED )
592 {
593 closed = true;
594 }
595
596
597 inNetBuffer.compact();
598
599
600 appBuffer.flip();
601
602
603
604
605
606
607
608
609
610
611 return checkStatus( res.getStatus() );
612 }
613
614 /***
615 * Do all the outstanding handshake tasks in the current Thread.
616 */
617 private SSLEngineResult.HandshakeStatus doTasks()
618 {
619 if( log.isLoggable( Level.FINEST ) )
620 {
621 log.log( Level.FINEST, session + " doTasks()" );
622 }
623
624
625
626
627
628 Runnable runnable;
629 while( ( runnable = sslEngine.getDelegatedTask() ) != null )
630 {
631 if( log.isLoggable( Level.FINEST ) )
632 {
633 log.log( Level.FINEST, session + " doTask: " + runnable );
634 }
635 runnable.run();
636 }
637 if( log.isLoggable( Level.FINEST ) )
638 {
639 log.log( Level.FINEST, session + " doTasks(): "
640 + sslEngine.getHandshakeStatus() );
641 }
642 return sslEngine.getHandshakeStatus();
643 }
644
645 /***
646 * Begin the shutdown process.
647 */
648 void doShutdown() throws SSLException
649 {
650
651 if( !shutdown )
652 {
653 sslEngine.closeOutbound();
654 shutdown = true;
655 }
656
657
658
659
660 outNetBuffer.clear();
661 SSLEngineResult result = sslEngine.wrap( hsBB, outNetBuffer );
662 if( result.getStatus() != SSLEngineResult.Status.CLOSED )
663 {
664 throw new SSLException( "Improper close state: " + result );
665 }
666 outNetBuffer.flip();
667 }
668 }