Branch data Line data Source code
1 : : // $Id: DPM.cc,v 1.1.4.14 2006/06/01 17:18:10 sommer Exp $
2 : :
3 : : #include "DPM.h"
4 : : #include "PIA.h"
5 : : #include "Hash.h"
6 : : #include "ICMP.h"
7 : : #include "UDP.h"
8 : : #include "TCP.h"
9 : : #include "Val.h"
10 : : #include "BackDoor.h"
11 : : #include "InterConn.h"
12 : : #include "SteppingStone.h"
13 : :
14 : :
15 : : ExpectedConn::ExpectedConn(const uint32* _orig, const uint32* _resp,
16 : 0 : uint16 _resp_p, uint16 _proto)
17 : : {
18 : : if ( orig )
19 : 0 : copy_addr(_orig, orig);
20 : : else
21 : : {
22 : : for ( int i = 0; i < NUM_ADDR_WORDS; ++i )
23 : : orig[i] = 0;
24 : : }
25 : :
26 : 0 : copy_addr(_resp, resp);
27 : :
28 : 0 : resp_p = _resp_p;
29 : 0 : proto = _proto;
30 : 0 : }
31 : :
32 : : ExpectedConn::ExpectedConn(uint32 _orig, uint32 _resp,
33 : 0 : uint16 _resp_p, uint16 _proto)
34 : : {
35 : : #ifdef BROv6
36 : : // Use the IPv4-within-IPv6 convention, as this is what's
37 : : // needed when we mix uint32's (like in this construction)
38 : : // with addr_type's (for example, when looking up expected
39 : : // connections).
40 : :
41 : : orig[0] = orig[1] = orig[2] = 0;
42 : : resp[0] = resp[1] = resp[2] = 0;
43 : : orig[3] = _orig;
44 : : resp[3] = _resp;
45 : : #else
46 : 0 : orig[0] = _orig;
47 : 0 : resp[0] = _resp;
48 : : #endif
49 : 0 : resp_p = _resp_p;
50 : 0 : proto = _proto;
51 : 0 : }
52 : :
53 : 0 : ExpectedConn::ExpectedConn(const ExpectedConn& c)
54 : : {
55 : 0 : copy_addr(c.orig, orig);
56 : 0 : copy_addr(c.resp, resp);
57 : 0 : resp_p = c.resp_p;
58 : 0 : proto = c.proto;
59 : 0 : }
60 : :
61 : :
62 : 3 : DPM::DPM()
63 : 3 : : expected_conns_queue(AssignedAnalyzer::compare)
64 : : {
65 : 3 : }
66 : :
67 : 1 : DPM::~DPM()
68 : : {
69 [ + - ][ # # ]: 1 : delete [] active_analyzers;
70 : 1 : }
71 : :
72 : 3 : void DPM::PreScriptInit()
73 : : {
74 [ + + ]: 177 : for ( int i = 1; i < int(AnalyzerTag::LastAnalyzer); i++ )
75 : : {
76 : : // Create IDs ANALYZER_*.
77 : : ID* id = install_ID(fmt("ANALYZER_%s",
78 : : Analyzer::analyzer_configs[i].name),
79 : 174 : GLOBAL_MODULE_NAME, true, false);
80 [ - + ]: 174 : assert(id);
81 : 174 : id->SetVal(new Val(i, TYPE_COUNT));
82 : 174 : id->SetType(id->ID_Val()->Type()->Ref());
83 : : }
84 : 3 : }
85 : :
86 : 1 : void DPM::PostScriptInit()
87 : : {
88 : 1 : active_analyzers = new bool[int(AnalyzerTag::LastAnalyzer)];
89 : :
90 [ + + ]: 59 : for ( int i = 1; i < int(AnalyzerTag::LastAnalyzer); i++ )
91 : : {
92 [ + + ]: 58 : if ( ! Analyzer::analyzer_configs[i].available )
93 : 16 : continue;
94 : :
95 : 42 : active_analyzers[i] = Analyzer::analyzer_configs[i].available();
96 [ + + ]: 42 : if ( active_analyzers[i] )
97 : 11 : AddConfig(Analyzer::analyzer_configs[i]);
98 : : }
99 : 1 : }
100 : :
101 : 11 : void DPM::AddConfig(const Analyzer::Config& cfg)
102 : : {
103 : : #ifdef USE_PERFTOOLS
104 : : HeapLeakChecker::Disabler disabler;
105 : : #endif
106 : :
107 : 11 : Val* index = new Val(cfg.tag, TYPE_COUNT);
108 : 11 : Val* v = dpd_config->Lookup(index);
109 : :
110 : : #ifdef DEBUG
111 : 11 : ODesc desc;
112 : : #endif
113 [ + + ]: 11 : if ( v )
114 : : {
115 : 5 : RecordVal* cfg_record = v->AsRecordVal();
116 : 5 : Val* ports = cfg_record->Lookup(0);
117 : :
118 [ + - ]: 5 : if ( ports )
119 : : {
120 : 5 : ListVal* plist = ports->AsTableVal()->ConvertToPureList();
121 : :
122 [ + + ]: 18 : for ( int i = 0; i< plist->Length(); ++i )
123 : : {
124 : 13 : PortVal* port = plist->Index(i)->AsPortVal();
125 : :
126 : : analyzer_map* ports =
127 [ + - ]: 13 : port->IsTCP() ? &tcp_ports : &udp_ports;
128 : :
129 : : analyzer_map::iterator j =
130 : 13 : ports->find(port->Port());
131 : :
132 [ + - ]: 13 : if ( j == ports->end() )
133 : : {
134 : 13 : tag_list* analyzers = new tag_list;
135 : 13 : analyzers->push_back(cfg.tag);
136 : 13 : ports->insert(analyzer_map::value_type(port->Port(), analyzers));
137 : : }
138 : : else
139 : 0 : j->second->push_back(cfg.tag);
140 : :
141 : : #ifdef DEBUG
142 : 13 : port->Describe(&desc);
143 : 13 : desc.SP();
144 : : #endif
145 : : }
146 : : }
147 : : }
148 : :
149 : 11 : DBG_LOG(DBG_DPD, "%s analyzer active on port(s) %s", cfg.name, desc.Description());
150 : :
151 : 11 : Unref(index);
152 : 11 : }
153 : :
154 : 1622 : AnalyzerTag::Tag DPM::GetExpected(int proto, const Connection* conn)
155 : : {
156 [ + - ]: 1622 : if ( ! expected_conns.Length() )
157 : 1622 : return AnalyzerTag::Error;
158 : :
159 : : ExpectedConn c(conn->OrigAddr(), conn->RespAddr(),
160 : 0 : ntohs(conn->RespPort()), proto);
161 : :
162 : : // Can't use sizeof(c) due to potential alignment issues.
163 : : // FIXME: I guess this is still not portable ...
164 : : HashKey key(&c, sizeof(c.orig) + sizeof(c.resp) +
165 : 0 : sizeof(c.resp_p) + sizeof(c.proto));
166 : :
167 : 0 : AssignedAnalyzer* a = expected_conns.Lookup(&key);
168 : :
169 [ # # ]: 0 : if ( ! a )
170 : : {
171 : : // Wildcard for originator.
172 [ # # ]: 0 : for ( int i = 0; i < NUM_ADDR_WORDS; ++i )
173 : 0 : c.orig[i] = 0;
174 : :
175 : : HashKey key(&c, sizeof(c.orig) + sizeof(c.resp) +
176 : 0 : sizeof(c.resp_p) + sizeof(c.proto));
177 : :
178 : 0 : a = expected_conns.Lookup(&key);
179 : : }
180 : :
181 [ # # ]: 0 : if ( ! a )
182 : 1622 : return AnalyzerTag::Error;
183 : :
184 : : // We don't delete it here. It will be expired eventually.
185 : 0 : return a->analyzer;
186 : : }
187 : :
188 : : bool DPM::BuildInitialAnalyzerTree(TransportProto proto, Connection* conn,
189 : 1627 : const u_char* data)
190 : : {
191 : 1627 : TCP_Analyzer* tcp = 0;
192 : 1627 : TransportLayerAnalyzer* root = 0;
193 : 1627 : AnalyzerTag::Tag expected = AnalyzerTag::Error;
194 : 1627 : analyzer_map* ports = 0;
195 : 1627 : PIA* pia = 0;
196 : 1627 : bool analyzed = false;
197 : :
198 [ + + + - ]: 1627 : switch ( proto ) {
199 : :
200 : : case TRANSPORT_TCP:
201 : 938 : root = tcp = new TCP_Analyzer(conn);
202 : 938 : pia = new PIA_TCP(conn);
203 : 938 : expected = GetExpected(proto, conn);
204 : 938 : ports = &tcp_ports;
205 : 938 : DBG_DPD(conn, "activated TCP analyzer");
206 : 938 : break;
207 : :
208 : : case TRANSPORT_UDP:
209 : 684 : root = new UDP_Analyzer(conn);
210 : 684 : pia = new PIA_UDP(conn);
211 : 684 : expected = GetExpected(proto, conn);
212 : 684 : ports = &udp_ports;
213 : 684 : DBG_DPD(conn, "activated UDP analyzer");
214 : 684 : break;
215 : :
216 : : case TRANSPORT_ICMP: {
217 : 5 : const struct icmp* icmpp = (const struct icmp *) data;
218 [ - + - - ]: 5 : switch ( icmpp->icmp_type ) {
219 : :
220 : : case ICMP_ECHO:
221 : : case ICMP_ECHOREPLY:
222 [ # # ]: 0 : if ( ICMP_Echo_Analyzer::Available() )
223 : : {
224 : 0 : root = new ICMP_Echo_Analyzer(conn);
225 : 0 : DBG_DPD(conn, "activated ICMP Echo analyzer");
226 : : }
227 : 0 : break;
228 : :
229 : : case ICMP_UNREACH:
230 [ - + ]: 5 : if ( ICMP_Unreachable_Analyzer::Available() )
231 : : {
232 : 0 : root = new ICMP_Unreachable_Analyzer(conn);
233 : 0 : DBG_DPD(conn, "activated ICMP Unreachable analyzer");
234 : : }
235 : 5 : break;
236 : :
237 : : case ICMP_TIMXCEED:
238 [ # # ]: 0 : if ( ICMP_TimeExceeded_Analyzer::Available() )
239 : : {
240 : 0 : root = new ICMP_TimeExceeded_Analyzer(conn);
241 : 0 : DBG_DPD(conn, "activated ICMP Time Exceeded analyzer");
242 : : }
243 : : break;
244 : : }
245 : :
246 [ + - ]: 5 : if ( ! root )
247 : 5 : root = new ICMP_Analyzer(conn);
248 : :
249 : 5 : analyzed = true;
250 : 5 : break;
251 : : }
252 : :
253 : : default:
254 : 0 : internal_error("unknown protocol");
255 : : }
256 : :
257 [ - + ]: 1627 : if ( ! root )
258 : : {
259 : 0 : DBG_DPD(conn, "cannot build analyzer tree");
260 : 0 : return false;
261 : : }
262 : :
263 : : // Any scheduled analyzer?
264 [ - + ]: 1627 : if ( expected != AnalyzerTag::Error )
265 : : {
266 : : Analyzer* analyzer =
267 : 0 : Analyzer::InstantiateAnalyzer(expected, conn);
268 : 0 : root->AddChildAnalyzer(analyzer, false);
269 : 0 : DBG_DPD_ARGS(conn, "activated %s analyzer as scheduled",
270 : : Analyzer::GetTagName(expected));
271 : :
272 : : // Hmm... Do we want *just* the expected analyzer, or all
273 : : // other potential analyzers as well? For now we only take
274 : : // the scheduled one.
275 : : }
276 : :
277 : : else
278 : : { // Let's see if it's a port we know.
279 [ + + ][ + - ]: 1627 : if ( ports && ! dpd_ignore_ports )
280 : : {
281 : : analyzer_map::const_iterator i =
282 : 1622 : ports->find(ntohs(conn->RespPort()));
283 : :
284 [ + + ]: 1622 : if ( i != ports->end() )
285 : : {
286 : 693 : tag_list* analyzers = i->second;
287 [ + + ]: 1386 : for ( tag_list::const_iterator j = analyzers->begin();
288 : : j != analyzers->end(); j++ )
289 : : {
290 : : Analyzer* analyzer =
291 : 693 : Analyzer::InstantiateAnalyzer(*j, conn);
292 : :
293 : 693 : root->AddChildAnalyzer(analyzer, false);
294 : 693 : DBG_DPD_ARGS(conn, "activated %s analyzer due to port %d", Analyzer::GetTagName(*j), conn->RespPort());
295 : : }
296 : : }
297 : : }
298 : : }
299 : :
300 [ + + ]: 1627 : if ( tcp )
301 : : {
302 : : // We have to decide whether to reassamble the stream.
303 : : // We turn it on right away if we already have an app-layer
304 : : // analyzer, reassemble_first_packets is true, or the user
305 : : // asks us to do so. In all other cases, reassembly may
306 : : // be turned on later by the TCP PIA.
307 : :
308 : : bool reass = root->GetChildren().size() ||
309 : : dpd_reassemble_first_packets ||
310 : : tcp_content_deliver_all_orig ||
311 [ + + ][ - + ]: 938 : tcp_content_deliver_all_resp;
[ # # ][ # # ]
312 : :
313 [ - + ][ # # ]: 938 : if ( tcp_contents && ! reass )
[ - + ]
314 : : {
315 : 0 : PortVal dport(ntohs(conn->RespPort()), TRANSPORT_TCP);
316 : : Val* result;
317 : :
318 [ # # ]: 0 : if ( ! reass )
319 : 0 : reass = tcp_content_delivery_ports_orig->Lookup(&dport);
320 : :
321 [ # # ]: 0 : if ( ! reass )
322 : 0 : reass = tcp_content_delivery_ports_resp->Lookup(&dport);
323 : : }
324 : :
325 [ + - ]: 938 : if ( reass )
326 : 938 : tcp->EnableReassembly();
327 : :
328 : : // Add a BackDoor analyzer if requested. This analyzer
329 : : // can handle both reassembled and non-reassembled input.
330 [ - + ]: 938 : if ( BackDoor_Analyzer::Available() )
331 : : {
332 : 0 : BackDoor_Analyzer* bd = new BackDoor_Analyzer(conn);
333 : 0 : tcp->AddChildAnalyzer(bd, false);
334 : : }
335 : :
336 : : // Add a InterConn analyzer if requested. This analyzer
337 : : // can handle both reassembled and non-reassembled input.
338 [ - + ]: 938 : if ( InterConn_Analyzer::Available() )
339 : : {
340 : 0 : InterConn_Analyzer* bd = new InterConn_Analyzer(conn);
341 : 0 : tcp->AddChildAnalyzer(bd, false);
342 : : }
343 : :
344 : : // Add a SteppingStone analyzer if requested. The port
345 : : // should really not be hardcoded here, but as it can
346 : : // handle non-reassembled data, it doesn't really fit into
347 : : // our general framing ... Better would be to turn it
348 : : // on *after* we discover we have interactive traffic.
349 : 938 : uint16 resp_port = ntohs(conn->RespPort());
350 [ - + # # ]: 938 : if ( SteppingStone_Analyzer::Available() &&
[ # # ][ # # ]
[ - + ]
351 : : (resp_port == 22 || resp_port == 23 || resp_port == 513) )
352 : : {
353 : 0 : AddrVal src(conn->OrigAddr());
354 [ # # ]: 0 : if ( ! stp_skip_src->Lookup(&src) )
355 : : {
356 : : SteppingStone_Analyzer* bd =
357 : 0 : new SteppingStone_Analyzer(conn);
358 : 0 : tcp->AddChildAnalyzer(bd, false);
359 : 0 : }
360 : : }
361 : :
362 : : // Add TCPStats analyzer. This needs to see packets so
363 : : // we cannot add it as a normal child.
364 [ - + ]: 938 : if ( TCPStats_Analyzer::Available() )
365 : 0 : tcp->AddChildPacketAnalyzer(new TCPStats_Analyzer(conn));
366 : : }
367 : :
368 [ + + ]: 1627 : if ( pia )
369 : 1622 : root->AddChildAnalyzer(pia->AsAnalyzer(), false);
370 : :
371 [ + + ]: 1627 : if ( root->GetChildren().size() )
372 : 1622 : analyzed = true;
373 : :
374 : 1627 : conn->SetRootAnalyzer(root, pia);
375 : 1627 : root->Init();
376 : 1627 : root->InitChildren();
377 : :
378 [ - + ]: 1627 : if ( ! analyzed )
379 : 0 : conn->SetLifetime(non_analyzed_lifetime);
380 : :
381 [ - + ]: 1627 : if ( expected != AnalyzerTag::Error )
382 : : conn->Event(expected_connection_seen, 0,
383 : 0 : new Val(expected, TYPE_COUNT));
384 : :
385 : 1627 : return true;
386 : : }
387 : :
388 : : void DPM::ExpectConnection(addr_type orig, addr_type resp, uint16 resp_p,
389 : : TransportProto proto, AnalyzerTag::Tag analyzer,
390 : 0 : double timeout, void* cookie)
391 : : {
392 : : // Use the chance to see if the oldest entry is already expired.
393 [ # # ]: 0 : if ( expected_conns_queue.size() )
394 : : {
395 : 0 : AssignedAnalyzer* a = expected_conns_queue.top();
396 [ # # ]: 0 : if ( a->timeout < network_time )
397 : : {
398 [ # # ]: 0 : if ( ! a->deleted )
399 : : {
400 : : HashKey* key = new HashKey(&a->conn,
401 : : sizeof(a->conn.orig) +
402 : : sizeof(a->conn.resp) +
403 : : sizeof(a->conn.resp_p) +
404 : 0 : sizeof(a->conn.proto));
405 : 0 : expected_conns.Remove(key);
406 [ # # ]: 0 : delete key;
407 : : }
408 : :
409 : 0 : expected_conns_queue.pop();
410 : :
411 : 0 : DBG_LOG(DBG_DPD, "Expired expected %s analyzer for %s",
412 : : Analyzer::GetTagName(analyzer),
413 : : fmt_conn_id(a->conn.orig, 0,
414 : : a->conn.resp,
415 : : a->conn.resp_p));
416 : :
417 : 0 : delete a;
418 : : }
419 : : }
420 : :
421 : 0 : ExpectedConn c(orig, resp, resp_p, proto);
422 : :
423 : : HashKey key(&c, sizeof(c.orig) + sizeof(c.resp) +
424 : 0 : sizeof(c.resp_p) + sizeof(c.proto));
425 : :
426 : 0 : AssignedAnalyzer* a = expected_conns.Lookup(&key);
427 : :
428 [ # # ]: 0 : if ( a )
429 : 0 : a->deleted = true;
430 : :
431 : 0 : a = new AssignedAnalyzer(c);
432 : :
433 : 0 : a->analyzer = analyzer;
434 : 0 : a->cookie = cookie;
435 : 0 : a->timeout = network_time + timeout;
436 : 0 : a->deleted = false;
437 : :
438 : 0 : expected_conns.Insert(&key, a);
439 : 0 : expected_conns_queue.push(a);
440 : 0 : }
441 : :
442 : 1 : void DPM::Done()
443 : : {
444 : : // Clean up expected-connection table.
445 [ - + ]: 1 : while ( expected_conns_queue.size() )
446 : : {
447 : 0 : AssignedAnalyzer* a = expected_conns_queue.top();
448 [ # # ]: 0 : if ( ! a->deleted )
449 : : {
450 : : HashKey* key = new HashKey(&a->conn,
451 : : sizeof(a->conn.orig) +
452 : : sizeof(a->conn.resp) +
453 : : sizeof(a->conn.resp_p) +
454 : 0 : sizeof(a->conn.proto));
455 : 0 : expected_conns.Remove(key);
456 [ # # ]: 0 : delete key;
457 : : }
458 : :
459 : 0 : expected_conns_queue.pop();
460 : 0 : delete a;
461 : : }
462 [ + - ][ + - ]: 7 : }
463 : 3 :
|