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 // host_tracker.cc author Steve Chew <stechew@cisco.com>
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <algorithm>
26 
27 #include "flow/flow.h"
28 #include "network_inspectors/rna/rna_flow.h"
29 #include "utils/util.h"
30 
31 #include "host_cache.h"
32 #include "host_cache_allocator.cc"
33 #include "host_tracker.h"
34 
35 using namespace snort;
36 using namespace std;
37 
38 #define USER_LOGIN_SUCCESS 1
39 #define USER_LOGIN_FAILURE 2
40 
41 THREAD_LOCAL struct HostTrackerStats host_tracker_stats;
42 
43 const uint8_t snort::zero_mac[MAC_SIZE] = {0, 0, 0, 0, 0, 0};
44 
45 
HostTracker()46 HostTracker::HostTracker() : hops(-1)
47 {
48     last_seen = nat_count_start = (uint32_t) packet_time();
49     last_event = -1;
50     visibility = host_cache.get_valid_id();
51 }
52 
update_last_seen()53 void HostTracker::update_last_seen()
54 {
55     lock_guard<mutex> lck(host_tracker_lock);
56     last_seen = (uint32_t) packet_time();
57 }
58 
update_last_event(uint32_t time)59 void HostTracker::update_last_event(uint32_t time)
60 {
61     lock_guard<mutex> lck(host_tracker_lock);
62     last_event = time ? time : last_seen;
63 }
64 
add_network_proto(const uint16_t type)65 bool HostTracker::add_network_proto(const uint16_t type)
66 {
67     lock_guard<mutex> lck(host_tracker_lock);
68 
69     for ( auto& proto : network_protos )
70     {
71         if ( proto.first == type )
72         {
73             if ( proto.second )
74                 return false;
75             else
76             {
77                 proto.second = true;
78                 return true;
79             }
80         }
81     }
82 
83     network_protos.emplace_back(type, true);
84     return true;
85 }
86 
add_xport_proto(const uint8_t type)87 bool HostTracker::add_xport_proto(const uint8_t type)
88 {
89     lock_guard<mutex> lck(host_tracker_lock);
90 
91     for ( auto& proto : xport_protos )
92     {
93         if ( proto.first == type )
94         {
95             if ( proto.second )
96                 return false;
97             else
98             {
99                 proto.second = true;
100                 return true;
101             }
102         }
103     }
104 
105     xport_protos.emplace_back(type, true);
106     return true;
107 }
108 
add_mac(const uint8_t * mac,uint8_t ttl,uint8_t primary)109 bool HostTracker::add_mac(const uint8_t* mac, uint8_t ttl, uint8_t primary)
110 {
111     if ( !mac or !memcmp(mac, zero_mac, MAC_SIZE) )
112         return false;
113 
114     HostMac_t* invisible_swap_candidate = nullptr;
115     lock_guard<mutex> lck(host_tracker_lock);
116 
117     for ( auto& hm_t : macs )
118     {
119         if ( !memcmp(mac, hm_t.mac, MAC_SIZE) )
120         {
121             if ( hm_t.visibility )
122             {
123                 return false;
124             }
125 
126             hm_t.visibility = true;
127             hm_t.last_seen = last_seen;
128             num_visible_macs++;
129             return true;
130         }
131 
132         if ( !invisible_swap_candidate and !hm_t.visibility )
133         {
134             invisible_swap_candidate = &hm_t;
135             break;
136         }
137     }
138 
139     if ( invisible_swap_candidate )
140     {
141         memcpy(invisible_swap_candidate->mac, mac, MAC_SIZE);
142         invisible_swap_candidate->ttl = ttl;
143         invisible_swap_candidate->primary = primary;
144         invisible_swap_candidate->visibility = true;
145         invisible_swap_candidate->last_seen = last_seen;
146         num_visible_macs++;
147         return true;
148     }
149 
150     macs.emplace_back(ttl, mac, primary, last_seen);
151     num_visible_macs++;
152 
153     return true;
154 }
155 
add_payload_no_lock(const AppId pld,HostApplication * ha,size_t max_payloads)156 bool HostTracker::add_payload_no_lock(const AppId pld, HostApplication* ha, size_t max_payloads)
157 {
158     Payload_t* invisible_swap_candidate = nullptr;
159 
160     for ( auto& p : ha->payloads )
161     {
162         if ( p.first == pld )
163         {
164             if ( p.second )
165             {
166                 return false;
167             }
168             else
169             {
170                 p.second = true;
171                 ha->num_visible_payloads++;
172                 return true;
173             }
174         }
175 
176         if ( !invisible_swap_candidate and !p.second )
177             invisible_swap_candidate = &p;
178     }
179 
180     if ( invisible_swap_candidate )
181     {
182         invisible_swap_candidate->first = pld;
183         invisible_swap_candidate->second = true;
184         ha->num_visible_payloads++;
185         return true;
186     }
187 
188     if ( ha->payloads.size() >= max_payloads )
189         return false;
190 
191     ha->payloads.emplace_back(pld, true);
192     ha->num_visible_payloads++;
193 
194     return true;
195 }
196 
get_hostmac(const uint8_t * mac,HostMac & hm)197 bool HostTracker::get_hostmac(const uint8_t* mac, HostMac& hm)
198 {
199     if ( !mac or !memcmp(mac, zero_mac, MAC_SIZE) )
200         return false;
201 
202     lock_guard<mutex> lck(host_tracker_lock);
203 
204     for ( auto& ahm : macs )
205         if ( !memcmp(mac, ahm.mac, MAC_SIZE) )
206         {
207             if ( !ahm.visibility )
208                 return false;
209 
210             hm = static_cast<HostMac>(ahm);
211             return true;
212         }
213 
214     return false;
215 }
216 
get_last_seen_mac(uint8_t * mac_addr)217 const uint8_t* HostTracker::get_last_seen_mac(uint8_t* mac_addr)
218 {
219     lock_guard<mutex> lck(host_tracker_lock);
220     const HostMac_t* max_hm = nullptr;
221 
222     for ( const auto& hm : macs )
223         if ( !max_hm or max_hm->last_seen < hm.last_seen )
224             if ( hm.visibility )
225                 max_hm = &hm;
226 
227     if ( max_hm )
228     {
229         memcpy(mac_addr, max_hm->mac, MAC_SIZE);
230         return mac_addr;
231     }
232 
233     return zero_mac;
234 }
235 
update_mac_ttl(const uint8_t * mac,uint8_t new_ttl)236 bool HostTracker::update_mac_ttl(const uint8_t* mac, uint8_t new_ttl)
237 {
238     if ( !mac or !memcmp(mac, zero_mac, MAC_SIZE) )
239         return false;
240 
241     lock_guard<mutex> lck(host_tracker_lock);
242 
243     for ( auto& hm : macs )
244         if ( !memcmp(mac, hm.mac, MAC_SIZE) )
245         {
246             if ( hm.ttl < new_ttl and hm.visibility )
247             {
248                 hm.ttl = new_ttl;
249                 return true;
250             }
251 
252             return false;
253         }
254 
255     return false;
256 }
257 
make_primary(const uint8_t * mac)258 bool HostTracker::make_primary(const uint8_t* mac)
259 {
260     if ( !mac or !memcmp(mac, zero_mac, MAC_SIZE) )
261         return false;
262 
263     HostMac_t* hm = nullptr;
264 
265     lock_guard<mutex> lck(host_tracker_lock);
266 
267     for ( auto& hm_iter : macs )
268         if ( !memcmp(mac, hm_iter.mac, MAC_SIZE) )
269         {
270             if ( !hm_iter.visibility )
271                 return false;
272 
273             hm = &hm_iter;
274             break;
275         }
276 
277     if ( !hm )
278         return false;
279 
280     hm->last_seen = last_seen;
281     if ( !hm->primary )
282     {
283         hm->primary = true;
284         return true;
285     }
286 
287     return false;
288 }
289 
reset_hops_if_primary()290 bool HostTracker::reset_hops_if_primary()
291 {
292     lock_guard<mutex> lck(host_tracker_lock);
293 
294     for ( auto& hm : macs )
295         if ( hm.primary and hm.visibility )
296         {
297             if ( !hops )
298                 return false;
299             hops = 0;
300             return true;
301         }
302 
303     return false;
304 }
305 
update_vlan(uint16_t vth_pri_cfi_vlan,uint16_t vth_proto)306 void HostTracker::update_vlan(uint16_t vth_pri_cfi_vlan, uint16_t vth_proto)
307 {
308     lock_guard<mutex> lck(host_tracker_lock);
309     vlan_tag_present = true;
310     vlan_tag.vth_pri_cfi_vlan = vth_pri_cfi_vlan;
311     vlan_tag.vth_proto = vth_proto;
312 }
313 
has_same_vlan(uint16_t pvlan)314 bool HostTracker::has_same_vlan(uint16_t pvlan)
315 {
316     lock_guard<mutex> lck(host_tracker_lock);
317     return vlan_tag_present and ( vlan_tag.vth_pri_cfi_vlan == pvlan );
318 }
319 
get_vlan_details(uint8_t & cfi,uint8_t & priority,uint16_t & vid)320 void HostTracker::get_vlan_details(uint8_t& cfi, uint8_t& priority, uint16_t& vid)
321 {
322     lock_guard<mutex> lck(host_tracker_lock);
323     cfi = vlan_tag.cfi();
324     priority = vlan_tag.priority();
325     vid = vlan_tag.vid();
326 }
327 
copy_data(uint8_t & p_hops,uint32_t & p_last_seen,list<HostMac> * & p_macs)328 void HostTracker::copy_data(uint8_t& p_hops, uint32_t& p_last_seen, list<HostMac>*& p_macs)
329 {
330     lock_guard<mutex> lck(host_tracker_lock);
331 
332     p_hops = hops;
333     p_last_seen = last_seen;
334     if ( !macs.empty() )
335         p_macs = new list<HostMac>(macs.begin(), macs.end());
336 }
337 
add_service(Port port,IpProtocol proto,AppId appid,bool inferred_appid,bool * added)338 bool HostTracker::add_service(Port port, IpProtocol proto, AppId appid, bool inferred_appid,
339     bool* added)
340 {
341     host_tracker_stats.service_adds++;
342     lock_guard<mutex> lck(host_tracker_lock);
343 
344     for ( auto& s : services )
345     {
346         if ( s.port == port and s.proto == proto )
347         {
348             if ( s.appid != appid and appid != APP_ID_NONE )
349             {
350                 s.appid = appid;
351                 s.inferred_appid = inferred_appid;
352                 if ( added )
353                     *added = true;
354             }
355 
356             if ( s.visibility == false )
357             {
358                 if ( added )
359                     *added = true;
360                 s.visibility = true;
361                 num_visible_services++;
362             }
363 
364             return true;
365         }
366     }
367 
368     services.emplace_back(port, proto, appid, inferred_appid);
369     num_visible_services++;
370     if ( added )
371         *added = true;
372 
373     return true;
374 }
375 
clear_service(HostApplication & ha)376 void HostTracker::clear_service(HostApplication& ha)
377 {
378     lock_guard<mutex> lck(host_tracker_lock);
379     ha.port = 0;
380     ha.proto = (IpProtocol) 0;
381     ha.appid = (AppId) 0;
382     ha.inferred_appid = false;
383     ha.hits = 0;
384     ha.last_seen = 0;
385     ha.payloads.clear();
386     ha.info.clear();
387     ha.banner_updated = false;
388 }
389 
add_client_payload(HostClient & hc,AppId payload,size_t max_payloads)390 bool HostTracker::add_client_payload(HostClient& hc, AppId payload, size_t max_payloads)
391 {
392     Payload_t* invisible_swap_candidate = nullptr;
393     std::lock_guard<std::mutex> lck(host_tracker_lock);
394 
395     for ( auto& client : clients )
396         if ( client.id == hc.id and client.service == hc.service )
397         {
398             for ( auto& pld : client.payloads )
399             {
400                 if ( pld.first == payload )
401                 {
402                     if ( pld.second )
403                         return false;
404 
405                     pld.second = true;
406                     client.num_visible_payloads++;
407                     hc.payloads = client.payloads;
408                     strncpy(hc.version, client.version, INFO_SIZE);
409                     return true;
410                 }
411                 if ( !invisible_swap_candidate and !pld.second )
412                     invisible_swap_candidate = &pld;
413             }
414 
415             if ( invisible_swap_candidate )
416             {
417                 invisible_swap_candidate->second = true;
418                 invisible_swap_candidate->first = payload;
419                 client.num_visible_payloads++;
420                 hc.payloads = client.payloads;
421                 strncpy(hc.version, client.version, INFO_SIZE);
422                 return true;
423             }
424 
425             if ( client.payloads.size() >= max_payloads )
426                 return false;
427 
428             client.payloads.emplace_back(payload, true);
429             hc.payloads = client.payloads;
430             strncpy(hc.version, client.version, INFO_SIZE);
431             client.num_visible_payloads++;
432             return true;
433         }
434 
435     return false;
436 }
437 
add_service(const HostApplication & app,bool * added)438 bool HostTracker::add_service(const HostApplication& app, bool* added)
439 {
440     host_tracker_stats.service_adds++;
441     lock_guard<mutex> lck(host_tracker_lock);
442 
443     for ( auto& s : services )
444     {
445         if ( s.port == app.port and s.proto == app.proto )
446         {
447             if ( s.appid != app.appid and app.appid != APP_ID_NONE )
448             {
449                 s.appid = app.appid;
450                 s.inferred_appid = app.inferred_appid;
451                 if ( added )
452                     *added = true;
453             }
454 
455             if ( s.visibility == false )
456             {
457                 if ( added )
458                     *added = true;
459                 s.visibility = true;
460                 num_visible_services++;
461             }
462 
463             return true;
464         }
465     }
466 
467     services.emplace_back(app.port, app.proto, app.appid, app.inferred_appid);
468     num_visible_services++;
469     if ( added )
470         *added = true;
471 
472     return true;
473 }
474 
get_appid(Port port,IpProtocol proto,bool inferred_only,bool allow_port_wildcard)475 AppId HostTracker::get_appid(Port port, IpProtocol proto, bool inferred_only,
476     bool allow_port_wildcard)
477 {
478     host_tracker_stats.service_finds++;
479     lock_guard<mutex> lck(host_tracker_lock);
480 
481     for ( const auto& s : services )
482     {
483         bool matched = (s.port == port and s.proto == proto and
484             (!inferred_only or s.inferred_appid == inferred_only));
485         if ( matched or ( allow_port_wildcard and s.inferred_appid ) )
486             return s.appid;
487     }
488 
489     return APP_ID_NONE;
490 }
491 
get_service_count()492 size_t HostTracker::get_service_count()
493 {
494     lock_guard<mutex> lck(host_tracker_lock);
495     return num_visible_services;
496 }
497 
find_service_no_lock(Port port,IpProtocol proto,AppId appid)498 HostApplication* HostTracker::find_service_no_lock(Port port, IpProtocol proto, AppId appid)
499 {
500     for ( auto& s : services )
501     {
502         if ( s.port == port and s.proto == proto )
503         {
504             if ( s.visibility == false )
505                 return nullptr;
506             if ( appid != APP_ID_NONE and s.appid == appid )
507                 return &s;
508         }
509     }
510 
511     return nullptr;
512 }
513 
add_payload(HostApplication & local_ha,Port port,IpProtocol proto,AppId payload,AppId service,size_t max_payloads)514 bool HostTracker::add_payload(HostApplication& local_ha, Port port, IpProtocol proto, AppId payload,
515     AppId service, size_t max_payloads)
516 {
517     // This lock is responsible for find_service and add_payload
518     lock_guard<mutex> lck(host_tracker_lock);
519 
520     auto ha = find_service_no_lock(port, proto, service);
521 
522     if ( ha )
523     {
524         bool success = add_payload_no_lock(payload, ha, max_payloads);
525         local_ha = *ha;
526         local_ha.payloads = ha->payloads;
527         return success;
528     }
529 
530     return false;
531 }
532 
find_and_add_service_no_lock(Port port,IpProtocol proto,uint32_t lseen,bool & is_new,AppId appid,uint16_t max_services)533 HostApplication* HostTracker::find_and_add_service_no_lock(Port port, IpProtocol proto,
534     uint32_t lseen, bool& is_new, AppId appid, uint16_t max_services)
535 {
536     host_tracker_stats.service_finds++;
537     HostApplication *available = nullptr;
538 
539     for ( auto& s : services )
540     {
541         if ( s.port == port and s.proto == proto )
542         {
543             if ( (appid != APP_ID_NONE and s.appid != appid) or !s.visibility )
544             {
545                 s.appid = appid;
546                 is_new = true;
547                 s.hits = 1;
548                 if ( !s.visibility )
549                 {
550                     s.visibility = true;
551                     num_visible_services++;
552                 }
553                 else
554                     s.hits = 0;
555             }
556             else if ( s.last_seen == 0 )
557             {
558                 is_new = true;
559                 s.hits = 1;
560             }
561             else
562                 ++s.hits;
563 
564             s.last_seen = lseen;
565 
566             return &s;
567         }
568         else if ( !available and !s.visibility )
569             available = &s;
570     }
571 
572     is_new = true;
573     host_tracker_stats.service_adds++;
574     num_visible_services++;
575     if ( available )
576     {
577         available->port = port;
578         available->proto = proto;
579         available->appid = appid;
580         available->hits = 1;
581         available->last_seen = lseen;
582         available->inferred_appid = false;
583         available->user[0] = '\0';
584         available->user_login = 0;
585         available->banner_updated = false;
586         available->visibility = true;
587         return available;
588     }
589 
590     if ( max_services == 0 or num_visible_services < max_services )
591     {
592         services.emplace_back(port, proto, appid, false, 1, lseen);
593         return &services.back();
594     }
595     return nullptr;
596 }
597 
add_service(Port port,IpProtocol proto,uint32_t lseen,bool & is_new,AppId appid)598 HostApplication HostTracker::add_service(Port port, IpProtocol proto, uint32_t lseen,
599     bool& is_new, AppId appid)
600 {
601     lock_guard<mutex> lck(host_tracker_lock);
602     HostApplication* ha = find_and_add_service_no_lock(port, proto, lseen, is_new, appid);
603     return *ha;
604 }
605 
update_service(const HostApplication & ha)606 void HostTracker::update_service(const HostApplication& ha)
607 {
608     host_tracker_stats.service_finds++;
609     lock_guard<mutex> lck(host_tracker_lock);
610 
611     for ( auto& s : services )
612     {
613         if ( s.port == ha.port and s.proto == ha.proto )
614         {
615             s.hits = ha.hits;
616             s.last_seen = ha.last_seen;
617             return;
618         }
619     }
620 }
621 
update_service_port(HostApplication & app,Port port)622 void HostTracker::update_service_port(HostApplication& app, Port port)
623 {
624     lock_guard<mutex> lck(host_tracker_lock);
625     app.port = port;
626 }
627 
update_service_proto(HostApplication & app,IpProtocol proto)628 void HostTracker::update_service_proto(HostApplication& app, IpProtocol proto)
629 {
630     lock_guard<mutex> lck(host_tracker_lock);
631     app.proto = proto;
632 }
633 
update_ha_no_lock(HostApplication & dst,HostApplication & src)634 void HostTracker::update_ha_no_lock(HostApplication& dst, HostApplication& src)
635 {
636     if ( dst.appid == APP_ID_NONE )
637         dst.appid = src.appid;
638     else
639         src.appid = dst.appid;
640 
641     for ( auto& i: src.info )
642         if ( i.visibility == true )
643             dst.info.emplace_back(i.version, i.vendor);
644 
645     dst.hits = src.hits;
646 }
647 
update_service_info(HostApplication & ha,const char * vendor,const char * version,uint16_t max_info)648 bool HostTracker::update_service_info(HostApplication& ha, const char* vendor,
649     const char* version, uint16_t max_info)
650 {
651     host_tracker_stats.service_finds++;
652     lock_guard<mutex> lck(host_tracker_lock);
653 
654     for ( auto& s : services )
655     {
656         if ( s.port == ha.port and s.proto == ha.proto )
657         {
658             if ( s.visibility == false )
659                 return false;
660 
661             if ( !version and !vendor )
662                 return true;
663 
664             HostApplicationInfo* available = nullptr;
665             for ( auto& i : s.info )
666             {
667                 if ( (version and !strncmp(version, i.version, INFO_SIZE-1)) and
668                     (vendor and !strncmp(vendor, i.vendor, INFO_SIZE-1)) )
669                 {
670                     if ( i.visibility == false )
671                     {
672                         i.visibility = true;  // rediscover it
673                         update_ha_no_lock(ha, s);
674                         return true;
675                     }
676                     return false;
677                 }
678                 else if ( !available and !i.visibility )
679                     available = &i;
680             }
681 
682             if ( available and (version or vendor) )
683             {
684                 if ( version )
685                 {
686                     strncpy(available->version, version, INFO_SIZE);
687                     available->version[INFO_SIZE-1]='\0';
688                 }
689 
690                 if ( vendor )
691                 {
692                     strncpy(available->vendor, vendor, INFO_SIZE);
693                     available->vendor[INFO_SIZE-1]='\0';
694                 }
695 
696                 available->visibility = true;
697             }
698             else if ( s.info.size() < max_info )
699                 s.info.emplace_back(version, vendor);
700             else
701                 return false;
702 
703             update_ha_no_lock(ha, s);
704             return true;
705         }
706     }
707     return false;
708 }
709 
update_service_banner(Port port,IpProtocol proto)710 bool HostTracker::update_service_banner(Port port, IpProtocol proto)
711 {
712     host_tracker_stats.service_finds++;
713     lock_guard<mutex> lck(host_tracker_lock);
714     for ( auto& s : services )
715     {
716         if ( s.port == port and s.proto == proto )
717         {
718             if ( !s.visibility or s.banner_updated )
719                 return false;
720 
721             s.banner_updated = true;
722             return true;
723         }
724     }
725     return false;
726 }
727 
update_service_user(Port port,IpProtocol proto,const char * user,uint32_t lseen,uint16_t max_services,bool success)728 bool HostTracker::update_service_user(Port port, IpProtocol proto, const char* user,
729     uint32_t lseen, uint16_t max_services, bool success)
730 {
731     host_tracker_stats.service_finds++;
732     bool is_new = false;
733     lock_guard<mutex> lck(host_tracker_lock);
734 
735     // Appid notifies user events before service events, so use find or add service function.
736     HostApplication* ha = find_and_add_service_no_lock(port, proto, lseen, is_new, 0,
737         max_services);
738     if ( !ha or ha->visibility == false )
739         return false;
740 
741     if ( user and strncmp(user, ha->user, INFO_SIZE-1) )
742     {
743         strncpy(ha->user, user, INFO_SIZE);
744         ha->user[INFO_SIZE-1] = '\0';
745         ha->user_login = success ? USER_LOGIN_SUCCESS : USER_LOGIN_FAILURE;
746         return true;
747     }
748 
749     if ( success )
750     {
751         if ( ha->user_login & USER_LOGIN_SUCCESS )
752             return false;
753         ha->user_login |= USER_LOGIN_SUCCESS;
754         return true;
755     }
756     else
757     {
758         if ( ha->user_login & USER_LOGIN_FAILURE )
759             return false;
760         ha->user_login |= USER_LOGIN_FAILURE;
761         return true;
762     }
763 }
764 
remove_inferred_services()765 void HostTracker::remove_inferred_services()
766 {
767     lock_guard<mutex> lck(host_tracker_lock);
768     for ( auto s = services.begin(); s != services.end(); )
769     {
770         if ( s->inferred_appid )
771             s = services.erase(s);
772         else
773             s++;
774     }
775 }
776 
add_tcp_fingerprint(uint32_t fpid)777 bool HostTracker::add_tcp_fingerprint(uint32_t fpid)
778 {
779     lock_guard<mutex> lck(host_tracker_lock);
780     auto result = tcp_fpids.emplace(fpid);
781     return result.second;
782 }
783 
add_udp_fingerprint(uint32_t fpid)784 bool HostTracker::add_udp_fingerprint(uint32_t fpid)
785 {
786     lock_guard<mutex> lck(host_tracker_lock);
787     auto result = udp_fpids.emplace(fpid);
788     return result.second;
789 }
790 
set_netbios_name(const char * nb_name)791 bool HostTracker::set_netbios_name(const char* nb_name)
792 {
793     std::lock_guard<std::mutex> lck(host_tracker_lock);
794     if ( nb_name && netbios_name != nb_name )
795     {
796         netbios_name = nb_name;
797         return true;
798     }
799     else
800         return false;
801 }
802 
add_smb_fingerprint(uint32_t fpid)803 bool HostTracker::add_smb_fingerprint(uint32_t fpid)
804 {
805     lock_guard<mutex> lck(host_tracker_lock);
806     auto result = smb_fpids.emplace(fpid);
807     return result.second;
808 }
809 
add_cpe_os_hash(uint32_t hash)810 bool HostTracker::add_cpe_os_hash(uint32_t hash)
811 {
812     lock_guard<mutex> lck(host_tracker_lock);
813     auto result = cpe_fpids.emplace(hash);
814     return result.second;
815 }
816 
set_visibility(bool v)817 bool HostTracker::set_visibility(bool v)
818 {
819     // get_valid_id may use its own lock, so get this outside our lock
820     size_t container_id = host_cache.get_valid_id();
821 
822     std::lock_guard<std::mutex> lck(host_tracker_lock);
823     size_t old_visibility = visibility;
824 
825     visibility = v ? container_id : HostCacheIp::invalid_id;
826 
827     if ( old_visibility != visibility )
828     {
829         for ( auto& proto : network_protos )
830             proto.second = false;
831 
832         for ( auto& proto : xport_protos )
833             proto.second = false;
834 
835         for ( auto& mac_t : macs )
836             mac_t.visibility = false;
837 
838         num_visible_macs = 0;
839 
840         for ( auto& s : services )
841         {
842             s.visibility = false;
843             for ( auto& info : s.info )
844                 info.visibility = false;
845             s.user[0] = '\0';
846             set_payload_visibility_no_lock(s.payloads, false, s.num_visible_payloads);
847         }
848         num_visible_services = 0;
849 
850         for ( auto& c : clients )
851         {
852             c.visibility = false;
853             set_payload_visibility_no_lock(c.payloads, false, c.num_visible_payloads);
854         }
855         num_visible_clients = 0;
856 
857         tcp_fpids.clear();
858         ua_fps.clear();
859         udp_fpids.clear();
860         smb_fpids.clear();
861         netbios_name.clear();
862         cpe_fpids.clear();
863     }
864 
865     return old_visibility == visibility;
866 }
867 
is_visible() const868 bool HostTracker::is_visible() const
869 {
870     std::lock_guard<std::mutex> lck(host_tracker_lock);
871     return visibility == host_cache.get_valid_id();
872 }
873 
874 
set_network_proto_visibility(uint16_t proto,bool v)875 bool HostTracker::set_network_proto_visibility(uint16_t proto, bool v)
876 {
877     std::lock_guard<std::mutex> lck(host_tracker_lock);
878     for ( auto& pp : network_protos )
879     {
880         if ( pp.first == proto )
881         {
882             pp.second = v;
883             return true;
884         }
885     }
886     return false;
887 }
888 
set_xproto_visibility(uint8_t proto,bool v)889 bool HostTracker::set_xproto_visibility(uint8_t proto, bool v)
890 {
891     std::lock_guard<std::mutex> lck(host_tracker_lock);
892     for ( auto& pp : xport_protos )
893     {
894         if ( pp.first == proto )
895         {
896             pp.second = v;
897             return true;
898         }
899     }
900     return false;
901 }
902 
set_payload_visibility_no_lock(PayloadVector & pv,bool v,size_t & num_vis)903 void HostTracker::set_payload_visibility_no_lock(PayloadVector& pv, bool v, size_t& num_vis)
904 {
905     for ( auto& p : pv )
906     {
907         if ( p.second != v )
908         {
909             p.second = v;
910             if ( v )
911                 num_vis++;
912             else
913                 num_vis--;
914         }
915     }
916 }
917 
set_service_visibility(Port port,IpProtocol proto,bool v)918 bool HostTracker::set_service_visibility(Port port, IpProtocol proto, bool v)
919 {
920     std::lock_guard<std::mutex> lck(host_tracker_lock);
921     for ( auto& s : services )
922     {
923         if ( s.port == port and s.proto == proto )
924         {
925             if ( s.visibility == true and v == false )
926             {
927                 assert(num_visible_services > 0);
928                 num_visible_services--;
929             }
930             else if ( s.visibility == false and v == true )
931                 num_visible_services++;
932 
933             s.visibility = v;
934             if ( s.visibility == false )
935             {
936                 for ( auto& info : s.info )
937                     info.visibility = false;
938                 s.user[0] = '\0';
939                 s.banner_updated = false;
940             }
941 
942             set_payload_visibility_no_lock(s.payloads, v, s.num_visible_payloads);
943             return true;
944         }
945     }
946     return false;
947 }
948 
set_client_visibility(const HostClient & hc,bool v)949 bool HostTracker::set_client_visibility(const HostClient& hc, bool v)
950 {
951     std::lock_guard<std::mutex> lck(host_tracker_lock);
952     bool deleted = false;
953     for ( auto& c : clients )
954     {
955         if ( c == hc )
956         {
957             if ( c.visibility == true and v == false )
958             {
959                 assert(num_visible_clients > 0 );
960                 num_visible_clients--;
961             }
962             else if ( c.visibility == false and v == true )
963                 num_visible_clients++;
964 
965             c.visibility = v;
966             set_payload_visibility_no_lock(c.payloads, v, c.num_visible_payloads);
967             deleted = true;
968         }
969     }
970     return deleted;
971 }
972 
DeviceFingerprint(uint32_t id,uint32_t type,bool jb,const char * dev)973 DeviceFingerprint::DeviceFingerprint(uint32_t id, uint32_t type, bool jb, const char* dev) :
974     fpid(id), fp_type(type), jail_broken(jb)
975 {
976     if ( dev )
977     {
978         strncpy(device, dev, INFO_SIZE);
979         device[INFO_SIZE-1] = '\0';
980     }
981 }
982 
add_ua_fingerprint(uint32_t fpid,uint32_t fp_type,bool jail_broken,const char * device,uint8_t max_devices)983 bool HostTracker::add_ua_fingerprint(uint32_t fpid, uint32_t fp_type, bool jail_broken,
984     const char* device, uint8_t max_devices)
985 {
986     lock_guard<mutex> lck(host_tracker_lock);
987 
988     int count = 0;
989     for ( const auto& fp : ua_fps )
990     {
991         if ( fpid != fp.fpid or fp_type != fp.fp_type )
992             continue;
993         ++count; // only count same fpid with different device information
994         if ( count >= max_devices )
995             return false;
996         if ( jail_broken == fp.jail_broken and ( ( !device and fp.device[0] == '\0') or
997             ( device and strncmp(fp.device, device, INFO_SIZE) == 0) ) )
998             return false;
999     }
1000 
1001     ua_fps.emplace_back(fpid, fp_type, jail_broken, device);
1002     return true;
1003 }
1004 
get_client_count()1005 size_t HostTracker::get_client_count()
1006 {
1007     lock_guard<mutex> lck(host_tracker_lock);
1008     return num_visible_clients;
1009 }
1010 
HostClient(AppId clientid,const char * ver,AppId ser)1011 HostClient::HostClient(AppId clientid, const char *ver, AppId ser) :
1012     id(clientid), service(ser)
1013 {
1014     if ( ver )
1015     {
1016         strncpy(version, ver, INFO_SIZE);
1017         version[INFO_SIZE-1] = '\0';
1018     }
1019 }
1020 
find_or_add_client(AppId id,const char * version,AppId service,bool & is_new)1021 HostClient HostTracker::find_or_add_client(AppId id, const char* version, AppId service,
1022     bool& is_new)
1023 {
1024     lock_guard<mutex> lck(host_tracker_lock);
1025     HostClient* available = nullptr;
1026     for ( auto& c : clients )
1027     {
1028         if ( c.id != APP_ID_NONE and c.id == id and c.service == service
1029             and ((c.version[0] == '\0' and !version) or
1030             (version and strncmp(c.version, version, INFO_SIZE-1) == 0)) )
1031         {
1032             if ( c.visibility == false )
1033             {
1034                 is_new = true;
1035                 c.visibility = true;
1036                 num_visible_clients++;
1037             }
1038 
1039             return c;
1040         }
1041         else if ( !available and !c.visibility )
1042             available = &c;
1043     }
1044 
1045     is_new = true;
1046     num_visible_clients++;
1047     if ( available )
1048     {
1049         available->id = id;
1050         available->service = service;
1051         available->visibility = true;
1052         if ( version )
1053         {
1054             strncpy(available->version, version, INFO_SIZE);
1055             available->version[INFO_SIZE-1] = '\0';
1056         }
1057         return *available;
1058     }
1059 
1060     clients.emplace_back(id, version, service);
1061     return clients.back();
1062 }
1063 
add_flow(RNAFlow * fd)1064 void HostTracker::add_flow(RNAFlow* fd)
1065 {
1066     lock_guard<mutex> lck(flows_lock);
1067     flows.insert(fd);
1068 }
1069 
remove_flow(RNAFlow * fd)1070 void HostTracker::remove_flow(RNAFlow* fd)
1071 {
1072     lock_guard<mutex> lck(flows_lock);
1073     flows.erase(fd);
1074 }
1075 
remove_flows()1076 void HostTracker::remove_flows()
1077 {
1078     // To lock, or not to lock? That is the question!
1079     //
1080     // The only way we get here is from LRU::update(), called by the allocator.
1081     // That is, we only get here from a HT::add_<> operation. All of those
1082     // operations lock the HT, so the HT is already locked when we get here.
1083     // Also, none of those operations modify the HT::flows set. So we should
1084     // not lock the HT (because we'd cause a deadlock), nor do we need to
1085     // (because there's no contention on HT::flows from those adds).
1086     //
1087     // However, this HT could be part of a different rna flow, which could
1088     // go out of existence exactly at the time when this thread modifies
1089     // the HT::flows set. The rna flow destructor calls on this
1090     // HT::remove_flow(), which does modify HT::flows. The for loop itself
1091     // does not modify the HT::flows set, but flows.clear() does - whether
1092     // or not we call it here explicitly. We, therefore, need to protect the
1093     // HT::flows() array with a lock on this host_tracker_lock.
1094     //
1095     // We have identified two situations with opposite requirements:
1096     // one requires locking, the other requires not locking.
1097     //
1098     // Now, note that the thread contention is not on the host tracker itself,
1099     // but on the HT::flows set. This means we may not lock the HT here,
1100     // to avoid the deadlock from the first case, but we SHOULD lock on
1101     // a different mutex to protect the HT::flows set.
1102     lock_guard<mutex> lck(flows_lock);
1103     for ( auto& rna_flow : flows )
1104     {
1105         rna_flow->clear_ht(*this);
1106     }
1107     flows.clear();
1108 }
1109 
HostApplicationInfo(const char * ver,const char * ven)1110 HostApplicationInfo::HostApplicationInfo(const char *ver, const char *ven)
1111 {
1112     if ( ver )
1113     {
1114         strncpy(version, ver, INFO_SIZE);
1115         version[INFO_SIZE-1] = '\0';
1116     }
1117     if ( ven )
1118     {
1119         strncpy(vendor, ven, INFO_SIZE);
1120         vendor[INFO_SIZE-1] = '\0';
1121     }
1122 }
1123 
to_time_string(uint32_t p_time)1124 static inline string to_time_string(uint32_t p_time)
1125 {
1126     time_t raw_time = (time_t) p_time;
1127     struct tm* timeinfo = gmtime(&raw_time);
1128     char buffer[30];
1129     strftime(buffer, 30, "%F %T", timeinfo);
1130     return buffer;
1131 }
1132 
to_mac_string(const uint8_t * mac)1133 static inline string to_mac_string(const uint8_t* mac)
1134 {
1135     char mac_addr[18];
1136     snprintf(mac_addr, 18, "%02X:%02X:%02X:%02X:%02X:%02X",
1137         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
1138     return mac_addr;
1139 }
1140 
1141 static std::vector<std::string> host_types = { "Host", "Router", "Bridge", "NAT", "Load Balancer" };
1142 
to_host_type_string(HostType type)1143 static inline string& to_host_type_string(HostType type)
1144 {
1145     return host_types[type];
1146 }
1147 
stringify(string & str)1148 void HostTracker::stringify(string& str)
1149 {
1150     lock_guard<mutex> lck(host_tracker_lock);
1151 
1152     str += "\n    type: " + to_host_type_string(host_type) + ", ttl: " + to_string(ip_ttl)
1153         + ", hops: " + to_string(hops) + ", time: " + to_time_string(last_seen);
1154 
1155     if ( !macs.empty() )
1156     {
1157         str += "\nmacs size: " + to_string(num_visible_macs);
1158         for ( const auto& m : macs )
1159         {
1160             if ( m.visibility )
1161             {
1162                 str += "\n    mac: " + to_mac_string(m.mac)
1163                     + ", ttl: " + to_string(m.ttl)
1164                     + ", primary: " + to_string(m.primary)
1165                     + ", time: " + to_time_string(m.last_seen);
1166             }
1167         }
1168     }
1169 
1170     if ( num_visible_services > 0 )
1171     {
1172         str += "\nservices size: " + to_string(num_visible_services);
1173 
1174         for ( const auto& s : services )
1175         {
1176             if ( s.visibility == false )
1177                 continue;
1178 
1179             str += "\n    port: " + to_string(s.port)
1180                 + ", proto: " + to_string((uint8_t) s.proto);
1181             if ( s.appid != APP_ID_NONE )
1182             {
1183                 str += ", appid: " + to_string(s.appid);
1184                 if ( s.inferred_appid )
1185                     str += ", inferred";
1186             }
1187 
1188             if ( !s.info.empty() )
1189                 for ( const auto& i : s.info )
1190                 {
1191                     if ( i.visibility == false )
1192                         continue;
1193 
1194                     if ( i.vendor[0] != '\0' )
1195                         str += ", vendor: " + string(i.vendor);
1196                     if ( i.version[0] != '\0' )
1197                         str += ", version: " + string(i.version);
1198                 }
1199 
1200             auto vis_payloads = s.num_visible_payloads;
1201             if ( vis_payloads > 0 )
1202             {
1203                 str += ", payload";
1204                 str += (vis_payloads > 1) ? "s: " : ": ";
1205                 for ( const auto& pld : s.payloads )
1206                 {
1207                     if ( pld.second )
1208                         str += to_string(pld.first) + (--vis_payloads ? ", " : "");
1209                 }
1210             }
1211             if ( *s.user )
1212                 str += ", user: " + string(s.user);
1213         }
1214     }
1215 
1216     if ( num_visible_clients > 0 )
1217     {
1218         str += "\nclients size: " + to_string(num_visible_clients);
1219         for ( const auto& c : clients )
1220         {
1221             if ( c.visibility == false )
1222                 continue;
1223 
1224             str += "\n    id: " + to_string(c.id)
1225                 + ", service: " + to_string(c.service);
1226             if ( c.version[0] != '\0' )
1227                 str += ", version: " + string(c.version);
1228 
1229             auto vis_payloads = c.num_visible_payloads;
1230             if ( vis_payloads )
1231             {
1232                 str += ", payload";
1233                 str += (vis_payloads > 1) ? "s: " : ": ";
1234                 for ( const auto& pld : c.payloads )
1235                 {
1236                     if ( pld.second )
1237                         str += to_string(pld.first) + (--vis_payloads ? ", " : "");
1238                 }
1239             }
1240         }
1241     }
1242 
1243     if ( any_of(network_protos.begin(), network_protos.end(),
1244         [] (const NetProto_t& proto) { return proto.second; }) )
1245     {
1246         str += "\nnetwork proto: ";
1247         auto total = network_protos.size();
1248         while ( total-- )
1249         {
1250             const auto& proto = network_protos[total];
1251             if ( proto.second == true )
1252                 str += to_string(proto.first) + (total? ", " : "");
1253         }
1254     }
1255 
1256     if ( any_of(xport_protos.begin(), xport_protos.end(),
1257         [] (const XProto_t& proto) { return proto.second; }) )
1258     {
1259         str += "\ntransport proto: ";
1260         auto total = xport_protos.size();
1261         while ( total-- )
1262         {
1263             const auto& proto = xport_protos[total];
1264             if ( proto.second == true )
1265                 str += to_string(proto.first) + (total? ", " : "");
1266         }
1267     }
1268 
1269     auto total = tcp_fpids.size();
1270     if ( total )
1271     {
1272         str += "\ntcp fingerprint: ";
1273         for ( const auto& fpid : tcp_fpids )
1274             str += to_string(fpid) + (--total ? ", " : "");
1275     }
1276 
1277     total = ua_fps.size();
1278     if ( total )
1279     {
1280         str += "\nua fingerprint: ";
1281         for ( const auto& fp : ua_fps )
1282         {
1283             str += to_string(fp.fpid) + " (type: " + to_string(fp.fp_type);
1284             if ( fp.jail_broken )
1285                 str += ", jail-broken";
1286             if ( fp.device[0] != '\0' )
1287                 str += ", device: " + string(fp.device);
1288             str += string(")") + (--total ? ", " : "");
1289         }
1290     }
1291 
1292     total = udp_fpids.size();
1293     if ( total )
1294     {
1295         str += "\nudp fingerprint: ";
1296         for ( const auto& fpid : udp_fpids )
1297             str += to_string(fpid) + (--total ? ", " : "");
1298     }
1299 
1300     total = smb_fpids.size();
1301     if ( total )
1302     {
1303         str += "\nsmb fingerprint: ";
1304         for ( const auto& fpid : smb_fpids )
1305             str += to_string(fpid) + (--total ? ", " : "");
1306     }
1307 
1308     if ( !netbios_name.empty() )
1309         str += "\nnetbios name: " + netbios_name;
1310 }
1311