diff --git a/scapy/layers/ntp.py b/scapy/layers/ntp.py index 53743f9acdd..0f668e9f2fa 100644 --- a/scapy/layers/ntp.py +++ b/scapy/layers/ntp.py @@ -213,41 +213,57 @@ def mysummary(self): return self.sprintf("NTP v%ir,NTP.version%, %NTP.mode%") -class _NTPAuthenticatorPaddingField(StrField): - """ - StrField handling the padding that may be found before the - "authenticator" field. - """ - - def getfield(self, pkt, s): - ret = None - remain = s - length = len(s) - - if length > _NTP_AUTH_MD5_TAIL_SIZE: - start = length - _NTP_AUTH_MD5_TAIL_SIZE - ret = s[:start] - remain = s[start:] - return remain, ret - - class NTPAuthenticator(Packet): """ Packet handling the "authenticator" part of a NTP packet, as defined in RFC 5905. """ + authenticator_length = 16 # MD5 + padding = b"" name = "Authenticator" fields_desc = [ - _NTPAuthenticatorPaddingField("padding", ""), IntField("key_id", 0), - XStrFixedLenField("dgst", "", length_from=lambda x: 16) + XStrFixedLenField("dgst", "", length_from=lambda x: x.authenticator_length) ] def extract_padding(self, s): return b"", s +class NTPAuthenticatorMD5(NTPAuthenticator): + pass + + +class NTPAuthenticatorSHA1(NTPAuthenticator): + authenticator_length = 20 + + +class NTPAuthenticatorSHA256(NTPAuthenticator): + authenticator_length = 32 + + +class NTPAuthenticatorSHA512(NTPAuthenticator): + authenticator_length = 64 + + +def _guess_authenticator_class(payload): + """ + Attempt to guess the NTP Authenticator class using the payload length + """ + plen = len(payload) + + authenticator_dispatch = {16: NTPAuthenticatorMD5, + 20: NTPAuthenticatorSHA1, + 32: NTPAuthenticatorSHA256, + 64: NTPAuthenticatorSHA512} + authenticator_length = plen - 4 + authenticator_class = authenticator_dispatch.get(authenticator_length, + None) + + return authenticator_class + + class NTPExtension(Packet): """ Packet handling a NTPv4 extension. @@ -450,12 +466,14 @@ class NTPHeader(NTP): def guess_payload_class(self, payload): """ - Handles NTPv4 extensions and MAC part (when authentication is used.) + Handles NTPv4 extensions and MAC part (when authentication is used) """ + plen = len(payload) + authenticator_class = _guess_authenticator_class(payload) - if plen - 4 in [16, 20, 32, 64]: # length of MD5, SHA1, SHA256, SHA512 - return NTPAuthenticator + if authenticator_class is not None: + return authenticator_class elif plen > _NTP_AUTH_MD5_TAIL_SIZE: return NTPExtensions @@ -810,7 +828,6 @@ class NTPControl(NTP): ShortField("count", None), NTPControlDataPacketLenField( "data", "", Packet, length_from=lambda p: p.count), - PacketField("authenticator", "", NTPAuthenticator), ] def post_build(self, p, pay): @@ -821,6 +838,18 @@ def post_build(self, p, pay): p = p[:11] + struct.pack("!H", length) + p[13:] return p + pay + def extract_padding(self, s): + # Check the 64 bit alignement + padding_length = len(self.raw_packet_cache) % 8 * 8 + if padding_length == 0: + return b"", s + + padding_length -= len(self.raw_packet_cache) + return s[padding_length:], s[:padding_length] + + def guess_payload_class(self, s): + return _guess_authenticator_class(s) + ############################################################################## # Private (mode 7) diff --git a/test/scapy/layers/ntp.uts b/test/scapy/layers/ntp.uts index 7a939bfdadd..fe1e41614ff 100644 --- a/test/scapy/layers/ntp.uts +++ b/test/scapy/layers/ntp.uts @@ -99,7 +99,7 @@ assert p.stratum == 2 s = hex_bytes("000c2962f268d094666d23750800450000640db640004011a519c0a80364c0a80305a51e007b0050731a2300072000000000000000000000000000000000000000000000000000000000000000000000000052c7bc1dda64b97d0000000bcdc3825dbf6b7ad02886ff45aa8b2eaf7ac78bc1") p = Ether(s) -assert NTPAuthenticator in p and p[NTPAuthenticator].key_id == 3452142173 +assert NTPAuthenticator in p and p[NTPAuthenticator].key_id == 11 ############ @@ -309,7 +309,7 @@ assert bytes_hex(p.authenticator.dgst) == b'bfa6d85ff96d1e326c293caceec2a539' = NTP Control (mode 6) - CTL_OP_SAVECONFIG (1) - request -s = b'\x16\t\x00\x1d\x00\x00\x00\x00\x00\x00\x00\x0fntp.test.2.conf\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc9\xfb\x8a\xbe<`_\xfa6\xd2\x18\xc3\xb7d\x89#' +s = b'\x16\t\x00\x1d\x00\x00\x00\x00\x00\x00\x00\x0fntp.test.2.conf\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc9\xfb\x8a\xbe<`_\xfa6\xd2\x18\xc3\xb7d' p = NTP(s) assert isinstance(p, NTPControl) assert p.version == 2 @@ -321,7 +321,7 @@ assert p.op_code == 9 assert p.count == 15 assert p.data.load == b'ntp.test.2.conf\x00' assert p.authenticator.key_id == 1 -assert bytes_hex(p.authenticator.dgst) == b'c9fb8abe3c605ffa36d218c3b7648923' +assert bytes_hex(p.authenticator.dgst) == b'c9fb8abe3c605ffa36d218c3b764' = NTP Control (mode 6) - CTL_OP_SAVECONFIG (2) - response @@ -1144,4 +1144,15 @@ raw_pkt = (b"#\x02\n\xec\x00\x00\x00\x00\x00\x00\xf2\xce\x7f\x00\x00\x01\x00" b"\x00\x00\x00\x00\x00\x00\x00\xe6}gt\x00\x00\x00\x00\xe6}gt\x00" b"\x00\x00\x00\xe6}gt\x00\x00\x00\x00") + assert raw(pkt_1) == raw(pkt_2) == raw_pkt + +############ +############ ++ Hash Algorithms Tests + += SHA1 + +r = b'\x00\x0c)\xd3\x93W\x00\x0c)b\xf2h\x08\x00E\xb8\x00d\x9f\xd3@\x00@\x11\x12\xa7\xc0\xa8\x03\x05\xc0\xa8\x03\x01\x00{\x00{\x00P\xdb;\xe3\x00\x06\xe8\x00\x00\x00\x00\x00\x00\x00\x00INIT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe2\x81a(H\xca\x0e\xf4\x00\x00\x00\x0c\xd2\x05\x98\x88\xb4\xa2\xad\xc1\x1d-\xbd\x14 \xa45`\xbf\x92\xc6\xf9' +p = Ether(r) +assert NTP in p and p.key_id == 12 and p.dgst == b"d2059888b4a2adc11d2dbd1420a43560bf92c6f9"