1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.handler.ssl;
17
18 import org.apache.tomcat.jni.Buffer;
19 import org.apache.tomcat.jni.SSL;
20 import org.jboss.netty.logging.InternalLogger;
21 import org.jboss.netty.logging.InternalLoggerFactory;
22 import org.jboss.netty.util.internal.EmptyArrays;
23
24 import javax.net.ssl.SSLEngine;
25 import javax.net.ssl.SSLEngineResult;
26 import javax.net.ssl.SSLException;
27 import javax.net.ssl.SSLSession;
28 import javax.net.ssl.SSLSessionContext;
29 import javax.security.cert.X509Certificate;
30 import java.nio.ByteBuffer;
31 import java.nio.ReadOnlyBufferException;
32 import java.security.Principal;
33 import java.security.cert.Certificate;
34 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
35
36 import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
37 import static javax.net.ssl.SSLEngineResult.Status.*;
38
39
40
41
42
43 public final class OpenSslEngine extends SSLEngine {
44
45 private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslEngine.class);
46
47 private static final Certificate[] EMPTY_CERTIFICATES = new Certificate[0];
48 private static final X509Certificate[] EMPTY_X509_CERTIFICATES = new X509Certificate[0];
49
50 private static final SSLException ENGINE_CLOSED = new SSLException("engine closed");
51 private static final SSLException RENEGOTIATION_UNSUPPORTED = new SSLException("renegotiation unsupported");
52 private static final SSLException ENCRYPTED_PACKET_OVERSIZED = new SSLException("encrypted packet oversized");
53
54 static {
55 ENGINE_CLOSED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
56 RENEGOTIATION_UNSUPPORTED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
57 ENCRYPTED_PACKET_OVERSIZED.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
58 }
59
60 private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024;
61 private static final int MAX_COMPRESSED_LENGTH = MAX_PLAINTEXT_LENGTH + 1024;
62 private static final int MAX_CIPHERTEXT_LENGTH = MAX_COMPRESSED_LENGTH + 1024;
63
64
65 static final int MAX_ENCRYPTED_PACKET_LENGTH = MAX_CIPHERTEXT_LENGTH + 5 + 20 + 256;
66
67 private static final AtomicIntegerFieldUpdater<OpenSslEngine> DESTROYED_UPDATER =
68 AtomicIntegerFieldUpdater.newUpdater(OpenSslEngine.class, "destroyed");
69
70
71 private long ssl;
72 private long networkBIO;
73
74
75
76
77 private int accepted;
78 private boolean handshakeFinished;
79 private boolean receivedShutdown;
80 @SuppressWarnings("UnusedDeclaration")
81 private volatile int destroyed;
82
83 private String cipher;
84 private volatile String applicationProtocol;
85
86
87 private boolean isInboundDone;
88 private boolean isOutboundDone;
89 private boolean engineClosed;
90
91 private int lastPrimingReadResult;
92
93 private final SslBufferPool bufPool;
94 private final String fallbackApplicationProtocol;
95 private SSLSession session;
96
97
98
99
100
101
102
103 public OpenSslEngine(long sslCtx, SslBufferPool bufPool, String fallbackApplicationProtocol) {
104 OpenSsl.ensureAvailability();
105 if (sslCtx == 0) {
106 throw new NullPointerException("sslContext");
107 }
108 if (bufPool == null) {
109 throw new NullPointerException("bufPool");
110 }
111
112 this.bufPool = bufPool;
113 ssl = SSL.newSSL(sslCtx, true);
114 networkBIO = SSL.makeNetworkBIO(ssl);
115 this.fallbackApplicationProtocol = fallbackApplicationProtocol;
116 }
117
118
119
120
121 public synchronized void shutdown() {
122 if (DESTROYED_UPDATER.compareAndSet(this, 0, 1)) {
123 SSL.freeSSL(ssl);
124 SSL.freeBIO(networkBIO);
125 ssl = networkBIO = 0;
126
127
128 isInboundDone = isOutboundDone = engineClosed = true;
129 }
130 }
131
132
133
134
135
136
137 private int writePlaintextData(final ByteBuffer src) {
138 final int pos = src.position();
139 final int limit = src.limit();
140 final int len = Math.min(limit - pos, MAX_PLAINTEXT_LENGTH);
141 final int sslWrote;
142
143 if (src.isDirect()) {
144 final long addr = Buffer.address(src) + pos;
145 sslWrote = SSL.writeToSSL(ssl, addr, len);
146 if (sslWrote > 0) {
147 src.position(pos + sslWrote);
148 return sslWrote;
149 }
150 } else {
151 final ByteBuffer buf = bufPool.acquireBuffer();
152 try {
153 assert buf.isDirect();
154 assert len <= buf.capacity() : "buffer pool write overflow";
155 final long addr = Buffer.address(buf);
156
157 src.limit(pos + len);
158
159 buf.put(src);
160 src.limit(limit);
161
162 sslWrote = SSL.writeToSSL(ssl, addr, len);
163 if (sslWrote > 0) {
164 src.position(pos + sslWrote);
165 return sslWrote;
166 } else {
167 src.position(pos);
168 }
169 } finally {
170 bufPool.releaseBuffer(buf);
171 }
172 }
173
174 throw new IllegalStateException("SSL.writeToSSL() returned a non-positive value: " + sslWrote);
175 }
176
177
178
179
180 private int writeEncryptedData(final ByteBuffer src) {
181 final int pos = src.position();
182 final int len = src.remaining();
183 if (src.isDirect()) {
184 final long addr = Buffer.address(src) + pos;
185 final int netWrote = SSL.writeToBIO(networkBIO, addr, len);
186 if (netWrote >= 0) {
187 src.position(pos + netWrote);
188 lastPrimingReadResult = SSL.readFromSSL(ssl, addr, 0);
189 return netWrote;
190 }
191 } else {
192 final ByteBuffer buf = bufPool.acquireBuffer();
193 try {
194 assert buf.isDirect();
195 assert len <= buf.capacity();
196 final long addr = Buffer.address(buf);
197
198 buf.put(src);
199
200 final int netWrote = SSL.writeToBIO(networkBIO, addr, len);
201 if (netWrote >= 0) {
202 src.position(pos + netWrote);
203 lastPrimingReadResult = SSL.readFromSSL(ssl, addr, 0);
204 return netWrote;
205 } else {
206 src.position(pos);
207 }
208 } finally {
209 bufPool.releaseBuffer(buf);
210 }
211 }
212
213 return 0;
214 }
215
216
217
218
219 private int readPlaintextData(final ByteBuffer dst) {
220 if (dst.isDirect()) {
221 final int pos = dst.position();
222 final long addr = Buffer.address(dst) + pos;
223 final int len = dst.limit() - pos;
224 final int sslRead = SSL.readFromSSL(ssl, addr, len);
225 if (sslRead > 0) {
226 dst.position(pos + sslRead);
227 return sslRead;
228 }
229 } else {
230 final ByteBuffer buf = bufPool.acquireBuffer();
231 try {
232 assert buf.isDirect();
233 final long addr = Buffer.address(buf);
234 final int len = Math.min(buf.capacity(), dst.remaining());
235 buf.limit(len);
236 final int sslRead = SSL.readFromSSL(ssl, addr, len);
237 if (sslRead > 0) {
238 buf.limit(sslRead);
239 dst.put(buf);
240 return sslRead;
241 }
242 } finally {
243 bufPool.releaseBuffer(buf);
244 }
245 }
246
247 return 0;
248 }
249
250
251
252
253 private int readEncryptedData(final ByteBuffer dst, final int pending) {
254 if (dst.isDirect() && dst.remaining() >= pending) {
255 final int pos = dst.position();
256 final long addr = Buffer.address(dst) + pos;
257 final int bioRead = SSL.readFromBIO(networkBIO, addr, pending);
258 if (bioRead > 0) {
259 dst.position(pos + bioRead);
260 return bioRead;
261 }
262 } else {
263 final ByteBuffer buf = bufPool.acquireBuffer();
264 try {
265 assert buf.isDirect();
266 final long addr = Buffer.address(buf);
267 assert buf.capacity() >= pending :
268 "network BIO read overflow (pending: " + pending + ", capacity: " + buf.capacity() + ')';
269
270 final int bioRead = SSL.readFromBIO(networkBIO, addr, pending);
271 if (bioRead > 0) {
272 buf.limit(bioRead);
273 dst.put(buf);
274 return bioRead;
275 }
276 } finally {
277 bufPool.releaseBuffer(buf);
278 }
279 }
280
281 return 0;
282 }
283
284 @Override
285 public synchronized SSLEngineResult wrap(
286 final ByteBuffer[] srcs, final int offset, final int length, final ByteBuffer dst) throws SSLException {
287
288
289 if (destroyed != 0) {
290 return new SSLEngineResult(CLOSED, NOT_HANDSHAKING, 0, 0);
291 }
292
293
294 if (srcs == null) {
295 throw new NullPointerException("srcs");
296 }
297 if (dst == null) {
298 throw new NullPointerException("dst");
299 }
300
301 if (offset >= srcs.length || offset + length > srcs.length) {
302 throw new IndexOutOfBoundsException(
303 "offset: " + offset + ", length: " + length +
304 " (expected: offset <= offset + length <= srcs.length (" + srcs.length + "))");
305 }
306
307 if (dst.isReadOnly()) {
308 throw new ReadOnlyBufferException();
309 }
310
311
312 if (accepted == 0) {
313 beginHandshakeImplicitly();
314 }
315
316
317
318 SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
319 if ((!handshakeFinished || engineClosed) && handshakeStatus == NEED_UNWRAP) {
320 return new SSLEngineResult(getEngineStatus(), NEED_UNWRAP, 0, 0);
321 }
322
323 int bytesProduced = 0;
324 int pendingNet;
325
326
327 pendingNet = SSL.pendingWrittenBytesInBIO(networkBIO);
328 if (pendingNet > 0) {
329
330 int capacity = dst.remaining();
331 if (capacity < pendingNet) {
332 return new SSLEngineResult(BUFFER_OVERFLOW, handshakeStatus, 0, bytesProduced);
333 }
334
335
336 try {
337 bytesProduced += readEncryptedData(dst, pendingNet);
338 } catch (Exception e) {
339 throw new SSLException(e);
340 }
341
342
343
344
345 if (isOutboundDone) {
346 shutdown();
347 }
348
349 return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), 0, bytesProduced);
350 }
351
352
353 int bytesConsumed = 0;
354 for (int i = offset; i < length; ++ i) {
355 final ByteBuffer src = srcs[i];
356 while (src.hasRemaining()) {
357
358
359 try {
360 bytesConsumed += writePlaintextData(src);
361 } catch (Exception e) {
362 throw new SSLException(e);
363 }
364
365
366 pendingNet = SSL.pendingWrittenBytesInBIO(networkBIO);
367 if (pendingNet > 0) {
368
369 int capacity = dst.remaining();
370 if (capacity < pendingNet) {
371 return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, bytesProduced);
372 }
373
374
375 try {
376 bytesProduced += readEncryptedData(dst, pendingNet);
377 } catch (Exception e) {
378 throw new SSLException(e);
379 }
380
381 return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
382 }
383 }
384 }
385
386 return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
387 }
388
389 @Override
390 public synchronized SSLEngineResult unwrap(
391 final ByteBuffer src, final ByteBuffer[] dsts, final int offset, final int length) throws SSLException {
392
393
394 if (destroyed != 0) {
395 return new SSLEngineResult(CLOSED, NOT_HANDSHAKING, 0, 0);
396 }
397
398
399 if (src == null) {
400 throw new NullPointerException("src");
401 }
402 if (dsts == null) {
403 throw new NullPointerException("dsts");
404 }
405 if (offset >= dsts.length || offset + length > dsts.length) {
406 throw new IndexOutOfBoundsException(
407 "offset: " + offset + ", length: " + length +
408 " (expected: offset <= offset + length <= dsts.length (" + dsts.length + "))");
409 }
410
411 int capacity = 0;
412 final int endOffset = offset + length;
413 for (int i = offset; i < endOffset; i ++) {
414 ByteBuffer dst = dsts[i];
415 if (dst == null) {
416 throw new IllegalArgumentException();
417 }
418 if (dst.isReadOnly()) {
419 throw new ReadOnlyBufferException();
420 }
421 capacity += dst.remaining();
422 }
423
424
425 if (accepted == 0) {
426 beginHandshakeImplicitly();
427 }
428
429
430
431 SSLEngineResult.HandshakeStatus handshakeStatus = getHandshakeStatus();
432 if ((!handshakeFinished || engineClosed) && handshakeStatus == NEED_WRAP) {
433 return new SSLEngineResult(getEngineStatus(), NEED_WRAP, 0, 0);
434 }
435
436
437 if (src.remaining() > MAX_ENCRYPTED_PACKET_LENGTH) {
438 isInboundDone = true;
439 isOutboundDone = true;
440 engineClosed = true;
441 shutdown();
442 throw ENCRYPTED_PACKET_OVERSIZED;
443 }
444
445
446 int bytesConsumed = 0;
447 lastPrimingReadResult = 0;
448 try {
449 bytesConsumed += writeEncryptedData(src);
450 } catch (Exception e) {
451 throw new SSLException(e);
452 }
453
454
455 String error = SSL.getLastError();
456 if (error != null && !error.startsWith(OpenSsl.IGNORABLE_ERROR_PREFIX)) {
457 if (logger.isInfoEnabled()) {
458 logger.info(
459 "SSL_read failed: primingReadResult: " + lastPrimingReadResult +
460 "; OpenSSL error: '" + error + '\'');
461 }
462
463
464 shutdown();
465 throw new SSLException(error);
466 }
467
468
469 int pendingApp = SSL.isInInit(ssl) == 0 ? SSL.pendingReadableBytesInSSL(ssl) : 0;
470
471
472 if (capacity < pendingApp) {
473 return new SSLEngineResult(BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, 0);
474 }
475
476
477 int bytesProduced = 0;
478 int idx = offset;
479 while (idx < endOffset) {
480 ByteBuffer dst = dsts[idx];
481 if (!dst.hasRemaining()) {
482 idx ++;
483 continue;
484 }
485
486 if (pendingApp <= 0) {
487 break;
488 }
489
490 int bytesRead;
491 try {
492 bytesRead = readPlaintextData(dst);
493 } catch (Exception e) {
494 throw new SSLException(e);
495 }
496
497 if (bytesRead == 0) {
498 break;
499 }
500
501 bytesProduced += bytesRead;
502 pendingApp -= bytesRead;
503
504 if (!dst.hasRemaining()) {
505 idx ++;
506 }
507 }
508
509
510 if (!receivedShutdown && (SSL.getShutdown(ssl) & SSL.SSL_RECEIVED_SHUTDOWN) == SSL.SSL_RECEIVED_SHUTDOWN) {
511 receivedShutdown = true;
512 closeOutbound();
513 closeInbound();
514 }
515
516 return new SSLEngineResult(getEngineStatus(), getHandshakeStatus(), bytesConsumed, bytesProduced);
517 }
518
519 @Override
520 public Runnable getDelegatedTask() {
521
522
523
524 return null;
525 }
526
527 @Override
528 public synchronized void closeInbound() throws SSLException {
529 if (isInboundDone) {
530 return;
531 }
532
533 isInboundDone = true;
534 engineClosed = true;
535
536 if (accepted != 0) {
537 if (!receivedShutdown) {
538 shutdown();
539 throw new SSLException("close_notify has not been received");
540 }
541 } else {
542
543 shutdown();
544 }
545 }
546
547 @Override
548 public synchronized boolean isInboundDone() {
549 return isInboundDone || engineClosed;
550 }
551
552 @Override
553 public synchronized void closeOutbound() {
554 if (isOutboundDone) {
555 return;
556 }
557
558 isOutboundDone = true;
559 engineClosed = true;
560
561 if (accepted != 0 && destroyed == 0) {
562 int mode = SSL.getShutdown(ssl);
563 if ((mode & SSL.SSL_SENT_SHUTDOWN) != SSL.SSL_SENT_SHUTDOWN) {
564 SSL.shutdownSSL(ssl);
565 }
566 } else {
567
568 shutdown();
569 }
570 }
571
572 @Override
573 public synchronized boolean isOutboundDone() {
574 return isOutboundDone;
575 }
576
577 @Override
578 public String[] getSupportedCipherSuites() {
579 return EmptyArrays.EMPTY_STRINGS;
580 }
581
582 @Override
583 public String[] getEnabledCipherSuites() {
584 return EmptyArrays.EMPTY_STRINGS;
585 }
586
587 @Override
588 public void setEnabledCipherSuites(String[] strings) {
589 throw new UnsupportedOperationException();
590 }
591
592 @Override
593 public String[] getSupportedProtocols() {
594 return EmptyArrays.EMPTY_STRINGS;
595 }
596
597 @Override
598 public String[] getEnabledProtocols() {
599 return EmptyArrays.EMPTY_STRINGS;
600 }
601
602 @Override
603 public void setEnabledProtocols(String[] strings) {
604 throw new UnsupportedOperationException();
605 }
606
607 @Override
608 public SSLSession getSession() {
609 SSLSession session = this.session;
610 if (session == null) {
611 this.session = session = new SSLSession() {
612 public byte[] getId() {
613 return String.valueOf(ssl).getBytes();
614 }
615
616 public SSLSessionContext getSessionContext() {
617 return null;
618 }
619
620 public long getCreationTime() {
621 return 0;
622 }
623
624 public long getLastAccessedTime() {
625 return 0;
626 }
627
628 public void invalidate() {
629 }
630
631 public boolean isValid() {
632 return false;
633 }
634
635 public void putValue(String s, Object o) {
636 }
637
638 public Object getValue(String s) {
639 return null;
640 }
641
642 public void removeValue(String s) {
643 }
644
645 public String[] getValueNames() {
646 return EmptyArrays.EMPTY_STRINGS;
647 }
648
649 public Certificate[] getPeerCertificates() {
650 return EMPTY_CERTIFICATES;
651 }
652
653 public Certificate[] getLocalCertificates() {
654 return EMPTY_CERTIFICATES;
655 }
656
657 public X509Certificate[] getPeerCertificateChain() {
658 return EMPTY_X509_CERTIFICATES;
659 }
660
661 public Principal getPeerPrincipal() {
662 return null;
663 }
664
665 public Principal getLocalPrincipal() {
666 return null;
667 }
668
669 public String getCipherSuite() {
670 return cipher;
671 }
672
673 public String getProtocol() {
674
675 String applicationProtocol = OpenSslEngine.this.applicationProtocol;
676 if (applicationProtocol == null) {
677 return "unknown";
678 } else {
679 return "unknown:" + applicationProtocol;
680 }
681 }
682
683 public String getPeerHost() {
684 return null;
685 }
686
687 public int getPeerPort() {
688 return 0;
689 }
690
691 public int getPacketBufferSize() {
692 return MAX_ENCRYPTED_PACKET_LENGTH;
693 }
694
695 public int getApplicationBufferSize() {
696 return MAX_PLAINTEXT_LENGTH;
697 }
698 };
699 }
700
701 return session;
702 }
703
704 @Override
705 public synchronized void beginHandshake() throws SSLException {
706 if (engineClosed) {
707 throw ENGINE_CLOSED;
708 }
709
710 switch (accepted) {
711 case 0:
712 SSL.doHandshake(ssl);
713 accepted = 2;
714 break;
715 case 1:
716
717
718
719
720
721
722 accepted = 2;
723 break;
724 case 2:
725 throw RENEGOTIATION_UNSUPPORTED;
726 default:
727 throw new Error();
728 }
729 }
730
731 private synchronized void beginHandshakeImplicitly() throws SSLException {
732 if (engineClosed) {
733 throw ENGINE_CLOSED;
734 }
735
736 if (accepted == 0) {
737 SSL.doHandshake(ssl);
738 accepted = 1;
739 }
740 }
741
742 private SSLEngineResult.Status getEngineStatus() {
743 return engineClosed? CLOSED : OK;
744 }
745
746 @Override
747 public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
748 if (accepted == 0 || destroyed != 0) {
749 return NOT_HANDSHAKING;
750 }
751
752
753 if (!handshakeFinished) {
754
755 if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
756 return NEED_WRAP;
757 }
758
759
760
761 if (SSL.isInInit(ssl) == 0) {
762 handshakeFinished = true;
763 cipher = SSL.getCipherForSSL(ssl);
764 String applicationProtocol = SSL.getNextProtoNegotiated(ssl);
765 if (applicationProtocol == null) {
766 applicationProtocol = fallbackApplicationProtocol;
767 }
768 if (applicationProtocol != null) {
769 this.applicationProtocol = applicationProtocol.replace(':', '_');
770 } else {
771 this.applicationProtocol = null;
772 }
773 return FINISHED;
774 }
775
776
777
778 return NEED_UNWRAP;
779 }
780
781
782 if (engineClosed) {
783
784 if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
785 return NEED_WRAP;
786 }
787
788
789 return NEED_UNWRAP;
790 }
791
792 return NOT_HANDSHAKING;
793 }
794
795 @Override
796 public void setUseClientMode(boolean clientMode) {
797 if (clientMode) {
798 throw new UnsupportedOperationException();
799 }
800 }
801
802 @Override
803 public boolean getUseClientMode() {
804 return false;
805 }
806
807 @Override
808 public void setNeedClientAuth(boolean b) {
809 if (b) {
810 throw new UnsupportedOperationException();
811 }
812 }
813
814 @Override
815 public boolean getNeedClientAuth() {
816 return false;
817 }
818
819 @Override
820 public void setWantClientAuth(boolean b) {
821 if (b) {
822 throw new UnsupportedOperationException();
823 }
824 }
825
826 @Override
827 public boolean getWantClientAuth() {
828 return false;
829 }
830
831 @Override
832 public void setEnableSessionCreation(boolean b) {
833 if (b) {
834 throw new UnsupportedOperationException();
835 }
836 }
837
838 @Override
839 public boolean getEnableSessionCreation() {
840 return false;
841 }
842 }