|
1 | 1 | /* |
2 | | - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. |
| 2 | + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 | 4 | * |
5 | 5 | * This code is free software; you can redistribute it and/or modify it |
|
28 | 28 | import java.io.IOException; |
29 | 29 | import java.nio.ByteBuffer; |
30 | 30 | import java.security.GeneralSecurityException; |
31 | | -import java.util.Collections; |
32 | | -import java.util.HashMap; |
33 | | -import java.util.Iterator; |
34 | | -import java.util.LinkedList; |
35 | | -import java.util.List; |
36 | | -import java.util.Set; |
37 | | -import java.util.TreeSet; |
| 31 | +import java.util.*; |
38 | 32 | import javax.crypto.BadPaddingException; |
39 | 33 | import javax.net.ssl.SSLException; |
40 | 34 | import javax.net.ssl.SSLProtocolException; |
|
46 | 40 | final class DTLSInputRecord extends InputRecord implements DTLSRecord { |
47 | 41 | private DTLSReassembler reassembler = null; |
48 | 42 | private int readEpoch; |
| 43 | + private SSLContextImpl sslContext; |
49 | 44 |
|
50 | 45 | DTLSInputRecord(HandshakeHash handshakeHash) { |
51 | 46 | super(handshakeHash, SSLReadCipher.nullDTlsReadCipher()); |
52 | 47 | this.readEpoch = 0; |
53 | 48 | } |
54 | 49 |
|
| 50 | + // Method to set TransportContext |
| 51 | + public void setTransportContext(TransportContext tc) { |
| 52 | + this.tc = tc; |
| 53 | + } |
| 54 | + |
| 55 | + // Method to set SSLContext |
| 56 | + public void setSSLContext(SSLContextImpl sslContext) { |
| 57 | + this.sslContext = sslContext; |
| 58 | + } |
| 59 | + |
55 | 60 | @Override |
56 | 61 | void changeReadCiphers(SSLReadCipher readCipher) { |
57 | 62 | this.readCipher = readCipher; |
@@ -544,6 +549,27 @@ public int compareTo(RecordFragment o) { |
544 | 549 | } |
545 | 550 | } |
546 | 551 |
|
| 552 | + /** |
| 553 | + * Turn a sufficiently-large initial ClientHello fragment into one that |
| 554 | + * stops immediately after the compression methods. This is only used |
| 555 | + * for the initial CH message fragment at offset 0. |
| 556 | + * |
| 557 | + * @param srcFrag the fragment actually received by the DTLSReassembler |
| 558 | + * @param limit the size of the new, cloned/truncated handshake fragment |
| 559 | + * |
| 560 | + * @return a truncated handshake fragment that is sized to look like a |
| 561 | + * complete message, but actually contains only up to the compression |
| 562 | + * methods (no extensions) |
| 563 | + */ |
| 564 | + private static HandshakeFragment truncateChFragment(HandshakeFragment srcFrag, |
| 565 | + int limit) { |
| 566 | + return new HandshakeFragment(Arrays.copyOf(srcFrag.fragment, limit), |
| 567 | + srcFrag.contentType, srcFrag.majorVersion, |
| 568 | + srcFrag.minorVersion, srcFrag.recordEnS, srcFrag.recordEpoch, |
| 569 | + srcFrag.recordSeq, srcFrag.handshakeType, limit, |
| 570 | + srcFrag.messageSeq, srcFrag.fragmentOffset, limit); |
| 571 | + } |
| 572 | + |
547 | 573 | private static final class HoleDescriptor { |
548 | 574 | int offset; // fragment_offset |
549 | 575 | int limit; // fragment_offset + fragment_length |
@@ -647,10 +673,17 @@ void expectingFinishFlight() { |
647 | 673 | // Queue up a handshake message. |
648 | 674 | void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException { |
649 | 675 | if (!isDesirable(hsf)) { |
650 | | - // Not a dedired record, discard it. |
| 676 | + // Not a desired record, discard it. |
651 | 677 | return; |
652 | 678 | } |
653 | 679 |
|
| 680 | + if (hsf.handshakeType == SSLHandshake.CLIENT_HELLO.id) { |
| 681 | + // validate the first or subsequent ClientHello message |
| 682 | + if ((hsf = valHello(hsf, hsf.messageSeq == 0)) == null) { |
| 683 | + return; |
| 684 | + } |
| 685 | + } |
| 686 | + |
654 | 687 | // Clean up the retransmission messages if necessary. |
655 | 688 | cleanUpRetransmit(hsf); |
656 | 689 |
|
@@ -783,6 +816,100 @@ void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException { |
783 | 816 | } |
784 | 817 | } |
785 | 818 |
|
| 819 | + private HandshakeFragment valHello(HandshakeFragment hsf, |
| 820 | + boolean firstHello) { |
| 821 | + ServerHandshakeContext shc = |
| 822 | + (ServerHandshakeContext) tc.handshakeContext; |
| 823 | + // Drop any fragment that is not a zero offset until we've received |
| 824 | + // a second (or possibly later) CH message that passes the cookie |
| 825 | + // check. |
| 826 | + if (shc == null || !shc.acceptCliHelloFragments) { |
| 827 | + if (hsf.fragmentOffset != 0) { |
| 828 | + return null; |
| 829 | + } |
| 830 | + } else { |
| 831 | + // Let this fragment through to the DTLSReassembler as-is |
| 832 | + return hsf; |
| 833 | + } |
| 834 | + |
| 835 | + try { |
| 836 | + ByteBuffer fragmentData = ByteBuffer.wrap(hsf.fragment); |
| 837 | + |
| 838 | + ProtocolVersion pv = ProtocolVersion.valueOf( |
| 839 | + Record.getInt16(fragmentData)); |
| 840 | + if (!pv.isDTLS) { |
| 841 | + return null; |
| 842 | + } |
| 843 | + // Read the random (32 bytes) |
| 844 | + if (fragmentData.remaining() < 32) { |
| 845 | + if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { |
| 846 | + SSLLogger.fine("Rejected client hello fragment (bad random len) " + |
| 847 | + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); |
| 848 | + } |
| 849 | + return null; |
| 850 | + } |
| 851 | + fragmentData.position(fragmentData.position() + 32); |
| 852 | + |
| 853 | + // SessionID |
| 854 | + byte[] sessId = Record.getBytes8(fragmentData); |
| 855 | + if (sessId.length > 0 && |
| 856 | + !SSLConfiguration.enableDtlsResumeCookie) { |
| 857 | + // If we are in a resumption it is possible that the cookie |
| 858 | + // exchange will be skipped. This is a server-side setting |
| 859 | + // and it is NOT the default. If enableDtlsResumeCookie is |
| 860 | + // false though, then we will buffer fragments since there |
| 861 | + // is no cookie exchange to execute prior to performing |
| 862 | + // reassembly. |
| 863 | + return hsf; |
| 864 | + } |
| 865 | + |
| 866 | + // Cookie |
| 867 | + byte[] cookie = Record.getBytes8(fragmentData); |
| 868 | + if (firstHello && cookie.length != 0) { |
| 869 | + if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { |
| 870 | + SSLLogger.fine("Rejected initial client hello fragment (bad cookie len) " + |
| 871 | + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); |
| 872 | + } |
| 873 | + return null; |
| 874 | + } |
| 875 | + // CipherSuites |
| 876 | + Record.getBytes16(fragmentData); |
| 877 | + // Compression methods |
| 878 | + Record.getBytes8(fragmentData); |
| 879 | + |
| 880 | + // If it's the first fragment, we'll truncate it and push it |
| 881 | + // through the reassembler. |
| 882 | + if (firstHello) { |
| 883 | + return truncateChFragment(hsf, fragmentData.position()); |
| 884 | + } else { |
| 885 | + HelloCookieManager hcMgr = sslContext. |
| 886 | + getHelloCookieManager(ProtocolVersion.DTLS10); |
| 887 | + ByteBuffer msgFragBuf = ByteBuffer.wrap(hsf.fragment, 0, |
| 888 | + fragmentData.position()); |
| 889 | + ClientHello.ClientHelloMessage chMsg = |
| 890 | + new ClientHello.ClientHelloMessage(shc, msgFragBuf, null); |
| 891 | + if (!hcMgr.isCookieValid(shc, chMsg, cookie)) { |
| 892 | + // Bad cookie check, truncate it and let the ClientHello |
| 893 | + // consumer recheck, fail and take the appropriate action. |
| 894 | + return truncateChFragment(hsf, fragmentData.position()); |
| 895 | + } else { |
| 896 | + // It's a good cookie, return the original handshake |
| 897 | + // fragment and let it go into the DTLSReassembler like |
| 898 | + // any other fragment so we can wait for the rest of |
| 899 | + // the CH message. |
| 900 | + shc.acceptCliHelloFragments = true; |
| 901 | + return hsf; |
| 902 | + } |
| 903 | + } |
| 904 | + } catch (IOException ioe) { |
| 905 | + if (SSLLogger.isOn && SSLLogger.isOn("verbose")) { |
| 906 | + SSLLogger.fine("Rejected client hello fragment " + |
| 907 | + "fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength); |
| 908 | + } |
| 909 | + return null; |
| 910 | + } |
| 911 | + } |
| 912 | + |
786 | 913 | // Queue up a ChangeCipherSpec message |
787 | 914 | void queueUpChangeCipherSpec(RecordFragment rf) |
788 | 915 | throws SSLProtocolException { |
|
0 commit comments