1 /* 2 * hunt-proton: AMQP Protocol library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.proton.engine.impl.ssl.SslHandshakeSniffingTransportWrapper; 13 14 import hunt.proton.engine.impl.HandshakeSniffingTransportWrapper; 15 import hunt.proton.engine.impl.TransportWrapper; 16 import hunt.proton.engine.impl.ssl.SslTransportWrapper; 17 import std.conv:to; 18 import hunt.Exceptions; 19 import hunt.io.ByteBuffer; 20 /** 21 * SslHandshakeSniffingTransportWrapper 22 * 23 */ 24 25 class SslHandshakeSniffingTransportWrapper : HandshakeSniffingTransportWrapper!(SslTransportWrapper, TransportWrapper) 26 , SslTransportWrapper 27 { 28 29 this(SslTransportWrapper ssl, TransportWrapper plain) { 30 super(ssl, plain); 31 } 32 33 public string getCipherName() 34 { 35 if(isSecureWrapperSelected()) 36 { 37 return _wrapper1.getCipherName(); 38 } 39 else 40 { 41 return null; 42 } 43 } 44 45 46 public string getProtocolName() 47 { 48 if (isSecureWrapperSelected()) 49 { 50 return _wrapper1.getProtocolName(); 51 } 52 else 53 { 54 return null; 55 } 56 } 57 58 override 59 int pending() 60 { 61 return super.pending(); 62 } 63 64 override 65 ByteBuffer head() 66 { 67 return super.head(); 68 } 69 70 override void pop(int bytes) 71 { 72 return super.pop(bytes); 73 } 74 75 override void close_head() 76 { 77 super.close_head(); 78 } 79 80 private bool isSecureWrapperSelected() 81 { 82 return _selectedTransportWrapper == _wrapper1; 83 } 84 85 override 86 protected int bufferSize() { 87 // minimum length for determination 88 return 5; 89 } 90 91 override 92 protected void makeDetermination(byte[] bytesInput) 93 { 94 bool isSecure = checkForSslHandshake(bytesInput); 95 if (isSecure) 96 { 97 _selectedTransportWrapper = _wrapper1; 98 } 99 else 100 { 101 _selectedTransportWrapper = _wrapper2; 102 } 103 } 104 105 // TODO perhaps the sniffer should save up the bytes from each 106 // input call until it has sufficient bytes to make the determination 107 // and only then pass them to the secure or plain wrapped transport? 108 private bool checkForSslHandshake(byte[] buf) 109 { 110 if (buf.length >= bufferSize()) 111 { 112 /* 113 * SSLv2 Client Hello format 114 * http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html 115 * 116 * Bytes 0-1: RECORD-LENGTH Byte 2: MSG-CLIENT-HELLO (1) Byte 3: 117 * CLIENT-VERSION-MSB Byte 4: CLIENT-VERSION-LSB 118 * 119 * Allowed versions: 2.0 - SSLv2 3.0 - SSLv3 3.1 - TLS 1.0 3.2 - TLS 120 * 1.1 3.3 - TLS 1.2 121 * 122 * The version sent in the Client-Hello is the latest version 123 * supported by the client. NSS may send version 3.x in an SSLv2 124 * header for maximum compatibility. 125 */ 126 bool isSSL2Handshake = buf[2] == 1 && // MSG-CLIENT-HELLO 127 ((buf[3] == 3 && buf[4] <= 3) || // SSL 3.0 & TLS 1.0-1.2 128 // (v3.1-3.3) 129 (buf[3] == 2 && buf[4] == 0)); // SSL 2 130 131 /* 132 * SSLv3/TLS Client Hello format RFC 2246 133 * 134 * Byte 0: ContentType (handshake - 22) Bytes 1-2: ProtocolVersion 135 * {major, minor} 136 * 137 * Allowed versions: 3.0 - SSLv3 3.1 - TLS 1.0 3.2 - TLS 1.1 3.3 - 138 * TLS 1.2 139 */ 140 bool isSSL3Handshake = buf[0] == 22 && // handshake 141 (buf[1] == 3 && buf[2] <= 3); // SSL 3.0 & TLS 1.0-1.2 142 // (v3.1-3.3) 143 144 return (isSSL2Handshake || isSSL3Handshake); 145 } 146 else 147 { 148 throw new IllegalArgumentException("Too few bytes (" ~ to!string(buf.length) ~ ") to make SSL/plain determination."); 149 } 150 } 151 152 }