From: Victor Julien Date: Fri, 12 Apr 2024 10:42:37 +0000 (+0200) Subject: decode/ppp: support different header formats X-Git-Tag: suricata-8.0.0-beta1~1480 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=68092ff33ce437a6f6720dd9d7f47aa16574ccf1;p=thirdparty%2Fsuricata.git decode/ppp: support different header formats Support compressed proto and optional HDLC header. Bug: #6942. --- diff --git a/src/decode-ppp.c b/src/decode-ppp.c index 5bf682bd2f..6507bc0a6e 100644 --- a/src/decode-ppp.c +++ b/src/decode-ppp.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2021 Open Information Security Foundation +/* Copyright (C) 2007-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -41,70 +41,93 @@ #include "util-unittest.h" #include "util-debug.h" -int DecodePPP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, - const uint8_t *pkt, uint32_t len) +static int DecodePPPCompressedProto(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, + const uint8_t *pkt, uint32_t len, uint16_t proto_offset) { - DEBUG_VALIDATE_BUG_ON(pkt == NULL); - - StatsIncr(tv, dtv->counter_ppp); + const uint32_t data_offset = proto_offset + 1; + switch (*(pkt + proto_offset)) { + case 0x21: { /* PPP_IP */ + if (unlikely(len < (data_offset + IPV4_HEADER_LEN))) { + ENGINE_SET_INVALID_EVENT(p, PPPVJU_PKT_TOO_SMALL); + return TM_ECODE_FAILED; + } + DEBUG_VALIDATE_BUG_ON(len < data_offset); + uint16_t iplen = MIN(USHRT_MAX, (uint16_t)(len - data_offset)); + return DecodeIPV4(tv, dtv, p, pkt + data_offset, iplen); + } + case 0x57: { /* PPP_IPV6 */ + if (unlikely(len < (data_offset + IPV6_HEADER_LEN))) { + ENGINE_SET_INVALID_EVENT(p, PPPIPV6_PKT_TOO_SMALL); + return TM_ECODE_FAILED; + } + DEBUG_VALIDATE_BUG_ON(len < data_offset); + uint16_t iplen = MIN(USHRT_MAX, (uint16_t)(len - data_offset)); + return DecodeIPV6(tv, dtv, p, pkt + data_offset, iplen); + } + case 0x2f: /* PPP_VJ_UCOMP */ + if (unlikely(len < (data_offset + IPV4_HEADER_LEN))) { + ENGINE_SET_INVALID_EVENT(p, PPPVJU_PKT_TOO_SMALL); + return TM_ECODE_FAILED; + } - if (unlikely(len < PPP_HEADER_LEN)) { - ENGINE_SET_INVALID_EVENT(p, PPP_PKT_TOO_SMALL); - return TM_ECODE_FAILED; - } - if (!PacketIncreaseCheckLayers(p)) { - return TM_ECODE_FAILED; - } + if (unlikely(len > data_offset + USHRT_MAX)) { + return TM_ECODE_FAILED; + } - p->ppph = (PPPHdr *)pkt; + if (likely(IPV4_GET_RAW_VER((IPV4Hdr *)(pkt + data_offset)) == 4)) { + return DecodeIPV4(tv, dtv, p, pkt + data_offset, (uint16_t)(len - data_offset)); + } else + return TM_ECODE_FAILED; + break; - SCLogDebug("p %p pkt %p PPP protocol %04x Len: %" PRIu32 "", - p, pkt, SCNtohs(p->ppph->protocol), len); + default: + ENGINE_SET_EVENT(p, PPP_UNSUP_PROTO); + return TM_ECODE_OK; + } +} - switch (SCNtohs(p->ppph->protocol)) - { +static int DecodePPPUncompressedProto(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, + const uint8_t *pkt, uint32_t len, const uint16_t proto, const uint32_t data_offset) +{ + switch (proto) { case PPP_VJ_UCOMP: - if (unlikely(len < (PPP_HEADER_LEN + IPV4_HEADER_LEN))) { - ENGINE_SET_INVALID_EVENT(p,PPPVJU_PKT_TOO_SMALL); - p->ppph = NULL; + if (unlikely(len < (data_offset + IPV4_HEADER_LEN))) { + ENGINE_SET_INVALID_EVENT(p, PPPVJU_PKT_TOO_SMALL); return TM_ECODE_FAILED; } - if (unlikely(len > PPP_HEADER_LEN + USHRT_MAX)) { + if (unlikely(len > data_offset + USHRT_MAX)) { return TM_ECODE_FAILED; } - if (likely(IPV4_GET_RAW_VER((IPV4Hdr *)(pkt + PPP_HEADER_LEN)) == 4)) { - return DecodeIPV4( - tv, dtv, p, pkt + PPP_HEADER_LEN, (uint16_t)(len - PPP_HEADER_LEN)); + if (likely(IPV4_GET_RAW_VER((IPV4Hdr *)(pkt + data_offset)) == 4)) { + return DecodeIPV4(tv, dtv, p, pkt + data_offset, (uint16_t)(len - data_offset)); } else return TM_ECODE_FAILED; break; case PPP_IP: - if (unlikely(len < (PPP_HEADER_LEN + IPV4_HEADER_LEN))) { - ENGINE_SET_INVALID_EVENT(p,PPPIPV4_PKT_TOO_SMALL); - p->ppph = NULL; + if (unlikely(len < (data_offset + IPV4_HEADER_LEN))) { + ENGINE_SET_INVALID_EVENT(p, PPPIPV4_PKT_TOO_SMALL); return TM_ECODE_FAILED; } - if (unlikely(len > PPP_HEADER_LEN + USHRT_MAX)) { + if (unlikely(len > data_offset + USHRT_MAX)) { return TM_ECODE_FAILED; } - return DecodeIPV4(tv, dtv, p, pkt + PPP_HEADER_LEN, (uint16_t)(len - PPP_HEADER_LEN)); + return DecodeIPV4(tv, dtv, p, pkt + data_offset, (uint16_t)(len - data_offset)); /* PPP IPv6 was not tested */ case PPP_IPV6: - if (unlikely(len < (PPP_HEADER_LEN + IPV6_HEADER_LEN))) { - ENGINE_SET_INVALID_EVENT(p,PPPIPV6_PKT_TOO_SMALL); - p->ppph = NULL; + if (unlikely(len < (data_offset + IPV6_HEADER_LEN))) { + ENGINE_SET_INVALID_EVENT(p, PPPIPV6_PKT_TOO_SMALL); return TM_ECODE_FAILED; } - if (unlikely(len > PPP_HEADER_LEN + USHRT_MAX)) { + if (unlikely(len > data_offset + USHRT_MAX)) { return TM_ECODE_FAILED; } - return DecodeIPV6(tv, dtv, p, pkt + PPP_HEADER_LEN, (uint16_t)(len - PPP_HEADER_LEN)); + return DecodeIPV6(tv, dtv, p, pkt + data_offset, (uint16_t)(len - data_offset)); case PPP_VJ_COMP: case PPP_IPX: @@ -138,11 +161,70 @@ int DecodePPP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, return TM_ECODE_OK; default: - SCLogDebug("unknown PPP protocol: %" PRIx32 "",SCNtohs(p->ppph->protocol)); + SCLogDebug("unknown PPP protocol: %x", proto); ENGINE_SET_INVALID_EVENT(p, PPP_WRONG_TYPE); return TM_ECODE_OK; } +} +int DecodePPP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint32_t len) +{ + DEBUG_VALIDATE_BUG_ON(pkt == NULL); + + StatsIncr(tv, dtv->counter_ppp); + if (unlikely(len < 1)) { + ENGINE_SET_INVALID_EVENT(p, PPP_PKT_TOO_SMALL); + return TM_ECODE_FAILED; + } + + uint16_t proto_offset = 0; + /* 0xff means we have a HDLC header: proto will start at offset 2 */ + if (*pkt == 0xff) { + proto_offset = 2; + /* make sure the proto field at the offset fits */ + if (len < 3) { + ENGINE_SET_INVALID_EVENT(p, PPP_PKT_TOO_SMALL); + return TM_ECODE_FAILED; + } + } + uint8_t proto_size = 0; + uint8_t proto_byte = *(pkt + proto_offset); + /* check if compressed protocol bit is set. */ + if (proto_byte & 0x01) { + proto_size = 1; + } else { + proto_size = 2; + } + if (len < (proto_size + proto_offset)) { + ENGINE_SET_INVALID_EVENT(p, PPP_PKT_TOO_SMALL); + return TM_ECODE_FAILED; + } + if (!PacketIncreaseCheckLayers(p)) { + return TM_ECODE_FAILED; + } + + const uint32_t data_offset = proto_offset + proto_size; + if (data_offset != 4) { + if (proto_size == 1) { + return DecodePPPCompressedProto(tv, dtv, p, pkt, len, proto_offset); + } else { + const uint16_t proto = SCNtohs(*(uint16_t *)(pkt + proto_offset)); + return DecodePPPUncompressedProto(tv, dtv, p, pkt, len, proto, data_offset); + } + } + /* implied proto_offset + proto_size == 4, so continue below */ + + p->ppph = (PPPHdr *)pkt; + + SCLogDebug("p %p pkt %p PPP protocol %04x Len: %" PRIu32 "", p, pkt, SCNtohs(p->ppph->protocol), + len); + + int r = DecodePPPUncompressedProto( + tv, dtv, p, pkt, len, SCNtohs(p->ppph->protocol), data_offset); + if (r < 0) { + p->ppph = NULL; + } + return r; } /* TESTS BELOW */