1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2002-2013 Sourcefire, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License Version 2 as published
7 // by the Free Software Foundation.  You may not use, modify or distribute
8 // this program under any other version of the GNU General Public License.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 //--------------------------------------------------------------------------
19 // cd_gre.cc author Josh Rosenbaum <jrosenba@cisco.com>
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "codecs/codec_module.h"
26 #include "framework/codec.h"
27 #include "log/messages.h"
28 #include "log/text_log.h"
29 #include "main/snort_config.h"
30 #include "protocols/gre.h"
31 
32 #include "checksum.h"
33 
34 #ifdef UNIT_TEST
35 #include "catch/snort_catch.h"
36 #endif
37 
38 using namespace snort;
39 
40 #define CD_GRE_NAME "gre"
41 #define CD_GRE_HELP "support for generic routing encapsulation"
42 
43 namespace
44 {
45 static const RuleMap gre_rules[] =
46 {
47     { DECODE_GRE_DGRAM_LT_GREHDR, "GRE header length > payload length" },
48     { DECODE_GRE_MULTIPLE_ENCAPSULATION, "multiple encapsulations in packet" },
49     { DECODE_GRE_INVALID_VERSION, "invalid GRE version" },
50     { DECODE_GRE_INVALID_HEADER, "invalid GRE header" },
51     { DECODE_GRE_V1_INVALID_HEADER, "invalid GRE v.1 PPTP header" },
52     { DECODE_GRE_TRANS_DGRAM_LT_TRANSHDR, "GRE trans header length > payload length" },
53     { 0, nullptr }
54 };
55 
56 class GreModule : public BaseCodecModule
57 {
58 public:
GreModule()59     GreModule() : BaseCodecModule(CD_GRE_NAME, CD_GRE_HELP) { }
60 
get_rules() const61     const RuleMap* get_rules() const override
62     { return gre_rules; }
63 };
64 
65 class GreCodec : public Codec
66 {
67 public:
GreCodec()68     GreCodec() : Codec(CD_GRE_NAME) { }
69 
70     void get_protocol_ids(std::vector<ProtocolId>& v) override;
71     bool decode(const RawData&, CodecData&, DecodeData&) override;
72     void log(TextLog* const, const uint8_t* pkt, const uint16_t len) override;
73     bool encode(const uint8_t* const raw_in, const uint16_t raw_len,
74         EncState&, Buffer&, Flow*) override;
75     void update(const ip::IpApi& api, const EncodeFlags flags, uint8_t* raw_pkt,
76         uint16_t lyr_len, uint32_t& updated_len) override;
77 };
78 
79 static const uint32_t GRE_HEADER_LEN = 4;
80 static const uint32_t GRE_CHKSUM_LEN = 2;
81 static const uint32_t GRE_OFFSET_LEN = 2;
82 static const uint32_t GRE_KEY_LEN = 4;
83 static const uint32_t GRE_SEQ_LEN = 4;
84 static const uint32_t GRE_SRE_HEADER_LEN = 4;
85 
86 /* GRE version 1 used with PPTP
87    static const uint32_t GRE_V1_HEADER_LEN == GRE_HEADER_LEN + GRE_KEY_LEN; */
88 static const uint32_t GRE_V1_ACK_LEN = 4;
89 
90 #define GRE_V1_FLAGS(x)   ((x)->version & 0x78)
91 #define GRE_V1_ACK(x)     ((x)->version & 0x80)
92 #define GRE_CHKSUM(x)  ((x)->flags & 0x80)
93 #define GRE_ROUTE(x)   ((x)->flags & 0x40)
94 #define GRE_KEY(x)     ((x)->flags & 0x20)
95 #define GRE_SEQ(x)     ((x)->flags & 0x10)
96 #define GRE_SSR(x)     ((x)->flags & 0x08)
97 #define GRE_RECUR(x)   ((x)->flags & 0x07)
98 #define GRE_FLAGS(x)   ((x)->version & 0xF8)
99 } // anonymous namespace
100 
get_protocol_ids(std::vector<ProtocolId> & v)101 void GreCodec::get_protocol_ids(std::vector<ProtocolId>& v)
102 { v.emplace_back(ProtocolId::GRE); }
103 
104 /*
105  * see RFCs 1701, 2784 and 2637
106  */
107 
update(const ip::IpApi & api,const EncodeFlags,uint8_t * raw_pkt,uint16_t lyr_len,uint32_t & updated_len)108 void GreCodec::update(const ip::IpApi& api, const EncodeFlags /*flags*/, uint8_t* raw_pkt,
109     uint16_t lyr_len, uint32_t& updated_len)
110 {
111     UNUSED(api);
112     gre::GREHdr* const greh = reinterpret_cast<gre::GREHdr*>(raw_pkt);
113 
114     updated_len += lyr_len;
115 
116     if (GRE_CHKSUM(greh))
117     {
118         assert(lyr_len >= 6);
119         // Checksum field is zero for computing checksum
120         *(uint16_t*)(raw_pkt + 4) = 0;
121         *(uint16_t*)(raw_pkt + 4) = checksum::cksum_add((uint16_t*)raw_pkt, updated_len);
122     }
123 }
124 
encode(const uint8_t * const raw_in,const uint16_t raw_len,EncState & enc,Buffer & buf,Flow *)125 bool GreCodec::encode(const uint8_t* const raw_in, const uint16_t raw_len,
126     EncState& enc, Buffer& buf, Flow*)
127 {
128     if (!buf.allocate(raw_len))
129         return false;
130 
131     gre::GREHdr* const greh_out = reinterpret_cast<gre::GREHdr*>(buf.data());
132     memcpy(buf.data(), raw_in, raw_len);
133     enc.next_proto = IpProtocol::GRE;
134     enc.next_ethertype = greh_out->proto();
135 
136     if (GRE_SEQ(greh_out))
137     {
138         uint16_t len = 4; // Flags, version and protocol
139 
140         if (GRE_CHKSUM(greh_out))
141             len += 4;
142 
143         if (GRE_KEY(greh_out))
144             len += 4;
145 
146         *(uint32_t*)(buf.data() + len) += ntohl(1);
147     }
148 
149     if (GRE_CHKSUM(greh_out))
150     {
151         assert(raw_len >= 6);
152         // Checksum field is zero for computing checksum
153         *(uint16_t*)(buf.data() + 4) = 0;
154         *(uint16_t*)(buf.data() + 4) = checksum::cksum_add((uint16_t*)buf.data(),
155             buf.size());
156     }
157 
158     return true;
159 }
160 
decode(const RawData & raw,CodecData & codec,DecodeData &)161 bool GreCodec::decode(const RawData& raw, CodecData& codec, DecodeData&)
162 {
163     if (raw.len < GRE_HEADER_LEN)
164     {
165         codec_event(codec, DECODE_GRE_DGRAM_LT_GREHDR);
166         return false;
167     }
168 
169     /* Note: Since GRE doesn't have a field to indicate header length and
170      * can contain a few options, we need to walk through the header to
171      * figure out the length
172      */
173 
174     const gre::GREHdr* const greh = reinterpret_cast<const gre::GREHdr*>(raw.data);
175     uint16_t len = GRE_HEADER_LEN;
176 
177     switch (greh->get_version())
178     {
179     case 0x00:
180         /* these must not be set */
181         if (GRE_RECUR(greh) || GRE_FLAGS(greh))
182         {
183             codec_event(codec, DECODE_GRE_INVALID_HEADER);
184             return false;
185         }
186 
187         if (GRE_CHKSUM(greh) || GRE_ROUTE(greh))
188             len += GRE_CHKSUM_LEN + GRE_OFFSET_LEN;
189 
190         if (GRE_KEY(greh))
191             len += GRE_KEY_LEN;
192 
193         if (GRE_SEQ(greh))
194             len += GRE_SEQ_LEN;
195 
196         /* if this flag is set, we need to walk through all of the
197          * Source Route Entries */
198         if (GRE_ROUTE(greh))
199         {
200             const uint8_t* sre_ptr = raw.data + len;
201 
202             while (true)
203             {
204                 len += GRE_SRE_HEADER_LEN;
205 
206                 if (len > raw.len)
207                     break;
208 
209                 uint16_t sre_addrfamily = ntohs(*((const uint16_t*)sre_ptr));
210 
211                 sre_ptr += sizeof(sre_addrfamily);
212                 sre_ptr += sizeof(uint8_t);  // sre_offset
213 
214                 uint8_t sre_length = *((const uint8_t*)sre_ptr);
215                 sre_ptr += sizeof(sre_length);
216 
217                 if ((sre_addrfamily == 0) && (sre_length == 0))
218                     break;
219 
220                 len += sre_length;
221                 sre_ptr += sre_length;
222             }
223         }
224 
225         break;
226 
227     /* PPTP */
228     case 0x01:
229         /* these flags should never be present */
230         if (GRE_CHKSUM(greh) || GRE_ROUTE(greh) || GRE_SSR(greh) ||
231             GRE_RECUR(greh) || GRE_V1_FLAGS(greh))
232         {
233             codec_event(codec, DECODE_GRE_V1_INVALID_HEADER);
234             return false;
235         }
236 
237         /* protocol must be 0x880B - PPP */
238         if (greh->proto() != ProtocolId::ETHERTYPE_PPP)
239         {
240             codec_event(codec, DECODE_GRE_V1_INVALID_HEADER);
241             return false;
242         }
243 
244         /* this flag should always be present */
245         if (!(GRE_KEY(greh)))
246         {
247             codec_event(codec, DECODE_GRE_V1_INVALID_HEADER);
248             return false;
249         }
250 
251         len += GRE_KEY_LEN;
252 
253         if (GRE_SEQ(greh))
254             len += GRE_SEQ_LEN;
255 
256         if (GRE_V1_ACK(greh))
257             len += GRE_V1_ACK_LEN;
258 
259         break;
260 
261     default:
262         codec_event(codec, DECODE_GRE_INVALID_VERSION);
263         return false;
264     }
265 
266     if (len > raw.len)
267     {
268         codec_event(codec, DECODE_GRE_DGRAM_LT_GREHDR);
269         return false;
270     }
271 
272     if (codec.conf->tunnel_bypass_enabled(TUNNEL_GRE))
273         codec.tunnel_bypass = true;
274 
275     codec.lyr_len = len;
276     codec.next_prot_id = greh->proto();
277     codec.codec_flags |= CODEC_NON_IP_TUNNEL | CODEC_ETHER_NEXT;
278     return true;
279 }
280 
log(TextLog * const text_log,const uint8_t * raw_pkt,const uint16_t)281 void GreCodec::log(TextLog* const text_log, const uint8_t* raw_pkt,
282     const uint16_t /*lyr_len*/)
283 {
284     const gre::GREHdr* greh = reinterpret_cast<const gre::GREHdr*>(raw_pkt);
285 
286     TextLog_Print(text_log, "version:%u flags:0x%02X ethertype:(0x%04X)",
287         greh->get_version(), greh->flags,
288         static_cast<uint16_t>(greh->proto()));
289 }
290 
291 //-------------------------------------------------------------------------
292 // api
293 //-------------------------------------------------------------------------
294 
mod_ctor()295 static Module* mod_ctor()
296 { return new GreModule; }
297 
mod_dtor(Module * m)298 static void mod_dtor(Module* m)
299 { delete m; }
300 
ctor(Module *)301 static Codec* ctor(Module*)
302 { return new GreCodec(); }
303 
dtor(Codec * cd)304 static void dtor(Codec* cd)
305 { delete cd; }
306 
307 static const CodecApi gre_api =
308 {
309     {
310         PT_CODEC,
311         sizeof(CodecApi),
312         CDAPI_VERSION,
313         0,
314         API_RESERVED,
315         API_OPTIONS,
316         CD_GRE_NAME,
317         CD_GRE_HELP,
318         mod_ctor,
319         mod_dtor,
320     },
321     nullptr, // pinit
322     nullptr, // pterm
323     nullptr, // tinit
324     nullptr, // tterm
325     ctor, // ctor
326     dtor, // dtor
327 };
328 
329 #ifdef BUILDING_SO
330 SO_PUBLIC const BaseApi* snort_plugins[] =
331 #else
332 const BaseApi* cd_gre[] =
333 #endif
334 {
335     &gre_api.base,
336     nullptr
337 };
338 
339 //--------------------------------------------------------------------------
340 // unit tests
341 //--------------------------------------------------------------------------
342 
343 #ifdef UNIT_TEST
344 TEST_CASE ("Validate error check for raw_len greater than GRE_HEADER_LEN", "[cd_gre]")
345 {
346     GreCodec grecodec;
347     const uint8_t raw_in = 0;
348     uint8_t raw_len = GRE_HEADER_LEN + 1;
349     ip::IpApi ip_api;
350     EncState enc(ip_api, ENC_FLAG_VAL, IpProtocol::GRE, 0, 0);
351     uint16_t size = 1;
352     uint8_t t = 0;
353     Buffer buf(&t, size);
354     Flow *flow = nullptr;
355 
356     CHECK (grecodec.encode(&raw_in,raw_len,enc,buf,flow) == false);
357 }
358 #endif
359