1 //--------------------------------------------------------------------------
2 // Copyright (C) 2016-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 
19 // ips_dce_iface.cc author Maya Dagon <mdagon@cisco.com>
20 // based on work by Todd Wease
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <cerrno>
27 
28 #include "detection/pattern_match_data.h"
29 #include "framework/module.h"
30 #include "framework/ips_option.h"
31 #include "framework/range.h"
32 #include "hash/hash_key_operations.h"
33 #include "profiler/profiler.h"
34 #include "target_based/snort_protocols.h"
35 #include "utils/util.h"
36 
37 #include "dce_common.h"
38 
39 using namespace snort;
40 
41 //-------------------------------------------------------------------------
42 // dcerpc2 interface rule options
43 //-------------------------------------------------------------------------
44 
45 #define DCE2_RTOKEN__ARG_SEP      " \t"   /* Rule option argument separator */
46 #define DCE2_RTOKEN__IFACE_SEP    "-"     /* Rule option interface separator */
47 
48 #define DCE2_IFACE__LEN  36  /* counting the dashes */
49 #define DCE2_IFACE__TIME_LOW_LEN    8
50 #define DCE2_IFACE__TIME_MID_LEN    4
51 #define DCE2_IFACE__TIME_HIGH_LEN   4
52 #define DCE2_IFACE__CLOCK_SEQ_LEN   4
53 #define DCE2_IFACE__NODE_LEN       12
54 
55 #define s_name "dce_iface"
56 #define s_help \
57     "detection option to check dcerpc interface"
58 
59 static THREAD_LOCAL ProfileStats dce2_iface_perf_stats;
60 
DCE2_ParseIface(char * token,Uuid * uuid)61 static bool DCE2_ParseIface(char* token, Uuid* uuid)
62 {
63     char* iface, * ifaceptr = nullptr;
64     char* if_hex, * if_hexptr = nullptr;
65     int num_pieces = 0;
66 
67     /* Has to be a uuid in string format, e.g 4b324fc8-1670-01d3-1278-5a47bf6ee188
68      * Check the length */
69     if (strlen(token) != DCE2_IFACE__LEN)
70         return false;
71 
72     /* Detach token */
73     iface = strtok_r(token, DCE2_RTOKEN__ARG_SEP, &ifaceptr);
74     if (iface == nullptr)
75         return false;
76 
77     /* Cut into pieces separated by '-' */
78     if_hex = strtok_r(iface, DCE2_RTOKEN__IFACE_SEP, &if_hexptr);
79     if (if_hex == nullptr)
80         return false;
81 
82     do
83     {
84         char* endptr;
85 
86         switch (num_pieces)
87         {
88         case 0:
89         {
90             unsigned long int time_low;
91 
92             if (strlen(if_hex) != DCE2_IFACE__TIME_LOW_LEN)
93                 return false;
94 
95             time_low = strtoul(if_hex, &endptr, 16);
96             if ((errno == ERANGE) || (*endptr != '\0'))
97                 return false;
98 
99             uuid->time_low = (uint32_t)time_low;
100         }
101 
102         break;
103 
104         case 1:
105         {
106             unsigned long int time_mid;
107 
108             if (strlen(if_hex) != DCE2_IFACE__TIME_MID_LEN)
109                 return false;
110 
111             time_mid = strtoul(if_hex, &endptr, 16);
112             if ((errno == ERANGE) || (*endptr != '\0'))
113                 return false;
114 
115             /* Length check ensures 16 bit value */
116             uuid->time_mid = (uint16_t)time_mid;
117         }
118 
119         break;
120 
121         case 2:
122         {
123             unsigned long int time_high;
124 
125             if (strlen(if_hex) != DCE2_IFACE__TIME_HIGH_LEN)
126                 return false;
127 
128             time_high = strtoul(if_hex, &endptr, 16);
129             if ((errno == ERANGE) || (*endptr != '\0'))
130                 return false;
131 
132             /* Length check ensures 16 bit value */
133             uuid->time_high_and_version = (uint16_t)time_high;
134         }
135 
136         break;
137 
138         case 3:
139         {
140             unsigned long int clock_seq_and_reserved, clock_seq_low;
141 
142             if (strlen(if_hex) != DCE2_IFACE__CLOCK_SEQ_LEN)
143                 return false;
144 
145             /* Work backwards */
146             clock_seq_low = strtoul(&if_hex[2], &endptr, 16);
147             if ((errno == ERANGE) || (*endptr != '\0'))
148                 return false;
149 
150             uuid->clock_seq_low = (uint8_t)clock_seq_low;
151 
152             /* Set third byte to null so we can _dpd.SnortStrtoul the first part */
153             if_hex[2] = '\x00';
154 
155             clock_seq_and_reserved = strtoul(if_hex, &endptr, 16);
156             if ((errno == ERANGE) || (*endptr != '\0'))
157                 return false;
158 
159             uuid->clock_seq_and_reserved = (uint8_t)clock_seq_and_reserved;
160         }
161 
162         break;
163 
164         case 4:
165         {
166             int i, j;
167 
168             if (strlen(if_hex) != DCE2_IFACE__NODE_LEN)
169                 return false;
170 
171             /* Walk back a byte at a time - 2 hex digits */
172             for (i = DCE2_IFACE__NODE_LEN - 2, j = sizeof(uuid->node) - 1;
173                 (i >= 0) && (j >= 0);
174                 i -= 2, j--)
175             {
176                 /* Only giving strtoul 1 byte */
177                 uuid->node[j] = (uint8_t)strtoul(&if_hex[i], &endptr, 16);
178                 if ((errno == ERANGE) || (*endptr != '\0'))
179                     return false;
180                 if_hex[i] = '\0';
181             }
182         }
183         break;
184 
185         default:
186             break;
187         }
188 
189         num_pieces++;
190     }
191     while ((if_hex = strtok_r(nullptr, DCE2_RTOKEN__IFACE_SEP, &if_hexptr)) != nullptr);
192 
193     if (num_pieces != 5)
194         return false;
195 
196     /* Check for more arguments */
197     iface = strtok_r(nullptr, DCE2_RTOKEN__ARG_SEP, &ifaceptr);
198     if (iface != nullptr)
199         return false;
200 
201     return true;
202 }
203 
204 class Dce2IfaceOption : public IpsOption
205 {
206 public:
Dce2IfaceOption(const RangeCheck & iface_version,bool iface_any_frag,const Uuid & iface_uuid)207     Dce2IfaceOption(const RangeCheck& iface_version, bool iface_any_frag, const Uuid& iface_uuid) :
208         IpsOption(s_name), version(iface_version), any_frag(iface_any_frag), uuid(iface_uuid),
209         pmd(), alt_pmd()
210     {
211         pmd.set_literal();
212         alt_pmd.set_literal();
213     }
214 
215     uint32_t hash() const override;
216     bool operator==(const IpsOption&) const override;
217     EvalStatus eval(Cursor&, Packet*) override;
218     PatternMatchData* get_pattern(SnortProtocolId snort_protocol_id, RuleDirection direction) override;
219     PatternMatchData* get_alternate_pattern() override;
220     ~Dce2IfaceOption() override;
221 
222 private:
223     const RangeCheck version;
224     const bool any_frag;
225     const Uuid uuid;
226     PatternMatchData pmd;
227     PatternMatchData alt_pmd;
228 };
229 
~Dce2IfaceOption()230 Dce2IfaceOption::~Dce2IfaceOption()
231 {
232     if ( pmd.pattern_buf)
233     {
234         snort_free(const_cast<char*>(pmd.pattern_buf));
235     }
236     if ( alt_pmd.pattern_buf)
237     {
238         snort_free(const_cast<char*>(alt_pmd.pattern_buf));
239     }
240 }
241 
make_pattern_buffer(const Uuid & uuid,DceRpcBoFlag type)242 static char* make_pattern_buffer( const Uuid &uuid, DceRpcBoFlag type )
243 {
244     int index = 0;
245     char* pattern_buf = (char*)snort_alloc(sizeof(Uuid));
246 
247     uint32_t time32 = DceRpcNtohl(&uuid.time_low, type);
248     memcpy(&pattern_buf[index], &time32, sizeof(uint32_t));
249     index += sizeof(uint32_t);
250 
251     uint16_t time16 = DceRpcNtohs(&uuid.time_mid, type);
252     memcpy(&pattern_buf[index], &time16, sizeof(uint16_t));
253     index += sizeof(uint16_t);
254 
255     time16 = DceRpcNtohs(&uuid.time_high_and_version, type);
256     memcpy(&pattern_buf[index], &time16, sizeof(uint16_t));
257     index += sizeof(uint16_t);
258 
259     pattern_buf[index] = uuid.clock_seq_and_reserved;
260     index += sizeof(uint8_t);
261 
262     pattern_buf[index] = uuid.clock_seq_low;
263     index += sizeof(uint8_t);
264 
265     memcpy(&pattern_buf[index], uuid.node, 6);
266 
267     return pattern_buf;
268 }
269 
get_pattern(SnortProtocolId snort_protocol_id,RuleDirection direction)270 PatternMatchData* Dce2IfaceOption::get_pattern(SnortProtocolId snort_protocol_id, RuleDirection direction)
271 {
272     if (pmd.pattern_buf)
273     {
274         return &pmd;
275     }
276 
277     if (snort_protocol_id == SNORT_PROTO_TCP)
278     {
279         const char client_fp[] = "\x05\x00\x00";
280         const char server_fp[] = "\x05\x00\x02";
281         const char no_dir_fp[] = "\x05\x00";
282 
283         switch (direction)
284         {
285         case RULE_FROM_CLIENT:
286             pmd.pattern_size = 3;
287             pmd.pattern_buf = (char*)snort_alloc(pmd.pattern_size);
288             memcpy((void*)pmd.pattern_buf, client_fp, pmd.pattern_size);
289             break;
290 
291         case RULE_FROM_SERVER:
292             pmd.pattern_size = 3;
293             pmd.pattern_buf = (char*)snort_alloc(pmd.pattern_size);
294             memcpy((void*)pmd.pattern_buf, server_fp, pmd.pattern_size);
295             break;
296 
297         default:
298             pmd.pattern_size = 2;
299             pmd.pattern_buf = (char*)snort_alloc(pmd.pattern_size);
300             memcpy((void*)pmd.pattern_buf, no_dir_fp, pmd.pattern_size);
301             break;
302         }
303         return &pmd;
304     }
305     else if (snort_protocol_id == SNORT_PROTO_UDP)
306     {
307         pmd.pattern_buf = make_pattern_buffer( uuid, DCERPC_BO_FLAG__LITTLE_ENDIAN );
308         pmd.pattern_size = sizeof(Uuid);
309         alt_pmd.pattern_buf = make_pattern_buffer( uuid, DCERPC_BO_FLAG__BIG_ENDIAN );
310         alt_pmd.pattern_size = sizeof(Uuid);
311 
312         return &pmd;
313     }
314 
315     return nullptr;
316 }
317 
get_alternate_pattern()318 PatternMatchData* Dce2IfaceOption::get_alternate_pattern()
319 {
320     if (alt_pmd.pattern_buf)
321     {
322         return &alt_pmd;
323     }
324 
325     return nullptr;
326 }
327 
hash() const328 uint32_t Dce2IfaceOption::hash() const
329 {
330     uint32_t a, b, c;
331 
332     a = uuid.time_low;
333     b = (uuid.time_mid << 16) | (uuid.time_high_and_version);
334     c = (uuid.clock_seq_and_reserved << 24) |
335         (uuid.clock_seq_low << 16) |
336         (uuid.node[0] << 8) |
337         (uuid.node[1]);
338 
339     mix(a, b, c);
340 
341     a += (uuid.node[2] << 24) |
342         (uuid.node[3] << 16) |
343         (uuid.node[4] << 8) |
344         (uuid.node[5]);
345     b += version.hash();
346     c += any_frag;
347 
348     mix(a, b, c);
349 
350     c += IpsOption::hash();
351 
352     finalize(a, b, c);
353 
354     return c;
355 }
356 
operator ==(const IpsOption & ips) const357 bool Dce2IfaceOption::operator==(const IpsOption& ips) const
358 {
359     // FIXIT-L
360     // Fast pattern is calculated only after the entire rule is parsed.
361     // The rule option can be mistaken as a duplicate because we don't take the fast pattern into
362     // account. Instead of comparing values, make sure it is the same object.
363     return this == &ips;
364 }
365 
eval(Cursor &,Packet * p)366 IpsOption::EvalStatus Dce2IfaceOption::eval(Cursor&, Packet* p)
367 {
368     RuleProfile profile(dce2_iface_perf_stats);
369 
370     if (p->dsize == 0)
371     {
372         return NO_MATCH;
373     }
374 
375     if (DceContextData::is_noinspect(p))
376     {
377         return NO_MATCH;
378     }
379 
380     DCE2_Roptions* ropts = DceContextData::get_current_ropts(p);
381 
382     if ( !ropts )
383         return NO_MATCH;
384 
385     if (ropts->first_frag == DCE2_SENTINEL)
386     {
387         return NO_MATCH;
388     }
389 
390     if (!any_frag && !ropts->first_frag)
391     {
392         return NO_MATCH;
393     }
394 
395     if (DCE2_UuidCompare((void*)&ropts->iface, &uuid) != 0)
396     {
397         return NO_MATCH;
398     }
399 
400     if (version.is_set())
401     {
402         if (p->has_tcp_data())
403         {
404             if (!version.eval(ropts->iface_vers_maj))
405             {
406                 return NO_MATCH;
407             }
408         }
409         else
410         {
411             if (!version.eval(ropts->iface_vers))
412             {
413                 return NO_MATCH;
414             }
415         }
416     }
417 
418     return MATCH;
419 }
420 
421 //-------------------------------------------------------------------------
422 // dce2_iface module
423 //-------------------------------------------------------------------------
424 
425 #define RANGE "0:"
426 
427 static const Parameter s_params[] =
428 {
429     { "uuid", Parameter::PT_STRING, nullptr, nullptr,
430       "match given dcerpc uuid" },
431     { "version",Parameter::PT_INTERVAL, RANGE, nullptr,
432       "interface version" },
433     { "any_frag", Parameter::PT_IMPLIED, nullptr, nullptr,
434       "match on any fragment" },
435     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
436 };
437 
438 class Dce2IfaceModule : public Module
439 {
440 public:
Dce2IfaceModule()441     Dce2IfaceModule() : Module(s_name, s_help, s_params) { }
442 
443     bool begin(const char*, int, SnortConfig*) override;
444     bool set(const char*, Value&, SnortConfig*) override;
445     ProfileStats* get_profile() const override;
446 
get_usage() const447     Usage get_usage() const override
448     { return DETECT; }
449 
450 public:
451     RangeCheck version;
452     bool any_frag;
453     Uuid uuid;
454 };
455 
begin(const char *,int,SnortConfig *)456 bool Dce2IfaceModule::begin(const char*, int, SnortConfig*)
457 {
458     version.init();
459     any_frag = false;
460     return true;
461 }
462 
set(const char *,Value & v,SnortConfig *)463 bool Dce2IfaceModule::set(const char*, Value& v, SnortConfig*)
464 {
465     if ( v.is("version") )
466         return version.validate(v.get_string(), RANGE);
467     else if ( v.is("any_frag") )
468         any_frag = true;
469     else if ( v.is("uuid") )
470     {
471         char* token = const_cast<char*>(v.get_string());
472         token = DCE2_PruneWhiteSpace(token);
473         return DCE2_ParseIface(token, &uuid);
474     }
475     return true;
476 }
477 
get_profile() const478 ProfileStats* Dce2IfaceModule::get_profile() const
479 {
480     return &dce2_iface_perf_stats;
481 }
482 
483 //-------------------------------------------------------------------------
484 // dce2_iface api
485 //-------------------------------------------------------------------------
486 
dce2_iface_mod_ctor()487 static Module* dce2_iface_mod_ctor()
488 {
489     return new Dce2IfaceModule;
490 }
491 
dce2_iface_mod_dtor(Module * m)492 static void dce2_iface_mod_dtor(Module* m)
493 {
494     delete m;
495 }
496 
dce2_iface_ctor(Module * p,OptTreeNode *)497 static IpsOption* dce2_iface_ctor(Module* p, OptTreeNode*)
498 {
499     Dce2IfaceModule* m = (Dce2IfaceModule*)p;
500     return new Dce2IfaceOption(m->version, m->any_frag, m->uuid);
501 }
502 
dce2_iface_dtor(IpsOption * p)503 static void dce2_iface_dtor(IpsOption* p)
504 {
505     delete p;
506 }
507 
508 static const IpsApi dce2_iface_api =
509 {
510     {
511         PT_IPS_OPTION,
512         sizeof(IpsApi),
513         IPSAPI_VERSION,
514         0,
515         API_RESERVED,
516         API_OPTIONS,
517         s_name,
518         s_help,
519         dce2_iface_mod_ctor,
520         dce2_iface_mod_dtor
521     },
522     OPT_TYPE_DETECTION,
523     0, PROTO_BIT__TCP | PROTO_BIT__UDP,
524     nullptr,
525     nullptr,
526     nullptr,
527     nullptr,
528     dce2_iface_ctor,
529     dce2_iface_dtor,
530     nullptr
531 };
532 
533 //-------------------------------------------------------------------------
534 // plugin
535 //-------------------------------------------------------------------------
536 
537 // added to snort_plugins in dce2.cc
538 const BaseApi* ips_dce_iface = &dce2_iface_api.base;
539 
540