1## This file is part of Scapy 2## Copyright (C) 2017 Maxence Tury 3## This program is published under a GPLv2 license 4 5""" 6SSLv2 Record. 7""" 8 9import struct 10 11from scapy.config import conf 12from scapy.error import log_runtime 13from scapy.compat import * 14from scapy.fields import * 15from scapy.packet import * 16from scapy.layers.tls.session import _GenericTLSSessionInheritance 17from scapy.layers.tls.record import _TLSMsgListField, TLS 18from scapy.layers.tls.handshake_sslv2 import _sslv2_handshake_cls 19from scapy.layers.tls.basefields import (_SSLv2LengthField, _SSLv2PadField, 20 _SSLv2PadLenField, _TLSMACField) 21 22 23############################################################################### 24### SSLv2 Record Protocol ### 25############################################################################### 26 27class _SSLv2MsgListField(_TLSMsgListField): 28 def __init__(self, name, default, length_from=None): 29 if not length_from: 30 length_from=lambda pkt: ((pkt.len & 0x7fff) - 31 (pkt.padlen or 0) - 32 len(pkt.mac)) 33 super(_SSLv2MsgListField, self).__init__(name, default, length_from) 34 35 def m2i(self, pkt, m): 36 cls = Raw 37 if len(m) >= 1: 38 msgtype = orb(m[0]) 39 cls = _sslv2_handshake_cls.get(msgtype, Raw) 40 41 if cls is Raw: 42 return Raw(m) 43 else: 44 return cls(m, tls_session=pkt.tls_session) 45 46 def i2m(self, pkt, p): 47 cur = b"" 48 if isinstance(p, _GenericTLSSessionInheritance): 49 p.tls_session = pkt.tls_session 50 if not pkt.tls_session.frozen: 51 cur = p.raw_stateful() 52 p.post_build_tls_session_update(cur) 53 else: 54 cur = raw(p) 55 else: 56 cur = raw(p) 57 return cur 58 59 def addfield(self, pkt, s, val): 60 res = b"" 61 for p in val: 62 res += self.i2m(pkt, p) 63 return s + res 64 65 66class SSLv2(TLS): 67 """ 68 The encrypted_data is the encrypted version of mac+msg+pad. 69 """ 70 __slots__ = ["with_padding", "protected_record"] 71 name = "SSLv2" 72 fields_desc = [ _SSLv2LengthField("len", None), 73 _SSLv2PadLenField("padlen", None), 74 _TLSMACField("mac", b""), 75 _SSLv2MsgListField("msg", []), 76 _SSLv2PadField("pad", "") ] 77 78 def __init__(self, *args, **kargs): 79 self.with_padding = kargs.get("with_padding", False) 80 self.protected_record = kargs.get("protected_record", None) 81 super(SSLv2, self).__init__(*args, **kargs) 82 83 ### Parsing methods 84 85 def _sslv2_mac_verify(self, msg, mac): 86 secret = self.tls_session.rcs.cipher.key 87 if secret is None: 88 return True 89 90 mac_len = self.tls_session.rcs.mac_len 91 if mac_len == 0: # should be TLS_NULL_WITH_NULL_NULL 92 return True 93 if len(mac) != mac_len: 94 return False 95 96 read_seq_num = struct.pack("!I", self.tls_session.rcs.seq_num) 97 alg = self.tls_session.rcs.hash 98 h = alg.digest(secret + msg + read_seq_num) 99 return h == mac 100 101 def pre_dissect(self, s): 102 if len(s) < 2: 103 raise Exception("Invalid record: header is too short.") 104 105 msglen = struct.unpack("!H", s[:2])[0] 106 if msglen & 0x8000: 107 hdrlen = 2 108 msglen_clean = msglen & 0x7fff 109 else: 110 hdrlen = 3 111 msglen_clean = msglen & 0x3fff 112 113 hdr = s[:hdrlen] 114 efrag = s[hdrlen:hdrlen+msglen_clean] 115 self.protected_record = s[:hdrlen+msglen_clean] 116 r = s[hdrlen+msglen_clean:] 117 118 mac = pad = b"" 119 120 cipher_type = self.tls_session.rcs.cipher.type 121 122 # Decrypt (with implicit IV if block cipher) 123 mfrag = self._tls_decrypt(efrag) 124 125 # Extract MAC 126 maclen = self.tls_session.rcs.mac_len 127 if maclen == 0: 128 mac, pfrag = b"", mfrag 129 else: 130 mac, pfrag = mfrag[:maclen], mfrag[maclen:] 131 132 # Extract padding 133 padlen = 0 134 if hdrlen == 3: 135 padlen = orb(s[2]) 136 if padlen == 0: 137 cfrag, pad = pfrag, b"" 138 else: 139 cfrag, pad = pfrag[:-padlen], pfrag[-padlen:] 140 141 # Verify integrity 142 is_mac_ok = self._sslv2_mac_verify(cfrag + pad, mac) 143 if not is_mac_ok: 144 pkt_info = self.firstlayer().summary() 145 log_runtime.info("TLS: record integrity check failed [%s]", pkt_info) 146 147 reconstructed_body = mac + cfrag + pad 148 return hdr + reconstructed_body + r 149 150 def post_dissect(self, s): 151 """ 152 SSLv2 may force us to commit the write connState here. 153 """ 154 if self.tls_session.triggered_prcs_commit: 155 if self.tls_session.prcs is not None: 156 self.tls_session.rcs = self.tls_session.prcs 157 self.tls_session.prcs = None 158 self.tls_session.triggered_prcs_commit = False 159 if self.tls_session.triggered_pwcs_commit: 160 if self.tls_session.pwcs is not None: 161 self.tls_session.wcs = self.tls_session.pwcs 162 self.tls_session.pwcs = None 163 self.tls_session.triggered_pwcs_commit = False 164 165 if self.tls_session.prcs is not None: 166 self.tls_session.prcs.seq_num += 1 167 self.tls_session.rcs.seq_num += 1 168 return s 169 170 def do_dissect_payload(self, s): 171 if s: 172 try: 173 p = SSLv2(s, _internal=1, _underlayer=self, 174 tls_session = self.tls_session) 175 except KeyboardInterrupt: 176 raise 177 except: 178 p = conf.raw_layer(s, _internal=1, _underlayer=self) 179 self.add_payload(p) 180 181 182 ### Building methods 183 184 def _sslv2_mac_add(self, msg): 185 secret = self.tls_session.wcs.cipher.key 186 if secret is None: 187 return msg 188 189 write_seq_num = struct.pack("!I", self.tls_session.wcs.seq_num) 190 alg = self.tls_session.wcs.hash 191 h = alg.digest(secret + msg + write_seq_num) 192 return h + msg 193 194 def _sslv2_pad(self, s): 195 padding = b"" 196 block_size = self.tls_session.wcs.cipher.block_size 197 padlen = block_size - (len(s) % block_size) 198 if padlen == block_size: 199 padlen = 0 200 padding = b"\x00" * padlen 201 return s + padding 202 203 def post_build(self, pkt, pay): 204 if self.protected_record is not None: 205 # we do not update the tls_session 206 return self.protected_record + pay 207 208 if self.padlen is None: 209 cfrag = pkt[2:] 210 else: 211 cfrag = pkt[3:] 212 213 if self.pad == b"" and self.tls_session.wcs.cipher.type == 'block': 214 pfrag = self._sslv2_pad(cfrag) 215 else: 216 pad = self.pad or b"" 217 pfrag = cfrag + pad 218 219 padlen = self.padlen 220 if padlen is None: 221 padlen = len(pfrag) - len(cfrag) 222 hdr = pkt[:2] 223 if padlen > 0: 224 hdr += struct.pack("B", padlen) 225 226 # Integrity 227 if self.mac == b"": 228 mfrag = self._sslv2_mac_add(pfrag) 229 else: 230 mfrag = self.mac + pfrag 231 232 # Encryption 233 efrag = self._tls_encrypt(mfrag) 234 235 if self.len is not None: 236 l = self.len 237 if not self.with_padding: 238 l |= 0x8000 239 hdr = struct.pack("!H", l) + hdr[2:] 240 else: 241 # Update header with the length of TLSCiphertext.fragment 242 msglen_new = len(efrag) 243 if padlen: 244 if msglen_new > 0x3fff: 245 raise Exception("Invalid record: encrypted data too long.") 246 else: 247 if msglen_new > 0x7fff: 248 raise Exception("Invalid record: encrypted data too long.") 249 msglen_new |= 0x8000 250 hdr = struct.pack("!H", msglen_new) + hdr[2:] 251 252 # Now we commit the pending write state if it has been triggered (e.g. 253 # by an underlying TLSChangeCipherSpec or a SSLv2ClientMasterKey). We 254 # update nothing if the pwcs was not set. This probably means that 255 # we're working out-of-context (and we need to keep the default wcs). 256 # SSLv2 may force us to commit the reading connState here. 257 if self.tls_session.triggered_pwcs_commit: 258 if self.tls_session.pwcs is not None: 259 self.tls_session.wcs = self.tls_session.pwcs 260 self.tls_session.pwcs = None 261 self.tls_session.triggered_pwcs_commit = False 262 if self.tls_session.triggered_prcs_commit: 263 if self.tls_session.prcs is not None: 264 self.tls_session.rcs = self.tls_session.prcs 265 self.tls_session.prcs = None 266 self.tls_session.triggered_prcs_commit = False 267 268 if self.tls_session.pwcs is not None: 269 self.tls_session.pwcs.seq_num += 1 270 self.tls_session.wcs.seq_num += 1 271 272 return hdr + efrag + pay 273 274