1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License Version 2 as published
6 // by the Free Software Foundation.  You may not use, modify or distribute
7 // this program under any other version of the GNU General Public License.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program; if not, write to the Free Software Foundation, Inc.,
16 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 //--------------------------------------------------------------------------
18 // cd_icmp4_ip.cc author Josh Rosenbaum <jrosenba@cisco.com>
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "codecs/codec_module.h"
25 #include "framework/codec.h"
26 #include "log/text_log.h"
27 #include "main/snort_config.h"
28 #include "protocols/icmp4.h"
29 #include "protocols/packet_manager.h"
30 #include "protocols/tcp.h"
31 #include "protocols/udp.h"
32 
33 using namespace snort;
34 
35 namespace
36 {
37 #define ICMP4_IP_NAME "icmp4_ip"
38 #define ICMP4_IP_HELP "support for IP in ICMPv4"
39 
40 class Icmp4IpCodec : public Codec
41 {
42 public:
Icmp4IpCodec()43     Icmp4IpCodec() : Codec(ICMP4_IP_NAME) { }
44 
45     void get_protocol_ids(std::vector<ProtocolId>&) override;
46     bool decode(const RawData&, CodecData&, DecodeData&) override;
47     void log(TextLog* const, const uint8_t* pkt, const uint16_t len) override;
48 };
49 } // namespace
50 
get_protocol_ids(std::vector<ProtocolId> & v)51 void Icmp4IpCodec::get_protocol_ids(std::vector<ProtocolId>& v)
52 { v.emplace_back(ProtocolId::IP_EMBEDDED_IN_ICMP4); }
53 
decode(const RawData & raw,CodecData & codec,DecodeData & snort)54 bool Icmp4IpCodec::decode(const RawData& raw, CodecData& codec, DecodeData& snort)
55 {
56     if (raw.len < ip::IP4_HEADER_LEN)
57     {
58         codec_event(codec, DECODE_ICMP_ORIG_IP_TRUNCATED);
59         return false;
60     }
61 
62     /* lay the IP struct over the raw data */
63     const ip::IP4Hdr* const ip4h = reinterpret_cast<const ip::IP4Hdr*>(raw.data);
64 
65     /*
66      * with datalink DLT_RAW it's impossible to differ ARP datagrams from IP.
67      * So we are just ignoring non IP datagrams
68      */
69     if ((ip4h->ver() != 4) && !snort.ip_api.is_ip6())
70     {
71         codec_event(codec, DECODE_ICMP_ORIG_IP_VER_MISMATCH);
72         return false;
73     }
74 
75     const uint16_t hlen = ip4h->hlen();
76 
77     if (raw.len < hlen)
78     {
79         codec_event(codec, DECODE_ICMP_ORIG_DGRAM_LT_ORIG_IP);
80         return false;
81     }
82 
83     /* set the remaining packet length */
84     if (ip4h->off() == 0)
85     {
86         const uint32_t ip_len = raw.len - hlen;
87 
88         /* Original IP payload should be 64 bits */
89         if (ip_len < 8)
90         {
91             codec_event(codec, DECODE_ICMP_ORIG_PAYLOAD_LT_64);
92             return false;
93         }
94         /* ICMP error packets could contain as much of original payload
95          * as possible, but not exceed 576 bytes
96          */
97         else if (snort.ip_api.dgram_len() > 576)
98         {
99             codec_event(codec, DECODE_ICMP_ORIG_PAYLOAD_GT_576);
100         }
101     }
102     else
103     {
104         /* RFC states that only first frag will get an ICMP response */
105         codec_event(codec, DECODE_ICMP_ORIG_IP_WITH_FRAGOFFSET);
106         return false;
107     }
108 
109     switch (ip4h->proto())
110     {
111     case IpProtocol::TCP:     /* decode the interesting part of the header */
112         codec.proto_bits |= PROTO_BIT__TCP_EMBED_ICMP;
113         break;
114 
115     case IpProtocol::UDP:
116         codec.proto_bits |= PROTO_BIT__UDP_EMBED_ICMP;
117         break;
118 
119     case IpProtocol::ICMPV4:
120         codec.proto_bits |= PROTO_BIT__ICMP_EMBED_ICMP;
121         break;
122     default:
123         codec.proto_bits |= PROTO_BIT__ICMP_EMBED_OTHER;
124         break;
125     }
126 
127     // If you change this, change the buffer and
128     // memcpy length in encode() below !!
129     codec.lyr_len = hlen;
130     return true;
131 }
132 
133 struct ip4_addr
134 {
135     union
136     {
137         uint32_t addr32;
138         uint8_t addr8[4];
139     };
140 };
141 
log(TextLog * const text_log,const uint8_t * raw_pkt,const uint16_t)142 void Icmp4IpCodec::log(TextLog* const text_log, const uint8_t* raw_pkt,
143     const uint16_t /*lyr_len*/)
144 {
145     const ip::IP4Hdr* const ip4h = reinterpret_cast<const ip::IP4Hdr*>(raw_pkt);
146     TextLog_Puts(text_log, "\n\t**** ORIGINAL DATAGRAM DUMP: ****");
147     TextLog_NewLine(text_log);
148     TextLog_Puts(text_log, "\tIPv4\n\t\t");
149 
150     // COPIED DIRECTLY FROM ipv4 CODEC.  This is specifically replicated since
151     //      the two are not necessarily the same.
152 
153     // FIXIT-RC this does NOT obfuscate correctly
154     if (SnortConfig::get_conf()->obfuscate())
155     {
156         TextLog_Print(text_log, "xxx.xxx.xxx.xxx -> xxx.xxx.xxx.xxx");
157     }
158     else
159     {
160         ip4_addr src, dst;
161         src.addr32 = ip4h->get_src();
162         dst.addr32 = ip4h->get_dst();
163 
164         TextLog_Print(text_log, "%d.%d.%d.%d -> %d.%d.%d.%d",
165             (int)src.addr8[0], (int)src.addr8[1],
166             (int)src.addr8[2], (int)src.addr8[3],
167             (int)dst.addr8[0], (int)dst.addr8[1],
168             (int)dst.addr8[2], (int)dst.addr8[3]);
169     }
170 
171     TextLog_NewLine(text_log);
172     TextLog_Puts(text_log, "\t\t");
173 
174     const uint16_t hlen = ip4h->hlen();
175     const uint16_t len = ip4h->len();
176     const uint16_t frag_off = ip4h->off_w_flags();
177 
178     TextLog_Print(text_log, "Next:%s(%02X) TTL:%u TOS:0x%X ID:%u IpLen:%u DgmLen:%u",
179         PacketManager::get_proto_name(ip4h->proto()),
180         static_cast<uint8_t>(ip4h->proto()), ip4h->ttl(), ip4h->tos(),
181         ip4h->id(), hlen, len);
182 
183     /* print the reserved bit if it's set */
184     if (frag_off & 0x8000)
185         TextLog_Puts(text_log, " RB");
186 
187     /* printf more frags/don't frag bits */
188     if (frag_off & 0x4000)
189         TextLog_Puts(text_log, " DF");
190 
191     bool mf = false;
192     if (frag_off & 0x2000)
193     {
194         mf = true;
195         TextLog_Puts(text_log, " MF");
196     }
197 
198 #if 0
199     // FIXIT-L more ip options fixits
200     /* print IP options */
201     if (p->ip_option_count > 0)
202     {
203         LogIpOptions(text_log, p);
204     }
205 #endif
206 
207     if ( mf && (frag_off & 0x1FFF) && ((len - hlen > 0)))
208     {
209         TextLog_NewLine(text_log);
210         TextLog_Puts(text_log, "\t\t");
211         TextLog_Print(text_log, "Frag Offset: 0x%04X   Frag Size: 0x%04X",
212             (frag_off & 0x1FFF), (len - hlen));
213     }
214 
215     TextLog_NewLine(text_log);
216     TextLog_Putc(text_log, '\t');
217 
218     /*  EMBEDDED PROTOCOL */
219     switch (ip4h->proto())
220     {
221     case IpProtocol::TCP:     /* decode the interesting part of the header */
222     {
223         const tcp::TCPHdr* tcph = reinterpret_cast<const tcp::TCPHdr*>
224             (raw_pkt + hlen);
225         TextLog_Puts(text_log, "TCP\n\t\t");
226         TextLog_Print(text_log, "SrcPort:%u  DstPort:%u  Seq: 0x%lX  "
227             "Ack: 0x%lX  Win: 0x%X  TcpLen: %d",ntohs(tcph->th_sport),
228             ntohs(tcph->th_dport), (u_long)ntohl(tcph->th_seq),
229             (u_long)ntohl(tcph->th_ack),
230             ntohs(tcph->th_win), tcph->off());
231 
232         break;
233     }
234 
235     case IpProtocol::UDP:
236     {
237         const udp::UDPHdr* udph = reinterpret_cast<const udp::UDPHdr*>
238             (raw_pkt + hlen);
239         TextLog_Puts(text_log, "UDP\n\t\t");
240         TextLog_Print(text_log, "SourcePort:%d DestPort:%d Len:%d",
241             ntohs(udph->uh_sport), ntohs(udph->uh_dport),
242             ntohs(udph->uh_len) - udp::UDP_HEADER_LEN);
243         break;
244     }
245 
246     case IpProtocol::ICMPV4:
247     {
248         const icmp::ICMPHdr* icmph = reinterpret_cast<const icmp::ICMPHdr*>
249             (raw_pkt + hlen);
250 
251         TextLog_Puts(text_log, "ICMPv4\n\t\t");
252         TextLog_Print(text_log, "Type:%d  Code:%d  Csum:%u",
253             icmph->type, icmph->code, ntohs(icmph->csum));
254 
255         switch (icmph->type)
256         {
257         case icmp::IcmpType::DEST_UNREACH:
258         case icmp::IcmpType::TIME_EXCEEDED:
259         case icmp::IcmpType::SOURCE_QUENCH:
260             break;
261 
262         case icmp::IcmpType::PARAMETERPROB:
263             if (icmph->code == 0)
264                 TextLog_Print(text_log, "  Ptr: %u", icmph->s_icmp_pptr);
265             break;
266 
267         case ICMP_REDIRECT:
268             // FIXIT-L -IPv6 "NOT YET IMPLEMENTED - ICMP printing"
269             break;
270 
271         case icmp::IcmpType::ECHO_4:
272         case icmp::IcmpType::ECHOREPLY:
273         case icmp::IcmpType::TIMESTAMP:
274         case icmp::IcmpType::TIMESTAMPREPLY:
275         case icmp::IcmpType::INFO_REQUEST:
276         case icmp::IcmpType::INFO_REPLY:
277         case icmp::IcmpType::ADDRESS:
278         case icmp::IcmpType::ADDRESSREPLY:
279             TextLog_Print(text_log, "  Id: %u  SeqNo: %u",
280                 ntohs(icmph->s_icmp_id), ntohs(icmph->s_icmp_seq));
281             break;
282 
283         case icmp::IcmpType::ROUTER_ADVERTISE:
284             TextLog_Print(text_log, "  Addrs: %u  Size: %u  Lifetime: %u",
285                 icmph->s_icmp_num_addrs, icmph->s_icmp_wpa,
286                 ntohs(icmph->s_icmp_lifetime));
287             break;
288 
289         default:
290             break;
291         }
292         break;
293     }
294     default:
295     {
296         TextLog_Print(text_log, "Protocol:%s(%02X)",
297             PacketManager::get_proto_name(ip4h->proto()),
298             static_cast<uint8_t>(ip4h->proto()));
299         break;
300     }
301     }
302 }
303 
304 //-------------------------------------------------------------------------
305 // api
306 //-------------------------------------------------------------------------
307 
ctor(Module *)308 static Codec* ctor(Module*)
309 { return new Icmp4IpCodec(); }
310 
dtor(Codec * cd)311 static void dtor(Codec* cd)
312 { delete cd; }
313 
314 static const CodecApi icmp4_ip_api =
315 {
316     {
317         PT_CODEC,
318         sizeof(CodecApi),
319         CDAPI_VERSION,
320         0,
321         API_RESERVED,
322         API_OPTIONS,
323         ICMP4_IP_NAME,
324         ICMP4_IP_HELP,
325         nullptr, // module constructor
326         nullptr  // module destructor
327     },
328     nullptr, // g_ctor
329     nullptr, // g_dtor
330     nullptr, // t_ctor
331     nullptr, // t_dtor
332     ctor,
333     dtor,
334 };
335 
336 #ifdef BUILDING_SO
337 SO_PUBLIC const BaseApi* snort_plugins[] =
338 #else
339 const BaseApi* cd_icmp4_ip[] =
340 #endif
341 {
342     &icmp4_ip_api.base,
343     nullptr
344 };
345 
346