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