1 //--------------------------------------------------------------------------
2 // Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 // Copyright (C) 2005-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 
20 // app_info_table.cc author Sourcefire Inc.
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "app_info_table.h"
27 
28 #include <climits>
29 #include <fstream>
30 #include <string>
31 #include <unistd.h>
32 
33 #include "log/unified2.h"
34 #include "main/snort_config.h"
35 #include "target_based/snort_protocols.h"
36 #include "utils/util_cstring.h"
37 #include "appid_api.h"
38 #include "appid_config.h"
39 #include "appid_inspector.h"
40 #include "appid_peg_counts.h"
41 
42 using namespace snort;
43 
44 #define MAX_TABLE_LINE_LEN      1024
45 static const char* CONF_SEPARATORS = "\t\n\r";
46 static const int MIN_MAX_TP_FLOW_DEPTH = 1;
47 static const int MAX_MAX_TP_FLOW_DEPTH = 1000000;
48 static const int MIN_HOST_PORT_APP_CACHE_LOOKUP_INTERVAL = 1;
49 static const int MAX_HOST_PORT_APP_CACHE_LOOKUP_INTERVAL = 1000000;
50 static const int MIN_HOST_PORT_APP_CACHE_LOOKUP_RANGE = 1;
51 static const int MAX_HOST_PORT_APP_CACHE_LOOKUP_RANGE = 1000000;
52 static const char* APP_CONFIG_FILE = "appid.conf";
53 static const char* USR_CONFIG_FILE = "userappid.conf";
54 const char* APP_MAPPING_FILE = "appMapping.data";
55 
AppInfoTableEntry(AppId id,char * name)56 AppInfoTableEntry::AppInfoTableEntry(AppId id, char* name)
57     : appId(id), serviceId(id), clientId(id), payloadId(id), app_name(name)
58 {
59     app_name_key = AppInfoManager::strdup_to_lower(name);
60 }
61 
AppInfoTableEntry(AppId id,char * name,AppId sid,AppId cid,AppId pid)62 AppInfoTableEntry::AppInfoTableEntry(AppId id, char* name, AppId sid, AppId cid, AppId pid) :
63     appId(id), serviceId(sid), clientId(cid), payloadId(pid), app_name(name)
64 {
65     app_name_key = AppInfoManager::strdup_to_lower(name);
66 }
67 
~AppInfoTableEntry()68 AppInfoTableEntry::~AppInfoTableEntry()
69 {
70     if (app_name)
71         snort_free(app_name);
72     if (app_name_key)
73         snort_free(app_name_key);
74 }
75 
is_existing_entry(AppInfoTableEntry * entry)76 bool AppInfoManager::is_existing_entry(AppInfoTableEntry* entry)
77 {
78     AppInfoNameTable::iterator app;
79 
80     app = app_info_name_table.find(entry->app_name_key);
81     return app != app_info_name_table.end();
82 }
83 
find_app_info_by_name(const char * app_name)84 AppInfoTableEntry* AppInfoManager::find_app_info_by_name(const char* app_name)
85 {
86     AppInfoTableEntry* entry = nullptr;
87     AppInfoNameTable::iterator app;
88     const char* search_name = AppInfoManager::strdup_to_lower(app_name);
89 
90     app = app_info_name_table.find(search_name);
91     if (app != app_info_name_table.end())
92         entry = app->second;
93 
94     snort_free((void*)search_name);
95     return entry;
96 }
97 
add_entry_to_app_info_name_table(const char * app_name,AppInfoTableEntry * entry)98 bool AppInfoManager::add_entry_to_app_info_name_table(const char* app_name,
99     AppInfoTableEntry* entry)
100 {
101     bool added = true;
102 
103     if (!is_existing_entry(entry))
104         app_info_name_table[app_name] = entry;
105     else
106     {
107         WarningMessage("App name, \"%s\" is a duplicate entry will be shared by each detector.\n",
108             app_name);
109         added = false;
110     }
111     return added;
112 }
113 
get_static_app_info_entry(AppId appid)114 AppId AppInfoManager::get_static_app_info_entry(AppId appid)
115 {
116     if (appid > 0 && appid < SF_APPID_BUILDIN_MAX)
117         return appid;
118     if ((appid >= SF_APPID_CSD_MIN) &&
119         appid < (SF_APPID_CSD_MIN + (SF_APPID_MAX - SF_APPID_BUILDIN_MAX)))
120         return (SF_APPID_BUILDIN_MAX + appid - SF_APPID_CSD_MIN);
121     return 0;
122 }
123 
strdup_to_lower(const char * source)124 char* AppInfoManager::strdup_to_lower(const char* source)
125 {
126     char* dest = snort_strdup(source);
127     char* lcd = dest;
128 
129     while (*lcd)
130     {
131         *lcd = tolower(*lcd);
132         lcd++;
133     }
134     return dest;
135 }
136 
configured()137 bool AppInfoManager::configured()
138 {
139     return !app_info_table.empty();
140 }
141 
get_app_info_entry(AppId appId,const AppInfoTable & lookup_table)142 AppInfoTableEntry* AppInfoManager::get_app_info_entry(AppId appId,
143     const AppInfoTable& lookup_table)
144 {
145     AppId tmp;
146     AppInfoTable::const_iterator app;
147     AppInfoTableEntry* entry = nullptr;
148 
149     if ((tmp = get_static_app_info_entry(appId)))
150     {
151         app = lookup_table.find(tmp);
152         if (app != lookup_table.end())
153             entry = app->second;
154     }
155     else
156     {
157         app = custom_app_info_table.find(appId);
158         if (app != custom_app_info_table.end())
159             entry = app->second;
160     }
161     return entry;
162 }
163 
get_app_info_entry(AppId appId)164 AppInfoTableEntry* AppInfoManager::get_app_info_entry(AppId appId)
165 {
166     return get_app_info_entry(appId, app_info_table);
167 }
168 
add_dynamic_app_entry(const char * app_name)169 AppInfoTableEntry* AppInfoManager::add_dynamic_app_entry(const char* app_name)
170 {
171     if (!app_name || strlen(app_name) >= MAX_EVENT_APPNAME_LEN)
172     {
173         ErrorMessage("Appname invalid or too long: %s\n", app_name);
174         return nullptr;
175     }
176 
177     AppInfoTableEntry* entry = find_app_info_by_name(app_name);
178     if (!entry)
179     {
180         entry = new AppInfoTableEntry(next_custom_appid++, snort_strdup(app_name));
181         custom_app_info_table[entry->appId] = entry;
182 
183         if (!add_entry_to_app_info_name_table(entry->app_name_key, entry))
184         {
185             delete entry;
186             return nullptr;
187         }
188     }
189     return entry;
190 }
191 
cleanup_appid_info_table()192 void AppInfoManager::cleanup_appid_info_table()
193 {
194     for (auto& kv: app_info_table)
195         delete(kv.second);
196     app_info_table.erase(app_info_table.begin(), app_info_table.end());
197 
198     for (auto& kv: custom_app_info_table)
199         delete(kv.second);
200 
201     custom_app_info_table.erase(custom_app_info_table.begin(), custom_app_info_table.end());
202     app_info_name_table.erase(app_info_name_table.begin(), app_info_name_table.end());
203 }
204 
dump_app_info_table()205 void AppInfoManager::dump_app_info_table()
206 {
207     LogMessage("Cisco provided detectors:\n");
208     for (auto& kv: app_info_table)
209         LogMessage("%s\t%d\t%s\n", kv.second->app_name, kv.second->appId,
210             (kv.second->flags & APPINFO_FLAG_ACTIVE) ? "active" : "inactive");
211 
212     LogMessage("User provided detectors:\n");
213     for (auto& kv: custom_app_info_table)
214         LogMessage("%s\t%d\t%s\n", kv.second->app_name, kv.second->appId,
215             (kv.second->flags & APPINFO_FLAG_ACTIVE) ? "active" : "inactive");
216 }
217 
get_appid_by_service_id(uint32_t id)218 AppId AppInfoManager::get_appid_by_service_id(uint32_t id)
219 {
220     AppInfoTableEntry* entry = get_app_info_entry(id, app_info_service_table);
221     return entry ? entry->appId : APP_ID_NONE;
222 }
223 
get_appid_by_client_id(uint32_t id)224 AppId AppInfoManager::get_appid_by_client_id(uint32_t id)
225 {
226     AppInfoTableEntry* entry = get_app_info_entry(id, app_info_client_table);
227     return entry ? entry->appId : APP_ID_NONE;
228 }
229 
get_appid_by_payload_id(uint32_t id)230 AppId AppInfoManager::get_appid_by_payload_id(uint32_t id)
231 {
232     AppInfoTableEntry* entry = get_app_info_entry(id, app_info_payload_table);
233     return entry ? entry->appId : APP_ID_NONE;
234 }
235 
get_app_name(int32_t id)236 const char* AppInfoManager::get_app_name(int32_t id)
237 {
238     AppInfoTableEntry* entry = get_app_info_entry(id);
239     return entry ? entry->app_name : nullptr;
240 }
241 
get_app_name_key(int32_t id)242 const char* AppInfoManager::get_app_name_key(int32_t id)
243 {
244     AppInfoTableEntry* entry = get_app_info_entry(id);
245     return entry ? entry->app_name_key : nullptr;
246 }
247 
get_appid_by_name(const char * app_name)248 AppId AppInfoManager::get_appid_by_name(const char* app_name)
249 {
250     AppInfoTableEntry* entry = find_app_info_by_name(app_name);
251     return entry ? entry->appId : APP_ID_NONE;
252 }
253 
set_app_info_active(AppId appId)254 void AppInfoManager::set_app_info_active(AppId appId)
255 {
256     if (appId == APP_ID_NONE)
257         return;
258 
259     AppInfoTableEntry* entry = get_app_info_entry(appId);
260     if (entry)
261         entry->flags |= APPINFO_FLAG_ACTIVE;
262     else
263         ParseWarning(WARN_PLUGINS, "appid: no entry in %s for %d", APP_MAPPING_FILE, appId);
264 }
265 
load_odp_config(OdpContext & odp_ctxt,const char * path)266 void AppInfoManager::load_odp_config(OdpContext& odp_ctxt, const char* path)
267 {
268     char buf[MAX_TABLE_LINE_LEN];
269     unsigned line = 0;
270 
271     FILE* config_file = fopen(path, "r");
272     if (config_file == nullptr)
273         return;
274 
275     while (fgets(buf, sizeof(buf), config_file) != nullptr)
276     {
277         char* context;
278 
279         line++;
280         char* token = strtok_r(buf, CONF_SEPARATORS, &context);
281         if (token == nullptr)
282         {
283             ParseWarning(WARN_CONF, "appid: No 'conf_type' value at line %s:%u\n", path, line);
284             continue;
285         }
286         char* conf_type = token;
287 
288         token = strtok_r(nullptr, CONF_SEPARATORS, &context);
289         if (token == nullptr)
290         {
291             ParseWarning(WARN_CONF, "appid: No 'conf_key' value at line %s:%u\n", path, line);
292             continue;
293         }
294         char* conf_key = token;
295 
296         token = strtok_r(nullptr, CONF_SEPARATORS, &context);
297         if (token == nullptr)
298         {
299             ParseWarning(WARN_CONF, "appid: No 'conf_val' value at line %s:%u\n", path, line);
300             continue;
301         }
302         char* conf_val = token;
303 
304         /* APPID configurations are for anything else - currently we only have ssl_reinspect */
305         if (!(strcasecmp(conf_type, "appid")))
306         {
307             if (!(strcasecmp(conf_key, "max_tp_flow_depth")))
308             {
309                 int max_tp_flow_depth = atoi(conf_val);
310                 if (max_tp_flow_depth < MIN_MAX_TP_FLOW_DEPTH
311                     || max_tp_flow_depth > MAX_MAX_TP_FLOW_DEPTH)
312                 {
313                     ParseWarning(WARN_CONF,
314                         "appid: invalid max_tp_flow_depth %d, must be between %d and %d\n.",
315                         max_tp_flow_depth, MIN_MAX_TP_FLOW_DEPTH, MAX_MAX_TP_FLOW_DEPTH);
316                 }
317                 else
318                 {
319                     odp_ctxt.max_tp_flow_depth = max_tp_flow_depth;
320                 }
321             }
322             else if (!(strcasecmp(conf_key, "host_port_app_cache_lookup_interval")))
323             {
324                 int host_port_app_cache_lookup_interval = atoi(conf_val);
325                 if (host_port_app_cache_lookup_interval <
326                     MIN_HOST_PORT_APP_CACHE_LOOKUP_INTERVAL ||
327                     host_port_app_cache_lookup_interval >
328                     MAX_HOST_PORT_APP_CACHE_LOOKUP_INTERVAL)
329                 {
330                     ParseWarning(WARN_CONF,
331                         "appid: invalid host_port_app_cache_lookup_interval %d, "
332                         "must be between %d and %d\n.",
333                         host_port_app_cache_lookup_interval,
334                         MIN_HOST_PORT_APP_CACHE_LOOKUP_INTERVAL,
335                         MAX_HOST_PORT_APP_CACHE_LOOKUP_INTERVAL);
336                 }
337                 else
338                 {
339                     odp_ctxt.host_port_app_cache_lookup_interval =
340                         host_port_app_cache_lookup_interval;
341                 }
342             }
343             else if (!(strcasecmp(conf_key, "host_port_app_cache_lookup_range")))
344             {
345                 int host_port_app_cache_lookup_range = atoi(conf_val);
346                 if (host_port_app_cache_lookup_range < MIN_HOST_PORT_APP_CACHE_LOOKUP_RANGE
347                     || host_port_app_cache_lookup_range > MAX_HOST_PORT_APP_CACHE_LOOKUP_RANGE)
348                 {
349                      ParseWarning(WARN_CONF,
350                         "appid: invalid host_port_app_cache_lookup_range %d, "
351                         "must be between %d and %d\n.", host_port_app_cache_lookup_range,
352                         MIN_HOST_PORT_APP_CACHE_LOOKUP_RANGE,
353                         MAX_HOST_PORT_APP_CACHE_LOOKUP_RANGE);
354                 }
355                 else
356                 {
357                     odp_ctxt.host_port_app_cache_lookup_range = host_port_app_cache_lookup_range;
358                 }
359             }
360             else if (!(strcasecmp(conf_key, "is_host_port_app_cache_runtime")))
361             {
362                 if (!(strcasecmp(conf_val, "enabled")))
363                 {
364                     odp_ctxt.is_host_port_app_cache_runtime = true;
365                 }
366             }
367             else if (!(strcasecmp(conf_key, "check_host_port_app_cache")))
368             {
369                 if (!(strcasecmp(conf_val, "enabled")))
370                 {
371                     odp_ctxt.check_host_port_app_cache = true;
372                 }
373             }
374             else if (!(strcasecmp(conf_key, "check_host_cache_unknown_ssl")))
375             {
376                 if (!(strcasecmp(conf_val, "enabled")))
377                 {
378                     odp_ctxt.check_host_cache_unknown_ssl = true;
379                 }
380             }
381             else if (!(strcasecmp(conf_key, "allow_port_wildcard_host_cache")))
382             {
383                 if (!(strcasecmp(conf_val, "enabled")))
384                 {
385                     odp_ctxt.allow_port_wildcard_host_cache = true;
386                 }
387             }
388             else if (!(strcasecmp(conf_key, "recheck_for_portservice_appid")))
389             {
390                 if (!(strcasecmp(conf_val, "enabled")))
391                 {
392                     odp_ctxt.recheck_for_portservice_appid = true;
393                 }
394             }
395             else if (!(strcasecmp(conf_key, "bittorrent_aggressiveness")))
396             {
397                 int aggressiveness = atoi(conf_val);
398                 LogMessage("AppId: bittorrent_aggressiveness %d\n", aggressiveness);
399                 if (aggressiveness >= 50)
400                 {
401                     odp_ctxt.host_port_app_cache_lookup_interval = 5;
402                     odp_ctxt.recheck_for_portservice_appid = true;
403                     set_app_info_flags(APP_ID_BITTORRENT, APPINFO_FLAG_DEFER);
404                     set_app_info_flags(APP_ID_BITTORRENT, APPINFO_FLAG_DEFER_PAYLOAD);
405                     odp_ctxt.max_tp_flow_depth = 25;
406                     LogMessage("AppId: host_port_app_cache_lookup_interval %d\n",
407                         odp_ctxt.host_port_app_cache_lookup_interval);
408                     LogMessage("AppId: recheck_for_portservice_appid enabled\n");
409                     LogMessage("AppId: defer_to_thirdparty %d\n", APP_ID_BITTORRENT);
410                     LogMessage("AppId: defer_payload_to_thirdparty %d\n", APP_ID_BITTORRENT);
411                     LogMessage("AppId: max_tp_flow_depth %d\n", odp_ctxt.max_tp_flow_depth);
412                 }
413                 if (aggressiveness >= 80)
414                 {
415                     odp_ctxt.allow_port_wildcard_host_cache = true;
416                     LogMessage("AppId: allow_port_wildcard_host_cache enabled\n");
417                 }
418             }
419             else if (!(strcasecmp(conf_key, "ultrasurf_aggressiveness")))
420             {
421                 int aggressiveness = atoi(conf_val);
422                 LogMessage("AppId: ultrasurf_aggressiveness %d\n", aggressiveness);
423                 if (aggressiveness >= 50)
424                 {
425                     odp_ctxt.check_host_cache_unknown_ssl = true;
426                     set_app_info_flags(APP_ID_ULTRASURF, APPINFO_FLAG_DEFER);
427                     set_app_info_flags(APP_ID_ULTRASURF, APPINFO_FLAG_DEFER_PAYLOAD);
428                     odp_ctxt.max_tp_flow_depth = 25;
429                     LogMessage("AppId: check_host_cache_unknown_ssl enabled\n");
430                     LogMessage("AppId: defer_to_thirdparty %d\n", APP_ID_ULTRASURF);
431                     LogMessage("AppId: defer_payload_to_thirdparty %d\n", APP_ID_ULTRASURF);
432                     LogMessage("AppId: max_tp_flow_depth %d\n", odp_ctxt.max_tp_flow_depth);
433                 }
434                 if (aggressiveness >= 80)
435                 {
436                     odp_ctxt.allow_port_wildcard_host_cache = true;
437                     LogMessage("AppId: allow_port_wildcard_host_cache enabled\n");
438                 }
439             }
440             else if (!(strcasecmp(conf_key, "psiphon_aggressiveness")))
441             {
442                 int aggressiveness = atoi(conf_val);
443                 LogMessage("AppId: psiphon_aggressiveness %d\n", aggressiveness);
444                 if (aggressiveness >= 50)
445                 {
446                     odp_ctxt.check_host_cache_unknown_ssl = true;
447                     set_app_info_flags(APP_ID_PSIPHON, APPINFO_FLAG_DEFER);
448                     set_app_info_flags(APP_ID_PSIPHON, APPINFO_FLAG_DEFER_PAYLOAD);
449                     odp_ctxt.max_tp_flow_depth = 25;
450                     LogMessage("AppId: check_host_cache_unknown_ssl enabled\n");
451                     LogMessage("AppId: defer_to_thirdparty %d\n", APP_ID_PSIPHON);
452                     LogMessage("AppId: defer_payload_to_thirdparty %d\n", APP_ID_PSIPHON);
453                     LogMessage("AppId: max_tp_flow_depth %d\n", odp_ctxt.max_tp_flow_depth);
454                 }
455                 if (aggressiveness >= 80)
456                 {
457                     odp_ctxt.allow_port_wildcard_host_cache = true;
458                     LogMessage("AppId: allow_port_wildcard_host_cache enabled\n");
459                 }
460             }
461             else if (!(strcasecmp(conf_key, "tp_allow_probes")))
462             {
463                 if (!(strcasecmp(conf_val, "enabled")))
464                 {
465                     odp_ctxt.tp_allow_probes = true;
466                 }
467             }
468             else if (!(strcasecmp(conf_key, "tp_client_app")))
469             {
470                 set_app_info_flags(atoi(conf_val), APPINFO_FLAG_TP_CLIENT);
471             }
472             else if (!(strcasecmp(conf_key, "ssl_reinspect")))
473             {
474                 set_app_info_flags(atoi(conf_val), APPINFO_FLAG_SSL_INSPECT);
475             }
476             else if (!(strcasecmp(conf_key, "defer_to_thirdparty")))
477             {
478                 set_app_info_flags(atoi(conf_val), APPINFO_FLAG_DEFER);
479             }
480             else if (!(strcasecmp(conf_key, "defer_payload_to_thirdparty")))
481             {
482                 set_app_info_flags(atoi(conf_val), APPINFO_FLAG_DEFER_PAYLOAD);
483             }
484             else if (!(strcasecmp(conf_key, "chp_userid")))
485             {
486                 if (!(strcasecmp(conf_val, "disabled")))
487                 {
488                     odp_ctxt.chp_userid_disabled = true;
489                     continue;
490                 }
491             }
492             else if (!(strcasecmp(conf_key, "chp_body_collection")))
493             {
494                 if (!(strcasecmp(conf_val, "disabled")))
495                 {
496                     odp_ctxt.chp_body_collection_disabled = true;
497                     continue;
498                 }
499             }
500             else if (!(strcasecmp(conf_key, "ftp_userid")))
501             {
502                 if (!(strcasecmp(conf_val, "disabled")))
503                 {
504                     odp_ctxt.ftp_userid_disabled = true;
505                     continue;
506                 }
507             }
508             else if (!(strcasecmp(conf_key, "max_bytes_before_service_fail")))
509             {
510                 uint64_t max_bytes_before_service_fail = atoi(conf_val);
511                 if (max_bytes_before_service_fail < MIN_MAX_BYTES_BEFORE_SERVICE_FAIL)
512                 {
513                     ParseWarning(WARN_CONF, "appid: invalid max_bytes_before_service_fail "
514                         "%" PRIu64 " must be greater than %u.\n", max_bytes_before_service_fail,
515                         MIN_MAX_BYTES_BEFORE_SERVICE_FAIL);
516                 }
517                 else
518                 {
519                     odp_ctxt.max_bytes_before_service_fail = max_bytes_before_service_fail;
520                 }
521             }
522             else if (!(strcasecmp(conf_key, "max_packet_before_service_fail")))
523             {
524                 uint16_t max_packet_before_service_fail = atoi(conf_val);
525                 if (max_packet_before_service_fail < MIN_MAX_PKTS_BEFORE_SERVICE_FAIL)
526                 {
527                     ParseWarning(WARN_CONF, "appid: invalid max_packet_before_service_fail "
528                         "%" PRIu16 ", must be greater than %u.\n", max_packet_before_service_fail,
529                         MIN_MAX_PKTS_BEFORE_SERVICE_FAIL);
530                 }
531                 else
532                 {
533                     odp_ctxt.max_packet_before_service_fail = max_packet_before_service_fail;
534                 }
535             }
536             else if (!(strcasecmp(conf_key, "max_packet_service_fail_ignore_bytes")))
537             {
538                 uint16_t max_packet_service_fail_ignore_bytes = atoi(conf_val);
539                 if (max_packet_service_fail_ignore_bytes <
540                     MIN_MAX_PKT_BEFORE_SERVICE_FAIL_IGNORE_BYTES)
541                 {
542                     ParseWarning(WARN_CONF, "appid: invalid max_packet_service_fail_ignore_bytes"
543                         "%" PRIu16 ", must be greater than %u.\n",
544                         max_packet_service_fail_ignore_bytes,
545                         MIN_MAX_PKT_BEFORE_SERVICE_FAIL_IGNORE_BYTES);
546                 }
547                 else
548                 {
549                     odp_ctxt.max_packet_service_fail_ignore_bytes =
550                         max_packet_service_fail_ignore_bytes;
551                 }
552             }
553             /* App Priority bit set*/
554             else if (!(strcasecmp(conf_key, "app_priority")))
555             {
556                 int temp_appid;
557                 temp_appid = strtol(conf_val, nullptr, 10);
558                 token = strtok_r(nullptr, CONF_SEPARATORS, &context);
559                 if (token == nullptr)
560                 {
561                     ParseWarning(WARN_CONF, "appid: Could not read app_priority at line %u\n", line);
562                     continue;
563                 }
564                 conf_val = token;
565                 uint8_t temp_val;
566                 temp_val = strtol(conf_val, nullptr, 10);
567                 set_app_info_priority (temp_appid, temp_val);
568             }
569             else if (!(strcasecmp(conf_key, "referred_appId")))
570             {
571                 if (!(strcasecmp(conf_val, "disabled")))
572                 {
573                     odp_ctxt.referred_appId_disabled = true;
574                     continue;
575                 }
576                 else if (!odp_ctxt.referred_appId_disabled)
577                 {
578                     char referred_app_list[4096];
579                     int referred_app_index = safe_snprintf(referred_app_list, 4096, "%d ",
580                         atoi(conf_val));
581                     set_app_info_flags(atoi(conf_val), APPINFO_FLAG_REFERRED);
582 
583                     while ((token = strtok_r(nullptr, CONF_SEPARATORS, &context)) != nullptr)
584                     {
585                         AppId id = atoi(token);
586                         referred_app_index += safe_snprintf(referred_app_list + referred_app_index,
587                             sizeof(referred_app_list) - referred_app_index, "%d ", id);
588                         set_app_info_flags(id, APPINFO_FLAG_REFERRED);
589                     }
590                 }
591             }
592             else if (!(strcasecmp(conf_key, "rtmp_max_packets")))
593             {
594                 odp_ctxt.rtmp_max_packets = atoi(conf_val);
595             }
596             else if (!(strcasecmp(conf_key, "mdns_user_report")))
597             {
598                 odp_ctxt.mdns_user_reporting = atoi(conf_val) ? true : false;
599             }
600             else if (!(strcasecmp(conf_key, "dns_host_report")))
601             {
602                 odp_ctxt.dns_host_reporting = atoi(conf_val) ? true : false;
603             }
604             else if (!(strcasecmp(conf_key, "chp_body_max_bytes")))
605             {
606                 odp_ctxt.chp_body_collection_max = atoi(conf_val);
607             }
608             else if (!(strcasecmp(conf_key, "ignore_thirdparty_appid")))
609             {
610                 set_app_info_flags(atoi(conf_val), APPINFO_FLAG_IGNORE);
611             }
612             else
613                 ParseWarning(WARN_CONF, "appid: unsupported configuration: %s\n", conf_key);
614         }
615     }
616 
617     fclose(config_file);
618 }
619 
dump_appid_configurations(const std::string & file_path) const620 void AppInfoManager::dump_appid_configurations(const std::string& file_path) const
621 {
622     std::ifstream conf_file(file_path);
623     if (!conf_file.is_open())
624         return;
625 
626     LogMessage("AppId: Configuration file %s\n", file_path.c_str());
627     std::string line;
628     while (getline(conf_file, line))
629         LogMessage("%s\n", line.c_str());
630 
631     conf_file.close();
632 }
633 
add_appid_protocol_reference(const char * protocol,SnortConfig * sc)634 SnortProtocolId AppInfoManager::add_appid_protocol_reference(const char* protocol,
635     SnortConfig* sc)
636 {
637     SnortProtocolId snort_protocol_id = sc->proto_ref->add(protocol);
638     return snort_protocol_id;
639 }
640 
init_appid_info_table(const AppIdConfig & config,SnortConfig * sc,OdpContext & odp_ctxt)641 void AppInfoManager::init_appid_info_table(const AppIdConfig& config,
642     SnortConfig* sc, OdpContext& odp_ctxt)
643 {
644     if (!config.app_detector_dir)
645     {
646         return;  // no lua detectors, no rule support, already warned
647     }
648 
649     char filepath[PATH_MAX];
650     snprintf(filepath, sizeof(filepath), "%s/odp/%s", config.app_detector_dir,
651         APP_MAPPING_FILE);
652 
653     FILE* tableFile = fopen(filepath, "r");
654 
655     if (!tableFile)
656     {
657         ParseError("appid: could not open %s", filepath);
658     }
659     else
660     {
661         char buf[MAX_TABLE_LINE_LEN];
662 
663         while (fgets(buf, sizeof(buf), tableFile))
664         {
665             AppId app_id;
666             uint32_t client_id, service_id, payload_id;
667             char* app_name;
668             char* context;
669 
670             const char* token = strtok_r(buf, CONF_SEPARATORS, &context);
671             if (!token)
672             {
673                 ErrorMessage("Could not read id for AppId\n");
674                 continue;
675             }
676             app_id = strtol(token, nullptr, 10);
677 
678             token = strtok_r(nullptr, CONF_SEPARATORS, &context);
679             if (!token)
680             {
681                 ErrorMessage("Could not read app_name. Line %s\n", buf);
682                 continue;
683             }
684             app_name = snort_strdup(token);
685 
686             token = strtok_r(nullptr, CONF_SEPARATORS, &context);
687             if (!token)
688             {
689                 ErrorMessage("Could not read service id for AppId\n");
690                 snort_free(app_name);
691                 continue;
692             }
693             service_id = strtol(token, nullptr, 10);
694 
695             token = strtok_r(nullptr, CONF_SEPARATORS, &context);
696             if (!token)
697             {
698                 ErrorMessage("Could not read client id for AppId\n");
699                 snort_free(app_name);
700                 continue;
701             }
702             client_id = strtol(token, nullptr, 10);
703 
704             token = strtok_r(nullptr, CONF_SEPARATORS, &context);
705             if (!token)
706             {
707                 ErrorMessage("Could not read payload id for AppId\n");
708                 snort_free(app_name);
709                 continue;
710             }
711             payload_id = strtol(token, nullptr, 10);
712 
713             AppInfoTableEntry* entry = new AppInfoTableEntry(app_id, app_name, service_id,
714                 client_id, payload_id);
715 
716             /* snort service key, if it exists */
717             token = strtok_r(nullptr, CONF_SEPARATORS, &context);
718 
719             // FIXIT-RC: Sometimes the token is "~". Should we ignore those?
720             if (token)
721                 entry->snort_protocol_id = add_appid_protocol_reference(token, sc);
722 
723             if ((app_id = get_static_app_info_entry(entry->appId)))
724             {
725                 app_info_table[app_id] = entry;
726                 AppIdPegCounts::add_app_peg_info(entry->app_name_key, app_id);
727             }
728 
729             if ((app_id = get_static_app_info_entry(entry->serviceId)))
730                 app_info_service_table[app_id] = entry;
731             if ((app_id = get_static_app_info_entry(entry->clientId)))
732                 app_info_client_table[app_id] = entry;
733             if ((app_id = get_static_app_info_entry(entry->payloadId)))
734                 app_info_payload_table[app_id] = entry;
735 
736             if (!add_entry_to_app_info_name_table(entry->app_name_key, entry))
737                 delete entry;
738         }
739         fclose(tableFile);
740 
741         snprintf(filepath, sizeof(filepath), "%s/odp/%s", config.app_detector_dir,
742             APP_CONFIG_FILE);
743         load_odp_config(odp_ctxt, filepath);
744         snprintf(filepath, sizeof(filepath), "%s/custom/%s", config.app_detector_dir,
745             USR_CONFIG_FILE);
746         if (access (filepath, F_OK))
747             snprintf(filepath, sizeof(filepath), "%s/../%s", config.app_detector_dir,
748                 USR_CONFIG_FILE);
749         load_odp_config(odp_ctxt, filepath);
750     }
751 }
752 
753